Three Educative Examples on Using Binders

来源:互联网 发布:晨曦2013软件视频教程 编辑:程序博客网 时间:2024/05/16 04:37

 

 

Three Educative Examples on Using Binders--------------------------------------------Let us study three simple programs to familiarize ourselves with a few keyideas about Binders. These examples are intended to present a non-superficial view of the binder framework. Therefore, the emphasis is on introducing a few important ideas, appealing to the common sense and intuition of the reader, without being too precise and pedantic in our explanation.Our first example lists names of services that use binders. Only those services that are executing are listed. Each service publishes one or more interfaces. However, in the following code, we are interested in listing only the names of the services, not in their interfaces or functionality. 1.  void listServices()2.  {3.      sp<IBinder> ctx = ProcessState::self()->getContextObject(NULL);4.      sp<IServiceManager> sm = IServiceManager::asInterface(ctx);5.6.      Vector<String16>  services = sm->listServices();7.      for (size_t i = 0; i < services.size(); ++i)8.          aout << services[i] << endl;9.  }This code (along with 'main' function) can be compiled as an executable. (Useful tip: You can use the 'readelf' command to inspect the dynamic libraires and symbols used in this program.The first thing that attracts attention is our use of a type named 'sp', that stands for 'strong pointer'. The binder objects are reference counted, and therefore, are 'strongly' tracked. When the function 'listServices' completes execution, two strong pointers, 'ctx' and 'sm' in the code above, automatically adjust the reference counts on objects they hold. This, in turn, ensures that objects do not leak memory across address spaces.    The line 3, in the code above, shows that our process is already enabled with a per-process portion of the binder framework. The 'getContextObject' callgets hold of a system-wide object that manages the 'context' or infrastructurenecessary for publishing interfaces implemented by different components. Notice that the object is parceled as a binder!It is important to observe that components that publish interfaces usually live in different processes. Therefore, it is essential to have a  mechanism toexport interface references (which are, after all, pointers) across process boundaries. A 'context object' takes up this responsibility and transparentlyhandles marshaling and unmarshaling values and references across processes. In the code above, the line 4 turns the binder object into an IServiceManagerpointer. In later portions of this primer, we will take a closer look at the 'asInterface' function; it is enough to know, at the moment, that this function either provides a raw pointer to the interface counterpart of the binder orgives out a pointer to a proxy object. In the first case, the object implementing the requested interface lives in the caller's process, and in thesecond case, proxy takes care of marshaling and unmarshaling remote calls.IServiceManager has a function named 'listServices', that returns a bunch of names of services that publish one or more interfaces. In lines 7 and 8, our code simply prints these names.The definitions of IBinder, ProcessState, IServiceManager, Vector, and String16can be found in various files under 'frameworks/base/libs/utils' folder. The dynamic library that hosts this code is 'libutils.so'. The Second Example: Get a State Dump of a Service---------------------------------------------------The next example uses the debugging support built into binders. A binder objectmay be asked to produce a dump of its state. The following code communicateswith the binder associated with Android's activity manager service, and producesa really detailed report in a stream.The code shown below looks slightly different from the first example. In line 6, we use the helper function, 'defaultServiceManager', to get to IServiceManager. This helper function is defined as a part of the binder API (which is packaged in 'libutils.so'). Refer back to lines 3 and 4 in 'listServices' example for comparison. Keep in mind that we have elided error handling code in our examples for brevity and simplicity.1.  void dumpActivityState()2.  {3.     const char *ACTIVITY_MANAGER_SERVICE = "activity";4.      const char *DUMP_FILE_PATH = "/data/app/dump.txt";5.6.      sp<IServiceManager> sm = defaultServiceManager();7.      sp<IBinder> amBinder = sm->getService(String16(8.                                                ACTIVITY_MANAGER_SERVICE));9.      int fd = ::open(DUMP_FILE_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0777);10.     if (fd > -1) {11.        Vector<String16>  args;12.        amBinder->dump(fd, args);13.        ::close(fd);14.    }15. }In line 7 we attempt to reach the binder interface of the activity manager service. The 'IServiceManager::getService' function searches for a registered service with the given name and retrieves its IBinder interface.In line 9 we create a file for writing, and in line 11 we ask the binderto dump its state. This will produce a detailed report of the running activitiesin the system, exactly same as the 'dumpsys' command might produce! The Third Example: Exercising a Binder Object--------------------------------------------------------------------Our third example uses some of the basic member functions of IBinder. It has agreat educational value since it draws our attention to some of the fundamentalaspects of binders. Take a look at the 'testBinders' function: 1.  void testBinders()2.  {3.      sp<IServiceManager> sm = defaultServiceManager();4.      sp<IBinder> amBinder = sm->getService(String16("activity"));5.6.      String16 amItfDescr = amBinder->getInterfaceDescriptor();7.      if (amItfDescr == String16("android.app.IActivityManager"))8.      {9.  aout << "got 'android.app.IActivityManager'" << endl;10. }11.12. if (amBinder->isBinderAlive() &&14. amBinder->pingBinder() == NO_ERROR &&            amBinder->queryLocalInterface(amItfDescr) == NULL)15. {16. aout << "The non-local interface binder is alive" << endl;17. }18.19. if ((amBinder->localBinder() == NULL)  && 20.         (amBinder->remoteBinder() != NULL)) 21.     {22. aout << "we really have a proxy for the remote interface!" << endl;23. }24.25.     if ((am->remoteBinder()->remoteBinder() != NULL) &&26.    (am->remoteBinder() == am->remoteBinder()->remoteBinder()) &&27.     am->remoteBinder()->remoteBinder() ==28.                am->remoteBinder()->remoteBinder()->remoteBinder())29.    {30.aout << "same remote binder" << endl;31.    }32. }By now, the lines 3 and 4 are quite familiar to us! On line 6, we retrieve theinterface descriptor associated with the activity manager's binder object. In theBinder framework, each interface should be uniquely identifiable, and usually a human readable name is used as a descriptor.Since we know, as of Android 1.5, that activity manager's interface descriptor is 'android.app.IActivityManager' we directly compare it with the name that'getInterfaceDescriptor' returns.  The lines 12 through 17 exercise three additional member functions of binders.Both 'isBinderAlive' and 'pingBinder' are used to check if the binder object isstill running. The function 'queryLocalInterface' is more interesting: it is intended to retrieve a reference to the requested interface. The reference is 'local', whichmeans that the object that implements the requested interface lives in the sameprocess as the caller.   In our example, we pass the descriptor 'android.app.IActivityManager' which wasobtained in line 6. Note that we expect 'queryLocalInterface' to retuen a NULLpointer! This is because we intuitively suppose that our code executes in aprocess other than that of the activity manager. Our hypothesis proves to be correct in this context!The code in lines 19 to 23 use additional tests to confirm our intuitive ideas.We use 'locaBinder' and 'remoteBinder' functions to check if activity manger's binder that we have been using is actually a binder object that operates fromwithin our process. Here again, commonsense prevails and the local binder pointer will be actually NULL, while the remote binder reference is non-NULL. Inother words, this implies that the binder object which we obtain in line 4 is actually a proxy to the remote binder that lives in the activity manager's process!Finally, lines 25 to 31 demonstrate that a program would receive the same remote binder given an arbitrary binder reference. This implies that we willnever encounter a situation where a long chain of remote binders have to be traversed to reach the final binder. This fact intuitively makes sense becausecreating a proxy for another proxy is neither cheap nor practical. For example, If such a scheme were allowed, proxies would be required to propagate events ornotifications such as the death of the actual object that implements an interface. With a single, unique remote binder identity, it also becomes easier to check the remote object identity. A Quick Summary:--------The abstractions in Binder framework are not too complex to understand. At thesame time, binders are powerful and efficient. Their implementation in Androidis lightweight, keeping in mind the resource constraints in mobile devices.    It is evident from these three examples that binder objects that live in different processes use IPC to communicate with one another. The Binder runtime transparently handles various details of object-oriented IPC.With this brief introduction to some of the important ideas in Binder framework,we are ready to learn the internals of binders. This is the topic of the next section.