AMS registers death notifications for the app

Call attachApplicationLocked, which binds AppThread to an ApplicationTread binder object. This process actually takes the ApplicationTread object, communicates with the Binder to the ActivityManagerService(AMS), and lets AMS remotely bind the App’s behavior.

private final boolean attachApplicationLocked(IApplicationThread thread, int pid, int callingUid, long startSeq) { .... final String processName = app.processName; try { AppDeathRecipient adr = new AppDeathRecipient( app, pid, thread); thread.asBinder().linkToDeath(adr, 0); app.deathRecipient = adr; } catch (RemoteException e) { app.resetPackageList(mProcessStats); startProcessLocked(app, "link fail", processName); return false; }... return true; }Copy the code

Here’s a new class, AppDeathRecipient, let’s see what it is

private final class AppDeathRecipient implements IBinder.DeathRecipient { final ProcessRecord mApp; final int mPid; final IApplicationThread mAppThread; AppDeathRecipient(ProcessRecord app, int pid, IApplicationThread thread) { mApp = app; mPid = pid; mAppThread = thread; } @Override public void binderDied() { synchronized(ActivityManagerService.this) { appDiedLocked(mApp, mPid, mAppThread, true); }}}Copy the code

You can see that the AppDeathRecipient is inherited from ibinder.deathrecipient.

public interface DeathRecipient {
        public void binderDied();
    }
Copy the code

This interface means the behavior after the binder’s death callback.

Details added

So, let’s strike while the iron is hot. Following the previous chapter, we transfer BBinder local binder to binder. We must go through the binder_transaction step driven by binder:

case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { .... if (fp->type == BINDER_TYPE_BINDER) fp->type = BINDER_TYPE_HANDLE; else fp->type = BINDER_TYPE_WEAK_HANDLE; fp->handle = ref->desc; . } break;Copy the code

As you can see, the BINDER_TYPE_BINDER set on the Parcel is converted to BINDER_TYPE_HANDLE as a handle type. Therefore, we can see that when AMS executes attachApplicationLocked, it must read the binder object from the Parcel (readStrongBinder method). Let’s look at Parcel’s method.

status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const { return unflatten_binder(ProcessState::self(), *this, val); } status_t unflatten_binder(const sp<ProcessState>& proc, const Parcel& in, wp<IBinder>* out) { const flat_binder_object* flat = in.readObject(false); if (flat) { switch (flat->hdr.type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast<IBinder*>(flat->cookie); return finish_unflatten_binder(NULL, *flat, in); case BINDER_TYPE_WEAK_BINDER: if (flat->binder ! = 0) { out->set_object_and_refs( reinterpret_cast<IBinder*>(flat->cookie), reinterpret_cast<RefBase::weakref_type*>(flat->binder)); } else { *out = NULL; } return finish_unflatten_binder(NULL, *flat, in); case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: *out = proc->getWeakProxyForHandle(flat->handle); return finish_unflatten_binder( static_cast<BpBinder*>(out->unsafe_get()), *flat, in); } } return BAD_TYPE; }Copy the code

If the type of BINDER_TYPE_HANDLE is not the same as that of BINDER_TYPE_HANDLE, it will be converted to BpBinder. Otherwise, it means that the local object reads the cookie directly and sets it into the data.

Therefore, we can deduce that IApplicationThread thread is a BpBinder object, corresponding to the Java layer BinderProxy object.

linkToDeath

public class Binder implements IBinder {
    public void linkToDeath(DeathRecipient recipient, int flags) {
    }

    public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
        return true;
    }
}

final class BinderProxy implements IBinder {
    public native void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException;
    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
}
Copy the code

In fact, the Binder itself has these two methods, but because it is a local class, its death should be handled locally, and the Proxy, as a Proxy class, tells the Binder that it is going to die by this means. If this is the case, look at the BinderProxy source code to find out.

Native layer linkToDeath

static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
        jobject recipient, jint flags) // throws RemoteException
{
...

    BinderProxyNativeData *nd = getBPNativeData(env, obj);
    IBinder* target = nd->mObject.get();

    if (!target->localBinder()) {
        DeathRecipientList* list = nd->mOrgue.get();
        sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient, list);
        status_t err = target->linkToDeath(jdr, NULL, flags);
        if (err != NO_ERROR) {
            jdr->clearReference();
            signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
        }
    }
}
Copy the code

What you’re actually doing here is simply removing the JavaDeathRecipient object in BinderProxy’s BinderProxyNativeData and calling the BpBinder’s linkToDeath method.

The BpBinder linkToDeath

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) { mObituaries = new Vector<Obituary>; if (! mObituaries) { return NO_MEMORY; } 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; }Copy the code

Call the requestDeathNotification method of IPCThreadState to write to the death notification method

Call flushCommands of IPCThreadState to send a death notification

Add DeathRecipient, cookie(NULL in this case), and flags to the mObituaries Vector.

RequestDeathNotification and flushCommands

status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) { 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); // The flush could have caused post-write refcount decrements to have // been executed, which in turn could result in BC_RELEASE/BC_DECREFS // being queued in mOut. So flush again, if we need to. if (mOut.dataSize() > 0) { talkWithDriver(false); }... }Copy the code

IPCThread writes the BC_REQUEST_DEATH_NOTIFICATION command to the binder driver, handle(representing the BpBinder handle), proxy(representing the current BpBinder pointer), This is then transmitted to the Binder driver via iocTL

Binder drive binder_transaction

Case uint32_t target: {uint32_t target;} binder_uintptr_t cookie; struct binder_ref *ref; struct binder_ref_death *death; If (get_user(target, (uint32_t __user *) PTR))// Handle reflow-efault; ptr += sizeof(uint32_t); if (get_user(cookie, (binder_uintptr_t __user *)ptr))//BpBinder return -EFAULT; ptr += sizeof(binder_uintptr_t); ref = binder_get_ref(proc, target); . If (CMD == BC_REQUEST_DEATH_NOTIFICATION) {... death = kzalloc(sizeof(*death), GFP_KERNEL); . binder_stats_created(BINDER_STAT_DEATH); INIT_LIST_HEAD(&death->work.entry); death->cookie = cookie; ref->death = death; 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;Copy the code

1. Obtain the remote binder reference through the handle

2. Create and initialize a Binder_death object (containing the list of deaths and the BpBinder) and set the corresponding binder reference to the object. Then initialize the death->work.entry hash header.

Another special case to handle is when the target process is dead, set the flag to BINDER_WORK_DEAD_BINDER. In this scenario, the remote process’s App dies before the binder object underneath the binder driver is removed.

The discussion is divided into two cases:

Binder_looper flags open BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED both, at this point in the current process of binder_thread todo.

Binder_looper flags didn’t open the BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED both, directly add to the current process process todo (idle work list), And immediately evokes the current process (the current scenario is AMS)

There seems to be nothing left to do here. Indeed, this command simply flags which binder reference is dying. It does nothing more than make the Java layer callbacks follow the binder references.

When the target dies (App process dies)

Recycling really starts when the App process dies, so we can look at the binder driver close scenario when the App process dies and starts recycling.

static const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .compat_ioctl = binder_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
};
Copy the code

As we can see, the binder_release method is called when the resource is closed.

static int binder_release(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc = filp->private_data;

    debugfs_remove(proc->debugfs_entry);
    binder_defer_work(proc, BINDER_DEFERRED_RELEASE);
    return 0;
}

static DECLARE_WORK(binder_deferred_work, binder_deferred_func);

static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
{
    mutex_lock(&binder_deferred_lock);
    proc->deferred_work |= defer;
    if (hlist_unhashed(&proc->deferred_work_node)) {
        hlist_add_head(&proc->deferred_work_node,
                &binder_deferred_list);
        queue_work(binder_deferred_workqueue, &binder_deferred_work);
    }
    mutex_unlock(&binder_deferred_lock);
}
Copy the code

As you can see, the processing here performs multiple close reclamation methods, which in turn leads to a hash list called binder_deferred_workQueue (this data structure is similar to a Hashmap, which will be resolved later in the algorithm section). The binder_deferred_func method that binder_deferred_work points to is called.

static void binder_deferred_func(struct work_struct *work) { struct binder_proc *proc; struct files_struct *files; int defer; do { binder_lock(__func__); mutex_lock(&binder_deferred_lock); if (! hlist_empty(&binder_deferred_list)) { proc = hlist_entry(binder_deferred_list.first, struct binder_proc, deferred_work_node); hlist_del_init(&proc->deferred_work_node); defer = proc->deferred_work; proc->deferred_work = 0; } else { proc = NULL; defer = 0; } mutex_unlock(&binder_deferred_lock); files = NULL; if (defer & BINDER_DEFERRED_PUT_FILES) { files = proc->files; if (files) proc->files = NULL; } if (defer & BINDER_DEFERRED_FLUSH) binder_deferred_flush(proc); if (defer & BINDER_DEFERRED_RELEASE) binder_deferred_release(proc); /* frees proc */ binder_unlock(__func__); if (files) put_files_struct(files); } while (proc); }Copy the code

And what we can see at this point is that we’re going into a loop. Remove the task from the Binder_DEFERred_list until there is nothing left in the queue, then jump out of the loop with the binder_proc pointing to null. It is worth noting that the flag bit passed in is BINDER_DEFERRED_RELEASE, so we will execute the core method BINDER_DEFERRED_RELEASE.

App process clears data from binder driver (binder_deferred_release)

The actual start of removing data attached to the Binder_proc from the Binder driver will be cleared in the Binder_deferred_release

static void binder_deferred_release(struct binder_proc *proc) { struct binder_transaction *t; struct rb_node *n; int threads, nodes, incoming_refs, outgoing_refs, buffers, active_transactions, page_count; BUG_ON(proc->vma); BUG_ON(proc->files); hlist_del(&proc->proc_node); If (binder_context_mgr_node && binder_context_mgr_node->proc == proc) { binder_debug(BINDER_DEBUG_DEAD_BINDER, "%s: %d context_mgr_node gone\n", __func__, proc->pid); binder_context_mgr_node = NULL; } threads = 0; active_transactions = 0; while ((n = rb_first(&proc->threads))) { struct binder_thread *thread; thread = rb_entry(n, struct binder_thread, rb_node); threads++; active_transactions += binder_free_thread(proc, thread); } // Release the data in nodes of binder_proc. Nodes = 0; incoming_refs = 0; while ((n = rb_first(&proc->nodes))) { struct binder_node *node; node = rb_entry(n, struct binder_node, rb_node); nodes++; rb_erase(&node->rb_node, &proc->nodes); incoming_refs = binder_node_release(node, incoming_refs); } // Release data from binder_proc refs_by_desc outgoing_refs = 0; while ((n = rb_first(&proc->refs_by_desc))) { struct binder_ref *ref; ref = rb_entry(n, struct binder_ref, rb_node_desc); outgoing_refs++; binder_delete_ref(ref); } binder_release_work(&proc->todo); binder_release_work(&proc->delivered_death); // Release the allocated_buffers data in use, buffers = 0; while ((n = rb_first(&proc->allocated_buffers))) { struct binder_buffer *buffer; buffer = rb_entry(n, struct binder_buffer, rb_node); t = buffer->transaction; if (t) { t->buffer = NULL; buffer->transaction = NULL; pr_err("release proc %d, transaction %d, not freed\n", proc->pid, t->debug_id); /*BUG(); */ } binder_free_buf(proc, buffer); buffers++; } binder_stats_deleted(BINDER_STAT_PROC); Page_count = 0; if (proc->pages) { int i; for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) { void *page_addr; if (! proc->pages[i]) continue; page_addr = proc->buffer + i * PAGE_SIZE; binder_debug(BINDER_DEBUG_BUFFER_ALLOC, "%s: %d: page %d at %p not freed\n", __func__, proc->pid, i, page_addr); unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); __free_page(proc->pages[i]); page_count++; } kfree(proc->pages); vfree(proc->buffer); } put_task_struct(proc->tsk); binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n", __func__, proc->pid, threads, nodes, incoming_refs, outgoing_refs, active_transactions, buffers, page_count); // Release binder_proc kfree(proc); }Copy the code

Data release is divided into seven steps.

If binder_context_mGR_node is added to the binder_context_mgr_node process, the global binder_node is cleared. This object is the binder_node of the Service_Manager process

2. Release the data in the binder_thread list of binder_proc. This list manages all binder_threads. This binder object is created for each new process that wants to communicate with a new binder process through the iocTL. And of course, when you use poll, you create a new poll. Binder_free_thread is called to clean up the todo work list in binder_thread. The option chosen here is to pass all of the binder’s work tasks one by one to each peer that requires that work, thus cleaning up the work list and dependency stack.

3. Release data in the Nodes link list of binder_PROC. This list manages all binder objects that are transferred through the current binder_proc process, so it also needs to be cleaned up.

4. Release the data in binder_proc refS_BY_DESC. This is a reference that represents all the entities in the binder_proc process. We will use the reference to find the corresponding entity, so we need to clean up the binder_node entity first and then clean up the reference.

5. Release the allocated_buffers data in use. This is because the binder_thread has already been cleaned up, but allocated_buffer is usually cleaned up after the response from the other end. In this case, allocated_buffer does not wait, but clears itself.

6. Release the todo list of binder_proc (binder_work), which is the list of work to be processed until the binder_thread has finished processing. There are delivered_death

7. Release the mapped address. In this case, release the shared address mapped by mMAP.

8. Release binder_proc data. If there is no data left in binder_proc, you can directly release the binder_proc data.

Free the binder_thread list for binder_proc

Binder_free_thread is particularly important as a binder driver with transaction passing at its core.

static int binder_free_thread(struct binder_proc *proc, struct binder_thread *thread) { struct binder_transaction *t; struct binder_transaction *send_reply = NULL; int active_transactions = 0; Rb_erase (&thread->rb_node, &proc->threads); T = thread->transaction_stack; if (t && t->to_thread == thread) send_reply = t; while (t) { active_transactions++; if (t->to_thread == thread) { t->to_proc = NULL; t->to_thread = NULL; if (t->buffer) { t->buffer->transaction = NULL; t->buffer = NULL; } t = t->to_parent; } else if (t->from == thread) { t->from = NULL; t = t->from_parent; } else BUG(); If (send_reply) binder_send_failed_reply(send_reply, BR_DEAD_REPLY); if (send_reply) binder_send_failed_reply(send_reply, BR_DEAD_REPLY); Binder_release_work (&thread->todo); // Release thread kfree(thread); binder_stats_deleted(BINDER_STAT_THREAD); return active_transactions; }Copy the code

1. First erase the binder_thread at the root of the red-black tree from the erasing threads in binder_proc.

2. Find out from the transaction dependency stack which transactions want to send the current process. Once you find tasks that you want to find to send the current closed process, replace those tasks and send the BR_DEAD_REPLY command directly.

3. Once these tasks are done, clear the todo list with binder_release_work

4. Release the binder_thread structure.

So there are two core logic points worth paying attention to:

1. Binder_send_failed_reply sends the death command

2. Binder_release_work releases the ToDO list

Binder_send_failed_reply Sends the death command

static void binder_send_failed_reply(struct binder_transaction *t, uint32_t error_code) { struct binder_thread *target_thread; struct binder_transaction *next; BUG_ON(t->flags & TF_ONE_WAY); while (1) { target_thread = t->from; if (target_thread) { if (target_thread->return_error ! = BR_OK && target_thread->return_error2 == BR_OK) { target_thread->return_error2 = target_thread->return_error; target_thread->return_error = BR_OK; } if (target_thread->return_error == BR_OK) { .... binder_pop_transaction(target_thread, t); target_thread->return_error = error_code; wake_up_interruptible(&target_thread->wait); } else { .... } return; } next = t->from_parent; . binder_pop_transaction(target_thread, t); if (next == NULL) { ... return; } t = next; . }}Copy the code

In fact, the logic here is very similar to that of binder_transaction. The difference is that all transactions in the stack of binder_transaction will always be found, the peer process that is blocking will be replaced, and the transaction will be ejected. When an App process dies, all transactions that communicate to other processes are eactivated and woken up (the binder_thread status is BR_OK). The BR_DEAD_REPLY command is sent to set the return code of the corresponding binder_thread at the peer end. Tell them that the service process is dead at this point.

Therefore, there are actually two scenarios that can trigger the read peer (AMS) to read data. The first is that when AMS sends the registration death notification and finds that it is dead, it immediately wakes up its own end to read data. The other is to send the corresponding BR_DEAD_REPLY when the current process (App) dies.

At this point, the two processes begin to separate and work at the same time. One App dies and continues to recycle resources, and the other AMS wakes up and reads data. Let’s move on to the binder_release_work method after the App dies.

binder_release_work

In fact, we see from the binder_free_thread section above that it is actually processing messages sent to those that require a response from the current process (in this case, the App process). In other words, you’re dealing with outgoing transactions.

Binder_release_work is actually handling the read command.

static void binder_release_work(struct list_head *list)
{
    struct binder_work *w;

    while (!list_empty(list)) {
        w = list_first_entry(list, struct binder_work, entry);
        list_del_init(&w->entry);
        switch (w->type) {
        case BINDER_WORK_TRANSACTION: {
            struct binder_transaction *t;

            t = container_of(w, struct binder_transaction, work);
            if (t->buffer->target_node &&
                !(t->flags & TF_ONE_WAY)) {
                binder_send_failed_reply(t, BR_DEAD_REPLY);
            } else {
                t->buffer->transaction = NULL;
                kfree(t);
                binder_stats_deleted(BINDER_STAT_TRANSACTION);
            }
        } break;
        case BINDER_WORK_TRANSACTION_COMPLETE: {
            kfree(w);
            binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
        } break;
        case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
        case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
            struct binder_ref_death *death;

            death = container_of(w, struct binder_ref_death, work);
            kfree(death);
            binder_stats_deleted(BINDER_STAT_DEATH);
        } break;
        default:
            break;
        }
    }
}
Copy the code

There are four kinds of jobs handled here:

BINDER_WORK_TRANSACTION reads the todo list, releases the work item, and sends a BR_DEAD_REPLY to simulate the above situation.

BINDER_WORK_TRANSACTION_COMPLETE encounters this and releases the current transaction work directly as before

BINDER_WORK_DEAD_BINDER_AND_CLEAR/BINDER_WORK_CLEAR_DEATH_NOTIFICATION: Find the death object in the work item and clean it.

binder_delete_ref

This method will remove the binder reference object

static void binder_delete_ref(struct binder_ref *ref)
{
...

    rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
    rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
    if (ref->strong)
        binder_dec_node(ref->node, 1, 1);
    hlist_del(&ref->node_entry);
    binder_dec_node(ref->node, 0, 1);
    if (ref->death) {
...
        list_del(&ref->death->work.entry);
        kfree(ref->death);
        binder_stats_deleted(BINDER_STAT_DEATH);
    }
    kfree(ref);
    binder_stats_deleted(BINDER_STAT_REF);
}
Copy the code

As you can see from the above panel, the current reference needs to be removed from the two common black trees that manage binder_ref, along with the node pointer. Finally, if a death callback is found in the reference, the death->work.entry is deleted.

The peer (AMS) is awakened to read data

At this point we’re going to start reading from the AMS peer and see what happens with binder_thread_read

case BINDER_WORK_DEAD_BINDER:
        case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
        case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
            struct binder_ref_death *death;
            uint32_t cmd;

            death = container_of(w, struct binder_ref_death, work);
            if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
                cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
            else
                cmd = BR_DEAD_BINDER;
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            if (put_user(death->cookie,
                     (binder_uintptr_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);
            binder_stat_br(proc, thread, cmd);
            if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
                list_del(&w->entry);
                kfree(death);
                binder_stats_deleted(BINDER_STAT_DEATH);
            } else
                list_move(&w->entry, &proc->delivered_death);
            if (cmd == BR_DEAD_BINDER)
                goto done; /* DEAD_BINDER notifications can cause transactions */
        } break;
Copy the code

At this point, the peer AMS will start to read the commands in todo. This is based on the binder_transaction method of the App process. The BINDER_WORK_DEAD_BINDER command is not read in Todo if the target process is not dead. But when the App dies, the BINDER_WORK_DEAD_BINDER command exists.

Three commands are processed in this branch, one is the BINDER_WORK_DEAD_BINDER command for registering death notifications, and the other two are to empty death notifications. We’re only interested in the first one.

What we can see is finding the death reference object (death_ref) from Todo, converting the command to BR_DEAD_BINDER and copying the data into user space.

Next, let’s see how looper in IPCThreadState gets the data.

IPCThreadState::executeCommand

    case BR_DEAD_BINDER:
        {
            BpBinder *proxy = (BpBinder*)mIn.readPointer();
            proxy->sendObituary();
            mOut.writeInt32(BC_DEAD_BINDER_DONE);
            mOut.writePointer((uintptr_t)proxy);
        } break;
Copy the code

There are two steps:

1. The sendObituary method is invoked after BpBinder is obtained.

2. Continue to write BC_DEAD_BINDER_DONE to the Binder driver. Death ->work.entry initialized when the method is sent back to delete the previous registration

BpBinder::sendObituary

void BpBinder::sendObituary() { mAlive = 0; if (mObitsSent) return; mLock.lock(); Vector<Obituary>* obits = mObituaries; if(obits ! = NULL) { IPCThreadState* self = IPCThreadState::self(); self->clearDeathNotification(mHandle, this); self->flushCommands(); mObituaries = NULL; } mObitsSent = 1; mLock.unlock(); if (obits ! = NULL) { const size_t N = obits->size(); for (size_t i=0; i<N; i++) { reportOneDeath(obits->itemAt(i)); } delete obits; }}Copy the code

In this case, there are two steps:

1. Run the clearDeathNotification command to send the command to BC_CLEAR_DEATH_NOTIFICATION.

2. Check how many death agents are bound in the vector and call reportOneDeath method one by one to report to the upper layer.

BC_CLEAR_DEATH_NOTIFICATION command

This command corresponds to the register death agent above

case BC_REQUEST_DEATH_NOTIFICATION: case BC_CLEAR_DEATH_NOTIFICATION: { uint32_t target; binder_uintptr_t cookie; struct binder_ref *ref; struct binder_ref_death *death; if (get_user(target, (uint32_t __user *)ptr))//BC_CLEAR_DEATH_NOTIFICATION return -EFAULT; ptr += sizeof(uint32_t); if (get_user(cookie, (binder_uintptr_t __user *)ptr))//BpBinder return -EFAULT; ptr += sizeof(binder_uintptr_t); ref = binder_get_ref(proc, target); if (ref == NULL) { break; } if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { .... } else { if (ref->death == NULL) { ... break; } death = ref->death; if (death->cookie ! = cookie) { ... break; } ref->death = NULL; if (list_empty(&death->work.entry)) { death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { list_add_tail(&death->work.entry,  &thread->todo); } else { list_add_tail(&death->work.entry, &proc->todo); wake_up_interruptible(&proc->wait); } } else { ... death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR; } } } break;Copy the code

In this case, BC_REQUEST_DEATH_NOTIFICATION is processed almost the same as above, except that when it is determined that todo does not have any work items, a new queue is set to wake up the current process to continue reading data. Otherwise, do nothing but find the corresponding binder reference and convert the type of death to BINDER_WORK_DEAD_BINDER_AND_CLEAR.

The current process (AMS) continues to read data

if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
                list_del(&w->entry);
                kfree(death);
                binder_stats_deleted(BINDER_STAT_DEATH);
            }
Copy the code

The job is simply to release the death object corresponding to binder_ref in the current process.

So the BC_CLEAR_DEATH_NOTIFICATION command just ends up freeing death’s memory from the callback.

ReportOneDeath is reflected back to the Java layer

void BpBinder::reportOneDeath(const Obituary& obit)
{
    sp<DeathRecipient> recipient = obit.recipient.promote();
    if (recipient == NULL) return;

    recipient->binderDied(this);
}
Copy the code

The DeathRecipient structure, which in this case is the JavaDeathRecipient class, is captured in Obituary. Let’s look at the method of binderDied.

void binderDied(const wp<IBinder>& who) { ... if (mObject ! = NULL) { JNIEnv* env = javavm_to_jnienv(mVM); env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mSendDeathNotice, mObject); . }}Copy the code

Here you can see the core call method, reflecting the call to the sendDeathNotice method of the Java layer BinderProxy.

private static final void sendDeathNotice(DeathRecipient recipient) { if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient); try { recipient.binderDied(); } catch (RuntimeException exc) { Log.w("BinderNative", "Uncaught exception from death notification", exc); }}Copy the code

Isn’t the Recipient the class of AppDeathRecipient that we registered in the very beginning? Just in time to return to binderDied.

conclusion

At this point, the entire linkToDeath process is complete. In fact, this method can be used to determine if linkToDeath is registered, and the binder object of the current process keeps watching the remote binder to see if it is alive.

When the peer end exits or processes end due to an exception, a death callback is notified to the binder object observing the other end. AMS will clear the information stored in the internal Activity and destroy the callback to stop listening for the other end’s death.

So, the core idea of the design is as follows: when the remote isn’t dead or is dying, it doesn’t do anything at all to add to its own process’s toDO list to read commands into the callback. Once the remote dies, the object is immediately added to the ToDO list and the other side wakes up to perform the callback.

Here is the linkToDeath sequence:

Below is the timing diagram of the death callback when the App is in progress