This analysis is based on Android S(12)

preface

When we use the AIDL interface, we get an object that is essentially a stub.Proxy class. With Binder communication, data can be transmitted to Stub objects (inherited from Binder classes) in the Server process. But these are just the tip of the iceberg. There are many other objects hidden below the surface of the water. With them, communication can be established. We ApplicationThread, for example, communication sponsors get is IApplicationThread Stub. The Proxy object, The data ultimately arrives in the ApplicationThread object (ApplicationThread inherits from the iApplicationThread.stub class). The following figure shows nine different objects that need to be created for the entire communication link setup (see my previous article “Binder Agents” for details).

The ordinals in the figure indicate the order in which these objects are created, with the first appearing as a Stub object (also known as a Binder object because its parent is Binder, which can be converted to a Binder object based on the polymorphism principle). Binder objects are constructed along with Native layer JavaBBinderHolder objects, so we have 1 and 2 objects. The subsequent objects 3~9 can only be created after the Binder objects are transferred to the peer process as data. After a communication link is established, a Binder object should not be recycled even if it is not referenced by any other Java object within the process, because it receives communication data from other processes at any time. This relationship can be described as: The Binder objects of the Server process are referenced by the BinderProxy objects of the Client process. Please remember this sentence, it is the core of the idea of this article. Binder objects can be reclaimed only if they are no longer able to receive new communication data after the corresponding Proxy objects in other processes have been reclaimed by GC, as shown in the figure below.

To establish this cross-process reference relationship, we need some complicated procedures and intermediate variables. Once we understand the inner details of this reference relationship, we can see how the life cycle of these objects is managed. In fact, the core answers are two questions:

  1. How do Binder objects ensure that they are not recycled when BinderProxy objects exist?
  2. How are Binder objects reclaimed when all BinderProxy dies?

With these questions in mind, let’s dive into the details of how Android manages the life cycle of these objects.

1. The Server process

1.1 Binder object

The whole process begins with the creation of the Binder objects.

public Binder(@Nullable String descriptor)  {
    mObject = getNativeBBinderHolder();
    NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
    mDescriptor = descriptor;
}
Copy the code

Binder’s construction method does three things:

  1. getNativeBBinderHolderA new one was created in the Native layerJavaBBinderHolderObject and returns its address to the mObject field.
  2. registerNativeAllocationFor managing native memory related to Java objects, please refer to my article”How to synchronize GC to reclaim native memory”.
  3. Record the AIDL interface name in mDescriptor.

The created Binder object is just a plain Java object, and if it is not transmitted to other processes as Binder data (Binder communication can transmit both plain data and instantiated objects of Binder classes), Subsequent JavaBBinder objects, binder_node objects, would not be created, and it would not have the ability to communicate across processes.

The JavaBBinderHolder object created by new is not managed by weak and weak Pointers (smart Pointers). Instead, the address is stored in a Binder object field, so its collection can only be initiated by the Binder object.

According to the NativeAllocationRegistry mechanism, Binder_destroy is called when Binder objects are collected by GC, and the JavaBBinderHolder object is destroyed.

static void Binder_destroy(void* rawJbh)
{
    JavaBBinderHolder* jbh = (JavaBBinderHolder*) rawJbh;
    ALOGV("Java Binder: deleting holder %p", jbh);
    delete jbh;
}
Copy the code

1.2 JavaBBinder object

When we transfer a Binder object as data to a peer process, we first need to load it into a Parcel object, which is like data packaging. The writeStrongBinder method for the Java layer goes to the Native layer, where there are two key steps: the ibinderForJavaObject function and the Parcel ->writeStrongBinder function.

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));   // Object is a Binder object
        if(err ! = NO_ERROR) {signalExceptionForError(env, clazz, err); }}}Copy the code

The ibinderForJavaObject function generates Native layer JavaBBinder objects from Java objects. When we construct JavaBBinder objects, we add Binder objects to the art:: GlobalS_ list using env->NewGlobalRef so that Binder objects are marked as GC Root on each GC and cannot be reclaimed. In this way, we deeply bind the Java layer Binder objects with the Native layer JavaBBinder objects. Binder objects can only be removed from Art :: GlobalS_ when the JavaBBinder objects are destroyed, and can be recycled once they are free again.

JavaBBinder(JNIEnv* env, jobject /* Java Binder */ object)
    : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
{
    ALOGV("Creating JavaBBinder %p\n".this);
    gNumLocalRefsCreated.fetch_add(1, std::memory_order_relaxed);
    gcIfManyNewRefs(env);
}
Copy the code

The ibinderForJavaObject function returns a value of type SP

. Sp = “strong Pointer “; sp = “strong pointer”; sp = “strong pointer”; Sp is a template class, where the template class must inherit from RefBase, as IBinder here does. Sp manages the C++ objects it points to by reference counting, in this case, newly created JavaBBinder objects. Detailed RefBase knowledge is not the focus of this article, so here is a reference article for those without background knowledge.

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{...// Instance of Binder?
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        return jbh->get(env, obj); }... }Copy the code
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
    AutoMutex _l(mLock);
    sp<JavaBBinder> b = mBinder.promote(a);if (b == NULL) {
        b = new JavaBBinder(env, obj);     // When b is assigned, the JavaBBinder object's strong reference count is +1, which is 1. }return b;
}
Copy the code

JavaBBinder is destroyed when the strong reference count of a JavaBBinder object is reduced to zero, which is the basic logic of smart Pointers in Android. Sp object creation increases JavaBBinder’s strong reference count (+1), and sp object destruction decreases JavaBBinder’s strong reference count (-1). Therefore, the strong reference count of JavaBBinder objects in the above get function shows the following pattern:

  1. b = new JavaBBinder(env, obj)Strong references to JavaBBinder objects are counted to a value of +1, so the value is 1.
  2. return bA return value of type SP is created from the local variable b, which is a copy construct, so JavaBBinder’s strong reference count is +1, which gives it a value of 2.
  3. The function returns, the local variable b is destroyed, and JavaBBinder’s strong reference count is -1, so the value is 1.

When ibinderForJavaObject returns, the return value continues as a parameter into the Parcel ->writeStrongBinder function.

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flattenBinder(val);
}
Copy the code
status_t Parcel::flattenBinder(const sp<IBinder>& binder)
{... flat_binder_object obj;if(binder ! =nullptr) {
        BBinder *local = binder->localBinder(a);if(! local) { ... }else{... obj.hdr.type = BINDER_TYPE_BINDER; obj.binder =reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local); }}...status_t status = writeObject(obj, false);
    return finishFlattenBinder(binder);
}
Copy the code

The flattenBinder constructs a flat_binder_object object from the JavaBBinder object and writes it to the Parcel. Binder, the sp object that is the argument, will not exist when the function returns, so the strong reference count of the JavaBBinder object to which it points will continue to decrease by 1. As we know, the JavaBBinder object’s strong referential count is 1. If the flattener does not add a JavaBBinder object’s strong referential count elsewhere on the bank, the strong referential count returns to zero and the JavaBBinder object is destroyed. This is clearly not in line with expectations.

Wait, let’s rethink this from a macro perspective.

When we write a constructed flat_binder_object to a Parcel, it means that the Parcel implicitly refers to a JavaBBinder object, so it needs to make sure that when it uses flat_binder_object, JavaBBinder objects are not destroyed. Based on this logic, a flatten_binder_object call on a mathematical binder that wrote a flat_binder_object to a Parcel must increase JavaBBinder’s strong reference count. Let’s see if the code is designed to follow our logic.

status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{...// Need to write meta-data?
    if(nullMetaData || val.binder ! =0) {
        mObjects[mObjectsSize] = mDataPos;
        acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize); mObjectsSize++; }... }Copy the code
static void acquire_object(const sp<ProcessState>& proc,
    const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
{
    switch (obj.hdr.type) {
        case BINDER_TYPE_BINDER:
            if (obj.binder) {
                LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
                reinterpret_cast<IBinder*>(obj.cookie)->incStrong(who);
            }
            return; . }Copy the code

Sure enough, when a JavaBBinder object is transferred, writeObject calls acquire_object to increase the strong reference count of the JavaBBinder object (via the incStrong function). That way, the JavaBBinder object does not get destroyed when the flattenBinder function returns.

When will the strong reference count added to acquire_object be subtracted? The answer, of course, is when a Parcel object is recycled. In fact, a Parcel object serves only as a middleman in the establishment of a communication link. It only keeps JavaBBinder alive until the peer process creates a BinderProxy object, leaving the life of JavaBBinder in the hands of the peer process.

Let’s look at a typical AIDL gnerated method.

@Override 
public void registerRemoteAnimations(android.os.IBinder token, android.view.RemoteAnimationDefinition definition) 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(token);  // The first step is to write the Binder object to a Parcel. Acquire_object is called to increase JavaBBinder's strong reference count
    if((definition! =null)) {
        _data.writeInt(1);
        definition.writeToParcel(_data, 0);
    }
    else {
        _data.writeInt(0);
    }
        mRemote.transact(Stub.TRANSACTION_registerRemoteAnimations, _data, _reply, 0);  // The second step is to communicate with binder
        _reply.readException();
    }
    finally {
        _reply.recycle();
        _data.recycle();   // The third step is to reclaim the Parcel object, which in turn calls Release_Object to reduce JavaBBinder's strong reference count}}Copy the code

The general process can be divided into three steps:

  1. _data.writeStrongBinderWriting a Binder object to a Parcel internally writes JavaBBinder to a Parcel(native) and increases JavaBBinder’s strong reference count.
  2. mRemote.transactThe Binder object is transferred to the peer process, which creates the corresponding BinderProxy object upon receiving the data.
  3. _data.recycleReclaim Parcel objects while reducing JavaBBinder’s strong reference count.

JavaBBinder’s strong reference count must be added to mremote. transact if communication links are not damaged after _data.recycle, which in effect puts JavaBBinder’s life in the hands of BinderProxy. Next, let’s take a closer look at the drive.

1.3 binder_node object

After entering the Binder driver, the binder_transaction function iterates through all the Binder objects transferred. The next crucial step is binder_translate_binder, which generates binder_node and binder_ref objects in kernel space from flat_binder_object. The BINDER_TYPE_BINDER type is changed to BINDER_TYPE_HANDLE, indicating that while we are transferring a Binder object, the peer end is getting a Proxy object.

case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
	struct flat_binder_object *fp;fp = to_flat_binder_object(hdr); ret = binder_translate_binder(fp, t, thread); . }break;
Copy the code

Binder_translate_binder first creates a new binder_node object through binder_new_node and then calls binder_INC_ref_for_node to create the binder_ref object.

static int binder_translate_binder(struct flat_binder_object *fp, struct binder_transaction *t, struct binder_thread *thread)
{
	struct binder_node *node;
	struct binder_proc *proc = thread->proc;
	struct binder_proc *target_proc = t->to_proc;
	struct binder_ref_data rdata;
	int ret = 0;

	node = binder_get_node(proc, fp->binder);
	if(! node) { node = binder_new_node(proc, fp);if(! node)return-ENOMEM; }... ret = binder_inc_ref_for_node(target_proc, node, fp->hdr.type == BINDER_TYPE_BINDER, &thread->todo, &rdata); . }Copy the code

The binder_inc_ref_for_node does two things. One is to generate the peer process’s binder_ref object, which can find the newly created binder_node object. The second is to call binder_inc_ref_olocked to manage the life cycle of binder_node and binder_ref objects.

static int binder_inc_ref_for_node(struct binder_proc *proc,
			struct binder_node *node,
			bool strong,
			struct list_head *target_list,
			struct binder_ref_data *rdata)
{
	struct binder_ref *ref;
	struct binder_ref *new_ref = NULL;
	int ret = 0;

	binder_proc_lock(proc);
	ref = binder_get_ref_for_node_olocked(proc, node, NULL);
	if(! ref) { binder_proc_unlock(proc); new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
		if(! new_ref)return -ENOMEM;
		binder_proc_lock(proc);
		ref = binder_get_ref_for_node_olocked(proc, node, new_ref);
	}
	ret = binder_inc_ref_olocked(ref, strong, target_list);
}
Copy the code

Binder_inc_ref_olocked increases the value of binder_ref’s strong and calls binder_inc_node to increase the reference value of the corresponding binder_node. The strong value of the binder_ref object is not 0, indicating that it is referenced and cannot be destroyed. This is added because subsequent data of type BINDER_TYPE_HANDLE is written to binder buffer, which therefore references the binder_ref object.

static int binder_inc_ref_olocked(struct binder_ref *ref, int strong,
				  struct list_head *target_list)
{
	int ret;

	if (strong) {
		if (ref->data.strong == 0) {
			ret = binder_inc_node(ref->node, 1.1, target_list);
			if (ret)
				return ret;
		}
		ref->data.strong++;
	} else {
		if (ref->data.weak == 0) {
			ret = binder_inc_node(ref->node, 0.1, target_list);
			if (ret)
				return ret;
		}
		ref->data.weak++;
	}
	return 0;
}
Copy the code

The binder_INC_node_nilocked does two things: increase the value of the binder_node object’s internal_STRONG_refs and add a BINDER_WORK_NODE task to the thread’s todo list. The reference fields in binder_node are a bit complicated, mainly because the names are ambiguous. The main fields used are internal_STRONG_refs and local_STRONG_refs. The former indicates that the binder_node object is referenced by a binder_ref object. The further meaning is that the binder_node object is referenced by another process. The latter indicates that the binder_node object is referenced by the process. It does not necessarily refer to a specific holding relationship, but rather that the binder_node object cannot be released while the process is performing certain tasks.

static int binder_inc_node_nilocked(struct binder_node *node, int strong,
				    int internal,
				    struct list_head *target_list)
{
	struct binder_proc *proc = node->proc;
	if (strong) {
		if (internal) {
			node->internal_strong_refs++;
		} else
			node->local_strong_refs++;
		if(! node->has_strong_ref && target_list) {struct binder_thread *thread =container_of(target_list, struct binder_thread, todo); binder_dequeue_work_ilocked(&node->work); BUG_ON(&thread->todo ! = target_list); binder_enqueue_deferred_thread_work_ilocked(thread, &node->work); }Copy the code

The task added to the Thread Todo list is a deferred task, but either synchronous or asynchronous communication will be completed before Transact ends.

The BINDER_WORK_NODE task does two things:

  1. Node has_WEAK_ref and has_STRONG_ref are set to true, Pending_Weak_ref and Pending_STRONG_ref are set to 1, and local_Weak_refs and local_STRONG_REFs are both self-increasing.
  2. Return BR_INCREFS and BR_ACQUIRE for user space.

So how do you make sense of these two things?

First, the instructions returned to userspace cause userspace to increase the strong and weak reference counts of JavaBBinder objects, ensuring that JavaBBinder will not be destroyed when referenced by other processes. And pending_WEAK_ref/Pending_STRONG_ref/local_WEAK_refs/LOCAL_STRONG_REFs is placed on, are waiting for the completion of user space operation. When userspace completes adding the reference count, it returns to kernel space with BC_INCREFS_DONE and BC_ACQUIRE_DONE directives that reset the previous four values to indicate that userspace has completed the task.

case BINDER_WORK_NODE: {
	struct binder_node *node = container_of(w, struct binder_node, work);
	int strong, weak;
	binder_uintptr_t node_ptr = node->ptr;
	binder_uintptr_t node_cookie = node->cookie;
	int node_debug_id = node->debug_id;
	int has_weak_ref;
	int has_strong_ref;
	void__user *orig_ptr = ptr; strong = node->internal_strong_refs || node->local_strong_refs; weak = ! hlist_empty(&node->refs) || node->local_weak_refs || node->tmp_refs || strong; has_strong_ref = node->has_strong_ref; has_weak_ref = node->has_weak_ref;if(weak && ! has_weak_ref) { node->has_weak_ref =1;
		node->pending_weak_ref = 1;
		node->local_weak_refs++;
	}
	if(strong && ! has_strong_ref) { node->has_strong_ref =1;
		node->pending_strong_ref = 1;
		node->local_strong_refs++;
	}

	if(weak && ! has_weak_ref) ret = binder_put_node_cmd( proc, thread, &ptr, node_ptr, node_cookie, node_debug_id, BR_INCREFS,"BR_INCREFS");
	if(! ret && strong && ! has_strong_ref) ret = binder_put_node_cmd( proc, thread, &ptr, node_ptr, node_cookie, node_debug_id, BR_ACQUIRE,"BR_ACQUIRE");
Copy the code

This ensures that JavaBBinder objects will not be destroyed after _data.recycle ends. At this point, the work of this process is complete.

Let’s sort out the dependencies of each object.

  1. Native layer JavaBBinderHolder objects can only be destroyed when Java layer Binder objects are recycled.
  2. When Native Layer JavaBBinder objects are destroyed, Java layer Binder objects are removed from the global reference table and can be collected by GC.
  3. Only when internal_STRONG_refs and local_STRONG_REFs of binder_node objects in the Kernel layer are reduced to 0, can the strong reference count of JavaBBinder objects in the Native layer return to zero and be destroyed.

So for this process, the destruction relationship of these objects is like a domino, only the first one falls, the next one can fall. Based on the dependencies above, if you want to destroy these objects, the first thing to do is to reduce internal_STRONG_refs and local_STRONG_REFs of the binder_node object to 0 (local_STRONG_REFs is usually 0). Internal_strong_refs actually records how many processes refer to the Binder object.

2. The Client process

The binder_ref object has been created and ref->data.strong has a value of 1, indicating that the binder buffer is referencing it. After communication process when the Client receives the request, will call IPCThreadState: first: executeCommand method. A local Parcel object buffer is created to receive data from the Binder buffer. Once the buffer is destroyed, the binder buffer is also released and the strong value added to the binder_ref object is reduced.

case BR_TRANSACTION:
    {
        ...
        Parcel buffer;
        buffer.ipcSetDataReference(
            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
            tr.data_size,
            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
            tr.offsets_size/sizeof(binder_size_t), freeBuffer); .if (tr.target.ptr) {
            error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer, &reply, tr.flags); }}Copy the code

The above transact function will eventually call the Java layer’s onTransact for processing. For example, the readStrongBinder method is used to parse the Binder object just passed in. The result is a BinderProxy object.

case TRANSACTION_bindService:
{
  data.enforceInterface(descriptor);
  android.app.IApplicationThread _arg0;
  _arg0 = android.app.IApplicationThread.Stub.asInterface(data.readStrongBinder());
Copy the code

ReadStrongBinder is a native method whose internal implementation can be divided into two steps: Parcel ->readStrongBinder() and javaObjectForIBinder.

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;
}
Copy the code

Parcel ->readStrongBinder() is used to create BpBinder objects based on data in a parcel.

303             case BINDER_TYPE_HANDLE: {
304                 sp<IBinder> binder =
305                     ProcessState::self() - >getStrongProxyForHandle(flat->handle);
306                 return finishUnflattenBinder(binder, out);
307             }
308         }
Copy the code

A new BpBinder object is created using the BpBinder::create method in getStrongProxyForHandle. Most of this code is used to prevent leak on BpBinder objects, which is irrelevant to the topic of this article (perhaps a separate article later) and will be skipped. Only the last line is really responsible for object creation. It does two things: first, it creates a new BpBinder object through new; The second is to create an SP object to point to it, which increases the BpBinder object’s strong reference count and calls BpBinder’s onFirstRef function (since it was referenced for the first time).

sp<BpBinder> BpBinder::create(int32_t handle) {
    int32_t trackedUid = - 1;
    if (sCountByUidEnabled) {
        trackedUid = IPCThreadState::self() - >getCallingUid(a); AutoMutex _l(sTrackingLock);uint32_t trackedValue = sTrackingMap[trackedUid];
        if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) {
            if (sBinderProxyThrottleCreate) {
                return nullptr; }}else {
            if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
                ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)".getuid(), trackedUid, trackedValue);
                sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
                if (sLimitCallback) sLimitCallback(trackedUid);
                if (sBinderProxyThrottleCreate) {
                    ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
                          " count drops below %d",
                          trackedUid, getuid(), sBinderProxyCountLowWatermark);
                    return nullptr;
                }
            }
        }
        sTrackingMap[trackedUid]++;
    }
    return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
}
Copy the code

The BpBinder constructor calls IPCThreadState’s incWeakHandle function, which sends BC_INCREFS to the driver to increase the weak value of the binder_ref object.

BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) {
    mTrackedUid = trackedUid;
    IPCThreadState::self() - >incWeakHandle(this->binderHandle(), this);
}
Copy the code

The onFirstRef function calls incStrongHandle of IPCThreadState, which sends BC_ACQUIRE to the driver to increase the strong value of the binder_ref object.

void BpBinder::onFirstRef(a)
{
    ALOGV("onFirstRef BpBinder %p handle %d\n".this.binderHandle());
    if (CC_UNLIKELY(isRpcBinder())) return;
    IPCThreadState* ipc = IPCThreadState::self(a);if (ipc) ipc->incStrongHandle(binderHandle(), this);
}
Copy the code

The BpBinder object is created, and the corresponding binder_re f has its strong and weak values increased by 1. As mentioned above, once the Parcel object is destroyed, the binder buffer is also released and the strong value added to the binder_ref object is reduced, so the strong and weak values added here will ensure that the binder_ref object is not destroyed.

After the BpBinder object is created, it is entered as a parameter in the javaObjectForIBinder function to create a Java layer BinderProxy object based on the Native layer BpBinder object. The function first creates a new BinderProxyNativeData object that takes the life of the BpBinder into its own hands, as held by SP. BpBinder can only be destroyed if the BinderProxyNativeData object is destroyed. The BinderProxy object is then created by calling the Java layer’s binderproxy.getInstance () method.

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

    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
    return object;
}
Copy the code
struct BinderProxyNativeData {
    // Both fields are constant and not null once javaObjectForIBinder returns this as
    // part of a BinderProxy.

    // The native IBinder proxied by this BinderProxy.
    sp<IBinder> mObject;

    // Death recipients for mObject. Reference counted only because DeathRecipients
    // hold a weak reference that can be temporarily promoted.
    sp<DeathRecipientList> mOrgue;  // Death recipients for mObject.
};
Copy the code

The newly created BinderProxy object will hold the address of the Native layer BinderProxyNativeData object, and bind the life cycle of the two objects through the mechanism of NativeAllocationRegistry. Only after the BinderProxy object is reclaimed, The BinderProxyNativeData object can be destroyed.

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) {
            // We're throwing an exception (probably OOME); don't drop nativeData.
            NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
                    nativeData);
            throw e;
        }
        NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
        // The registry now owns nativeData, even if registration threw an exception.
        sProxyMap.set(iBinder, result);
    }
    return result;
}
Copy the code

Also note that newly created BinderProxy objects are added to sProxyMap so that the same Binder object can be transferred twice to find the same BinderProxy object. In order not to interfere with the normal life cycle of BinderProxy objects, WeakReference is adopted in sProxyMap to indirectly reference BinderProxy objects.

Once we have the BinderProxy object, we are one step away from establishing a communication link. Finally, the asInterface method is called to generate the final stub.proxy object, which holds a reference to the BinderProxy object.

_arg0 = android.app.IApplicationThread.Stub.asInterface(data.readStrongBinder());
Copy the code

At this point, the whole Binder communication link is completed. The reference of a Parcel object to a JavaBBinder object in a Server process ensures that the JavaBBinder object will not be destroyed before the cross-process reference relationship is established. The binder_ref object is referenced by the binder buffer in the Client process to ensure that the binder_ref object will not be destroyed before the cross-process reference relationship is established. As shown in the figure below.

When the whole process is over, the states of each intermediate variable are summarized as follows:

  • JavaBBinder objects have a strong reference count of 1 and a weak reference count of 2.
  • Internal_strong_refs of the binder_node object is 1, and has_STRONG_ref and has_Weak_ref are both true.
  • The strong and weak values of the binder_ref object are 1 and 1 respectively.
  • BpBinder objects have a strong reference count of 1 and a weak reference count of 1.

If the Binder object is sent to another process, the Binder object will be:

  • JavaBBinder objects have a strong reference count of 2 and a weak reference count of 3.
  • Internal_strong_refs of the binder_node object is 2, and has_STRONG_ref and has_Weak_ref are both true.

Destruction of the process

When can a Binder object be recycled? The answer is when all of its BinderProxy objects die. The destruction process is as follows:

  1. When the stub.proxy object is no longer in use, it is reclaimed by the GC.
  2. After the stub. Proxy object is reclaimed, the BinderProxy object is no longer referenced and therefore will be reclaimed by GC.
  3. Due to the NativeAllocationRegistry mechanism, the BinderProxyNativeData object is also destroyed when the BinderProxy object is reclaimed.
  4. After the BinderProxyNativeData object is destroyed, the BpBinder object’s strong reference count is reduced to 0 and is therefore also destroyed.
  5. BpBinder’s destructor calls IPCThreadState’s decStrongHandle and decWeakHandle functions, which send BC_RELEASE and BC_DECREFS instructions to the driver, respectively.
  6. The driver receives these two instructions and subtracts the strong and weak values of the binder_ref object, respectively. When the value of strong is reduced to 0, internal_STRONG_refs of the corresponding binder_node object is also reduced by 1. When both strong and weak are 0, the binder_ref object is released.
  7. When internal_STRONG_REFs of the binder_node object is reduced to 0, the BINDER_WORK_NODE task is executed to release the binder_node object on one hand and return the BR_RELEASE and BR_DECREFS instructions to the user space respectively.
  8. After receiving these two commands, the user space will call decStrong and decWeak functions respectively. JavaBBinder’s strong reference count -1 and weak reference count -2 (decStrong executes decWeak at the same time).
  9. JavaBBinder has a strong reference count of 0 and is therefore destroyed.
  10. A Binder object is removed from the Global Refs list in JavaBBinder’s destructor, so a Binder object is no longer treated as GC roots, it is reclaimed by GC.
  11. Due to the NativeAllocationRegistry mechanism, when Binder objects are reclaimed, the JavaBBinderHolder object is also destroyed.

The whole process was a joy, and eventually all the objects on the link were destroyed.

conclusion

To be honest, this article is difficult. Understanding it requires some background and understanding of the Binder communication process, otherwise it can look confusing. So why did I write it? The reason is that some knowledge is rarely used, but when it is needed, it is scarce. The public explosion does a lot of people, minority boutique also need someone to cultivate. Either way, the essence of the approach is to contribute to the Chinese language technology community.