Android Binder communication principle (5): service registration and acquisition on the Java side

Source code based on: Android R

0. Preface

In explaining the relevant knowledge points, first organize a frame diagram, followed by a frame diagram to analyze the process:

The use of binder on the Java side is roughly divided into:

  • Java client Java service
  • Java client native service

The registration of the service on the Java side uses ServiceManager.addService()

Use ServiceManager.getService() to obtain the service on the Java side

The entry is through ServiceManager.java, the file is located in frameworks/base/core/java/android/os/ServiceManager.java

Before analyzing addService and getService, you need to understand that Binder will eventually call the native side. Then, Java needs to go through JNI, so you need to understand the registration process of JNI before that.

1. Binder JNI Registration

Students who understand the startup process of zygote should know that zygote will first create a virtual machine when it starts, and then register the JNI interface required by the system through startReg().

frameworks/base/core/jni/AndroidRuntime.cpp

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    ...
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    ...
}

The registered interface is defined in:

frameworks/base/core/jni/AndroidRuntime.cpp

static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_com_android_internal_os_RuntimeInit),
        REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
        REG_JNI(register_android_os_SystemClock),
        REG_JNI(register_android_util_EventLog),
        REG_JNI(register_android_util_Log),
        REG_JNI(register_android_util_MemoryIntArray),
        ...
        REG_JNI(register_android_os_Binder),
        ...

The array is very long, so I won’t paste too much here. The binder-related registration functions are:

        REG_JNI(register_android_os_Binder),

Finally, the function register_android_os_Binder() will be called:

frameworks/base/core/jni/android_util_Binder.cpp

int register_android_os_Binder(JNIEnv* env)
{
    if (int_register_android_os_Binder(env) < 0)
        return -1;
    if (int_register_android_os_BinderInternal(env) < 0)
        return -1;
    if (int_register_android_os_BinderProxy(env) < 0)
        return -1;
    ...

I won’t analyze too much for the rest. Interested students can enter to view the source code process in detail (of course, the following encounters will also be analyzed incidentally). What you need to know here is that the Binder on the Java side requires native operations. The JNI interface is all in zygote It is registered at startup.

2. ServiceManager proxy

Whether it is ServiceManager.addService() or ServiceManager.getService() , it needs to communicate with native in the end, and it needs to obtain the proxy of ServiceManager. That is, the function getIServiceManager() :

frameworks/base/core/java/android/os/ServiceManager.java 

    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }

Here are two main points:

  • BidnerInternal.getcontextObject()
  • ServiceManagerNative.asInterface()

2.1 BinderInternal.getContextObject()

frameworks/base/core/java/com/android/internal/os/BinderInternal.java

public static final native IBinder getContextObject();

This needs to call the JNI interface, and this interface registration has been explained in Section 1 , see int_register_android_os_BinderInternal() for details :

frameworks/base/core/jin/android_util_Binder.cpp

static const JNINativeMethod gBinderInternalMethods[] = {
     /* name, signature, funcPtr */
    { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
    ...
    }
};
static int int_register_android_os_BinderInternal(JNIEnv* env)
{
    ...
    return RegisterMethodsOrDie(
env, kBinderInternalPathName,
 gBinderInternalMethods, NELEM(gBinderInternalMethods));
}

Known by registering the gBinderInternalMethods array, the interface android_os_BinderInternal_getContextObject() is finally called:

frameworks/base/core/jin/android_util_Binder.cpp

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}

It is very familiar here, what is obtained is the BpBinder of the servicemanager whose context is 0, and the BpBinder is  converted into a Java BinderProxy object through the function javaObjectForIBinder() and returned.

2.1.1 javaObjectForIBinder()

frameworks/base/core/jin/android_util_Binder.cpp

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    ...
    BinderProxyNativeData* nativeData = new BinderProxyNativeData();
    nativeData->mOrgue = new DeathRecipientList;
    nativeData->mObject = val;

    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
    if (env->ExceptionCheck()) {
        // In the exception case, getInstance still took ownership of nativeData.
        return NULL;
    }
    ...

    return object;
}

Here, geInstance() in BinderProxy is mainly called through CallStaticObjectMethod() , and the global object gBinderProxyOffsets here is specified in int_register_android_os_BinderProxy() :

frameworks/base/core/jin/android_util_Binder.cpp

static int int_register_android_os_BinderProxy(JNIEnv* env)
{
    gErrorOffsets.mError = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Error"));
    gErrorOffsets.mOutOfMemory =
        MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/OutOfMemoryError"));
    gErrorOffsets.mStackOverflow =
        MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/StackOverflowError"));

    jclass clazz = FindClassOrDie(env, kBinderProxyPathName);
    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
            "(JJ)Landroid/os/BinderProxy;");
    gBinderProxyOffsets.mSendDeathNotice =
            GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
                                   "(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
    gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");

    clazz = FindClassOrDie(env, "java/lang/Class");
    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");

    return RegisterMethodsOrDie(
        env, kBinderProxyPathName,
        gBinderProxyMethods, NELEM(gBinderProxyMethods));
}

javaObjectForIBinder() needs to call the getInstance() interface of BinderProxy through the CallStaticObjectMethod() function, let's see:

frameworks/base/core/java/android/os/BinderProxy.java

    private static BinderProxy getInstance(long nativeData, long iBinder) {
        BinderProxy result;
        synchronized (sProxyMap) {
            try {
                result = sProxyMap.get(iBinder);
                if (result != null) {
                    return result;
                }
                result = new BinderProxy(nativeData);
            } catch (Throwable e) {
                ...
            }
            ...
        }
        return result;
    }

So far, it can be seen that what BidnerInternal.getcontextObject() gets is the BinderProxy object whose context is 0.

2.2 ServiceManagerNative.asInterface()

framworks/base/core/java/android/os/ServiceManagerNative.java

    public static IServiceManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }

        // ServiceManager is never local
        return new ServiceManagerProxy(obj);
    }

After the BinderProxy object is obtained, use asInterface to create the ServiceManager's Java-side proxy ServiceManagerProxy:

framworks/base/core/java/android/os/ServiceManagerNative.java

class ServiceManagerProxy implements IServiceManager {
    public ServiceManagerProxy(IBinder remote) {
        mRemote = remote;
        mServiceManager = IServiceManager.Stub.asInterface(remote);
    }
    ...
    public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
            throws RemoteException {
        mServiceManager.addService(name, service, allowIsolated, dumpPriority);
    }
    ...
    private IBinder mRemote;
    private IServiceManager mServiceManager;
}

mRemote is the BinderProxy object, mServiceManager is the entrance of all interfaces, the real proxy of ServiceManager, the analysis will continue below.

3. ServiceManager.addService()

Through Section 2 , we know that the binder communication on the Java side may be the server proxy obtained by the client through ServiceManager.getService()  , or it may be that the server side registers the service in the ServiceManager through ServiceManager.addService() . But no matter which interface, it is operated through the code BinderProxy of ServiceManager on the java side.

This section analyzes addService():

frameworks/base/core/java/android/os/ServiceManager.java 

    public static void addService(String name, IBinder service, boolean allowIsolated,
            int dumpPriority) {
        try {
            getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

getIServiceManager() is the ServiceManagerProxy object returned in Section 2.2 above. As shown in the code in Section 2.2 above, addService() finally calls mServiceManager.addService() , and this mServiceManager is the BinderProxy object of context 0 mentioned above. Bring in the proxy obtained through IServiceManager as a parameter:

 mServiceManager = IServiceManager.Stub.asInterface(remote);

Continue to analyze.

Usually andorid will compile *.aidl files into corresponding java files, and the final java source code is under out/soong/.intermediates/ , see the seventh article in this series of articles for details: CS under Java

The JAVA files compiled by aidl will eventually be packaged into aidl*.srcjar, so how to determine the specific files? It needs to depend on the *.aidl.d file in the frameworks folder or system folder .

If the aidl file is defined under frameworks, then you can go to the corresponding directory under the framework to find the corresponding *.aidl.d file.

This IServiceManager is the compiled class name of IServiceManager.aidl

      @Override public void addService(java.lang.String name, android.os.IBinder service, boolean allowIsolated, int dumpPriority) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeStrongBinder(service);
          ...
          boolean _status = mRemote.transact(Stub.TRANSACTION_addService, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().addService(name, service, allowIsolated, dumpPriority);
            return;
          }
          _reply.readException();
        }
        ...
      }

The proxy code is relatively simple:

  • Write the service to be added to Parcel through the interface writeStrongBinder();
  • Communicate through mRemote.transact() to confirm whether the purpose of addService() is achieved;
  • If the above fails, try addService() via getDefaultImpl();

Usually the second step is taken, and the default impl of the third step needs to be specified through the interface setDefaultImpl() .

According to Section 2.2 , mRemote is the BinderProxy object of ServiceManager's java side.

3.1 writeStrongBinder()

frameworks/base/core/java/android/os/Parcel.java

    public final void writeStrongBinder(IBinder val) {
        nativeWriteStrongBinder(mNativePtr, val);
    }

The JNI interface is located in android_os_Parcel.cpp, and this interface is also loaded when zygote starts. There is no more analysis here, please refer to the member register_android_os_Parcel in the array gRegJNI in AndroidRuntime.cpp for details.

Finally call to JNI android_os_Parcel_writeStrongBinder():

frameworks/base/core/jni/android_os_Parcel.cpp

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

Let's look at the core ibinderForJavaObject():

frameworks/base/core/jni/android_util_Binder.cpp

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;

    // Instance of Binder?
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        return jbh->get(env, obj);
    }

    // Instance of BinderProxy?
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        return getBPNativeData(env, obj)->mObject;
    }

    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    return NULL;
}

The pure service belongs to the Binder type, so it matches with gBinderOffsets.mClass when matching, and this variable is also assigned when registering, see int_register_android_os_Binder() for details .

gBinderOffsets.mObject obtains the mObject object in Binder.java (see int_register_android_os_Binder function for details), that is, the BBinderHolder pointer obtained from native when Binder is constructed:

frameworks/base/core/java/android/os/Binder.java

    public Binder(@Nullable String descriptor)  {
        mObject = getNativeBBinderHolder();

        ...
    }

----> 

frameworks/base/core/jni/android_util_Binder.cpp

static jlong android_os_Binder_getNativeBBinderHolder(JNIEnv* env, jobject clazz)
{
    JavaBBinderHolder* jbh = new JavaBBinderHolder();
    return (jlong) jbh;
}

ibinderForJavaObject() In addition to the JavaBBinderHolder obtained above , it finally calls the get() interface through the holder to obtain the BBinder of the servcie:

frameworks/base/core/jni/android_util_Binder.cpp

    sp<JavaBBinder> get(JNIEnv* env, jobject obj)
    {
        AutoMutex _l(mLock);
        sp<JavaBBinder> b = mBinder.promote();
        if (b == NULL) {
            b = new JavaBBinder(env, obj);
            ...
        }

        return b;
    }

Take a look at JavaBBinder:

frameworks/base/core/jni/android_util_Binder.cpp

class JavaBBinder : public BBinder
{
    ...
}

Therefore, writeStrongBinder() is ultimately the BBinder in native to write parcel into transact().

3.2 BinderProxy.transact()

See Section 5 for details

3.3 JavaBBinder.onTransact()

When the Client communicates through transact(), BBinder will eventually trigger onTransact(). For java service, the final trigger is JavaBBinder.onTransact() :

frameworks/base/core/jni/android_util_Binder.cpp

    status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override
    {
        ...
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);

gBinderOffsets.mExecTransact is also assigned by int_register_android_os_Binder(), which calls execTransact() in Binder.java, but I will not elaborate here, and finally call stub.onTransact.

4. ServiceManager.getService()

Same as addService, getService is finally called into IServiceManager:

      @Override public android.os.IBinder checkService(java.lang.String name) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        android.os.IBinder _result;
        try {
          ...
          boolean _status = mRemote.transact(Stub.TRANSACTION_checkService, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().checkService(name);
          }
          _reply.readException();
          _result = _reply.readStrongBinder();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }

        return _result;
      }

In the same way as addService(), the binder information transmitted from the native side will be obtained through _reply.readStrongBinder() .

4.1 readStrongBinder()

Eventually it will be called to JNI android_os_Parcel.cpp

frameworks/base/core/jni/android_os_Parcel.cpp

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}

Section 2.1 has been analyzed, and finally the BinderProxy object of the service on the java side is obtained through javaObjectForIBinder().

5. BinderProxy.transact()

frameworks/base/core/java/android/os/BinderProxy.java

    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");

        ....
        try {
            return transactNative(code, data, reply, flags);
        } finally {
            ...
        }
    }

In fact, the core here is to call transactNative:

frameworks/base/core/jni/android_util_Binder.cpp

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
    ...

    status_t err = target->transact(code, *data, reply, flags);
    ...
}

The next step is BpBinder->transact, see the third article in this series of articles for details : service registration

6. Summary 

Finally, let's summarize according to the framework diagram:

  • The Java client obtains the proxy of the service through getService(), and getService() finally obtains the BinderProxy object through readStrongBinder()  ;
  • The Java client communicates with the service through transact, and calls BpBinder through the native interface transactNative() ;
  • The Java service is registered through addService(), and addService() will call the native interface through writeStrongBinder() to register to JNI;
  • When Java service is registered, JavaBBinderHolder will be created in JNI, which encapsulates JavaBBinder communicating with native , and JavaBBinder inherits from BBinder;
  • The Java client call will eventually call onTransact() for the java service, and call back to the java side stub.onTransact();

Guess you like

Origin blog.csdn.net/jingerppp/article/details/131420251