I finally found an article titled "ServiceManager Relationships at the Java and Native tiers"

Relationship between Java Layer ServiceManager and Native Layer ServiceManager.
From Here .Thank you for writing such articles for reference.However, I can't remember what others said. One day, one day, I will rewrite it myself:)

Initialization of Java-tier Binder

Ultimately, Binder has to deal with its drivers. What the Java layer adjusts to the Linux kernel layer must go through the native method. For android system services, there will always be a registration process between native and java, depending on where the registration is implemented.

Registration of JNI Binder

Zygote at startup:

app_main.cpp
int main(int argc, char* const argv[])
{
    ......
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ......
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    }
    ......
}

AppRuntime is a subclass of AndroidRuntime and does not override the start method itself, so look at the start method in the AndroidRuntime class

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ......
    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    ......
}

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

gRegJNI is an array:

static const RegJNIRec gRegJNI[] = {
    ......
    REG_JNI(register_android_os_Binder),
    ......
};

REG_JNI is a macro definition

#ifdef NDEBUG
    #define REG_JNI(name)      { name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };
#else
    #define REG_JNI(name)      { name, #name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
        const char* mName;
    };
#endif

GRegJNI is an array of RegJNIRec, so the definition of gRegJNI becomes this:

static const RegJNIRec gRegJNI[] = {
    ......
    { register_android_os_Binder },
    ......
};

The register_jni_procs function is as follows:

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
            ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
            return -1;
        }
    }
    return 0;
}

It is a circular call to a function in the gRegJNI array, and register_android_os_Binder(env) is called.

register_android_os_Binder is declared extern in AndroidRuntime.cpp:

extern int register_android_os_Binder(JNIEnv* env);

It is implemented in 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;
    ......
    return 0;
}

By name, it associates three Java classes: Binder, BinderInternal, and BinderProxy. One seems to be:

const char* const kBinderPathName = "android/os/Binder";

static int int_register_android_os_Binder(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, kBinderPathName);

    gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
    gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");

    return RegisterMethodsOrDie(
        env, kBinderPathName,
        gBinderMethods, NELEM(gBinderMethods));
}

Associated with the android.os.Binder class, the mExecTransact field and the execTransact method of the class are used to save it to the gBinderOffsets object, which means that native will save a value to the mExecTransact and callback the execTransact method.

const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";

static int int_register_android_os_BinderInternal(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, kBinderInternalPathName);

    gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V");

    return RegisterMethodsOrDie(
        env, kBinderInternalPathName,
        gBinderInternalMethods, NELEM(gBinderInternalMethods));
}

The com.android.internal.os.BinderInternal class associated with java saves its forceBinderGc method.

const char* const kBinderProxyPathName = "android/os/BinderProxy";

static int int_register_android_os_BinderProxy(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, "java/lang/Error");
    gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);

    clazz = FindClassOrDie(env, kBinderProxyPathName);
    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "()V");
    gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
            "(Landroid/os/IBinder$DeathRecipient;)V");

    gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
    gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",
                                                "Ljava/lang/ref/WeakReference;");
    gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J");

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

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

This method is longer and saves more Java stuff. It is worth noting that it also saves the constructor of BinderProxy, which means it may actively new a BinderProxy instance.

So far, native's Binder corresponds to three classes of Java, android.os.Binder, com.android.internal.os.BinderInternal, and android.os.BinderProxy.For ease of reading, summarize their corresponding relationships in the form of class diagrams:

 
Write a picture description here

The process of registering Java-tier services with ServiceManager

We know that the native layer Binder has the words Bn and Bp. Bn should be the abbreviation of Binder Native, meaning local implementation of Binder or S in C/S, and Bp is the abbreviation of Binder Proxy, equivalent to C.

Take PackageManagerService as an example:

    public static PackageManagerService main(Context 
        ......
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        ......
        ServiceManager.addService("package", m);
        return m;
    }
    public static void addService(String name, IBinder service) {
        try {
            getIServiceManager().addService(name, service, false);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

getIServiceManager returns an IServiceManager class, is a singleton, and the last call is

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

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

There are three issues with the above code, one being BinderInternal.getContextObject(), one is ServiceManagerNative.asInterface The other is the role of addService:

BinderInternal.getContextObjectSubstance of ()

ServiceManagerNative.asInterface The parameter in is BinderInternal.getContextObject(), see that it is a native method, and the corresponding native code is just the android_you just sawUtil_Binder.cppFile:

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    // Is a BpBinder
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    if (val == NULL) return NULL;

    if (val->checkSubclass(&gBinderOffsets)) {
        // One of our own!
        jobject object = static_cast<JavaBBinder*>(val.get())->object();
        LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
        return object;
    }

    // For the rest of the function we will hold this lock, to serialize
    // looking/creation/destruction of Java proxies for native Binder proxies.
    AutoMutex _l(mProxyLock);

    // Someone else's...  do we know about it?
    // 1
    jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
    if (object != NULL) {
        jobject res = jniGetReferent(env, object);
        if (res != NULL) {
            ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
            return res;
        }
        LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
        android_atomic_dec(&gNumProxyRefs);
        val->detachObject(&gBinderProxyOffsets);
        env->DeleteGlobalRef(object);
    }

    //2
    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
    if (object != NULL) {
        LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
        // The proxy holds a reference to the native object.
        //3
        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
        val->incStrong((void*)javaObjectForIBinder);

        // 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));
        ......
        // Note that a new object reference has been created.
        //4
        android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    }

    return object;
}
  1. First, determine if the BpBinder of the Native layer associated with the Java layer exists and, if so, delete it.
  2. Next, create a BinderProxy instance for the Java layer.
  3. Save the BpBinder pointer of the Native layer to the mObject field of the java BinderProxy instance so that the BpBinder of the Native layer can be manipulated through the mObject field of the BinderProxy.
  4. Atoms increase the number of gNumProxyRefs and gNumRefsCreated, and when the value of gNumRefsCreated exceeds 200, the forceBinderGc method of BinderInternal is called.

To summarize:

  • Create a BpBinder for the Native layer;
  • Save BpBinder's pointer to BinderProxy's mObject, and BpBinder's communication object is Native's ServiceManager, which sets the stage for communication between Java and Native's ServiceManager.
  • You can talk about BinderProxy as the Bp side of the Java layer.

ServiceManagerNative.asInterface Role

    static public IServiceManager asInterface(IBinder obj)
    {
        if (obj == null) {
            return null;
        }
        //1
        IServiceManager in =
            (IServiceManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        //2
        return new ServiceManagerProxy(obj);
    }
  1. obj is a BinderProxy object instantiated by Native, and BinderProxy's code is Binder.java In the same file as Binder.The return value of queryLocalInterface is null.
  2. Instantiates a ServiceManagerProxy object with an IBinder-type mRemote field that holds the BinderProxy instance.
    public ServiceManagerProxy(IBinder remote) {
        mRemote = remote;
    }

Summary:

  • ServiceManagerNative.asInterface Returns an instance of ServiceManagerProxy that implements the IServiceManager interface.
  • BinderProxy is saved in ServiceManagerProxy through the mRemote field, which is the Bp side of the Java layer.

Role of addService

In the above analysis, when you call ServiceManager.addService, you are actually calling addService in the ServiceManagerProxy class, which is located in the ServiceManagerNative.java file.

    public void addService(String name, IBinder service, boolean allowIsolated)
            throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        data.writeStrongBinder(service);
        data.writeInt(allowIsolated ? 1 : 0);
        //1
        mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
        reply.recycle();
        data.recycle();
    }
  1. mRemote is BinderProxy and it calls transactNative, which is a native method. Then look at this method.
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
    //1
    IBinder* target = (IBinder*)
        env->GetLongField(obj, gBinderProxyOffsets.mObject);
    if (target == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
        return JNI_FALSE;
    }

    //2
    status_t err = target->transact(code, *data, reply, flags);
    ......
    return JNI_FALSE;
}
  1. Remove the pointer to the BpBinder object stored in BinderProxy
  2. Send requests to ServiceManager at the Native layer

Summary:

  • When the Java layer calls ServiceManger.addService, it ultimately requests an additional service from the ServiceManager in the Native layer.
  • There is only one ServiceManager in the Native layer, and the ServiceManager in the java layer is only C-side compared to the ServiceManager in the Native layer, so that the entire Android can get the Binder of the service, whether through Native add service or through java add service.

Where is the Bp on the Java Service side

There is also a question at this point. When calling ServiceManager.addService, the incoming Service should correspond to Native's BnBinder. How does this correspond now?This relationship is about addService.

The second parameter of addService is of type IBinder, which is an interface, and its implementation class is Binder. When the Java upper level can call addService to add services, all instances are Binders. According to java's rules, the corresponding constructor of its parent class must be called whenever it is instantiated:

    public Binder() {
        init();
        ......
    }

init() is a native method, which is implemented in android_util_Binder.cpp:

static void android_os_Binder_init(JNIEnv* env, jobject obj)
{
    JavaBBinderHolder* jbh = new JavaBBinderHolder();
    ...
    env->SetLongField(obj, gBinderOffsets.mObject, (jlong)jbh);
}

new JavaBBinderHolder for a native layer and saves it in Binder's mObject.Note that although the gBinderOffsets.mObject of the native layer is static, the mObject of the java layer Binder is not static, that is, each Binder instance has a Native JavaBBinderHolder object pointer.

The JavaBBinderHolder class is declared as follows:

class JavaBBinderHolder : public RefBase
{
}

Instead of inheriting BBinder or finding a trail as a Bn-end, you need to review the previous process to see what happened when ServiceManagerProxy added the service:

    public void addService(String name, IBinder service, boolean allowIsolated)
            throws RemoteException {
        Parcel data = Parcel.obtain();
        ......
        data.writeStrongBinder(service);
        ......
    }

WteStrongBinder is questionable

What happened to the writeStrongBinder

This function of Parcel is as follows:

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

The implementation of nativeWriteStrongBinder is in 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);
        }
    }
}

An ibinderForJavaObject function was called in android_util_Binder.cpp

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

    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        return jbh != NULL ? jbh->get(env, obj) : NULL;
    }

    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        return (IBinder*)
            env->GetLongField(obj, gBinderProxyOffsets.mObject);
    }

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

The obj passed in is a Binder instance, so the get function of the JavaBBinderHolder you just created is called:

    sp<JavaBBinder> get(JNIEnv* env, jobject obj)
    {
        AutoMutex _l(mLock);
        sp<JavaBBinder> b = mBinder.promote();
        if (b == NULL) {
            b = new JavaBBinder(env, obj);
            mBinder = b;
            ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
                 b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());
        }

        return b;
    }

The JavaBBinder instance returned,

class JavaBBinder : public BBinder
{
}

JavaBBinder inherits BBinder, so this object was added to Native ServiceManager to summarize their class diagrams:

 
Write a picture description here

The Java layer Binder object has a pointer to the Native JavaBBinderHolder object, the JavaBBinderHolder has a JavaBBinder, and the JavaBBinder has a Java layer Binder object.


 

Tags: Mobile Java Android Linux REST

Posted on Wed, 13 May 2020 12:39:49 -0400 by toniknik1982