从源码角度看 Binder.linkToDeath

简介

Android系统当中的Binder消息传递无处不在,从运行一个新应用到发送一个常见的TIME PICK广播,再到注册一个ContentObserver去监听短信数据的变化,这些功能都需要使用到Binder通信。正如"凡人必有一死",system_server进程虽然只要手机保持开机状态就会存在,但是普通应用无论优先级多么高,当系统内存匮乏、用户手动杀死应用进程又或者是应用出现不能解决的BUG直接Force Close了。这时,作为进程通信服务端的应用既然死亡了,那么对应的客户端相应的服务端之前保存下来的数据就没有必要保存了。否则的话,应用生老病死之间,如果手机一直不关机,system_server一直在存活期间也不清除死亡进程的遗留信息,那么这样的手机系统使用起来会造成内存泄露,系统资源会慢慢被耗尽直至用户能察觉到的系统卡顿出现。

所以Binder进程通信必然需要一种死亡回调的机制,当通信服务端死亡后可以通知客户端们来进行相关的清理工作。Android已经实现好了一套DeathRecipient机制,客户端只要实现死亡回调binderDied方法并且调用linkToDeath方法,当服务端死亡时binderDied将会被调用。

framework示例

linkToDeath的源码大部分都是c++代码,晦涩难懂。所以在讲解源码之前,我会给出尽量多的framework实例

ContentService

上一周我写了一篇关于ContentService如何实现数据变化监听的分析,有兴趣的可以看看,这里我只着重讲ContentService与应用端的死亡回调

ContentService.java

private class ObserverEntry implements IBinder.DeathRecipient {
...

    public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
            int _uid, int _pid, int _userHandle) {
...
        try {
            // 在ObserverEntry实例化时调用了linkToDeath接口
            observer.asBinder().linkToDeath(this, 0);
        } catch (RemoteException e) {
            binderDied();
        }
    }

    public void binderDied() {
        synchronized (observersLock) {
            removeObserverLocked(observer);
         }
    }
...
}

private void addObserverLocked(Uri uri, int index, IContentObserver observer,
        boolean notifyForDescendants, Object observersLock,
        int uid, int pid, int userHandle) {
    if (index == countUriSegments(uri)) {
        // 新建一个ObserverEntry的实例并保存仅mObservers链表中
        mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
                uid, pid, userHandle));
        return;
    }
...
}

ObserverService将会在ObserverEntry实例被插入mObservers链表中注册死亡回调接口。ObserverEntry实现了DeathRecipient接口,当应用端进程死亡时,binderDied将会被调用,从而调用removeObserverLocked来清除进程保存在mObservers的数据。

需要注意的是,ContentService这时担任着进程通信时客户端的角色,当它调用linkToDeath时,实际上调用的时Binder Proxy的方法。

BroadcastReceiver

我从工作后一直在看BroadcastReceiver的源码,对这块比较熟悉。这里我主要讲讲当注册完广播后,如果注册应用进程死亡后AMS将如何调用死亡回调

ActivityManagerService.java

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
        IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
                    ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
...
        if (rl == null) {
            rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                    userId, receiver);
            if (rl.app != null) {
                rl.app.receivers.add(rl);
            } else {
                try {
                    // 动态注册的广播新建ReceiverList后,调用linkToDeath方法
                    receiver.asBinder().linkToDeath(rl, 0);
                } catch (RemoteException e) {
                    return sticky;
                }
                rl.linkedToDeath = true;
            }
            mRegisteredReceivers.put(receiver.asBinder(), rl);
        }
...
}

ReceiverList.java

// 广播的死亡回调接口在ReceiverList中实现
final class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient {
    final ActivityManagerService owner;

    ReceiverList(ActivityManagerService _owner, ProcessRecord _app,
            int _pid, int _uid, int _userId, IIntentReceiver _receiver) {
        owner = _owner;
...
    }
...
    public void binderDied() {
        linkedToDeath = false;
        // 应用进程死亡后,AMS将会调用反注册广播接口
        owner.unregisterReceiver(receiver);
    }
...
}

AudioManager

AudioManager这块有些生僻,稍微讲讲

MediaFocusControl.java

protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
        IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {
                    AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
...
        try {
            // 在这里注册死亡回掉
            cb.linkToDeath(afdh, 0);
        } catch (RemoteException e) {
            // client has already died!
            Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
        }
...

protected class AudioFocusDeathHandler implements IBinder.DeathRecipient {
    private IBinder mCb; // To be notified of client's death

    AudioFocusDeathHandler(IBinder cb) {
        mCb = cb;
    }

    public void binderDied() {
        synchronized(mAudioFocusLock) {
            Log.w(TAG, "  AudioFocus   audio focus client died");
            // 清除死亡进程的数据
            removeFocusStackEntryForClient(mCb);
        }
    }

    public IBinder getBinder() {
        return mCb;
    }
}

小问题

上面三个framework实例有这样的表现:

  1. ContentService: 同应用调用registerContentObserver注册同一个ContentObserver传入的IContentObserver实例相同,每次都会重新注册一个死亡回调实例
  2. ActivityManagerService: 同应用调用registerReceiver注册同一个Receiver传入的IIntentReceiver实例相同,每次不会重新注册一个死亡回调
  3. AudioService: 同应用调用requestAudioFocus注册同一个OnAudioFocusChangeListener传入的mICallback实例相同,每次都会重新注册一个死亡回调实例

可以看到,AMS的广播注册与死亡回掉注册是正常的,同一个receiver只会注册一次死亡回调;但是ContentService和AudioService就不正常了,重复的ConentObserver还是会重复注册不同的死亡回调实例,这是有问题的。C++层维护了一个map用来保存死亡回调的信息,这个map超过一定的值会导致安卓系统死机重启,如果重复的Bp也重复去注册同一个回调的话,这个空间就会很快被消耗完,容易造成手机重启。

源码分析:注册死亡通知

Framework层:Binder.java

先从最顶层的JAVA代码开始看起:

public class Binder implements IBinder {
...
    // native 端并不需要监听死亡回调,所以是空实现
    public void linkToDeath(DeathRecipient recipient, int flags) {
    }

    /**
     * Local implementation is a no-op.
     */
    public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
        return true;
    }
...

final class BinderProxy implements IBinder {
...
    // proxy 端直接通过JNI调用c++方法
    public native void linkToDeath(DeathRecipient recipient, int flags)
        throws RemoteException;
     public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
...
}

Native层:android_util_Binder.cpp

...
class DeathRecipient : public virtual RefBase
{
public:
    virtual void binderDied(const wp<IBinder>& who) = 0;
};

virtual status_t        linkToDeath(const sp<DeathRecipient>& recipient,
                                    void* cookie = NULL,
                                    uint32_t flags = 0) = 0;

virtual status_t        unlinkToDeath(  const wp<DeathRecipient>& recipient,
                                        void* cookie = NULL,
                                        uint32_t flags = 0,
                                        wp<DeathRecipient>* outRecipient = NULL) = 0;
...
static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
        jobject recipient, jint flags) // throws RemoteException
{
    // 回调实例如果为空直接抛出异常返回
    if (recipient == NULL) {
        jniThrowNullPointerException(env, NULL);
        return;
    }

    // 获取Bp Binder
    IBinder* target = (IBinder*)
        env->GetLongField(obj, gBinderProxyOffsets.mObject);
    if (target == NULL) {
        ALOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient);
        assert(false);
    }

    LOGDEATH("linkToDeath: binder=%p recipient=%p\n", target, recipient);

    // 只有Bp才能进行回调注册
    if (!target->localBinder()) {
        // 获取死亡回调的记录链表
        DeathRecipientList* list = (DeathRecipientList*)
                env->GetLongField(obj, gBinderProxyOffsets.mOrgue);
        sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient, list);
        // 在c++层进行死亡回调注册
        status_t err = target->linkToDeath(jdr, NULL, flags);
        if (err != NO_ERROR) {
            // Failure adding the death recipient, so clear its reference
            // now.
            jdr->clearReference();
            signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
        }
    }
}
class JavaDeathRecipient : public IBinder::DeathRecipient
{
public:
    JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
          mObjectWeak(NULL), mList(list)
    {
        // These objects manage their own lifetimes so are responsible for final bookkeeping.
        // The list holds a strong reference to this object.
        LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
        // 将这个对象加入到回调记录链表中
        list->add(this);

        android_atomic_inc(&gNumDeathRefs);
        incRefsCreated(env);
    }
}


static void incRefsCreated(JNIEnv* env)
{
    int old = android_atomic_inc(&gNumRefsCreated);
    // 每增加到200则执行一次force GC
    if (old == 200) {
        android_atomic_and(0, &gNumRefsCreated);
        env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
                gBinderInternalOffsets.mForceGc);
    } else {
        ALOGV("Now have %d binder ops", old);
    }
}

BpBinder.cpp

status_t BpBinder::linkToDeath(
    const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
{
    Obituary ob;
    // 回调指针
    ob.recipient = recipient;
    ob.cookie = cookie;
    ob.flags = flags;

    LOG_ALWAYS_FATAL_IF(recipient == NULL,
                        "linkToDeath(): recipient must be non-NULL");

    {
        AutoMutex _l(mLock);

        if (!mObitsSent) {
            if (!mObituaries) {
                // 一个Bp可以注册多个死亡回调
                mObituaries = new Vector<Obituary>;
                if (!mObituaries) {
                    return NO_MEMORY;
                }
                ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);
                getWeakRefs()->incWeak(this);
                IPCThreadState* self = IPCThreadState::self();
                self->requestDeathNotification(mHandle, this);
                self->flushCommands();
            }
            ssize_t res = mObituaries->add(ob);
            return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;
        }
    }

    return DEAD_OBJECT;
}

IPCThreadState.cpp

status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
{
    // 对binder驱动发出命令
    mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
    mOut.writeInt32((int32_t)handle);
    mOut.writePointer((uintptr_t)proxy);
    return NO_ERROR;
}

void IPCThreadState::flushCommands()
{
    if (mProcess->mDriverFD <= 0)
        return;
    talkWithDriver(false);
}

Kernel层:binder.c

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            void __user *buffer, int size, signed long *consumed)
{
    uint32_t cmd; 
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;
    while (ptr < end && thread->return_error == BR_OK) {
        // 获取命令,此处为BC_REQUEST_DEATH_NOTIFICATION
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
...  
        switch (cmd) {
...
        case BC_REQUEST_DEATH_NOTIFICATION:
        case BC_CLEAR_DEATH_NOTIFICATION: {
            uint32_t target;
            void __user *cookie;
            struct binder_ref *ref;
            struct binder_ref_death *death;
            // 取出handle值
            if (get_user(target, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);

            if (get_user(cookie, (void __user * __user *)ptr))
                return -EFAULT;
            ptr += sizeof(void *);
            // 通过 handle 值取 ref
            ref = binder_get_ref(proc, target);
...

            if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
                // 上面好像 Bp 可以注册多个,但是在kernel里面只能注册一个咧
                if (ref->death) {
                    break;
                }
                // 这个 binder_ref_death 是kernel中保存死亡通知回调的结构
                death = kzalloc(sizeof(*death), GFP_KERNEL);
                if (death == NULL) {
                    thread->return_error = BR_ERROR;
                    break;
                }
                binder_stats_created(BINDER_STAT_DEATH);
                // 初始化的工作队列列表
                INIT_LIST_HEAD(&death->work.entry);
                // 把用户空间传过来的那个指针保存起来
                death->cookie = cookie;
                // 每一个 ref 保存有一个 binder_ref_death
                ref->death = death;
                // 如果 ref 对应的 node 的 进程已经挂了,这里直接就发通知了
                if (ref->node->proc == NULL) {
                    ref->death->work.type = BINDER_WORK_DEAD_BINDER;
                    if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
                        list_add_tail(&ref->death->work.entry, &thread->todo);
                    } else {
                        list_add_tail(&ref->death->work.entry, &proc->todo);
                        wake_up_interruptible(&proc->wait);
                    }
                }
            } else {
...
            }
        } break;
...
        default:
            printk(KERN_ERR "binder: %d:%d unknown command %d\n",
                   proc->pid, thread->pid, cmd);
            return -EINVAL;
        }
        *consumed = ptr - buffer;
    }
    return 0;
}

数据结构

struct binder_work {
    struct list_head entry;
...
};

struct binder_ref_death {
    struct binder_work work; 
    void __user *cookie;
};

struct binder_ref {
    int debug_id;
    struct rb_node rb_node_desc;
    struct rb_node rb_node_node;
    struct hlist_node node_entry;
    struct binder_proc *proc; 
    struct binder_node *node;
    uint32_t desc;
    int strong;
    int weak;
    // 注册了的死亡通知结构
    struct binder_ref_death *death;
};

源码分析:触发死亡通知

注册操作是从framework上层传到kernel下层的,对应的触发操作是从kernel下层传到framework上层的

kernel层:binder.c

top Created with Sketch.