Analysis of Binder (I)

After the introduction of the last blog, I believe you have a certain understanding of binder mechanism. This blog will explain the code flow of COMMUNICATION between AMS and SM, and it will be a long one. Please be patient to finish it!

(The following analysis is Android 6.0 source code)

How did SM get started?

AMS is registered with a ServiceManager (hereafter referred to as SM) upon startup. So how does SM get started?

SM is created by the init process by parsing the init.rc file. The corresponding executable program servicemanager is created by the source file service_manager.c and the process name is Servicemanager.

The entry function to start SM is the main method in service_manager.c.

int main(int argc, char **argv)
{
    struct binder_state *bs;
    // Open binder driver to map 128K memory space
    bs = binder_open(128*1024); .// Register the current process as SM
    if (binder_become_context_manager(bs))
    {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return - 1; }...// Enter an infinite loop to listen for requests from AMS
    binder_loop(bs, svcmgr_handler);
    return 0;
}
Copy the code

Let’s first look at what the binder_open function does.

// frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;.// Open the binder driver, get the file descriptor, and call binder_open in the binder driver
    bs->fd = open("/dev/binder", O_RDWR); .// Map memory size: 128K
    bs->mapsize = mapsize;
    // Call binder_mmap in binder drivers
    // Map 128K memory space between user space and kernel space of the current process
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); .returnbs; . }Copy the code

It is important to note that the size of SM’s cross-process communication data cannot exceed 128K. Since SM records only references to entity nodes in binder drivers for registered services (which will be covered later), 128K is sufficient.

So let’s see what the binder_become_context_manager function does.

// frameworks/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
    // The binder_ioctl function in binder drivers is called by the system with the instruction BINDER_SET_CONTEXT_MGR
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

// kernel/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, 
                        unsigned int cmd, // CMD is the BINDER_SET_CONTEXT_MGR we passed in
                        unsigned long arg
){...switch (cmd)
    {
        ......
        case BINDER_SET_CONTEXT_MGR:
            ret = binder_ioctl_set_ctx_mgr(filp); .break; . }...return ret;
}

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
    // Obtain device node entities for binder drivers by using filp->private_data
    struct binder_proc *proc =filp->private_data; .// Ensure that binder_context_mgr_node is created only once
    if (context->binder_context_mgr_node)
    {
        ......
        gotoout; }...// whether the uid is valid, currently not
    if (uid_valid(context->binder_context_mgr_uid))
    {
        if (!uid_eq(context->binder_context_mgr_uid, curr_euid))
        {
            ......
            gotoout; }}else
    {
        // Set the current thread euID as the UID of the SM
        context->binder_context_mgr_uid = curr_euid;
    }
    // Create SM's binder node entities in binder drivers
    context->binder_context_mgr_node = binder_new_node(proc, 0.0); . out:return ret;
}

static struct binder_node *binder_new_node(struct binder_proc *proc, // Device node entities driven by binder
					   binder_uintptr_t ptr,
					   binder_uintptr_t cookie
){
    struct rb_node支那p = &proc->nodes.rb_node;
    struct rb_node *parent = NULL;
    struct binder_node *node;.// Allocate kernel space to the newly created binder_node
    node = kzalloc(sizeof(*node), GFP_KERNEL); .// Add the newly created binder_node to the binder_node red-black tree of the device node
    rb_link_node(&node->rb_node, parent, p);
    rb_insert_color(&node->rb_node, &proc->nodes); .// Initialize the newly created binder_node
    node->proc = proc;
    node->ptr = ptr;
    node->cookie = cookie;
    node->work.type = BINDER_WORK_NODE;
    INIT_LIST_HEAD(&node->work.entry);
    INIT_LIST_HEAD(&node->async_todo); .return node;
}
Copy the code

The binder_become_context_manager function basically creates the SM node entities in the Binder driver.

Now let’s see what the binder_loop function does.

// frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    // Send BC_ENTER_LOOPER to binder driver,
    // Add the flag BINDER_LOOPER_STATE_ENTERED to the current thread's looper
    binder_write(bs, readbuf, sizeof(uint32_t));

    for (;;)
    {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        Binder_ioctl is called by the binder driver with BINDER_WRITE_READ
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); . }}// kernel/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, 
                        unsigned int cmd, // CMD is the BINDER_WRITE_READ we pass in
                        unsigned long arg // arg is the BWR we passed in
){
    // Obtain device nodes driven by binder
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;./ / create binder_thread
    thread = binder_get_thread(proc); .switch (cmd)
    {
        case BINDER_WRITE_READ:
            ret = binder_ioctl_write_read(filp, cmd, arg, thread); .break; . }...return ret;
}

static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread
){
    struct binder_proc *proc =filp->private_data; .void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;
    
    // Copy the binder_write_read structure (ubuF) describing the data from user space to kernel space (BWR)
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
    ......
    // bwr.write_size = 0, if not matched
    if (bwr.write_size > 0) {... }// bwr.read_size > 0, if hit
    if (bwr.read_size > 0)
    {
        // Read the data returned by binder drivers
        ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); . }...return ret;
}

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block
){
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    // pointer to the starting address of bwr.read_buffer in binder_loop
    void __user *ptr = buffer + *consumed;
    // pointer to the end address of bwr.read_buffer in binder_loop
    void __user *end = buffer + size;
    
    int ret = 0;
    int wait_for_proc_work;

    // Now consumed = 0, if matches
    if (*consumed == 0)
    {
        // Copy the directive BR_NOOP into user space
        if (put_user(BR_NOOP, (uint32_t __user *)ptr))
            return -EFAULT;
        // Move the pointer back
        ptr += sizeof(uint32_t);
    }

    Thread ->todo = NULL, wait_for_proc_work = true
    wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); .if (wait_for_proc_work)
    {
        ......
        // Filp ->f_flags & O_NONBLOCK is already assigned
        // non_block > 0, else
        if (non_block)
        {
            ......
        }
        else
            Thread ->todo = NULL, binder_has_proc_work returns false, and suspends the SM thread
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    }
    else{... }}Copy the code

At this point, the SM is suspended and waits for another process to communicate with it to wake it up.

The awakened SM continues to execute the unfinished binder_thread_read function.

How do I register for AMS?

After the SM is started, it waits for AMS to communicate with it. The process of registering AMS into SM is also the process of COMMUNICATION between AMS and SM.

Registration for AMS is performed in SystemServer#main.

// frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args)
{
    new SystemServer().run();
}

private void run(a)
{.../ / create SystemServiceManager
    mSystemServiceManager = newSystemServiceManager(mSystemContext); . startBootstrapServices(); }private void startBootstrapServices(a)
{.../ / create ActivityManagerService through reflection. Lifecycle and get the AMS objectmActivityManagerService = mSystemServiceManager.startService( ActivityManagerService.Lifecycle.class).getService(); .// Start the system process for AMS
    mActivityManagerService.setSystemProcess();
}

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void setSystemProcess(a)
{...// Register AMS with SM
    ServiceManager.addService(Context.ACTIVITY_SERVICE, this.true); . }Copy the code

Let’s move on to the ServiceManager#addService function.

// frameworks/base/core/java/android/os/ServiceManager.java
public static void addService(String name, IBinder service, boolean allowIsolated)
{... getIServiceManager().addService(name, service, allowIsolated); . }private static IServiceManager getIServiceManager(a)
{
    if(sServiceManager ! =null)
    {
        return sServiceManager;
    }
    / / create ServiceManagerProxy
    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
    return sServiceManager;
}

// frameworks/base/core/java/com/android/internal/os/BinderInternal.java
public static final native IBinder getContextObject(a);

// frameworks/base/core/jni/android_util_Binder.cpp
static const JNINativeMethod gBinderInternalMethods[] = {
    ......
    { "getContextObject"."()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
    ......
};

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    // ProcessState::self() --> Open binder driver and create ProcessState (ProcessState is singleton)
    / / ProcessState: : getContextObject (NULL) - > create BpBinder object and return
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    // Create a BinderProxy object and return it
    return javaObjectForIBinder(env, b);
}
Copy the code

Let’s first look at what the ProcessState::self function does.

// frameworks/native/libs/binder/ProcessState.cpp
sp<ProcessState> ProcessState::self(a)
{
    Mutex::Autolock _l(gProcessMutex);
    // Singleton mode
    if(gProcess ! =NULL)
    {
        return gProcess;
    }
    / / create ProcessState
    gProcess = new ProcessState;
    return gProcess;
}

// BINDER_VM_SIZE the BINDER_VM_SIZE ranges from 1M to 8K
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))

ProcessState::ProcessState()
    : mDriverFD(open_driver()) // Start the binder driver. {// If the binder driver is enabled successfully, the binder driver node is obtained
    if (mDriverFD >= 0)
    {
        // Call binder_mmap in binder drivers
        // Map BINDER_VM_SIZE (1m-8K) between user space and kernel space of the current process
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); . }... }Copy the code

The size of AMS cross-process communication data cannot exceed 1m-8K.

This is a common interview question: Does an Intent have a size limit on the data it transmits? What is the size limit?

The answer is 1M minus 8K. Because Intents are how the four components communicate, and AMS manages the four components, using Intents requires communication with AMS. Due to limitations of binder mechanisms, data sizes for communication with AMS cannot exceed 1m-8K.

We then see ProcessState: : what did getContextObject function.

// frameworks/native/libs/binder/ProcessState.cpp
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{ sp<IBinder> result; .// The current handle is 0, and if matches
    if (handle == 0)
    {
        Parcel data;
        // Test SM readiness through the ping operation
        status_t status = IPCThreadState::self() - >transact(
                0, IBinder::PING_TRANSACTION, data, NULL.0);
        if (status == DEAD_OBJECT)
            return NULL;
    }
    // Create a BpBinder object with handle 0
    b = new BpBinder(handle); . result = b; .return result;
}
Copy the code

Let’s go back to the android_os_BinderInternal_getContextObject function and see what the javaObjectForIBinder function does.

jobject javaObjectForIBinder(JNIEnv* env, 
                            const sp<IBinder>& val // val = BpBinder
){...// Find the BinderProxy object from gBinderProxyOffsets
    // Object is null because BinderProxy has not been created
    jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
    if(object ! =NULL)
    {
        jobject res = jniGetReferent(env, object);
        if(res ! =NULL) {...// If a BinderProxy object has been created in the current process, it is returned directly
            returnres; }... }.../ / create BinderProxy
    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
    if(object ! =NULL)
    {
        // Assign mObject (BpBinder) to gBinderProxyOffsets,
        // Record the BpBinder object
        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get()); .// Add BinderProxy object information to BpBinder's member variable mObjects,
        // Bind BinderProxy to BpBinder
        val->attachObject(&gBinderProxyOffsets, refObject, jnienv_to_javavm(env), proxy_cleanup); .// Binderproxy. mOrgue member variable records the death notification object
        env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get())); . }return object;
}
Copy the code

You can see that BinderProxy is created only once per process.

Each BinderProxy object is bound to a BpBinder object. In this case, the BpBinder object corresponds to SM (handle is 0).

Now that we’ve analyzed what getIServiceManager does, we return ServiceManagerProxy# addService, and look at what ServiceManagerProxy#addSevice does.

// frameworks/base/core/java/android/os/ServiceManagerNative.java$ServiceManagerProxy.java
public void addService(String name, IBinder service, boolean allowIsolated) throws RemoteException
{
    / / create the data
    Parcel data = Parcel.obtain();
    / / create the reply
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IServiceManager.descriptor);
    data.writeString(name);
    // write AMS to data
    data.writeStrongBinder(service);
    data.writeInt(allowIsolated ? 1 : 0);
    // mRemote = BinderProxy, equivalent to calling BinderProxy#transact
    mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
    reply.recycle();
    data.recycle();
}
Copy the code

How does AMS package data?

Let’s first look at how AMS is written to data.

// frameworks/base/core/java/android/os/Parcel.java
public final void writeStrongBinder(IBinder val)
{
    nativeWriteStrongBinder(mNativePtr, val);
}

// frameworks/base/core/jni/android_os_Parcel.cpp
static const JNINativeMethod gParcelMethods[] = {
    ......
    {"nativeWriteStrongBinder"."(JLandroid/os/IBinder;) V", (void*)android_os_Parcel_writeStrongBinder},
    ......
}

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object // object = AMS ){
    // Cast Java layer Parcel type to Native layer Parcel type.
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if(parcel ! = NULL) {conststatus_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object)); . }}// frameworks/base/core/jni/android_util_Binder.cpp
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj ){
    if (obj == NULL)
        return NULL;
    // if obj is type Binder, then obj = AMS, AMS inherits from binder.java, if hit
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass))
    {
        // Get the JavaBBinderHolder object from the gBinderOffsets. MObject member property
        JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
        // create JavaBBinder with JavaBBinderHolder#get
        returnjbh ! = NULL ? jbh->get(env, obj) : NULL; }...return NULL;
}
Copy the code

The JavaBBinderHolder is created with AMS creation and is stored in gBinderOffsets. MObject.

JavaBBinder objects can be created by calling the JavaBBinderHolder#get function. The JavaBBinder object created at this point holds AMS in the mObject field.

Let’s look at what the Parcel#writeStrongBinder function does.

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

status_t flatten_binder(const sp<ProcessState>& /*proc*/.const sp<IBinder>& binder, // binder = JavaBBinder
    Parcel* out // out = ServiceManagerProxy#addSevice data defined in the function
){
    // Encapsulates JavaBBinderflat_binder_object obj; .if(binder ! =NULL)
    {
        // Check whether binder object is BpBinder or BBinder
        // JavaBBinder is BBinder, local is not NULL, else
        IBinder *local = binder->localBinder(a);if(! local) { ...... }else
        {
            obj.type = BINDER_TYPE_BINDER;
            // local->getWeakRefs() = JavaBBinder = AMS
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local); }}else{... }return finish_flatten_binder(binder, obj, out);
}

inline static status_t finish_flatten_binder(const sp<IBinder>& /*binder*/.const flat_binder_object& flat, Parcel* out)
{
    // Write the flat_binder_object object to data
    return out->writeObject(flat, false);
}
Copy the code

Flat_binder_object will appear later in the process, and you need to remember it. When the data is transferred to the binder driver, it is converted to a flat_binder_object structure for processing.

Flat_binder_object encapsulates JavaBBinder, which in turn holds AMS. Flat_binder_object encapsulates AMS.

Note that flat_binder_object.type = BINDER_TYPE_BINDER.

AMS is eventually encapsulated as flat_binder_object and written to data.

How does AMS send data to SM?

Let’s look at what the BinderProxy#transact function does.

// android\frameworks\base\core\java\android\os\Binder.java
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {...return transactNative(code, data, reply, flags);
}

public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
            
// android\frameworks\base\core\jni\android_util_Binder.cpp
static const JNINativeMethod gBinderProxyMethods[] = {
    ......
    {"transactNative"."(ILandroid/os/Parcel; Landroid/os/Parcel; I)Z", 
            (void*)android_os_BinderProxy_transact},
    ......
};
Copy the code

Android_os_BinderProxy_transact (); android_os_BinderProxy_transact (); android_os_BinderProxy_transact ();

// android\frameworks\base\core\jni\android_util_Binder.cpp
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
                    jint code, // code = ADD_SERVICE_TRANSACTION
                    jobject dataObj,
                    jobject replyObj,
                    jint flags
){...// Convert the type of dataObj to Parcel
    Parcel* data = parcelForJavaObject(env, dataObj); .// Convert the type of replyObj to Parcel
    Parcel* reply = parcelForJavaObject(env, replyObj); ./ / get gBinderProxyOffsets mObject BpBinder objects stored in
    IBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject); .// Call the transact function to the BpBinder object
    status_t err = target->transact(code, *data, reply, flags); . }Copy the code

Let’s move on to the BpBinder#transact function.

// android\frameworks\native\libs\binder\BpBinder.cpp
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{...// mHandle = 0
    status_t status = IPCThreadState::self() - >transact(mHandle, code, data, reply, flags); . }Copy the code

Let’s look at what the IPCThreadState#self function does.

// android\frameworks\native\libs\binder\BpBinder.cpp
IPCThreadState* IPCThreadState::self(a)
{...// Create IPCThreadState
    return newIPCThreadState; . } IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()), // Call ProcessState::self. {... mIn.setDataCapacity(256); // Set the mIn size
    mOut.setDataCapacity(256); // Set the size of mOut
}
Copy the code

In the IPCThreadState#self function, the IPCThreadState and ProcessState objects are primarily initialized (ProcessState was initialized earlier).

Now let’s look at what’s going on in the IPCThreadState#transact function.

// android\frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle, // handle = 0
                        uint32_t code, // code = ADD_SERVICE_TRANSACTION
                        const Parcel& data,
                        Parcel* reply,
                        uint32_t flags
){
    // Data error check
    status_t err = data.errorCheck(a);// TF_ACCEPT_FDS = 0x10: allow the file descriptor to be included in the reply
    // TF_ONE_WAY: The current service is asynchronous and there is no need to wait
    // TF_ROOT_OBJECT: Contains the root object
    // TF_STATUS_CODE: contains a 32-bit status valueflags |= TF_ACCEPT_FDS; .// Write flat_binder_object (data) to mOut
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); .// Check whether the current service is asynchronous.
    // AIDL is synchronous by default, unless the oneway keyword is used to define the AIDL interface.
    if ((flags & TF_ONE_WAY) == 0) {...// Reply is not empty
        if (reply)
        {
            // Wait for binder drivers to respond to events
            err = waitForResponse(reply);
        }
        else
        {
            Parcel fakeReply;
            // Wait for binder drivers to respond to events
            err = waitForResponse(&fakeReply); }... }else
    {
        // Do not wait for binder drivers to respond to events
        err = waitForResponse(NULL.NULL); }... }Copy the code

MOut is divided into two parts:

  • Command part (address high in mOut)
  • The data section, where the encapsulated FLAT_binder_object is located (address low in mOut)

Continue with the IPCThreadState#waitForResponse function.

// android\frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(
                    Parcel *reply,
                    status_t *acquireResult
){
    uint32_t cmd;
    int32_t err;
    // Loop to wait for results
    while (1)
    {
        // Call talkWithDriver to send instructions to binder drivers
        if ((err=talkWithDriver()) < NO_ERROR) break; .// Continue if there is no data in mIn
        if (mIn.dataAvail() = =0) continue; . }... }Copy the code

Let’s look at how the IPCThreadState#talkWithDriver function sends instructions to the binder driver.

// android\frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{...// mIn is the Parcel variable defined by the macro, and mIn is not assigned
    // min.dataposition () = min.datasize () = 0, needRead is true
    const bool needRead = mIn.dataPosition() >= mIn.dataSize(a); .const size_toutAvail = (! doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    // at this point, bwr.write_buffer points to the starting address of mOut
    bwr.write_buffer = (uintptr_t)mOut.data(a);// If you need to read data
    if (doReceive && needRead)
    {
        // dataCapacity = 256
        bwr.read_size = mIn.dataCapacity(a);// min.data () = 0
        bwr.read_buffer = (uintptr_t)mIn.data(a); }else // Set to zero without reading data.
         // Then binder_thread_read will not be executed in the binder_ioctl_write_read function
    {
        bwr.read_size = 0;
        bwr.read_buffer = 0; }... bwr.write_consumed =0;
    bwr.read_consumed = 0;
    // Loop instructions to binder drivers until they succeed
    do{...Binder_ioctl = binder_ioctl; // Binder_ioctl = binder_ioctl
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        elseerr = -errno; . }while (err == -EINTR); // The while condition is true only if an ioctl error occurs,
                             // Therefore, if the message is sent successfully, it is executed only once. }Copy the code

BINDER_WRITE_READ When an ioctl function is called, a system call (Syscall) is made to BINDER_WRITE_READ in Binder drivers.

// kernel\drivers\staging\android\binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, 
                        unsigned long arg
){...void __user *ubuf = (void__user *)arg; .// Get the command (BINDER_WRITE_READ)
    trace_binder_ioctl(cmd, arg); .switch (cmd)
    {
        case BINDER_WRITE_READ:
            ret = binder_ioctl_write_read(filp, cmd, arg, thread); . }}Copy the code

Continue with the binder_iocTL_write_read function.

// kernel\drivers\staging\android\binder.c
static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
    struct binder_proc *proc =filp->private_data; .void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;

    // Copy the starting address of mOut from user space to kernel space (BWR)
    if (copy_from_user(&bwr, ubuf, sizeof(bwr)))
    {
        ret = -EFAULT;
        gotoout; }...// bwr.write_size > 0, if hit
    if (bwr.write_size > 0)
    {
        // Send instructions to binder drivers
        ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); . }// bwr.read_size > 0, if hit
    if (bwr.read_size > 0)
    {
        // Read the instructions returned by binder drivers
        ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); . }... out:return ret;
}
Copy the code

Let’s first look at what the binder_thread_write function does.

// kernel\drivers\staging\android\binder.c
static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,
			binder_uintptr_t binder_buffer, size_t size,
			binder_size_t *consumed)
{
    uint32_t cmd;
    struct binder_context *context = proc->context;
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    // A pointer to the starting address of mOut
    void __user *ptr = buffer + *consumed;
    // A pointer to the end address of mOut
    void __user *end = buffer + size;
    
    // Iterate over the mOut memory pointer to fetch the instruction
    while (ptr < end && thread->return_error == BR_OK)
    {
        // Copy the commands in the mOut command section from user space to kernel space (CMD),
        CMD = BC_TRANSACTION
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        // Move the pointer back
        ptr += sizeof(uint32_t); .switch (cmd)
        {
            ......
            case BC_TRANSACTION:
            case BC_REPLY:
            {
                struct binder_transaction_data tr;
                // Copy the starting address of the mOut data section from user space to kernel space (binder_transaction_data)
                if (copy_from_user(&tr, ptr, sizeof(tr)))
                    return -EFAULT;
                // Move the pointer back
                ptr += sizeof(tr);
                // Call binder_transaction
                binder_transaction(proc, thread, &tr, cmd == BC_REPLY, 0);
                break; }... }... }... }Copy the code

It’s not really a copy yet.

Continue with the binder_TRANSACTION function.

// kernel\drivers\staging\android\binder.c
static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, 
                               int reply, // Reply = false
			       binder_size_t extra_buffers_size)
{...struct binder_transaction *t;
    struct binder_work *tcomplete;
    binder_size_t*offp, *off_end, *off_start; .struct binder_proc *target_proc;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct list_head *target_list;
    wait_queue_head_t*target_wait; .// Reply is false (CMD == BC_TRANSACTION)
    if (reply)
    {
        ......
    }
    else
    {
        // tr->target.handle = 0, else
        if (tr->target.handle)
        {
            ......
        }
        else
        {
            // Get the binder_node for SMtarget_node = context->binder_context_mgr_node; . }...// Get the binder_proc for SMtarget_proc = target_node->proc; . }// Get the TOdo queue and wait queue for SMtarget_list = &target_proc->todo; target_wait = &target_proc->wait; .// Generate a binder_transaction variable,
    // Describes the transaction to be done (target_thread->todo).
    // So that when the target object is woken up, it can fetch work from the queue.
    t = kzalloc(sizeof(*t), GFP_KERNEL); .// Generate a binder_work variable (tcomplete) to indicate that AMS has an unfinished job
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); .// If hits
    if(! reply && ! (tr->flags & TF_ONE_WAY))// Non-oneway communication where the current thread is stored in the FROM field of the transaction
        t->from = thread;
    else
        t->from = NULL; .// Record the target process of this communicationt->to_proc = target_proc; .// Code = ADD_SERVICE_TRANSACTION
    t->code = tr->code;
    // Flags = 0t->flags = tr->flags; .// Allocate buffer from SM process
    // To complete this transaction, allocate memory from binder_mmap.
    t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, extra_buffers_size, ! reply && (t->flags & TF_ONE_WAY)); .// Record SM's binder_nodet->buffer->target_node = target_node; .// Record the starting address of the mOut data section
    off_start = (binder_size_t *)(t->buffer->data +
            ALIGN(tr->data_size, sizeof(void*))); offp = off_start; .// Copy mOut data from user space to kernel space (t->buffer->data)
    if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
                    tr->data.ptr.buffer, tr->data_size))
    ......
    // Record the end address of the mOut data section
    off_end = (void*)off_start + tr->offsets_size; .// Iterate over the memory pointer to the mOut data section
    for (; offp < off_end; offp++)
    {
        struct binder_object_header *hdr;.// Convert the data part of mOut to binder_object_headerhdr = (struct binder_object_header *)(t->buffer->data + *offp); .switch (hdr->type) HDR ->type = flat_binder_object.type = BINDER_TYPE_BINDER
        {
            case BINDER_TYPE_BINDER:
            case BINDER_TYPE_WEAK_BINDER:
            {
                struct flat_binder_object *fp;
                // Convert the data to a flat_binder_object structure
                fp = to_flat_binder_object(hdr);
                // Create an entity node for AMS in binder drivers and create a reference to that node (handle)
                ret = binder_translate_binder(fp, t, thread); . }break; . }}...CMD = BC_TRANSACTION, CMD! = BC_REPLY, reply = false
    if (reply)
    {
        ......
    }
    else if(! (t->flags & TF_ONE_WAY))// Operation is synchronous, hit{...// Record this transaction for future query
        // (SM uses this to know who called and returns data)
        thread->transaction_stack = t;
    }
    else{... }// Set t's type to BINDER_WORK_TRANSACTION
    t->work.type = BINDER_WORK_TRANSACTION;
    // Add t to the TOdo queue of SM process
    list_add_tail(&t->work.entry, target_list);
    // Set the binder_work type to BINDER_WORK_TRANSACTION_COMPLETE
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    // Add tComplete to the AMS thread todo queue
    list_add_tail(&tcomplete->entry, &thread->todo);
    // when SM is suspended, target_wait! = NULL, if hit
    if (target_wait)
        // Wake up SM process
        wake_up_interruptible(target_wait);
    return; . }Copy the code

The binder_transaction function does several things:

  1. Get the binder_proc structure for the SM process.
  2. Generate a binder_transaction variable (for SM) and a binder_work variable (for AMS) to hold the information.
  3. Copy the encapsulated FLAT_binder_object into the shared memory created by binder_mmap (emphasis, one copy).
  4. Create an entity node for AMS in binder drivers and create a reference to that node (Handle).
  5. Wake up the SM process by adding the binder_TRANSACTION variable to its TODO queue.
  6. Add the binder_work variable to the TODO queue of the AMS calling thread, and AMS hangs and waits.

So in this function really do IPC communication transmission.

We know that SM and AMS execute code asynchronously, so let’s take a look at what AMS does next.

In AMS, when the binder_transaction function returns from execution, the binder_thread_write function completes execution, followed by the binder_thread_read function.

// kernel\drivers\staging\android\binder.c
static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    // a pointer to the starting address of mIn memory
    void __user *ptr = buffer + *consumed;
    // Pointer to the end address of mIn memory
    void __user *end = buffer + size;

    int ret = 0;
    intwait_for_proc_work; . retry:Thread ->todo is not empty and wait_for_proc_work is false
    Thread ->todo is empty and wait_for_proc_work is true
    wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
    1. Execute else
    // 2. Run if
    if (wait_for_proc_work)
    {
        ......
        if (non_block) // Filp ->f_flags & O_NONBLOCK is already assigned
                       // non_block > 0, else{... }else
            Thread ->todo is null, binder_has_thread_work returns false, and AMS thread is suspended
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    }
    else
    {
        if (non_block) // Filp ->f_flags & O_NONBLOCK is already assigned
                       // non_block > 0, else{... }else
            Thread ->todo is not null. Binder_has_thread_work returns true. AMS threads will not be suspended
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); }...// 1. The thread is not suspended and continues the while loop
    while (1)
    {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
        
        If thread->todo is null, thread->todo is null. If thread->todo is null, thread->todo is null
        BINDER_WORK_TRANSACTION_COMPLETE binder_work_complete binder_work_complete
        // If thread->todo is null, execute else
        if (!list_empty(&thread->todo))
        {
            // 1. Fetch binder_work
            w = list_first_entry(&thread->todo, struct binder_work, entry);
        }
        else
        {
            // 2
            if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                // 2. Run the code back to the retry position
                goto retry;
            break; }...W ->type is BINDER_WORK_TRANSACTION_COMPLETE
        switch (w->type)
        {
            ......
            case BINDER_WORK_TRANSACTION_COMPLETE:
            {
                // 1. Change the directive BR_TRANSACTION_COMPLETE
                cmd = BR_TRANSACTION_COMPLETE;
                // 1. Copy the command to mIn
                if (put_user(cmd, (uint32_t __user *)ptr))
                    return -EFAULT;
                // 1
                ptr += sizeof(uint32_t); .// 1. Delete the current binder_work from thread->todo
                list_del(&w->entry);
                // 1. Release binder_work memory
                kfree(w); . }break; . }// 1. T = null, if hit
        if(! t)continue; . } *consumed = ptr - buffer; .return 0;
}
Copy the code

In the binder_thread_read function, the while loop executes twice, so the comments in the code indicate the ordinal number that corresponds to the number of times in the while loop.

AMS is suspended at this point, waiting for the SM process to return data and wake up. When AMS is woken up, the binder_thread_read function resumes execution from there.

What does the SM process do when it wakes up?

Let’s look at what the SM process does when it wakes up.

In the SM process, when the binder_transaction function returns from completion, the SM process wakes up to continue the unfinished binder_thread_read function.

// kernel\drivers\staging\android\binder.c
static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{...while (1)
    {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL; .// Add a binder_transaction to the binder_transaction function, proc->todo is not null
        else if (!list_empty(&proc->todo) && wait_for_proc_work)
        {
            / / remove binder_transaction
            w = list_first_entry(&proc->todo, struct binder_work, entry); }...W ->type is BINDER_WORK_TRANSACTION
        switch (w->type)
        {
            ......
            case BINDER_WORK_TRANSACTION:
            {
                t = container_of(w, struct binder_transaction, work);
            } break; . }// t! = null, if not hit
        if(! t)continue; .T ->buffer->target_node not null, if hit
        if (t->buffer->target_node)
        {
            struct binder_node *target_node =t->buffer->target_node; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; .// The instruction is changed to BR_TRANSACTION
            cmd = BR_TRANSACTION;
        }
        else{... }// t->code = ADD_SERVICE_TRANSACTION
        tr.code = t->code;
        // t->flags = 0
        tr.flags = t->flags;
        
        // if t->from not NULL, if hit
        if (t->from)
        {
            struct task_struct *sender = t->from->proc->tsk;
            // Get the PID of AMS thread and record
            tr.sender_pid = task_tgid_nr_ns(sender, task_active_pid_ns(current));
        }
        else{... }// Records information about the flat_binder_object, such as the memory address and data size
        tr.data_size = t->buffer->data_size;
        tr.offsets_size = t->buffer->offsets_size;
        tr.data.ptr.buffer = (binder_uintptr_t) ((uintptr_t)t->buffer->data + proc->user_buffer_offset);
        tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));
        
        // Copy the instruction BR_TRANSACTION into user space (bwr.read_buffer in binder_loop)
        if (put_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        // Move the pointer back
        ptr += sizeof(uint32_t);
        
        // Copy flat_binder_object information into user space (bwr.read_buffer in binder_loop)
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        // Move the pointer back
        ptr += sizeof(tr); .// If hits
        if(cmd == BR_TRANSACTION && ! (t->flags & TF_ONE_WAY)) { t->to_parent = thread->transaction_stack; t->to_thread = thread;// Store t in the transaction_stack in the SM thread
            thread->transaction_stack = t;
        }
        else{... }// Break the loop
        break; . } *consumed = ptr - buffer; .return 0;
}
Copy the code

When the binder_thread_read function completes, and the binder_ioctl_write_read function completes, we return to the binder_loop function and continue.

// frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{...for(;;) {... res =binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); . }}Copy the code

We then look at the binder_parse function.

// frameworks/native/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs, 
                struct binder_io *bio,
                uintptr_t ptr, // PTR is the starting address of bwr.read_buffer in binder_loop
                size_t size, // size is bwr.read_size in binder_loop
                binder_handler func
){
    int r = 1;
    // pointer to the bwr.read_buffer end address in binder_loop
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end)
    {
        // the first uint32_t size of the bwr.read_buffer is the instruction, the current instruction is BR_TRANSACTION
        uint32_t cmd = *(uint32_t *) ptr;
        // Move the pointer back
        ptr += sizeof(uint32_t); .switch(cmd)
        {
            ......
            case BR_TRANSACTION:
            {
                // Convert flat_binder_object information to binder_transaction_data (TXN)
                struct binder_transaction_data *txn =(struct binder_transaction_data *) ptr; .// func = service_manager#svcmgr_handler, if hit
                if (func)
                {
                    unsigned rdata[256/4];
                    struct binder_io msg;
                    struct binder_io reply;
                    int res;
                    // Initialize reply
                    bio_init(&reply, rdata, sizeof(rdata), 4);
                    // Initialize the MSG and copy the flat_binder_object information to the MSG
                    bio_init_from_txn(&msg, txn);
                    // Execute the service_manager#svcmgr_handler function
                    res = func(bs, txn, &msg, &reply);
                    // Send reply to the binder driver
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }
                // Move the pointer back
                ptr += sizeof(*txn);
                break; }... }}return r;
}
Copy the code

We then look at the service_manager#svcmgr_handler function.

// frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data *txn, struct binder_io *msg, struct binder_io *reply ){
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    intallow_isolated; .TXN ->code = ADD_SERVICE_TRANSACTION = 3 = SVC_MGR_ADD_SERVICE
    switch(txn->code)
    {
        ......
        case SVC_MGR_ADD_SERVICE:
            // Get the AMS name from the flat_binder_object information
            s = bio_get_string16(msg, &len); .// Get a reference to AMS from flat_binder_object (handle)
            handle = bio_get_ref(msg);
            allow_isolated = bio_get_uint32(msg) ? 1 : 0;
            // Record AMS in SM
            if (do_add_service(bs, s, len, handle, txn->sender_euid,
                allow_isolated, txn->sender_pid))
                return - 1;
            break; . }// There is no data in reply at this time
    bio_put_uint32(reply, 0);
    return 0;
}

int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid
){
    // The svcinfo structure is used to store the registered service information in SM
    struct svcinfo *si;.// Find out if AMS is registered with SM
    si = find_svc(s, len);
    // If AMS is already registered
    if (si)
    {
        if (si->handle)
        {
            ......
            // Clear the reference (handle) of the current svcinfo record
            svcinfo_death(bs, si);
        }
        // Reassign the reference (handle) in the current svcinfo
        si->handle = handle;
    }
    else // If AMS is not registered
    {
        // Create a new svcinfo structure
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); .// Record the AMS reference (handle)
        si->handle = handle;
        si->len = len;
        // Copy the AMS information
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\ 0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        // Add the created svcinfo structure to svclist
        si->next = svclist;
        svclist = si;
    }
    // The BC_ACQUIRE command, handle, is sent to binder drivers via ioctl,
    // make binder_ref strongly reference the increment operation
    binder_acquire(bs, handle);
    // The BC_REQUEST_DEATH_NOTIFICATION command is sent to binder drivers via ioctl,
    // Clean up the memory
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}
Copy the code

You can see that AMS is logged into SM in the service_manager#svcmgr_handler function, completing the registration.

Stored in SM is the name of AMS and a reference to the entity node of AMS in binder drivers (Handle).

When another process later requests AMS by accessing SM, SM will return this reference to the requester.

Let’s move on to the binder_send_reply function.

// frameworks/native/cmds/servicemanager/binder.c
void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply, // Reply is empty at this point
                       binder_uintptr_t buffer_to_free, // flat_binder_object A pointer to the start address of memory
                       int status // status = 0
){
    struct {
        uint32_t cmd_free; / / position
        binder_uintptr_t buffer;
        uint32_t cmd_reply; / / low
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;

    data.cmd_free = BC_FREE_BUFFER; // Free buffer command
    data.buffer = buffer_to_free;
    data.cmd_reply = BC_REPLY; / / reply command
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    
    // status = 0, else
    if (status)
    {
        ......
    }
    else
    {
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
    // Communicates with binder drivers to execute instructions
    binder_write(bs, &data, sizeof(data));
}

int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len;
    bwr.write_consumed = 0;
    // at this point, bwr.write_buffer points to the start address of reply
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    
    Binder_ioctl is called by the binder driver with BINDER_WRITE_READ
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); .return res;
}

// kernel/drivers/staging/android/binder.c
static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
    int ret = 0;
    struct binder_proc *proc =filp->private_data; .void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;.// Copy the start address of reply from user space to kernel space (BWR)
    if (copy_from_user(&bwr, ubuf, sizeof(bwr)))
    {
        ret = -EFAULT;
        gotoout; }...if (bwr.write_size > 0)
    {
        // Send instructions to binder drivers
        ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); . }// bwr.read_size = 0, if not matched
    if (bwr.read_size > 0) {... }... out:return ret;
}

static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,
			binder_uintptr_t binder_buffer, // binder_buffer = the start address of reply
                        size_t size, // size = Size of data in reply
			binder_size_t *consumed
){
    uint32_t cmd;
    struct binder_context *context = proc->context;
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    // A pointer to the start address of the reply memory
    void __user *ptr = buffer + *consumed;
    // A pointer to the end address of reply memory
    void __user *end = buffer + size;
    
    while (ptr < end && thread->return_error == BR_OK)
    {
        // Read instructions from reply
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        // Move the pointer back
        ptr += sizeof(uint32_t);
        // start with BC_FREE_BUFFER and BC_REPLY
        switch (cmd)
        {
            ......
            case BC_FREE_BUFFER:
            {
                // The memory area pointed to by data.buffer is emptied, so there is no more analysis here.break; }...case BC_TRANSACTION:
            case BC_REPLY:
            {
                struct binder_transaction_data tr;
                // Copy the starting address of the data part of reply from user space to kernel space (binder_transaction_data)
                if (copy_from_user(&tr, ptr, sizeof(tr)))
                    return -EFAULT;
                // Move the pointer back
                ptr += sizeof(tr);
                // Call binder_transaction
                binder_transaction(proc, thread, &tr, cmd == BC_REPLY, 0);
                break; }... }... }return 0;
}
Copy the code

At this point, the binder_transaction function is called again to communicate with the AMS process, notifying AMS that the registration was successful.

// kernel\drivers\staging\android\binder.c
static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, 
                               int reply, // This time reply = true
			       binder_size_t extra_buffers_size)
{...struct binder_transaction *t;
    struct binder_work *tcomplete;.struct binder_proc *target_proc;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct list_head *target_list;
    wait_queue_head_t*target_wait; .// Here reply is true (CMD == BC_REPLY)
    if (reply)
    {
        // Get binder_transaction recorded in thread->transaction_stackin_reply_to = thread->transaction_stack; . thread->transaction_stack = in_reply_to->to_parent;// in_reply_to->from = AMS threadtarget_thread = in_reply_to->from; . target_proc = target_thread->proc; }else{... }// now the target_thread! = NULL, if hit
    if (target_thread)
    {
        // Get the PID value of AMS thread
        e->to_thread = target_thread->pid;
        // Get the TOdo queue and wait queue of AMS thread
        target_list = &target_thread->todo;
        target_wait = &target_thread->wait;
    }
    else{... }...// Generate a binder_transaction variable,
    // Describes the transaction to be done (target_thread->todo).
    // So that when the target object is woken up, it can fetch work from the queue.
    t = kzalloc(sizeof(*t), GFP_KERNEL); .// Generate a binder_work variable (tcomplete) to indicate that AMS has an unfinished job
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); .// If not matched, else is executed
    if(! reply && ! (tr->flags & TF_ONE_WAY)) ......else
        t->from = NULL; .// Record the target process of this communication (AMS)t->to_proc = target_proc; .// This communication code = 0
    t->code = tr->code;
    // Flags = 0t->flags = tr->flags; .// Allocate buffer from AMS process
    // To complete this transaction, allocate memory from binder_mmap.
    t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, extra_buffers_size, ! reply && (t->flags & TF_ONE_WAY)); .// Record AMS binder_node with target_node = NULLt->buffer->target_node = target_node; .// Copy the data part of reply from user space to kernel space (t->buffer->data)
    if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
                    tr->data.ptr.buffer, tr->data_size))
    ......
    CMD = BC_REPLY, reply = true
    if (reply)
    {
        ......
        // Clear the data and release the cache
        binder_pop_transaction(target_thread, in_reply_to);
    }
    else if(! (t->flags & TF_ONE_WAY)) { ...... }else{... }// Set t's type to BINDER_WORK_TRANSACTION
    t->work.type = BINDER_WORK_TRANSACTION;
    // add t to the AMS thread todo queue
    list_add_tail(&t->work.entry, target_list);
    // Set the binder_work type to BINDER_WORK_TRANSACTION_COMPLETE
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    // Add tComplete to the TOdo queue of the SM process
    list_add_tail(&tcomplete->entry, &thread->todo);
    // when AMS is suspended, target_wait! = NULL, if hit
    if (target_wait)
        // Wake up the AMS process
        wake_up_interruptible(target_wait);
    return; . }Copy the code

Let’s look at what the SM process does next.

In the SM process, binder_transaction returns all the way back to binder_loop, and the binder driver’s instructions are read through the loop by calling ioctl again.

We looked directly at the binder_thread_read function.

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block
){
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    // pointer to the starting address of bwr.read_buffer in binder_loop
    void __user *ptr = buffer + *consumed;
    // pointer to the bwr.read_buffer end address in binder_loop
    void __user *end = buffer + size;

    int ret = 0;
    intwait_for_proc_work; . retry:Thread ->todo is not empty and wait_for_proc_work is false
    Thread ->todo is empty and wait_for_proc_work is true
    wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); .1. Execute else
    // 2. Run if
    if (wait_for_proc_work)
    {
        ......
        if (non_block) // Filp ->f_flags & O_NONBLOCK is already assigned
                       // non_block > 0, else{... }else
            If thread->todo is empty, binder_has_thread_work returns false to suspend the SM thread
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    }
    else
    {
        if (non_block) // Filp ->f_flags & O_NONBLOCK is already assigned
                       // non_block > 0, else{... }else
            Thread ->todo is not null. Binder_has_thread_work returns true. SM threads will not be suspended
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); }...// 1. The thread is not suspended and continues the while loop
    while (1)
    {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
        
        If thread->todo is null, thread->todo is null. If thread->todo is null, thread->todo is null
        BINDER_WORK_TRANSACTION_COMPLETE binder_work_complete binder_work_complete
        // If thread->todo is null, execute else
        if (!list_empty(&thread->todo))
        {
            // 1. Fetch binder_work
            w = list_first_entry(&thread->todo, struct binder_work, entry);
        }
        else
        {
            // 2
            if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                // 2. Run the code back to the retry position
                goto retry;
            break; }...W ->type is BINDER_WORK_TRANSACTION_COMPLETE
        switch (w->type)
        {
            ......
            case BINDER_WORK_TRANSACTION_COMPLETE:
            {
                // 1. Change the directive BR_TRANSACTION_COMPLETE
                cmd = BR_TRANSACTION_COMPLETE;
                // 1. Copy the instruction to bwr.read_buffer in binder_loop
                if (put_user(cmd, (uint32_t __user *)ptr))
                    return -EFAULT;
                // 1
                ptr += sizeof(uint32_t); .// 1. Delete the current binder_work from thread->todo
                list_del(&w->entry);
                // 1. Release binder_work memory
                kfree(w); . }break; . }// 1. T = null, if hit
        if(! t)continue; . } *consumed = ptr - buffer; .return 0;
}
Copy the code

In the binder_thread_read function, the while loop executes twice, so the comments in the code indicate the ordinal number that corresponds to the number of times in the while loop.

At this point, the SM is suspended, waiting for another process to communicate with it and wake up. When the SM wakes up, the binder_thread_read function resumes execution from there.

What does the AMS process do when it wakes up?

Let’s look at what the AMS process does when it wakes up.

In the AMS process, when the binder_transaction function returns from completion, the AMS process wakes up to continue the unfinished binder_thread_read function.

// kernel\drivers\staging\android\binder.c
static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{...while (1)
    {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL; .// Add a binder_transaction to the binder_transaction function, proc->todo is not null
        else if (!list_empty(&proc->todo) && wait_for_proc_work)
        {
            / / remove binder_transaction
            w = list_first_entry(&proc->todo, struct binder_work, entry); }...W ->type is BINDER_WORK_TRANSACTION
        switch (w->type)
        {
            ......
            case BINDER_WORK_TRANSACTION:
            {
                t = container_of(w, struct binder_transaction, work);
            } break; . }// t! = null, if not hit
        if(! t)continue; .// when t->buffer->target_node is null, if is not hit
        if (t->buffer->target_node)
        {
            ......
        }
        else
        {
            tr.target.ptr = 0;
            tr.cookie = 0;
            // The directive changes to BR_REPLY
            cmd = BR_REPLY;
        }
        
        // t->code = 0
        tr.code = t->code;
        // t->flags = 0
        tr.flags = t->flags;
        
        T ->from = NULL
        if (t->from)
        {
            ......
        }
        else
        {
            tr.sender_pid = 0;
        }
        // Record the memory address and data size of the reply data section
        tr.data_size = t->buffer->data_size;
        tr.offsets_size = t->buffer->offsets_size;
        tr.data.ptr.buffer = (binder_uintptr_t) ((uintptr_t)t->buffer->data + proc->user_buffer_offset);
        tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));
        
        // Copy the directive BR_REPLY to user space (mIn)
        if (put_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        // Move the pointer back
        ptr += sizeof(uint32_t);
        
        // Copy the information from the reply data section to user space (mIn)
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        // Move the pointer back
        ptr += sizeof(tr); .// If does not match
        if(cmd == BR_TRANSACTION && ! (t->flags & TF_ONE_WAY)) { ...... }else
        {
            t->buffer->transaction = NULL;
            // Free memory
            kfree(t);
        }
        // Break the loop
        break; . } *consumed = ptr - buffer; .return 0;
}
Copy the code

When the binder_thread_read function completes and returns, continue execution until IPCThreadState#waitForResponse is returned.

// android\frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(
                    Parcel *reply, // reply = Reply defined in ServiceManagerProxy#addSevice
                    status_t *acquireResult
){
    uint32_t cmd;
    int32_t err;
    // Loop to wait for results
    while (1) {...// Read the instructions in mIn (BR_REPLY)
        cmd = (uint32_t)mIn.readInt32(a); .switch(cmd)
        {
            ......
            case BR_REPLY:
            {
                binder_transaction_data tr;
                // Read the information in the reply data section
                err = mIn.read(&tr, sizeof(tr)); .// Reply is not empty, if hit
                if (reply)
                {
                    // Tr. flags = 0, if hit
                    if ((tr.flags & TF_STATUS_CODE) == 0)
                    {
                        // Write the data portion of reply (in kernel space) to Reply (in user space)
                        reply->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, this);
                    }
                    else{... }}else{... }}// Break the loop
            gotofinish; . } } finish:if(err ! = NO_ERROR) {if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    return err;
}
Copy the code

When the IPCThreadState#waitForResponse function completes, it returns until ServiceManagerProxy#addSevice.

At this point, the whole AMS registration process is over.

conclusion

In accordance with the order of instructions, the AMS registration process is roughly shown as follows

If you look at the flow of the code with this flow chart, it’s a little bit clearer.

The AMS registration process is only the tip of the iceberg with the Binder IPC mechanism, but by tracking the AMS registration process we can see more clearly how the Binder IPC mechanism works, which is very helpful to understand the Binder IPC mechanism.