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
Post a Comment