Binder | 对象的生命周期

6,096

本文分析基于Android S(12)

前言

当我们使用AIDL接口时,拿到的对象本质上属于Stub.Proxy类。通过Binder通信,便可以将数据传输给Server进程中的Stub对象(继承于Binder类)。然而这些只是冰山一角,在水面以下还隐藏着许多其他对象。有了它们,通信才能够建立。我们以ApplicationThread为例,通信发起方拿到的是IApplicationThread.Stub.Proxy对象,数据最终到达的是ApplicationThread对象(ApplicationThread继承于IApplicationThread.Stub类)。下图展示了整个通信链路建立时需要创建的不同对象,总共有9个(其中的细节可以参考本人先前的文章《Binder代理机制上》)。

Binder所有对象创建时序.jpg

图中的序号表示的是这些对象的创建顺序,第一个出现的是Stub对象(也可称为Binder对象,因为它的父类是Binder,根据多态原则我们可以将它转为Binder对象)。Binder对象构造时会顺带创建Native层的JavaBBinderHolder对象,这样我们便拥有了1、2两个对象。而后续3~9这些对象只有当我们将Binder对象作为数据传输给对端进程后才能创建出来。通信链路建立以后,即便Binder对象在本进程内没有被任何其他Java对象引用,它也不应该被回收,因为它随时会收到来自其他进程的通信数据。这种关系可以被形容为:Server进程的Binder对象被Client进程的BinderProxy对象引用了。请记住这句话,它是本文思想的核心。只有当其他进程中对应的Proxy对象都被GC回收以后,Binder对象不再可能接收新的通信数据时,它才可以被回收,如下图所示。

设计原则.png

为了建立这种跨进程的引用关系,我们需要一些复杂的过程和中间变量的配合。一旦了解了这种引用关系的内部细节,我们便可以知道这些对象的生命周期是如何管理的。理解生命周期,其实核心回答的就是两个问题:

  1. Binder对象如何保证在BinderProxy对象存在时不被回收?
  2. Binder对象如何在所有BinderProxy消亡时被回收?

带着这些问题,接下来我们将要深入细节,看看Android到底是如何管理这些对象的生命周期的。

1. Server进程

1.1 Binder对象

整个过程始于Binder对象的创建。

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

Binder的构造方法中做了三件事:

  1. getNativeBBinderHolder在Native层创建了一个新的JavaBBinderHolder对象,并将它的地址返回给mObject字段。
  2. registerNativeAllocation用于管理与Java对象有关的native内存,具体可参考本人文章《如何让GC同步回收native内存》。
  3. 将AIDL接口名称记录在mDescriptor中。

此时创建的Binder对象还只是一个普通的Java对象,如果不将它作为Binder数据(Binder通信既可以传输普通数据,也可以传输Binder类实例化后的对象)传输给其他进程,那么后续的JavaBBinder对象、binder_node对象都不会创建,它也就不会具备跨进程通信的能力。

通过new方式创建出来的JavaBBinderHolder对象并没有通过强弱指针(智能指针)进行管理,而是将地址寄存在Binder对象的字段中,因此它的回收也只能由Binder对象发起。

根据NativeAllocationRegistry机制可知,Binder对象被GC回收时会调用Binder_destroy函数,这样JavaBBinderHolder对象也就被销毁了。

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

1.2 JavaBBinder对象

当我们将Binder对象作为数据传输给对端进程时,首先需要将它装入Parcel对象,这就好比数据打包。Java层的writeStrongBinder方法会进入到Native层,其中会有两个关键的步骤,一个是ibinderForJavaObject函数,另一个是parcel->writeStrongBinder函数。

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即为Binder对象
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

ibinderForJavaObject函数会根据Java对象生成Native层的JavaBBinder对象。在构造JavaBBinder对象时,我们会通过env->NewGlobalRef的方式将Binder对象加入到art::globals_列表中,这样Binder对象在每次GC时都会被标记为GC Root,也便无法被回收。通过这种方式,我们将Java层的Binder对象和Native层的JavaBBinder对象进行了深度捆绑,只有当JavaBBinder对象销毁时,Binder对象才能从art::globals_中清除,重新恢复自由身后他就可以被回收了。

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);
}

ibinderForJavaObject函数的返回值为sp<IBinder>类型。这里的sp表示"strong pointer",它是Android中单独实现的一套智能指针系统,切勿和C++中的shared_ptr混淆。sp是一个模板类,其中作为模板的类必须继承自RefBase,比如这里的IBinder就继承了RefBase。sp通过引用计数的方式来管理所指向的C++对象,这里的话是管理新创建的JavaBBinder对象。关于RefBase的详细知识不是本文的重点,因此这里介绍一个参考文章,供没有背景知识的同学参考。

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);
    }
	...
}
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
    AutoMutex _l(mLock);
    sp<JavaBBinder> b = mBinder.promote();    
    if (b == NULL) {
        b = new JavaBBinder(env, obj);     //b被赋值时,JavaBBinder对象的强引用计数+1,此时该值为1
		...
    }
    return b;
}

当JavaBBinder对象的强引用计数减为0时,JavaBBinder便会被销毁,这就是Android中智能指针的基本逻辑。sp对象的创建会增加JavaBBinder的强引用计数(+1),sp对象的销毁会减少JavaBBinder的强引用计数(-1)。因此上述get函数中JavaBBinder对象的强引用计数值呈现出如下规律:

  1. b = new JavaBBinder(env, obj)会对JavaBBinder对象的强引用计数值+1,于是该值为1。
  2. return b会根据局部变量b创建一个sp类型的返回值,这里采用的是拷贝构造,因此JavaBBinder的强引用计数值+1,于是该值为2。
  3. 函数返回,局部变量b销毁,JavaBBinder的强引用计数值-1,于是该值为1。

ibinderForJavaObject返回后,返回值将作为参数继续进入到parcel->writeStrongBinder函数。

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flattenBinder(val);
}
status_t Parcel::flattenBinder(const sp<IBinder>& binder)
{
    ...
    flat_binder_object obj;
    if (binder != nullptr) {
        BBinder *local = binder->localBinder();
        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);
}

flattenBinder会根据JavaBBinder对象构造一个flat_binder_object对象,并将其写入Parcel。而作为参数的sp对象binder在函数返回后将不复存在,因此指向的JavaBBinder对象强引用计数值将会继续减1。根据上文可知,此时JavaBBinder对象的强引用计数值为1,如果flattenBinder里没有其他地方增加JavaBBinder对象的强引用计数值,那么函数返回后,强引用计数值归零,JavaBBinder对象将会被销毁。而这显然是不符合预期的。

等等,让我们站在宏观的角度来重新思考这个问题。

当我们将构造后的flat_binder_object对象写入Parcel,意味着该Parcel对象隐式地引用了JavaBBinder对象,因此它需要保证自己在使用flat_binder_object对象的过程中,JavaBBinder对象不被销毁。基于这个逻辑的判断,flattenBinder中调用writeObject将flat_binder_object对象写入Parcel的过程里一定会增加JavaBBinder的强引用计数。下面我们来看看代码的设计是否符合我们逻辑的推断。

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++;
    }
    ...
}
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;
    ...
}

果然,当传输的是JavaBBinder对象时,writeObject会调用acquire_object来增加JavaBBinder对象的强引用计数(通过incStrong函数)。这样一来,flattenBinder函数返回后JavaBBinder对象也就不会被销毁了。

那么acquire_object里增加的强引用计数何时再减掉呢?答案自然是Parcel对象回收的时候。事实上,Parcel对象在通信链路的建立过程中只是二传手的角色,它只能保证JavaBBinder一直存活到对端进程创建出BinderProxy对象的时候,而后续JavaBBinder的生命就交给了对端进程。

下面我们来看一个典型的AIDL gnerated方法。

@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);  //第一步是将Binder对象写入Parcel中,会调用acquire_object增加JavaBBinder的强引用计数
    if ((definition!=null)) {
        _data.writeInt(1);
        definition.writeToParcel(_data, 0);
    }
    else {
        _data.writeInt(0);
    }
        mRemote.transact(Stub.TRANSACTION_registerRemoteAnimations, _data, _reply, 0);  //第二步是进行binder通信
        _reply.readException();
    }
    finally {
        _reply.recycle();
        _data.recycle();   //第三步是回收Parcel对象,进而会调用release_object减少JavaBBinder的强引用计数
    }
}

大体过程可以分为三步:

  1. _data.writeStrongBinder将Binder对象写入Parcel,其内部会将JavaBBinder写入Parcel(native),并同时增加JavaBBinder的强引用计数。
  2. mRemote.transact将Binder对象传输给对端进程,对端进程接收到数据后会创建相应的BinderProxy对象。
  3. _data.recycle回收Parcel对象,同时减少JavaBBinder的强引用计数。

如果希望通信链路在_data.recycle后不被破坏,那么必须要在mRemote.transact里增加JavaBBinder的强引用计数,这实际上也是将JavaBBinder的生命交由BinderProxy管理的过程。接下来,让我们深入到驱动里去一探究竟。

1.3 binder_node对象

进入Binder驱动后,binder_transaction函数会遍历本次传输的所有Binder对象。接着最关键的一步是binder_translate_binder,它会根据flat_binder_object生成内核空间中相应的binder_node和binder_ref对象,并将BINDER_TYPE_BINDER的类型更改为BINDER_TYPE_HANDLE,表明我们虽然传输的是一个Binder对象,但是对端拿到的将是Proxy对象。

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;

binder_translate_binder首先会通过binder_new_node创建一个新的binder_node对象,紧接着调用binder_inc_ref_for_node去创建binder_ref对象。

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);
	...
}

binder_inc_ref_for_node里会做两件事,一是生成对端进程的binder_ref对象,通过它能够找到刚刚创建的binder_node对象;二是调用binder_inc_ref_olocked来管理binder_node和binder_ref对象的生命周期。

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);
}

binder_inc_ref_olocked里既增加binder_ref对象的strong值,也会进一步调用binder_inc_node增加相应binder_node对象的引用值。binder_ref对象的strong不为0,表示它被人引用无法被销毁。这里之所以增加,是因为接下来type为BINDER_TYPE_HANDLE的数据会写入binder buffer,因此binder buffer会引用binder_ref对象。

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;
}

binder_inc_node_nilocked里会做两件事,一是增加binder_node对象的internal_strong_refs值 ,另一个是往thread的todo列表中新增一个BINDER_WORK_NODE的任务。binder_node里的引用字段有些复杂,主要是名称具有歧义。其主要使用的字段有internal_strong_refs和local_strong_refs,前者表明binder_node对象被一个binder_ref对象所引用,进一步引申的含义是binder_node对象被另一个进程所引用;后者表明binder_node对象被本进程所引用,它不一定指具体的持有关系,而是在本进程执行某些任务时,binder_node对象不能被释放。

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);
		}

这个被加入到thread todo列表中的任务是一个延迟任务,但不管是同步通信还是异步通信,它们都会在transact结束前完成。

BINDER_WORK_NODE任务处理时会做两件事:

  1. node的has_weak_ref和has_strong_ref设置为true,pending_weak_ref和pending_strong_ref设置为1,local_weak_refs和local_strong_refs均自增。
  2. 给用户空间返回BR_INCREFS和BR_ACQUIRE。

那么如何去理解这两件事呢?

首先,给用户空间返回的指令会让用户空间去增加JavaBBinder对象的强引用计数和弱引用计数,保证JavaBBinder在有其他进程引用时不会被销毁。而pending_weak_ref/pending_strong_ref/local_weak_refs/local_strong_refs之所以被置上,均是在等待用户空间操作的完成。当用户空间完成增加引用计数的操作后,会重新回到内核空间,带回BC_INCREFS_DONE和BC_ACQUIRE_DONE的指令,将之前的四个值复位,表明用户空间已经完成这项任务。

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");

这样一来,我们就可以保证_data.recycle结束后,JavaBBinder对象依然不被销毁的效果了。至此,本进程的工作就已经完成了。

让我们来梳理一下各个对象的依赖关系。

  1. Java层的Binder对象回收时,Native层的JavaBBinderHolder对象才能被销毁。
  2. Native层的JavaBBinder对象被销毁时,Java层的Binder对象才能从全局引用表中出来,也才能够被GC回收。
  3. Kernel层的binder_node对象的internal_strong_refs和local_strong_refs值均减为0时,Native层的JavaBBinder对象的强引用计数才能归零,也才能被销毁。

所以对于本进程而言,这些对象的销毁关系仿佛像多米诺骨牌一样,只有前面的倒下,后面的才能够顺次倒下。根据上述依赖关系可知,如果想要销毁这些对象,首先要做的就是将binder_node对象的internal_strong_refs和local_strong_refs的值减为0(local_strong_refs通常为0)。而internal_strong_refs其实记录的就是有多少个进程引用了该Binder对象。

2. Client进程

此时的binder_ref对象已经创建,ref->data.strong的值为1,表明binder buffer正在引用它。当Client进程接收到通信请求后,会先调用IPCThreadState::executeCommand方法。这里会先创建一个局部的Parcel对象buffer,来接收binder buffer中的数据。一旦buffer销毁后,binder buffer也将被释放,而此前给binder_ref对象增加的strong值也将减回去。

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);

        }
    }

上面的transact函数最终会调用Java层的onTransact来进行处理。以下面的代码为例,首先需要通过readStrongBinder的方法将刚刚传递过来的Binder对象解析出来,解析出来的结果便是一个BinderProxy对象。

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

readStrongBinder是一个native方法,其内部实现可以分为两步:parcel->readStrongBinder()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;
}

parcel->readStrongBinder()用于根据Parcel中的数据创建BpBinder对象。

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

getStrongProxyForHandle中使用BpBinder::create的方法来创建新的BpBinder对象。其中大部分代码是用来防止BpBinder对象leak的,这与本文要讨论的话题无关(或许后面会单独成文),暂且跳过。真正负责对象创建的其实只有最后一行,它做了两件事:一是通过new的方式创建了新的BpBinder对象;二是创建一个sp对象来指向它,这会增加BpBinder对象的强引用计数,并且会回调BpBinder的onFirstRef函数(因为是第一次被引用)。

sp<BpBinder> BpBinder::create(int32_t handle) {
    int32_t trackedUid = -1;
    if (sCountByUidEnabled) {
        trackedUid = IPCThreadState::self()->getCallingUid();
        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);
}

BpBinder的构造函数会调用IPCThreadState的incWeakHandle函数,它会往驱动中发送BC_INCREFS命令,让驱动去增加binder_ref对象的weak值。

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

onFirstRef函数会调用IPCThreadState的incStrongHandle函数,它会往驱动中发送BC_ACQUIRE命令,让驱动去增加binder_ref对象的strong值。

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

这样一来,BpBinder对象创建了出来,同时对应的binder_ref对象的strong和weak值也都增加了1。上文提到,一旦Parcel对象销毁后,binder buffer也将被释放,而此前给binder_ref对象增加的strong值也将减回去,因此这里增加的strong和weak便可以保证binder_ref对象不被销毁。

BpBinder对象创建出来以后,它会作为参数进入javaObjectForIBinder函数,根据Native层的BpBinder对象创建出Java层的BinderProxy对象。该函数首先会创建一个新的BinderProxyNativeData对象,通过sp持有的方式,将BpBinder的生命掌控在了自己手里。只有BinderProxyNativeData对象销毁时,BpBinder才能够被销毁。接着调用Java层的BinderProxy.getInstance()方法创建出BinderProxy对象。

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());
    return object;
}
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.
};

新创建的BinderProxy对象会持有Native层BinderProxyNativeData对象的地址,并通过NativeAllocationRegistry的机制来将二者的生命周期进行捆绑,只有BinderProxy对象被回收后,BinderProxyNativeData对象才能被销毁。

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;
}

此外还需要注意,新创建的BinderProxy对象会加入sProxyMap,这样传输两次相同的Binder对象才能找到同一个BinderProxy对象。为了不干扰BinderProxy对象正常的生命周期,sProxyMap中采用WeakReference的方式来间接引用BinderProxy对象。

当我们拿到BinderProxy对象后,离通信链路的建立也只有一步之遥了。最后调用asInterface方法,来生成最终的Stub.Proxy对象,它会持有BinderProxy对象的引用。

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

至此,整个Binder通信链路才算建立完全。Server进程中Parcel对象对JavaBBinder对象的引用保证了跨进程引用关系建立前,JavaBBinder对象不会被销毁;Client进程中binder buffer对binder_ref对象的引用保证了跨进程引用关系建立前,binder_ref对象不会被销毁。如下图所示。

引用传递.png

当整个过程都结束以后,各中间变量的状态汇总如下:

  • JavaBBinder对象的强引用计数为1,弱引用计数为2。
  • binder_node对象的internal_strong_refs为1,has_strong_ref和has_weak_ref均为true。
  • binder_ref对象的strong为1,weak为1。
  • BpBinder对象的强引用计数为1,弱引用计数为1。

如果再将Binder对象发送给另一个进程,那么此时的:

  • JavaBBinder对象的强引用计数为2,弱引用计数为3。
  • binder_node对象的internal_strong_refs为2,has_strong_ref和has_weak_ref均为true。

销毁过程

一个Binder对象何时可以被回收?答案是它的所有BinderProxy对象都消亡时。销毁过程如下所示:

  1. 当Client进程中的Stub.Proxy对象不再被使用时,它会被GC回收。
  2. Stub.Proxy对象被回收后,BinderProxy对象便不再被人引用,因此也会被GC回收。
  3. 由于NativeAllocationRegistry机制的存在,BinderProxy对象回收时,BinderProxyNativeData对象也被销毁。
  4. BinderProxyNativeData对象销毁后,BpBinder对象的强引用计数减为0,因此也会被销毁。
  5. BpBinder的析构过程会调用IPCThreadState的decStrongHandle和decWeakHandle函数,它们会分别向驱动发送BC_RELEASE和BC_DECREFS指令。
  6. 驱动接收到这两条指令,将会分别减去binder_ref对象的strong值和weak值。当strong值减为0时,相应的binder_node对象的internal_strong_refs也减1。当strong和weak同时为0时,释放binder_ref对象。
  7. 当binder_node对象的internal_strong_refs减为0时,会将BINDER_WORK_NODE任务发送给Server进程。
  8. Server进程处理BINDER_WORK_NODE任务时,一方面会释放binder_node对象,另一方面会给用户空间分别返回BR_RELEASE和BR_DECREFS指令。
  9. 用户空间接收到这两个命令后,会分别调用decStrong和decWeak函数,JavaBBinder的强引用计数-1,弱引用计数-2(decStrong中会同时执行decWeak)。
  10. JavaBBinder的强引用计数为0,因此被销毁。
  11. JavaBBinder的析构函数中会将Binder对象从Global Refs列表中清除,因此Binder对象不再被当作GC roots,它会被GC回收。
  12. 由于NativeAllocationRegistry机制的存在,Binder对象回收时,JavaBBinderHolder对象也被销毁。

整个过程如同消消乐一般,最后所有链路上的对象全被销毁。

结语

实事求是的说,这篇文章是有难度的。理解它需要具备一些背景知识,也需要对Binder通信过程比较了解,否则看起来很可能云里雾里。那既然如此,我为什么要写它呢?原因是有些知识虽然用的人不多,但一旦需要时,这些资料又显得十分稀有。大众爆款做的人很多,小众精品也需要有人耕耘。不管是哪种途径,本质都是希望为中文技术社区做些贡献。