Android Binder Tutorial [Part Five]: Registering a Java Service.

Introduction.

In this tutorial we will walk through WifiService to analyze how to register a Service in the Java framework. We will stick to the WiFiService [System Service] as it will give us all the required insights on android services.

Ofcourse any application can add a service and it behaves roughly same as a system service with some exceptions which is beyond the scope of this tutorial and will be covered sometime later.

Before analyzing the registration of WifiService let us first look at the WifiService related classes.





We know that SystemServer.java starts all the java services used by the system and hence the first pit stop in our journey.



The servicemanager gets the classname to start and calls startService() for that class.




Remember the following statemements in systemServiceManager.java

1.Constructor<T> constructor = serviceClass.getConstructor(Context.class);
            service = constructor.newInstance(mContext);
2. // Register it.
        mServices.add(service);

        // Start it.
        try {
            service.onStart();
        } catch (RuntimeException ex) {
            throw new RuntimeException("Failed to start service " + name
                    + ": onStart threw an exception", ex);
        }

Coming back to WifiService().

From systemserver.java we see.
    private static final String WIFI_SERVICE_CLASS =
            "com.android.server.wifi.WifiService";

Let us see the WifiService() class and its members.






In the WifiService() onStart() function we call publishBinderService() calls ServiceService addService() method, the first parameter Context.WIFI_SERVICE is "wifi" string; the second parameter is WifiService object, WifiService is inherited in Binder. Let us look at the addService() method.





First we call getIServiceManager to get a ServiceManagerProxy object, as follows.


Let's take a look at the class structure of ServiceManager related to the Java layer.



Before analyzing BinderInternal.getContextObject(), Let us first analyze the android_util_Binder.cpp file, which is the bridge between the Java layer binder and the native layer binder. 
The android_util_Binder.cpp initialization function is register_android_os_Binder() which is loaded and executed during Android Runtime initialization. 
The register_android_os_Binder() function is divided into three steps.
1. The first step is to map the Binder class of the Java layer and register the native function.
2. The second step is to map the BinderInternal class of the Java layer and register the native function.
3. The third step is to map The Java layer's BinderProxy class, and register the native function.



First let us look at the int_register_android_os_Binder() function.



First we load the Binder.class through JNI's FindClassOrDie(). We then save the reference of Binder.class and Binder's execTransact function ID in gBinderOffsets. Also save the ID of the mObject variable in Binder through mObject. This variable is used to store the JavaBBinderHolder address in the future. 
Finally, registerNativeMethods() is called to establish a mapping relationship between functions of the Java layer and functions of the Native layer.
 Int_register_android_os_BinderInternal and int_register_android_os_BinderProxy are similar to the implementation of int_register_android_os_Binder.
Through the above three functions, we can see the following data relationships in the JNI layer.

We see some of the structures [Globally used] here.



We can understand more on these by their use as below.


Structure
variable name
Explanation
gBinderOffsets
mClass
Binder.class's ref

mExecTransact
Binder.class execTransact function ID

mObject
Binder.class mObject member ID, actually save the JavaBBinderHolder address
gBinderInternalOffsets
mClass
BinderInternal.class

mForceGc
BinderInternal.class's forceBinderGc function ID
gBinderProxyOffsets
mClass
Ref of BinderProxy.class

mConstructor
init function ID of BinderProxy.class

mSendDeathNotice
sendDeathNotice function ID of BinderProxy.class

mObject
mObject member ID of BinderProxy.class and actually save the BpBinder address

mSelf
mSelf member ID of BinderProxy.class for registering binding BpBinder and BinderProxy

mOrgue
mOrgue member ID of BinderProxy.class and actually save the DeathRecipientList address


Additionally the register_android_os_Binder() function saves some functions and variable addresses of the Log, ParcelFileDescriptor, and StrictMode utility classes. 
Getting back to getIServiceManager () method, BinderInternal.getContextObject () this function, which is called by JNI, implemented in android_util_Binder.cpp.


We have already analyzed ProcessState::self()->getContextObject(NULL) which returns BpBinder(0), in detailed earlier and we skip here for the sake of repetition.

Let us have a look at javaObjectForIBinder() method.



In binder.cpp, checkSubclass() returns false by default. In BpBinder.
It maintains a KeyedVector to save the mapping between the current BpBinder and BinderProxy. If there is a binding between BinderProxy and BpBinder in front, it can be found directly through the findObject. BinderProxy, which can get BpBinder. 
This assumes that in the first call it will create a BinderProxy object, and then set the BinderByMox object's mObject BpBinder (0) memory address. get BinderProxy object's mSelf this WeakReference object, and call BpBinder's attachObject bind BinderProxy and BpBinder Set it and set mOrgue to the address of the DeathRecipientList object. 
Then returns the BinderProxy object to the getIServiceManager() method. ServiceManagerNative asInterface method is as follows.


Since obj above is a BinderProxy object, its queryLocalInterface() returns NULL and hence a ServiceManagerProxy() object will be created later. 


Calling getIServiceManager().addService method is actually calling addService() of ServiceManagerProxy. Let's look at the addService method of ServiceManagerProxy.
  


Here first obtain two Parcel objects, Let us briefly look at Parcel's observe() and recycle() methods. 
Parcel in the Java layer maintains a Parcel pool. When the user needs to apply Parcel, call Parcel.obtain, when the user finishes out it calls Parcel.recycle method to give it back to the pool for later use.




POOL_SIZE is 6, This means that up to 6 Parcel objects can be requested at a time in any process. For the first time, all sOwnedPool is NULL, so Parcel(0) is created to assign the Parcel object of the native layer.


Since nativePtr is equal to 0, nativeCreate() is called to create a Parcel object for the native layer. The code is in android_os_Parcel.cpp.





Returning to ServiceManagerProxy.addService() method and Native layer registration Service. We write Strict mode and "android.os.IServiceManager" and then write the name of the service to be registered "wifi", then write the service itself,
Here we mainly analyze writeInterfaceToken() and writeStrongBinder() two methods.











We have already seen how the flatten binder sends command and receives the reply using flat_binder_object from the binder driver in the previous tutorials and will skip this here.

Returning back ahead of the Binder's constructor we will call JNI's android_os_Binder_init() method. 



This constructs a JavaBBinderHolder object and stores its address on the mObject of the Binder class. Let us look at the ibinderForJavaObject method. 


Because WifiService inherits from Binder we first extract the JavaBBinderHolder object from the mObject of the WifiSerivce object, and then call its get method.

The first time you enter the get method, mBinder has no assignment, so its promote method returns NULL hence we we create a JavaBBinder object, assign it to mBinder, and return. 
The JavaBBinder here is inherited from the BBinder, so it is a Bn [native] side and implements the onTransact() method. 
We will analyze the call to onTransact() method here in the next chapter. 
It is only necessary to remember that the mObject variable in Binder.java holds the JavaBBinderHolder object, a JavaBBinder object is stored in mBinder in the JavaBBinderHolder object, and the real WifiService object is stored in JavaBBinder. 
Back to the android_os_Parcel_writeStrongBinder function, then call the writeStrongBinder method of Native Parcel to write the above JavaBBinder. 
Return to ServiceManagerProxy.addService() method and then call mRemote.transact method(), where mRemote is BinderProxy object Let us look at BinderProxy transact, its implementation is in android_util_Binder.cpp.



We first extract the data and reply two Parcel objects, and then extract BpBinder (0) from BinderProxy's mObject, and then call its transact method. We have already seen this in the  Native layer registration Service and will skip it here.


Comments

Popular posts from this blog

Android Audio Tutorial [Part Three] : AudioFlinger Introduction and Initialization

Android External Storage Support: Volume Daemon (vold) Architecture

Android Audio Tutorial [Part One] : Introduction