1. Why are Binder often asked in job interviews? What exactly is a Binder?

  • Interprocess communication mechanism
  • It’s also a drive
  • Binder.java inherits the Ibinder interface for cross-process capabilities

What is the relationship between processes and threads in Android? The difference between them?

  1. Thread is the smallest unit of CPU scheduling, and thread is a limited system resource. A process generally refers to a unit of execution, and on PC and mobile devices refers to a program or application.
  2. In general, an App has at least one process, a process of at least one thread (including relationship with included), popular terms, is that there is a process in App this factory, the inside of the thread is production line, but the main thread (i.e., the main production line) is only one, but the child thread (that is, the deputy production line) can have multiple.
  3. A process has its own independent address space, which is shared by threads in the process and can execute concurrently.

How do I enable multiple processes? Can N processes be enabled for an application?

AndroidManifest assigns attributes to four components: Android :process Enable multi-process mode, under the condition that memory permits, N processes can be started.

Why is IPC needed?

All the four major components (Activity, Service, Receiver, and ContentProvider) running on different processes fail to share data. This is because Android allocates a separate VIRTUAL machine for each application. Different virtual machines have different address Spaces for memory allocation. This results in multiple copies of objects of the same class being accessed in different virtual machines.

Advantages of multiple processes

  1. Expand memory space: Access more memory space by enabling multiple processes, sharing data between two or more applications, and wechat family bucket.
  2. Risk isolation. In general, an APP has only one process. If a module crashes, the entire app will be suspended.

Each process is assigned a limited number of processes

Adb shell getProp Dalvik.vm. heapSize adb shell getProp Dalvik.vm. heapsizeCopy the code

Usage scenarios

  • Development of applications: WebView, video playback, music playback, large picture browsing, push
  • System services: phone calls, alarm clocks and so on

Possible problems with multi-process communication?

Generally speaking, the use of multi-process communication will cause the following problems:

  1. Static member mode and singleton mode completely fail: independent VM.
  2. The thread synchronization mechanism is completely invalid: The thread synchronization mechanism is caused by an independent VM.
  3. The reliability of SharedPreferences deteriorates: Sp does not support concurrent read and write of two processes, which may lead to data loss.
  4. Applications are created multiple times: The Android system assigns separate virtual machines to create new processes, so this process is actually the process of starting an Application, and of course new applications will be created.

2. What are the advantages and disadvantages of Binder versus other processes?

Advantages and disadvantages of IPC mode and various modes in Android?

What are the interprocess communication mechanisms in Linux?

As we know, Android is also based on the Linux kernel. The existing communication means of Linux process are as follows:

  • Pipes: Allocate a page-size memory at creation time, and the cache size is limited;
  • Message queuing: Information is copied twice, extra CPU consumption; Not suitable for frequent or informative communication;
  • Shared memory: without replication, the shared buffer is directly attached to the process virtual address space, which is fast. However, the synchronization problem between processes cannot be realized by the operating system and must be solved by each process using the synchronization tool.
  • Socket: As a more general interface, low transmission efficiency, mainly used for communication between different machines or across networks;
  • Semaphore: Often used as a locking mechanism to prevent other processes from accessing a shared resource while one process is accessing it. Therefore, it is mainly used as a means of synchronization between processes and between different threads within the same process. Not suitable for information exchange, more suitable for process interrupt control, such as illegal memory access, killing a process, etc.

Why Binder?

With existing IPC, why redesign a Binder mechanism? Mainly due to the above three aspects of consideration:

  1. Efficiency: The transmission efficiency is mainly affected by the number of memory copies. The fewer memory copies, the higher the transmission rate. Analysis from the perspective of Android process architecture: For message queues, sockets and pipes, data is first copied from the sender’s cache to the cache created by the kernel, and then copied from the kernel cache to the receiver’s cache, twice in total, as shown in the figure:

    With Binder, data is copied from the sender’s cache to the kernel’s cache, while the receiver’s cache and the kernel’s cache are mapped to the same physical address, saving a data copy process, as shown in the figure below:

    Shared memory requires no copying, and Binders are second in performance to shared memory.

  2. Shared memory performs better than binders. Shared memory can handle concurrent synchronization issues, deadlocks, and resource contention, resulting in poor stability. Although Socket is based on C/S architecture, it is mainly used for communication between networks and the transmission efficiency is low. Binder based on C/S architecture, the Server and Client are relatively independent, providing high stability.

  3. ** Security: ** The receiver of traditional Linux IPC cannot obtain the reliable UID/PID of the other process and thus cannot authenticate the other process; The Binder mechanism assigns a UID/PID to each process and checks its validity when communicating with the Binder.

3. What are the functions and principles of Binder mechanisms?

Traditional IPC transfers data

Linux systems divide a process into user space and kernel space. Data in user space cannot be shared between processes, while data in kernel space can be shared. To ensure security and independence, one process cannot directly operate or access another process. In other words, Android processes are independent and isolated from each other, which requires cross-process data communication. Common cross-process communication requires two memory copies, as shown in the following figure:

Data transmission by Binder

A complete Binder IPC communication process usually looks like this:

  1. The Binder driver first creates a data receive cache in kernel space.
  2. Then, a kernel cache is created in the kernel space, and the mapping relationship between the kernel cache and the kernel data receiving cache, as well as the mapping relationship between the kernel data receiving cache and the user space address of the receiving process is established.
  3. The sender process uses the system call copyfromuser() to copy the data to the kernel cache in the kernel. Since there is memory mapping between the kernel cache and the user space of the receiving process, the data is sent to the user space of the receiving process, thus completing an inter-process communication.

The principle of MMAP

Linux initializes the contents of a virtual memory area by associating it with an object on a disk, a process called Memory mapping.

Mmap files to allocate address space in the process’s virtual memory, creating mapping relationships.

After this mapping is achieved, the memory can be read and written in the way of Pointers, and the system will automatically write back to the corresponding file disk.

All system resource management is done in kernel space. Such as reading and writing disk files, allocating reclaimed memory, reading and writing data from network interfaces, and so on. User space lets kernel space do this through system calls.

Procedure for writing a file:

1. Call write to tell the kernel the starting address and length of the data to be written. 2. The kernel copies the data to the kernel cache. 3. It is called by the operating system to copy data to the disk and write data.

What is the role of ServiceManager in Binder framework?

The Binder framework is based on the C/S architecture. It consists of a series of components, including Client, Server, ServiceManager, and Binder drivers. Client, Server, and ServiceManager run in user space, and Binder drivers run in kernel space. As shown below:

4. The Binder

Binder architecture

Binder jNI method registration tutorial

1. The zygote

1-1. Start zygote

Zygote is created by the init process by parsing init.zygote.rc. The executable app_process corresponding to zygote is app_main. CPP.

// system/core/rootdir/init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --startsystem-server    
    class main    
    socket zygote stream 660 root system    
    onrestart write /sys/android_power/request_state wake    
    onrestart write /sys/power/state on    
    onrestart restart media    
    onrestart restart netd    
    writepid /dev/cpuset/foreground/tasks
Copy the code
1-2. Run the main method in app_main. CPP

The entry function to start Zygote is the main method in app_main.cpp.

//frameworks/base/cmds/app_process/app_main.cpp

/ / 186
int main(int argc, char* const argv[])

// 248 Set zygote flag position to true.
if (strcmp(arg, "--zygote") = =0) {    
    zygote = true; 
}

// 306 Run the start method on androidRuntime. CPP
if (zygote) {    
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote); 
}

Copy the code
1-3.AndroidRuntime::start

Call the startReg method to complete the registration of the JNI method.

frameworks/base/core/jni/AndroidRuntime.cpp

//frameworks/base/core/jni/AndroidRuntime.cpp

/ / 1007
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)

/ / 1051
if (startReg(env) < 0) {
    
    

/ / 1440
int AndroidRuntime::startReg(JNIEnv* env)

// register the JNI method
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {

    


/ / 1283
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) {    
    // Loop to register jNI methods
    for (size_t i = 0; i < count; i++) {        
        if (array[i].mProc(env) < 0) {            
            return - 1; }}return 0; 
}

     
    
/ / 1296
static const RegJNIRec gRegJNI[] = {
    
    / / 1312
    REG_JNI(register_android_os_Binder), 
    
}

Copy the code

2.register_android_os_Binder

//frameworks/base/core/jni/android_util_Binder.cpp
    
/ / 1282
int register_android_os_Binder(JNIEnv* env) {    
    if (int_register_android_os_Binder(env) < 0)        
        return - 1;    
    if (int_register_android_os_BinderInternal(env) < 0)        
        return - 1;    
    if (int_register_android_os_BinderProxy(env) < 0)        
        return - 1;
}
Copy the code
2-1.int_register_android_os_Binder
//frameworks/base/core/jni/android_util_Binder.cpp

/ / 843
static const JNINativeMethod gBinderMethods[] = {     
    /* name, signature, funcPtr */    
    { "getCallingPid"."()I", (void*)android_os_Binder_getCallingPid },    
    { "getCallingUid"."()I", (void*)android_os_Binder_getCallingUid },    
    { "clearCallingIdentity"."()J", (void*)android_os_Binder_clearCallingIdentity },    
    { "restoreCallingIdentity"."(J)V", (void*)android_os_Binder_restoreCallingIdentity},
    { "setThreadStrictModePolicy"."(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },    
    { "getThreadStrictModePolicy"."()I", (void*)android_os_Binder_getThreadStrictModePolicy },    
    { "flushPendingCommands"."()V", (void*)android_os_Binder_flushPendingCommands },   
    { "init"."()V", (void*)android_os_Binder_init },    
    { "destroy"."()V", (void*)android_os_Binder_destroy },    
    { "blockUntilThreadAvailable"."()V", (void*)android_os_Binder_blockUntilThreadAvailable } 
};

/ / 857
const char* const kBinderPathName = "android/os/Binder";

/ / 859
static int int_register_android_os_Binder(JNIEnv* env) {    
    // kBinderPathName = "Android/OS /Binder", return the corresponding Class object
    jclass clazz = FindClassOrDie(env, kBinderPathName);
    // The gBinderOffsets structure stores information about Binder classes of the Java layer and provides a channel for the JNI layer to access the Java layer
    gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);    
    gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact"."(IJJI)Z");     
    gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject"."J");
    // With RegisterMethodsOrDie, the mapping is done for the gBinderMethods array, providing a channel for the Java layer to access the JNI layer
    return RegisterMethodsOrDie(        
        env, kBinderPathName,        
        gBinderMethods, 
        NELEM(gBinderMethods));
}

Copy the code
2-2.int_register_android_os_BinderInternal
//frameworks/base/core/jni/android_util_Binder.cpp
    
/ / 925
static const JNINativeMethod gBinderInternalMethods[] = {     
    /* name, signature, funcPtr */    
    { "getContextObject"."()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },    
    { "joinThreadPool"."()V", (void*)android_os_BinderInternal_joinThreadPool },    
    { "disableBackgroundScheduling"."(Z)V", (void*)android_os_BinderInternal_disableBackgroundScheduling },    
    { "handleGc"."()V", (void*)android_os_BinderInternal_handleGc } };

/ / 933
const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";

/ / 935
static int int_register_android_os_BinderInternal(JNIEnv* env){    
    / / find file kBinderInternalPathName = "com/android/internal/OS/BinderInternal", returns the Class object
    jclass clazz = FindClassOrDie(env, kBinderInternalPathName);
    // gBinderInternalOffsets store information about the BinderInternal class of the Java layer, providing a channel for the JNI layer to access the Java layer
    gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc"."()V");
    
    // With RegisterMethodsOrDie(), the mapping is done for the gBinderInternalMethods array, providing a channel for the Java layer to access the JNI layer
    return RegisterMethodsOrDie(        
        env, kBinderInternalPathName,        
        gBinderInternalMethods, 
        NELEM(gBinderInternalMethods));
}

Copy the code
2-3.int_register_android_os_BinderProxy
//frameworks/base/core/jni/android_util_Binder.cpp

/ / 1241
static const JNINativeMethod gBinderProxyMethods[] = {     
    /* name, signature, funcPtr */    
    {"pingBinder"."()Z", (void*)android_os_BinderProxy_pingBinder},    
    {"isBinderAlive"."()Z", (void*)android_os_BinderProxy_isBinderAlive},    
    {"getInterfaceDescriptor"."()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
    {"transactNative"."(ILandroid/os/Parcel; Landroid/os/Parcel; I)Z", (void*)android_os_BinderProxy_transact}, 
    {"linkToDeath"."(Landroid/os/IBinder$DeathRecipient; I)V", (void*)android_os_BinderProxy_linkToDeath}, 
    {"unlinkToDeath"."(Landroid/os/IBinder$DeathRecipient; I)Z", (void*)android_os_BinderProxy_unlinkToDeath},    
    {"destroy"."()V", (void*)android_os_BinderProxy_destroy}, };

/ / 1252
const char* const kBinderProxyPathName = "android/os/BinderProxy";

/ / 1254
static int int_register_android_os_BinderProxy(JNIEnv* env) {    
    // find the file kBinderProxyPathName = "Android/OS /BinderProxy" and return the corresponding Class object
    jclass clazz = FindClassOrDie(env, "java/lang/Error");    
    gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);

    // gBinderProxyOffsets are used to store information about the BinderProxy class of the Java layer, providing a channel for the JNI layer to access Java
    clazz = FindClassOrDie(env, kBinderProxyPathName);    
    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);    
    gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>"." ()V");    
    gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice"."(Landroid/os/IBinder$DeathRecipient;) V");
    
    gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject"."J");    
    gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf"."Ljava/lang/ref/WeakReference;");  
    
    gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue"."J");
    
    clazz = FindClassOrDie(env, "java/lang/Class");    
    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName"." ()Ljava/lang/String;");
    
    // With RegisterMethodsOrDie(), the mapping is completed for the gBinderProxyMethods array, thus providing the Java layer with access to the JNI layer
    return RegisterMethodsOrDie(        
        env, kBinderProxyPathName,        
        gBinderProxyMethods, NELEM(gBinderProxyMethods)); 
}
Copy the code

Binder driver registration guide

1.binder_init

Main Work:

  1. Allocate memory
  2. Initializing the device
  3. Put in the linked list binder_devices

kernel/drivers/staging/android/binder.c

// 4290 Device driver entry function
device_initcall(binder_init);

/ / 4213
static int __init binder_init(void)
    
// 4220 Create a single-threaded work queue named binder
binder_deferred_workqueue = create_singlethread_workqueue("binder");

/ / 4269
ret = init_binder_device(device_name);
Copy the code

kernel/drivers/staging/android/binder.c

/ / 4186
static int __init init_binder_device(const char *name)
{    
    int ret;    
    struct binder_device *binder_device;
    
    // 4191 1. Allocate memory for binder devices
    binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
    
    // 4195 2. Initialize devices
    binder_device->miscdev.fops = &binder_fops; // The device file operation structure, this is the file_operations structure
    
    binder_device->miscdev.minor = MISC_DYNAMIC_MINOR; // The device number is dynamically allocated
    binder_device->miscdev.name = name; // Device name,"binder"
    
    binder_device->context.binder_context_mgr_uid = INVALID_UID;    
    binder_device->context.name = name;
    
    // 4202 MISC driver registration
    ret = misc_register(&binder_device->miscdev);
    
    // 4208 3. Add the hlist node to the device list headed by binder_devices
    hlist_add_head(&binder_device->hlist, &binder_devices);
    return ret; 
}
Copy the code

Misc device: is a section of memory without hardware, the advantage is easy to register

2.binder_open

Main Work:

  1. Create a binder_proc object
  2. Current process information proc
  3. filp->private_data = proc
  4. Add to the binder_procs list

kernel/drivers/staging/android/binder.c

/ / 3454
static int binder_open(struct inode *nodp, struct file *filp)

// 3462 allocates memory space in the kernel for the binder_proc structure
proc = kzalloc(sizeof(*proc), GFP_KERNEL);

// 3465 saves the current thread's task to the TSK of the binder process
get_task_struct(current); proc->tsk = current; 
INIT_LIST_HEAD(&proc->todo); // Initialize the todo list
init_waitqueue_head(&proc->wait); // Initialize the WAIT queue
proc->default_priority = task_nice(current); // Convert the nice value of the current process to the process priority

// 3474 Synchronous lock because binder supports multithreaded access
binder_lock(__func__);
binder_stats_created(BINDER_STAT_PROC); // binder_proc The number of objects created increases by 1
hlist_add_head(&proc->proc_node, &binder_procs); // Add the proc_node node to the queue head of binder_procs
proc->pid = current->group_leader->pid; / / process pid
INIT_LIST_HEAD(&proc->delivered_death); // Initializes the distributed death notification list
filp->private_data = proc; // Associate the binder_proc with filp so that the proc will be found next time through filp
binder_unlock(__func__); // Release the synchronization lock
Copy the code

3.binder_mmap

Main Work:

  1. Allocate a block of kernel virtual memory based on the virtual memory size of user space
  2. Allocate a block of physical memory (4KB)
  3. Map this physical memory to user space virtual memory and kernel virtual memory respectively.

kernel/drivers/staging/android/binder.c

// 3355 VMA: virtual memory of a process
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
// 3366 Ensure that the mapped memory size does not exceed 4 MB
if ((vma->vm_end - vma->vm_start) > SZ_4M)    
    vma->vm_end = vma->vm_start + SZ_4M;

// 3382 Synchronization lock to ensure that only one process can allocate memory at a time
mutex_lock(&binder_mmap_lock); 
If yes, the binder_mmap method is terminated after the synchronization lock is released. If yes, the binder_mmap method is terminated after the synchronization lock is released
if (proc->buffer) {   
    goto err_already_mapped;
} 
// Use VM_IOREMAP to allocate a continuous kernel virtual memory, which is the same size as the process virtual memory
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); 
// Failed to allocate memory
if (area == NULL) {    
    ret = -ENOMEM;    
    failure_string = "get_vm_area";    
    goto err_get_vm_area_failed; 
} 
// Points the buffer pointer in proc to the kernel's virtual memory
proc->buffer = area->addr; 
// Calculate the user space and kernel space address offsets. Address offset = user virtual memory address - kernel virtual memory address
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; 
mutex_unlock(&binder_mmap_lock); / / releases the lock

// 3407 Allocates an array of Pointers to physical pages, the size of which is the equivalent number of pages in the VMA, and 4KB of physical memory
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);

// 3418 Allocate physical page to kernel space and process space, allocate 1 physical page first.
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) 
Copy the code

kernel/drivers/staging/android/binder.c

/ / 576
static int binder_update_page_range(struct binder_proc *proc, int allocate,
                                    void *start, void *end,                    
                                    struct vm_area_struct *vma)

// 609 allocate is 1. A value of 0 represents the process of freeing memory
if (allocate == 0)        
      goto free_range;

// 624 Allocates one page of physical memory
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);

// 630 physical space is mapped to virtual kernel space
ret = map_kernel_range_noflush((unsigned long)page_addr,                   
                               PAGE_SIZE, PAGE_KERNEL, page);

// 641 physical space is mapped to virtual process space
ret = vm_insert_page(vma, user_page_addr, page[0]);

Copy the code

kernel/drivers/staging/android/binder.c

/ / 3355
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)

/ / 3425
list_add(&buffer->entry, &proc->buffers);// Add buffers to the buffers list
buffer->free = 1; // This memory is available
binder_insert_free_buffer(proc, buffer);// Insert buffer into the proc->free_buffers list
proc->free_async_space = proc->buffer_size / 2; // The amount of free space available for asynchrony
barrier(); 
proc->files = get_files_struct(current); 
proc->vma = vma; 
proc->vma_vm_mm = vma->vm_mm;
Copy the code
3-1.binder_insert_free_buffer

kernel/drivers/staging/android/binder.c

/ / 494
static void binder_insert_free_buffer(struct binder_proc *proc,struct binder_buffer *new_buffer)
    
/ / 511
while (*p) {    
    parent = *p;    
    buffer = rb_entry(parent, struct binder_buffer, rb_node);
    
    // Calculate the size of free memory
    buffer_size = binder_buffer_size(proc, buffer);
    if (new_buffer_size < buffer_size)        
        p = &parent->rb_left;   
    else        
        p = &parent->rb_right; 
} 
rb_link_node(&new_buffer->rb_node, parent, p); // Insert buffer into the proc->free_buffers list
rb_insert_color(&new_buffer->rb_node, &proc->free_buffers)
Copy the code

4.binder_ioctl

Just do the read and write

kernel/drivers/staging/android/binder.c

/ / 3241
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
    
// 3254 goes to sleep until the interrupt wakes up
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);

// 3259 Select binder_thread from binder_proc based on pid of current process.
// If the current thread has been added to the proc thread queue,
// If not, create binder_thread and add the current thread to the current proc
thread = binder_get_thread(proc);

// 3265 performs read and write operations with binder
switch (cmd) {    
    case BINDER_WRITE_READ:        
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);        
        if (ret)
Copy the code
4-1.binder_ioctl_write_read

kernel/drivers/staging/android/binder.c

/ / 3136
static int binder_ioctl_write_read(struct file *filp,                
                                   unsigned int cmd, unsigned long arg,                
                                   struct binder_thread *thread)
    
// 3150 copy user space data uBUF to BWR, copy header, not valid data
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
    
/ / 3160
if (bwr.write_size > 0) { // Bind writes when data is in the write cache
        ret = binder_thread_write(proc, thread,                  
                                  bwr.write_buffer,                  
                                  bwr.write_size,                  
                                  &bwr.write_consumed); 
} 
if (bwr.read_size > 0) { // Bind reads when data is in the read cache
    ret = binder_thread_read(proc, thread, bwr.read_buffer,                 
                             bwr.read_size,                
                             &bwr.read_consumed,                 
                             filp->f_flags & O_NONBLOCK);    
    // If the todo queue is not empty, the thread in the queue will be woken up
    if(! list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); }// copy the kernel space data BWR to uBUF
if (copy_to_user(ubuf, &bwr, sizeof(bwr))){

Copy the code

The data structure

file_operations
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
binder_proc

Each process that calls open() to open the Binder driver creates this structure, which is used to manage the various information required for IPC.

struct binder_proc {    
    struct hlist_node proc_node; // Process node
    struct rb_root threads; // binder_thread The root node of the red-black tree
    struct rb_root nodes; // binder_node The root node of the red-black tree
    struct rb_root refs_by_desc; // binder_ref Root node of red-black tree (handle as key)
    struct rb_root refs_by_node; // binder_ref root node of red-black tree (with PTR as key)
    int pid; // The corresponding process id
    struct vm_area_struct *vma; // A pointer to the process's virtual address space
    struct mm_struct *vma_vm_mm; // The memory structure of the corresponding process
    struct task_struct *tsk; // The task structure of the corresponding process
    struct files_struct *files; // The file structure of the corresponding process
    struct hlist_node deferred_work_node;    
    int deferred_work;    
    void *buffer; // The starting address of the kernel space
    ptrdiff_t user_buffer_offset; // Address offset between kernel space and user space
    struct list_head buffers; // All buffers
    struct rb_root free_buffers; // Free buffer
    struct rb_root allocated_buffers; // Allocated buffer
    size_t free_async_space; // The amount of free space available for asynchrony
    struct page六四屠杀pages; // A pointer to the physical memory page pointer
    size_t buffer_size; // The size of the mapped kernel space
    uint32_t buffer_free; // Total available memory size
    struct list_head todo; // What the process is going to do
    wait_queue_head_t wait; // Wait for the queue
    struct binder_stats stats; // Binder statistics
    struct list_head delivered_death; // Death notices have been distributed
    int max_threads; // Maximum number of threads
    int requested_threads; // The number of threads requested
    int requested_threads_started; // Number of request threads started
    int ready_threads; // The number of threads ready
    long default_priority; // Default priority
    struct dentry *debugfs_entry;    
    struct binder_context *context; 
};
Copy the code
binder_node
struct binder_node {    
    int debug_id; // The node is allocated when it is created. It is globally unique and used for debugging
    struct binder_work work;    
    union {        
        struct rb_node rb_node; // Binder nodes are used properly, union
        struct hlist_node dead_node; // The binder node is destroyed, union
    };    
    struct binder_proc *proc; // The process with binder, see the following section
    struct hlist_head refs; // All binder reference queues to this node
    int internal_strong_refs;    
    int local_weak_refs;    
    int local_strong_refs;    
    binder_uintptr_t ptr; // A pointer to user-space binder_node corresponding to flat_binder_object.binder
    binder_uintptr_t cookie; // Pointer to user-space binder_node, attached data, corresponding to flat_binder_object.cookie
    unsigned has_strong_ref:1; / / to take 1 bit
    unsigned pending_strong_ref:1; / / to take 1 bit
    unsigned has_weak_ref:1; / / to take 1 bit
    unsigned pending_weak_ref:1; / / to take 1 bit
    unsigned has_async_transaction:1; / / to take 1 bit
    unsigned accept_fds:1; / / to take 1 bit
    unsigned min_priority:8; // Hold 8bit, minimum priority
    struct list_head async_todo; // Asynchronous TODO queue
};

Copy the code
binder_buffer
struct binder_buffer {    
    struct list_head entry; // Buffer entity address
    struct rb_node rb_node; // Buffer entity address
            /* by address */    
    unsigned free:1; // Indicates whether it is an idle buffer, occupying 1bit
    unsigned allow_user_free:1; // whether to allow the user to release, placeholder 1bit
    unsigned async_transaction:1; / / to take 1 bit
    unsigned debug_id:29; / / placeholder - 29
    struct binder_transaction *transaction; // The transactions that need to be processed in this cache
    struct binder_node *target_node; // Binder entities to be processed for this cache
    size_t data_size; // Data size
    size_t offsets_size; // Data offset
    size_t extra_buffers_size;    
    uint8_t data[0]; // Data address
};
Copy the code

5. How do I start the Service_manager service

1. Start the Servicemanager process

The ServiceManager is created by the init process by parsing the init.rc file. The executable program ServiceManager corresponds to the source file Service_manager. c and the process name is ServiceManager.

system/core/rootdir/init.rc
/ / 602
service servicemanager /system/bin/servicemanager    
    class core    
    user system    
    group system    
    critical    
    onrestart restart healthd    
    onrestart restart zygote    
    onrestart restart media    
    onrestart restart surfaceflinger    
    onrestart restart drm

Copy the code

2.main

The entry function to start the ServiceManager is the main() method in service_manager.c.

frameworks/native/cmds/servicemanager/service_manager.c
/ / 354
int main(int argc, char **argv)

// 358 Open the Binder driver and request 128K bytes of memory space -- see the following section
bs = binder_open(128*1024);

// 364 is set as a daemon and becomes the binder master - see section below
if (binder_become_context_manager(bs)) {

// 391 enters an infinite loop to process requests from the client -- see the following section
binder_loop(bs, svcmgr_handler);

Copy the code

2-1.binder_open

2-2.binder_become_context_manager

2-2-1.binder_ioctl
2-2-2.binder_ioctl_set_ctx_mgr
2-2-2-1.binder_new_node

2-3.binder_loop

2-3-1.binder_write
2-3-2.binder_thread_write
2-3-3.binder_thread_read

6. How do I obtain the Service_manager service

7.AIDL

Aidl is mainly responsible for communication between two processes. In terms of usage, it is divided into two roles: server (responsible for being called) and client (responsible for calling). Both processes can call each other for both the server and the client.

  1. Create the same AIDL on both ends, with the same package name and file name, and the same data class if any.
  2. The server implements a service and its stub methods.
  3. The client binds the service and invokes the method, where the implementation is done by the proxy

Usage reference: blog.csdn.net/weixin_3381…

Classes generated by AIDL:reference

Implement an AILD manually

Called interface

public interface IPersonManager extends IInterface {

    void addPerson(Person person) throws RemoteException;

    List<Person> getPersonList(a) throws RemoteException;
}
Copy the code

Person

public class Person implements Parcelable {

    private String name;
    private int grade;

    public Person(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    protected Person(Parcel in) {
        this.name = in.readString();
        this.grade = in.readInt();
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return newPerson[size]; }};@Override
    public int describeContents(a) {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(grade);
    }

    @Override
    public String toString(a) {
        return "Person{" +
                "name='" + name + '\' ' +
                ", grade=" + grade +
                '} '; }}Copy the code

The service side

Stub

public abstract class Stub extends Binder implements IPersonManager {

    private static final String DESCRIPTOR = "com.enjoy.binder.common.IPersonManager";

    public Stub(a) {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static IPersonManager asInterface(IBinder binder) {
        if ((binder == null)) {
            return null;
        }
        IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
        if((iin ! =null) && (iin instanceof IPersonManager)) {
            return (IPersonManager) iin;
        }
        return new Proxy(binder);
    }

    @Override
    public IBinder asBinder(a) {
        return this;
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;

            case TRANSACTION_addPerson:
                Log.e("leo"."Stub,TRANSACTION_addPerson: " + Thread.currentThread());
                data.enforceInterface(DESCRIPTOR);
                Person arg0 = null;
                if ((0! = data.readInt())) { arg0 = Person.CREATOR.createFromParcel(data); }this.addPerson(arg0);
                reply.writeNoException();
                return true;

            case TRANSACTION_getPersonList:
                data.enforceInterface(DESCRIPTOR);
                List<Person> result = this.getPersonList();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

    static final int TRANSACTION_addPerson = IBinder.FIRST_CALL_TRANSACTION;
    static final int TRANSACTION_getPersonList = IBinder.FIRST_CALL_TRANSACTION + 1;
}
Copy the code

RemoteService

public class RemoteService extends Service {

    private ArrayList<Person> persons;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        persons = new ArrayList<>();
        Log.e("LeoAidlService"."success onBind");
        return iBinder;
    }

    private IBinder iBinder = new Stub() {
        @Override
        public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override
        public List<Person> getPersonList(a) throws RemoteException {
            returnpersons; }}; }Copy the code

AndroidManifest.xml

 <service
      android:name=".server.RemoteService"
      android:exported="true"
      android:process=":remote">
      <intent-filter>
            <action android:name="com.xxx.binder" />
            <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
</service>
Copy the code

The client

Proxy

public class Proxy implements IPersonManager {

    private static final String DESCRIPTOR = "com.enjoy.binder.common.IPersonManager";

    private IBinder mRemote;

    public Proxy(IBinder remote) {
        mRemote = remote;
    }

    @Override
    public void addPerson(Person person) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if((person ! =null)) {
                data.writeInt(1);
                person.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            Log.e("leo"."Proxy,addPerson: " + Thread.currentThread());
            // In the case of synchronization, transcat is suspended after being called
            mRemote.transact(Stub.TRANSACTION_addPerson, data, reply, 0);
            reply.readException();
        } finally{ reply.recycle(); data.recycle(); }}@Override
    public List<Person> getPersonList(a) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        List<Person> result;
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getPersonList, data, reply, 0);
            reply.readException();
            result = reply.createTypedArrayList(Person.CREATOR);
        } finally {
            reply.recycle();
            data.recycle();
        }
        return result;
    }

    @Override
    public IBinder asBinder(a) {
        returnmRemote; }}Copy the code

ClientActivity

public class ClientActivity extends AppCompatActivity {

    private IPersonManager iPersonManager;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Bind the service
        initAndBindService();
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Log.e("leo"."------------onClick:" + Thread.currentThread());
                    iPersonManager.addPerson(new Person("leo".3));
                    List<Person> persons = iPersonManager.getPersonList();
                    Log.e("leo", persons.toString() + "," + Thread.currentThread());
                } catch(RemoteException e) { e.printStackTrace(); }}}); }private void initAndBindService(a){
        Intent intent = new Intent(this, RemoteService.class);
        intent.setAction("com.enjoy.binder");
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("leo"."onServiceConnected: success");
            iPersonManager = Stub.asInterface(service);// proxy
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("leo"."onServiceDisconnected: success");
            iPersonManager = null; }}; }Copy the code