Binder system runs in native environment, not in JAVA VM. So for JAVA VM, it needs some JNI native implementation which lies in libs/android_runtime/android_util_Binder.cpp.
Get IServiceManager
ServiceManager.java manages all JAVA layer services. Any call in ServiceManager.java will first get a JAVA IServiceManager interface.
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
BinderInternal.getContextObject is a JNI native function named android_os_BinderInternal_getContextObject. It calls into C++ layer binder system to get an IBinder(Actually it points a C++ layer BpServiceManager object), then call javaObjectForIBinder to create a corresponding JAVA class to represent the C++ IBinder object.
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
javaObjectForIBinder will determine whether the object is the service provider or a service user. In getIServiceManager scenario, the object is a service user. So it will new a JAVA BinderProxy object to represent the C++ IBinder object.
jobject javaObjectForIBinder(JNIEnv* env, const sp& val)
{
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
if (object != NULL) {
LOGV("objectForBinder %p: created new %p!/n", val.get(), object);
// The proxy holds a reference to the native object.
env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
val->incStrong(object);
// The native object needs to hold a weak reference back to the
// proxy, so we can retrieve the same proxy if it is still active.
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
}
return object;
}
Okay, now we have a JAVA BinderProxy object. Then ServiceManagerNative.asInterface will new a ServiceManagerProxy on the BinderProxy object.
static public IServiceManager asInterface(IBinder obj)
{
return new ServiceManagerProxy(obj);
}
So getIServiceManager call finally returns a ServiceManagerProxy instance.
Service create
When any JAVA service object(derived from Binder) is created, it will call init native function which points to android_os_Binder_init. Here it will new an JavaBBinderHolder, which will be used later.
static void android_os_Binder_init(JNIEnv* env, jobject clazz)
{
JavaBBinderHolder* jbh = new JavaBBinderHolder(env, clazz);
if (jbh == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
return;
}
LOGV("Java Binder %p: acquiring first ref on holder %p", clazz, jbh);
jbh->incStrong(clazz);
env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh);
}
Add service RPC call
Any JAVA layer service should call IServiceManager.addService API to register itself. IServiceManager.addService equals to ServiceManagerProxy.addService.
public void addService(String name, IBinder service)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
data.writeStrongBinder(service);
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
reply.recycle();
data.recycle();
}
During in ServiceManagerProxy.addService, the JAVA service object will be written to Parcel, which finally calls a native function android_os_Parcel_writeStrongBinder.
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jobject clazz, jobject object)
{
Parcel* parcel = parcelForJavaObject(env, clazz);
if (parcel != NULL) {
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
if (err != NO_ERROR) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
}
}
}
android_os_Parcel_writeStrongBinder call ibinderForJavaObject to generate an C++ layer IBinder object corresponding to the JavaBBinderHolder object.
sp ibinderForJavaObject(JNIEnv* env, jobject obj)
{
if (obj == NULL) return NULL;
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetIntField(obj, gBinderOffsets.mObject);
return jbh != NULL ? jbh->get(env) : NULL;
}
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
return (IBinder*)
env->GetIntField(obj, gBinderProxyOffsets.mObject);
}
LOGW("ibinderForJavaObject: %p is not a Binder object", obj);
return NULL;
}
JavaBBinderHolder will finally new a JavaBBinder instance.
sp get(JNIEnv* env)
{
AutoMutex _l(mLock);
sp b = mBinder.promote();
if (b == NULL) {
b = new JavaBBinder(env, mObject);
mBinder = b;
LOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d/n",
b.get(), b->getWeakRefs(), mObject, b->getWeakRefs()->getWeakCount());
}
return b;
}
The JavaBBinder object is derived from BBinder. After ibinderForJavaObject, Android has successfully created a C++ layer BBinder object from JAVA layer service class.
ServiceManagerProxy then calls BinderProxy JNI native function android_os_BinderProxy_transact to dispatch the RPC call from JAVA to C++. In this function a C++ IBinder object(Mentioned before, actually it’s a BpServiceManager object) corresponding to the JAVA BinderProxy object is gotten, which is originally saved in javaObjectForIBinder.
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj,
jobject replyObj, jint flags)
{
IBinder* target = (IBinder*)
env->GetIntField(obj, gBinderProxyOffsets.mObject);
if (target == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
return JNI_FALSE;
}
status_t err = target->transact(code, *data, reply, flags);
if (err == NO_ERROR) {
return JNI_TRUE;
} else if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
}
signalExceptionForError(env, obj, err);
return JNI_FALSE;
}
Okay, until now. Android have successfully send RPC call from JAVA layer to C++ layer. The RPC call will be handled by service_manager process. The C++ layer’s handling is another story, which won’t be included in this topic.You can refer to my C++ layer Binder system introduction.
Get ISensorService
The only way to get an interface is through IServiceManager.getService. Just like the process of IServiceManager.addService, the RPC call is sent from JAVA ServiceManagerProxy to C++ BpServiceManager. BpServiceManager finally delivers the call to service_manager process. After service_manager process this RPC call, BpServiceManager will continue executing. The returned C++ IBinder object actually is a BpBinder instance.
virtual sp getService(const String16& name) const
{
unsigned n;
for (n = 0; n < 5; n++){
sp svc = checkService(name);
if (svc != NULL) return svc;
LOGI("Waiting for sevice %s.../n", String8(name).string());
sleep(1);
}
return NULL;
}
virtual sp checkService( const String16& name) const
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
return reply.readStrongBinder();
}
Then ServiceManagerProxy will get executed after transact, it calls a JNI native function android_os_Parcel_readStrongBinder. android_os_Parcel_readStrongBinder will call javaObjectForIBinder to generate a JAVA object for the returned C++ IBinder object.
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jobject clazz)
{
Parcel* parcel = parcelForJavaObject(env, clazz);
if (parcel != NULL) {
return javaObjectForIBinder(env, parcel->readStrongBinder());
}
return NULL;
}
javaObjectForIBinder will determine whether the object is the service provider or a service user. In this scenario, the object is a service user. So it will new a JAVA BinderProxy object to represent the C++ IBinder object just like the scenario to get IServiceManager. Okay, now we have a JAVA BinderProxy object. Then ISensorService.Stub.asInterface will new a ISensorService.Stub.Proxy on the BinderProxy object.
/**
* Cast an IBinder object into an ISensorService interface,
* generating a proxy if needed.
*/
public static android.hardware.ISensorService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.hardware.ISensorService in = (android.hardware.ISensorService)obj.queryLocalInterface(DESCRIPTOR);
if ((in!=null)) {
return in;
}
return new android.hardware.ISensorService.Stub.Proxy(obj);
}
So we finally get an ISensorService.Stub.Proxy instance.
ISensorService.reportAccuracy RPC Call
It equals to call ISensorService.Stub.Proxy.reportAccuracy. The corresponding code is generated by aidl. It calls BinderProxy JNI native function android_os_BinderProxy_transact to dispatch the RPC call from JAVA to C++. Okay, until now. Android have successfully send RPC call from JAVA layer to C++ layer.
Handle ISensorService.reportAccuracy RPC Call
The RPC call will be first handled by C++ JavaBBinder object (This step is introduced in my Binder system introduction.), which is generated during IServiceManager.addService call.
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
{
JNIEnv* env = javavm_to_jnienv(mVM);
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, (int32_t)&data, (int32_t)reply, flags);
jthrowable excep = env->ExceptionOccurred();
if (excep) {
report_exception(env, excep,
"*** Uncaught remote exception! "
"(Exceptions are not yet supported across processes.)");
res = JNI_FALSE;
/* clean up JNI local ref -- we don't return to Java */
env->DeleteLocalRef(excep);
}
JavaBBinder will call JAVA layer’s function through JNI. The function is Binder.executeTransact.
private boolean execTransact(int code, int dataObj, int replyObj,
int flags) {
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
// theoretically, we should call transact, which will call onTransact,
// but all that does is rewind it, and we just got these from an IPC,
// so we'll just call it directly.
boolean res;
try {
res = onTransact(code, data, reply, flags);
} catch (RemoteException e) {
reply.writeException(e);
res = true;
} catch (RuntimeException e) {
reply.writeException(e);
res = true;
}
reply.recycle();
data.recycle();
return res;
}
Finally Binder.executeTransact will call ISensorService.onTransact. ISensorService.onTransact will call SensorService.reportAccuracy to do the real work。