Binder Analysis – Overall overview
Binder mechanism is familiar to you. This blog will explain the code process of AMS registration, and it will be a long one. Please be patient.
(The following analysis is Android 6.0 source code)
How does the ServiceManager start?
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 function 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
binder_open()
// 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
binder_become_context_manager()
// 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);
}
Copy the code
binder_ioctl()
// 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;.// Get the binder thread
// If the pid has not created a binder thread, create a binder thread and return.
// thread->looper = BINDER_LOOPER_STATE_NEED_RETURN
// thread->return_error = BR_OK
// thread->return_error2 = BR_OK
thread = binder_get_thread(proc); .switch (cmd)
{
......
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp); .break; . }...return ret;
}
Copy the code
binder_ioctl_set_ctx_mgr()
// kernel/drivers/staging/android/binder.c
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;
}
Copy the code
binder_new_node()
// kernel/drivers/staging/android/binder.c
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;
// Initialize toDO and wait queues
INIT_LIST_HEAD(&node->work.entry);
INIT_LIST_HEAD(&node->async_todo); .return node;
}
Copy the code
inbinder_become_context_manager
The main function is to create SM node entities in binder drivers
binder_loop()
// 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 drivers
binder_write(bs, readbuf, sizeof(uint32_t)); . }Copy the code
binder_write()
// frameworks/native/cmds/servicemanager/binder.c
int binder_write(struct binder_state *bs,
void *data, // Start address of readbuf
size_t len // Readbuf length
){
struct binder_write_read bwr;
int res;
bwr.write_size = len;
bwr.write_consumed = 0;
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);
if (res < 0) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n".strerror(errno));
}
return res;
}
Copy the code
binder_ioctl()
// 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;.// Get the binder thread
// If the pid has already created a binder thread, return the corresponding binder thread.
// thread->looper = BINDER_LOOPER_STATE_NEED_RETURN
// thread->return_error = BR_OK
// thread->return_error2 = BR_OK
thread = binder_get_thread(proc); .switch (cmd)
{
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread); .break; . }...return ret;
}
Copy the code
binder_get_thread()
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
// Binder thread pool root pointer
struct rb_node **p = &proc->threads.rb_node;
// Walk through the binder thread pool to find a binder thread that matches the current thread (with the same PID)
while (*p)
{
parent = *p;
Rb_node -> binder_thread
thread = rb_entry(parent, struct binder_thread, rb_node);
if (current->pid < thread->pid)
p = &(*p)->rb_left;
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
// If it is found, break the loop and stop iterating
break;
}
// If there are no records in the binder thread pool, a new binder thread is created
if (*p == NULL)
{
thread = kzalloc(sizeof(*thread), GFP_KERNEL);
if (thread == NULL)
return NULL;
binder_stats_created(BINDER_STAT_THREAD);
// Record the process that binder threads belong to
thread->proc = proc;
// Record the PID of binder threads
thread->pid = current->pid;
// Initializes the WAIT queue for binder threads
init_waitqueue_head(&thread->wait);
// Initializes the TOdo queue for binder threads
INIT_LIST_HEAD(&thread->todo);
// Put binder threads into binder thread pools
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;
}
return thread;
}
Copy the code
A binder thread pool is essentially a red-black tree data structure that binder threads are converted to rB_node type for storage
binder_ioctl_write_read()
// 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 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 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 not matched
if (bwr.read_size > 0) {... }...return ret;
}
Copy the code
binder_thread_write()
// kernel/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, // Start address of readbuf
size_t size, // Readbuf length
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 readbuf memory
void __user *ptr = buffer + *consumed;
// A pointer to the end address of readbuf memory
void __user *end = buffer + size;
// Iterate over the memory pointer of readbuf to fetch the instruction
while (ptr < end && thread->return_error == BR_OK)
{
// Read instructions from readbuf, BC_ENTER_LOOPER
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
// Move the pointer back
ptr += sizeof(uint32_t);
switch (cmd)
{
......
case BC_ENTER_LOOPER:
......
// Add BINDER_LOOPER_STATE_ENTERED to the looper flag bit for binder threads
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break; . }... }return 0;
}
Copy the code
When the binder_thread_write function is finished, it returns to the binder_loop function and continues in an infinite loop
// 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; .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); . }}Copy the code
binder_ioctl()
// 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;.// Get the binder thread
// If the pid has already created a binder thread, return the corresponding binder thread.
// thread->looper = BINDER_LOOPER_STATE_NEED_RETURN + BINDER_LOOPER_STATE_ENTERED
// thread->return_error = BR_OK
// thread->return_error2 = BR_OK
thread = binder_get_thread(proc); .switch (cmd)
{
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread); .break; . }...return ret;
}
Copy the code
binder_ioctl_write_read()
// 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 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 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); . }...return ret;
}
Copy the code
binder_thread_read()
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 readbuf memory
void __user *ptr = buffer + *consumed;
// A pointer to the end address of readbuf memory
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
// Now consumed = 0, if matches
if (*consumed == 0)
{
// Add the directive BR_NOOP to readbuf
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
// Move the pointer back
ptr += sizeof(uint32_t);
}
Thread ->todo = NULL, thread->transaction_stack = NULL,
// wait_for_proc_work = true
wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); . thread->looper |= BINDER_LOOPER_STATE_WAITING;if (wait_for_proc_work)
// The number of binder threads available to the current process +1proc->ready_threads++; .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,
// Suspend the binder thread
ret = wait_event_freezable_exclusive(proc->wait,
binder_has_proc_work(proc, thread));
}
else{... }... }Copy the code
At this point, the SM thread is suspended, waiting for another process to communicate with it and wake it up
The awakened SM thread continues to execute the unfinished binder_thread_read function here
The structure of readBUf is as follows
How do I register for AMS?
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); . }// frameworks/base/core/java/android/os/ServiceManager.java
public static void addService(String name, IBinder service, boolean allowIsolated)
{... getIServiceManager().addService(name, service, allowIsolated); . }Copy the code
ServiceManager.getIServiceManager()
// frameworks/base/core/java/android/os/ServiceManager.java
private static IServiceManager getIServiceManager(a)
{
if(sServiceManager ! =null)
{
return sServiceManager;
}
/ / return ServiceManagerProxy
sServiceManager =
ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
Copy the code
BinderInternal.getContextObject()
// 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
ProcessState::self()
// 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
Intents are a means of communication between the four major components, and AMS manages the four components. Therefore, communication with AMS is required with intEnts. Due to limitations of binder mechanisms, the data size of communication with AMS cannot exceed 1M to 8K
ProcessState::getContextObject()
// 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
javaObjectForIBinder()
// frameworks/base/core/jni/android_util_Binder.cpp
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)
{
// Return the BinderProxy object by looking up the reference chain of the BinderProxy object to determine whether it is being referenced
jobject res = jniGetReferent(env, object);
// If BinderProxy object is being referenced
if(res ! =NULL)
{
ALOGV("objectForBinder %p: found existing %p! \n", val.get(), res);
// Return the reference object
returnres; }...// Remove all references to the current BinderProxy object
android_atomic_dec(&gNumProxyRefs);
val->detachObject(&gBinderProxyOffsets);
env->DeleteGlobalRef(object);
}
Create a new BinderProxy object
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());
// Create a smart pointer to BpBinder
val->incStrong((void*)javaObjectForIBinder);
The JNI environment variable (env) generates a weak reference to the BinderProxy object for retrieval
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
// 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())); .// The BinderProxy object reference count is incremented by one to indicate that the BinderProxy object has been referenced
android_atomic_inc(&gNumProxyRefs); . }return object;
}
Copy the code
Each BinderProxy object is bound to a BpBinder object, a BinderProxy object is regenerated and a BpBinder object is rebound each time javaObjectForIBinder() is called
The BpBinder object bound to BinderProxy corresponds to SM (handle is 0).
BinderInternal.getContextObject()
The function returns a BinderProxy object with a BpBinder object bound to the Native layer (corresponding to SM).
ServiceManagerNative.asInterface()
// frameworks/base/core/java/android/os/ServiceManagerNative.java
static public IServiceManager asInterface( IBinder obj // obj = BinderProxy ){
if (obj == null)
{
return null;
}
IServiceManager in = (IServiceManager)obj.queryLocalInterface(descriptor);
if(in ! =null)
{
return in;
}
return new ServiceManagerProxy(obj);
}
// frameworks/base/core/java/android/os/BinderProxy.java
public IInterface queryLocalInterface(String descriptor)
{
return null;
}
Copy the code
getIServiceManager()
The ServiceManagerProxy function returns a ServiceManagerProxy object that holds a BinderProxy object
ServiceManagerProxy.addService()
// frameworks/base/core/java/android/os/ServiceManagerNative$ServiceManagerProxy.java
public void addService(String name, IBinder service, boolean allowIsolated)
{
/ / create the data
Parcel data = Parcel.obtain();
/ / create the reply
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
// Write the AMS name to data
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 itself?
// frameworks/base/core/java/android/os/ServiceManagerNative$ServiceManagerProxy.java
public void addService(String name, IBinder service, boolean allowIsolated)
{
/ / create the dataParcel data = Parcel.obtain(); .// write AMS to datadata.writeStrongBinder(service); . }Copy the code
Parcel.writeStrongBinder()
// 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)); . }}Copy the code
ibinderForJavaObject()
// frameworks/base/core/jni/android_util_Binder.cpp
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj // object = AMS ){
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
ibinderForJavaObject
The function returns a JavaBBinder object that holds AMS
Parcel::writeStrongBinder()
// frameworks/native/libs/binder/Parcel.cpp
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 function defined in the data
){
// 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, // binder = JavaBBinder
const flat_binder_object& flat,
Parcel* out / / out = ServiceManagerProxy. AddSevice function defined in the data
){
// 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 should be remembered; 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 itself to SM?
// frameworks/base/core/java/android/os/ServiceManagerNative$ServiceManagerProxy.java
public void addService(String name, IBinder service, boolean allowIsolated)
{
/ / create the dataParcel data = Parcel.obtain(); .// mRemote = BinderProxy, equivalent to calling binderproxy.transact
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0); . }Copy the code
BinderProxy.transact()
// android\frameworks\base\core\java\android\os\Binder.java
public boolean transact(int code, Parcel data, Parcel reply, int flags)
{...return transactNative(code, data, reply, flags);
}
public native boolean transactNative(int code, Parcel data, Parcel reply, int flags);
// 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},
......
};
// 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 ParcelParcel* data = parcelForJavaObject(env, dataObj); .// Convert the type of replyObj to ParcelParcel* reply = parcelForJavaObject(env, replyObj); ./ / get gBinderProxyOffsets mObject BpBinder objects stored inIBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject); .// Call the transact function to the BpBinder objectstatus_t err = target->transact(code, *data, reply, flags); . }// android\frameworks\native\libs\binder\BpBinder.cpp
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
......
// mHandle = 0status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags); . }Copy the code
IPCThreadState::self()
// android\frameworks\native\libs\binder\IPCThreadState.cpp
static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; // Concurrent lock object
static bool gHaveTLS = false; // Indicates whether TLS has been created
static pthread_key_t gTLS = 0; // The key stored in TLS
IPCThreadState* IPCThreadState::self(a)
{
if (gHaveTLS)
{
restart:
const pthread_key_t k = gTLS;
// Call pthread_getSpecific to retrieve stored data via gTLS
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return newIPCThreadState; }...pthread_mutex_lock(&gTLSMutex); // Lock to prevent concurrent access by thread
// gHaveTLS If the initial value is false, TLS is not created
if(! gHaveTLS) {// Call pthread_key_create to create TLS and assign to gTLS
// (pthread_key_create returns 0 on success, error code on failure)
if (pthread_key_create(&gTLS, threadDestructor) ! =0)
{
pthread_mutex_unlock(&gTLSMutex); / / releases the lock
return NULL; // create TLS error return null
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex); / / releases the lock
goto restart;
}
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()), // Call ProcessState::self. {// Call pthread_setSpecific to save the IPCThreadState object to TLS
pthread_setspecific(gTLS, this); . mIn.setDataCapacity(256); // Set the mIn size
mOut.setDataCapacity(256); // Set the size of mOut
}
Copy the code
The IPCThreadState::self() function completes the creation of the IPCThreadState object
In order to make the IPCThreadState object unique for each thread, the system uses TLS technology in Linux
TLS refers to Thread local storage (TLS). Each Thread has its own TLS and is a private space that is not shared by threads
You can get/set the contents of TLS space through the pthread_getSpecific/pthread_setSpecific functions
In the IPCThreadState::self() function, the IPCThreadState object is stored using TLS, making the IPCThreadState object unique to each thread
IPCThreadState::transact()
// 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)
The structure is shown in the figure below
IPCThreadState::waitForResponse()
// 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
IPCThreadState::talkWithDriver()
// 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 bwr.read_size 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.return err;
}
Copy the code
binder_ioctl()
// 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;.// Get the binder thread
// If the pid has not created a binder thread, create a binder thread and return.
// thread->looper = BINDER_LOOPER_STATE_NEED_RETURN
// thread->return_error = BR_OK
// thread->return_error2 = BR_OK
thread = binder_get_thread(proc); .switch (cmd)
{
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread); .break; . }...return ret;
}
Copy the code
binder_ioctl_write_read()
// 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
binder_thread_write()
// 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 binder_transaction_data in the mOut data section from user space to kernel space
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
binder_transaction()
// kernel\drivers\staging\android\binder.c
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, // mOut data section
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:
- Get the binder_proc structure for the SM process
- Generate a binder_transaction variable (for SM) and a binder_work variable (for AMS) to hold the information
- Copy the encapsulated FLAT_binder_object into shared memory created by binder_mmap (emphasis, one copy)
- Create AMS entity node in binder driver and create a reference to that node (Handle)
- Wake up the SM process by adding the binder_TRANSACTION variable to its TODO queue
- Add the binder_work variable to the TODO queue of the AMS calling thread, and AMS hangs and waits
The binder_transaction function really does IPC traffic
binder_thread_read()
// 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
The while loop in the binder_thread_read function 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?
When the binder_transaction function completes, the SM thread wakes up and continues with the unfinished binder_thread_read function
binder_thread_read()
// 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)
{...if (wait_for_proc_work)
// Number of binder threads available to the current process -1proc->ready_threads--; thread->looper &= ~BINDER_LOOPER_STATE_WAITING; .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)
{
// Fetch binder_transaction, but of type binder_work
w = list_first_entry(&proc->todo, struct binder_work, entry); }...W ->type is BINDER_WORK_TRANSACTION
switch (w->type)
{
......
case BINDER_WORK_TRANSACTION:
{
// Type conversion (binder_work -> binder_transaction)
t = container_of(w, struct binder_transaction, work);
} break; . }// t! = null, if not hit
if(! t)continue; .T ->buffer->target_node = SM, 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{... }// Record the memory address, size and other information of the mOut data part
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 (readbuf in binder_loop)
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
// Move the pointer back
ptr += sizeof(uint32_t);
// Copy information from the mOut data section to user space (readbuf 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 SM thread's transaction_stack
thread->transaction_stack = t;
}
else{... }// Break the loop
break; . } *consumed = ptr - buffer;Proc ->requested_threads = proc->ready_threads = 0
// proc->requested_threads_started = 0 < proc->max_threads (default: 15),
Thread ->looper = BINDER_LOOPER_STATE_NEED_RETURN + BINDER_LOOPER_STATE_ENTERED,
// If the condition is met, if is hit
if (proc->requested_threads + proc->ready_threads == 0 &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))
{
......
// The number of binder threads that the current process needs to create +1
proc->requested_threads++;
// Copy the BR_SPAWN_LOOPER directive to user space (readbuf in binder_loop)
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return-EFAULT; . }return 0;
}
Copy the code
When the binder_thread_read function completes, the binder_ioctl_write_read function completes, and returns to the binder_loop function to continue execution
The structure of readBUf is as follows
binder_loop()
// 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
binder_parse()
The rest of the instruction processing flow in readbuf can be ignored and the most important one is BR_TRANSACTION
// frameworks/native/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs,
struct binder_io *bio,
uintptr_t ptr, // PTR points to the starting address of readbuf in binder_loop
size_t size, // size = length of readbuf 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)
{
// Uint32_t = uint32_t
uint32_t cmd = *(uint32_t *) ptr;
// Move the pointer back
ptr += sizeof(uint32_t); .switch(cmd)
{
......
case BR_TRANSACTION:
{
// Convert the mOut data section to binder_transaction_data
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 to copy the information from the mOut data section 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
Func is a function pointer to the service_manager.svcmgr_handler function, so res = func(bs, TXN, & MSG, &reply); This line calls the service_manager.svcmgr_handler function
service_manager.svcmgr_handler()
// frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn, // mOut data section
struct binder_io *msg, // mOut data section information
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 mOut data section
s = bio_get_string16(msg, &len); .// Get a reference to AMS from the mOut data section (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;
}
Copy the code
do_add_service()
// frameworks/native/cmds/servicemanager/service_manager.c
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
The service_manager.do_add_service function logs AMS into SM, 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
binder_send_reply()
// 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, // mOut is a pointer to the starting address of the memory in the data portion
int status // status = 0
){
// Define a structure, data, to encapsulate the command and reply
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
{
// Use binder_transaction_data (TXN) in data to record the address information of reply
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));
}
Copy the code
binder_write()
// frameworks/native/cmds/servicemanager/binder.c
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;
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;
}
Copy the code
binder_ioctl()
// 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;.// Get the binder thread
// If the pid has already created a binder thread, return the corresponding binder thread.
// thread->looper = BINDER_LOOPER_STATE_NEED_RETURN + BINDER_LOOPER_STATE_ENTERED
// thread->return_error = BR_OK
// thread->return_error2 = BR_OK
thread = binder_get_thread(proc); .switch (cmd)
{
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread); .break; . }...return ret;
}
Copy the code
binder_ioctl_write_read()
// 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 starting address of data 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;
}
Copy the code
binder_thread_write()
// kernel/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, // binder_buffer = the start address of data
size_t size, // Size of data
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 data memory
void __user *ptr = buffer + *consumed;
// A pointer to the end address of data memory
void __user *end = buffer + size;
while (ptr < end && thread->return_error == BR_OK)
{
// Read instructions from data
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.
// This is the cache of the mOut data part copied into the kernel space.
// No more analysis here.break; }...case BC_TRANSACTION:
case BC_REPLY:
{
struct binder_transaction_data tr;
// Copy the reply recorded in data 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
binder_transaction()
The binder_transaction function is called again to communicate with the AMS process, notifying the AMS registration success
// 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 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
Binder_transaction returns all the way back to binder_loop, where the binder_loop calls ioctl again to read the binder driver’s instructions
binder_ioctl_write_read()
// 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 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 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); . }...return ret;
}
Copy the code
binder_thread_read()
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
The SM is suspended again, waiting for other processes 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?
When the binder_transaction function completes, the AMS process wakes up to continue the unfinished binder_thread_read function
binder_thread_read()
// 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)
{
// Fetch binder_transaction, but of type binder_work
w = list_first_entry(&proc->todo, struct binder_work, entry); }...W ->type is BINDER_WORK_TRANSACTION
switch (w->type)
{
......
case BINDER_WORK_TRANSACTION:
{
// Type conversion (binder_work -> binder_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 start address and size of the memory in reply
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 reply information 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
Binder_thread_read function, after the completion of execution has been returned to the IPCThreadState: : talkWithDriver () function, to continue
IPCThreadState::talkWithDriver()
// android\frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{...do{...// execute the return
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
elseerr = -errno; . }while (err == -EINTR); If no ioctl error occurs, exit the loop.// If no ioctl error occurs, if hits
if (err >= NO_ERROR)
{
// If the mOut data is already sent to the Binder driver
if (bwr.write_consumed > 0)
{
if (bwr.write_consumed < mOut.dataSize())
// Delete the data already sent in mOut
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
// If there is data in mIn, return all mIn
if (bwr.read_consumed > 0)
{
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0); }...return NO_ERROR;
}
return err;
}
Copy the code
IPCThreadState::waitForResponse()
// android\frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(
Parcel *reply, / / reply = ServiceManagerProxy addSevice in reply
// This reply is in user space
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 message from reply (in kernel space)
err = mIn.read(&tr, sizeof(tr)); .// Reply (in user space) is not empty, if hit
if (reply)
{
// Tr. flags = 0, if hit
if ((tr.flags & TF_STATUS_CODE) == 0)
{
Write reply (in kernel space) to reply (in user space),
. / / that is written to ServiceManagerProxy addSevice in reply
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 IPCThreadState: : waitForResponse function, after the completion of execution has been returned to ServiceManagerProxy. AddSevice function, to continue
ServiceManagerProxy.addSevice()
// frameworks/base/core/java/android/os/ServiceManagerNative$ServiceManagerProxy.java
public void addService(String name, IBinder service, boolean allowIsolated)
throws RemoteException
{...// execute the return
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
// Recycle data and reply
reply.recycle();
data.recycle();
}
Copy the code
At this point, the whole process ends
conclusion
In accordance with the order of instructions, the AMS registration process is roughly shown as follows
According to the order of the call stack, the AMS registration process is roughly shown below
It might be a little clearer if you look at the code flow with the flow chart