Three Educative Examples on Using Binders -------------------------------------------- Let us study three simple programs to familiarize ourselves with a few key ideas 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. spctx = ProcessState::self()->getContextObject(NULL); 4. sp sm = IServiceManager::asInterface(ctx); 5. 6. Vector 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' call gets hold of a system-wide object that manages the 'context' or infrastructure necessary 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 to export interface references (which are, after all, pointers) across process boundaries. A 'context object' takes up this responsibility and transparently handles marshaling and unmarshaling values and references across processes. In the code above, the line 4 turns the binder object into an IServiceManager pointer. 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 or gives 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 the second 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 String16 can 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 object may be asked to produce a dump of its state. The following code communicates with the binder associated with Android's activity manager service, and produces a 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 sm = defaultServiceManager(); 7. sp 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 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 binder to dump its state. This will produce a detailed report of the running activities in 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 a great educational value since it draws our attention to some of the fundamental aspects of binders. Take a look at the 'testBinders' function: 1. void testBinders() 2. { 3. sp sm = defaultServiceManager(); 4. sp 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 the interface descriptor associated with the activity manager's binder object. In the Binder 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 is still running. The function 'queryLocalInterface' is more interesting: it is intended to retrieve a reference to the requested interface. The reference is 'local', which means that the object that implements the requested interface lives in the same process as the caller. In our example, we pass the descriptor 'android.app.IActivityManager' which was obtained in line 6. Note that we expect 'queryLocalInterface' to retuen a NULL pointer! This is because we intuitively suppose that our code executes in a process 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 from within our process. Here again, commonsense prevails and the local binder pointer will be actually NULL, while the remote binder reference is non-NULL. In other 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 will never encounter a situation where a long chain of remote binders have to be traversed to reach the final binder. This fact intuitively makes sense because creating 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 or notifications 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 the same time, binders are powerful and efficient. Their implementation in Android is 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.