Android Binder Tutorial [Part Six]: Obtaining and using Java service.
Introduction.
We previously introduced the process of
registering Java Service. In this tutorial we look at how the application
obtains and makes calls to the Java Service interface.
AIDL.
In the Java Services,
the AIDL tool is often used. The explanation of AIDL in the google official
document is: " AIDL
(Android Interface Definition Language)" is similar to other IDLs you
might have worked with. It
allows you to define the programming interface that both .The client and
service agree upon in order to communicate with each other using interprocess
communication (IPC).
That
is, AIDL is an interface description language used to define interfaces that
are mutually agreed upon by client and service. It sounds more
circumstantial and directly looks at the general contents of IWifiManger.aidl.
In the conversion
of the AIDL tool, you can generate a Java file from the above aidl file. Its
contents are as follows.
public interface IWifiManager extends
android.os.IInterface {
public
static abstract class Stub extends android.os.Binder implements
android.net.wifi.IWifiManager {
private
static final java.lang.String DESCRIPTOR =
"android.net.wifi.IWifiManager";
public
Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public
static android.net.wifi.IWifiManager asInterface(
android.os.IBinder obj) {
if
((obj == null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface) obj
.queryLocalInterface(DESCRIPTOR);
if
(((iin != null) && (iin instanceof android.net.wifi.IWifiManager)))
{
return ((android.net.wifi.IWifiManager) iin);
}
return new android.net.wifi.IWifiManager.Stub.Proxy(obj);
}
public
android.os.IBinder asBinder() {
return this;
}
@Override
public
boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
}
}
return super.onTransact(code, data, reply, flags);
}
private
static class Proxy implements android.net.wifi.IWifiManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
}
#########################################################
IWifiManager.java mainly contains three classes viz:
IWifiManager class,
IWifiManager.Stub class,
IWifiManager.Stub.Proxy class.
WifiServiceImpl is inherited from IWifiManager.Stub class.
We have seen the relationship between it before, the
following look at the general method to obtain the WifiService.
So, If we were to re-understand the above. From SystemServiceRegistry.java.
This calls the getService() method of ServiceManager.
WIFI_SERVICE is the "wifi" string.
sCache is used to cache all used Java Service. It has
ActivityManagerService copied in the fork process. We will look at sCache again
when we analyze ActivityManagerService.
Here we assume that sCache does not have the "wifi"
service we want. Then it calls the getService method of
getIServiceManager().
In the previous analysis of getIServiceManager() we have seen
it actually returns ServiceManagerNative (BinderNative), so it calls the ServiceManagerNative
getService method here.
We first get two Parcel objects, then write data to Strict
mode and "android.os.IServiceManager", and then write the service
name you want to get, that is "wifi".
Then call mRemote.transact() method, the implementation of
this in android_util_Binder.cpp.
We already analyzed in the previous tutorial, here we directly
call BpBinder (0) transact method, the GET_SERVICE_TRANSACTION command sent to
the binder driver.
Let's see how the Java layer reads the desired Servcie from
Parcel.
In the previous tutorial of the Native Service, we said that the binder driver first determines ref->node->proc == target_proc, if that is true (the registered process is the same as the process that obtains the MediaPlayerService). Then it rewrite fp-> Type is BINDER_TYPE_BINDER, and fp->binder is set to binder->getWeakRefs(), and fp->cookie is equal to binder->local itself.
If it is not in the same process (this is the most usual case of IPC), It first calls binder_get_ref_for_node() to allocate a new binder_ref structure for the process that gets MediaPlayerService, where the binder id value may not be the previously registered binder id value.
Let us first look at Parcel's readStrongBinder method of the Native layer.
There are two cases here.
First, the process of registering the WifiService and the
process of acquiring the WifiService are the same (this is often the case, because the registered WifiService is in the
systemserver, and the systemserver
will register many other services. These services will call each other).
At this time the type is BINDER_TYPE_BINDER, and then through
static_cast the pointer stored in the cookie is directly converted to the
previously registered JavaBBinder().
Second, the process of registering WifiSerivce and the
process of obtaining WifiService is not the same; here call
getStrongProxyForHandle to return a BpBinder (handle id).
Back to the android_os_Parcel_readStrongBinder() method above
and we call javaObjectForIBinder() to do a layer encapsulation of the IBinder
object.
The function was analyzed in the previous tutorial.
Here a BinderProxy object is constructed and the current
IBinder (BpBinder) is bound to BinderProxy. Then it returns the BinderProxy
object to the process that obtained the Service. Then calls IWifiManager
service = IWifiManager.Stub.asInterface(b), which is implemented as follows.
public static android.net.wifi.IWifiManager asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return
null;
}
android.os.IInterface iin = (android.os.IInterface) obj
.queryLocalInterface(DESCRIPTOR);
if (((iin != null)
&& (iin instanceof android.net.wifi.IWifiManager))) {
return
((android.net.wifi.IWifiManager) iin);
}
return new
android.net.wifi.IWifiManager.Stub.Proxy(obj);
}
Here obj is a BinderProxy object whose queryLocalInterface
defaults to NULL, so here an IWifiManager.Stub.Proxy is constructed and
returned. Finally, a WifiManager object is constructed for the application
layer through IWifiManager.Stub.Proxy.
Java Service call
Let's take a look at a
simple API (setWifiApEnabled) usage in WifiManager:
The mService here is the IWifiManager.Stub.Proxy object
constructed above, Let us see its setWifiApEnabled method.
public boolean setWifiApEnabled(
android.net.wifi.WifiConfiguration wifiConfig,
boolean
enable) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean
_result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if
((wifiConfig != null)) {
_data.writeInt(1);
wifiConfig.writeToParcel(_data, 0);
} else
{
_data.writeInt(0);
}
_data.writeInt(((enable) ? (1) : (0)));
mRemote.transact(Stub.TRANSACTION_setWifiApEnabled, _data,
_reply, 0);
_reply.readException();
_result
= (0 != _reply.readInt());
} finally
{
_reply.recycle();
_data.recycle();
}
return
_result;
}
The step first is to get two Parcel objects, one for storing
data sent to the WifiService and one for getting the WifiService reply.
Write strict mode and
"android.net.wifi.IWifiManager" to data and write two more
parameters.
Finally call the mRemote.transact method. We know that
mRemote is the previously constructed BinderProxy object, so let us look at its
transact method, which is implemented in android_util_Binder.cpp.
There are two situations to discuss here.
One is in the same process, where the target is JavaBBinder;
if in different processes, the target here is BpBinder.
First let us look at the case of the same process, we call
the JavaTransactor's transact method, because JavaBBinder is inherited from the
BBinder, so directly to the Transact method of the BBinder.
Directly call the onTransact method, of course, the
onTransact method is re-written after the JavaBBinder.
When called in the same process, target is BpBinder, so BpBinder's
transact method is called, and finally send the command to the JavaBBinder (the
WifiService's wrapper) that is registered in the binder driver by ioctl.
It will still call the onTransact method of JavaBBinder. A
cross-process data transfer process.
Here is the main call to Binder.java execTransact to handle
the request.
This directly calls the onTransact method of WIfiService. Its
implementation is in IWifiManager.Stub.
#############
public boolean onTransact(int code, android.os.Parcel
data,
android.os.Parcel reply, int flags)
throws
android.os.RemoteException {
switch (code)
{
case
TRANSACTION_setWifiApEnabled: {
data.enforceInterface(DESCRIPTOR);
android.net.wifi.WifiConfiguration _arg0;
if ((0 !=
data.readInt())) {
_arg0 =
android.net.wifi.WifiConfiguration.CREATOR
.createFromParcel(data);
} else {
_arg0 =
null;
}
boolean
_arg1;
_arg1 = (0 !=
data.readInt());
boolean _result
= this.setWifiApEnabled(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return
true;
}
################
Finally the actual implementation of setWifiApEnabled() is
called.
This concludes our six part tutorial on basic Android Binder
usage.
Comments
Post a Comment