Android framework: Binder fully parses and understands everything

Link: https://juejin.im/post/6869953788388573192

Get ServiceManager from Java layer

In the previous article, I went to the getIServiceManager() method to get the ServiceManager object.

getIServiceManager()

//frameworks/base/core/java/android/os/ServiceManager.java
private static IServiceManager getIServiceManager() {
    if (sServiceManager != null) {
        return sServiceManager;
    }
    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
    return sServiceManager;
}

Is through servicemanagernative asInterface () method to get the ServiceManager object. Binderinternal. Is called in the parameters of asInterface method Getcontextobject() method. This is a native method.

getContextObject()

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b); 
}

Let's take a look at the method ProcessState::self():

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

This is a singleton pattern used to obtain ProcessState, which is only one for each process. The sharp eyed little friend should find a familiar string. Yes, it is the / dev/binder, which is the Binder driver without physical media. So what does the construction method of ProcessState do?

Initialize ProcessState

//frameworks/native/libs/binder/ProcessState.cpp
ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
    , mDriverFD(open_driver(driver))//1 this line is very important. The Binder driver is turned on
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mStarvationStartTimeMs(0)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        //2 mmap memory mapping
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
            close(mDriverFD);
            mDriverFD = -1;
            mDriverName.clear();
        }
    }
    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

At note 1, the construction method of ProcessState first calls open_driver() method. This method can tell from the name that it is to open the driver. Its parameters are not the / dev/binder we just saw passed in, that is, ProcessState opens the Binder driver at the constructor. Let's see how it turns on the driver

open_driver
//frameworks/native/libs/binder/ProcessState.cpp
static int open_driver(const char *driver)
{
    int fd = open(driver, O_RDWR | O_CLOEXEC);//1
    if (fd >= 0) {
        ...
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);//2
        if (result == -1) {
            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
        }
    } else {
        ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
    }
    return fd;
}

Note 1 is used to open the / dev/binder device and return the file operator fd, so that the Binder driver of the kernel can be operated.

In Linux system, everything is regarded as a file. When a process opens an existing file or creates a new file, the kernel returns a file descriptor to the process. The file descriptor is the index created by the kernel in order to efficiently manage the opened file, which is used to point to the opened file. All system calls executing I/O operations will pass through the file descriptor.

The ioctl function in Note 2 is used to pass parameters to the binder device. The ioctl function here is used to set the maximum number of threads supported by the binder to 15 (the value of maxThreads is 15).

In user space, the ioctl method is used to control the device. This is the method prototype:

/*
fd:File descriptor
cmd:control command
...:Optional parameter: insert * argp. The specific content depends on cmd*/
int ioctl(int fd,unsigned long cmd,...);

All the user program does is tell the driver what it wants to do through the command code. As for how to interpret these commands and how to implement them, these are what the driver has to do. Therefore, in the user space, we tell the Binder driver what we want to do in the form of commands through this method. The Binder driver receives commands and performs corresponding operations.

mmap

Note 2 just now is the famous memory mapping. The memory mapping function mmap allocates a virtual address space to the binder. It will apply for a memory of the same size as the user's virtual memory in the kernel virtual address space, and then apply for physical memory. The same physical memory is mapped to the kernel virtual address space and the user's virtual memory space respectively, so as to realize the data synchronization operation between the kernel virtual address space and the user's virtual memory space.

This is the function prototype:

//prototype
/*
addr: Represents the starting address mapped to the process address space. When the value is equal to 0, the kernel selects the appropriate address, here is 0;
size: Represents the size of the memory address space to be mapped, which is 1M-8K here;
prot: Represents the read-write and other attribute values of the memory mapping area. This is prot_ Read (readable);
flags: Flag bit, here is map_ Private (private mapping, change of content not shared between multiple processes) and map_ NoReserve (no swap space reserved)
fd: Represents the file descriptor associated with mmap, here mDriverFD;
offset: Offset, 0 here.

Here mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
*/
void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) 

In general, ProcessState does two things: one is to open the Binder driver and return the file operator fd; the other is to allocate a virtual memory space for Binder through mmap to achieve the purpose of memory mapping.

After getting the ProcessState object, it calls its getContextObject method to get the BpBinder object.

ProcessState.getContextObject

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

As mentioned in the previous section, when the handler of the process requesting the service is 0, it is to obtain the BpBinder object of the ServiceManager. Here is to get the IBinder with handle=0.

ProcessState.getStrongProxyForHandle

//frameworks/native/libs/binder/ProcessState.cpp
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);
    //Find the resource item corresponding to the handle
    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                Parcel data;
                //Test whether the binder is ready by ping
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
            //When the IBinder corresponding to the handle value does not exist or the weak reference is invalid, the BpBinder object is created
            b = new BpBinder(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
    return result;
}

First, find the resource corresponding to the handler. When the IBinder corresponding to the handle value does not exist or the weak reference is invalid, a BpBinder will be created. Otherwise, it will be obtained directly. For the special case of handle==0, that is, the service manager is obtained, which needs to pass PING_TRANSACTION to determine readiness.

Create BpBinder

//frameworks/native/libs/binder/BpBinder.cpp
BpBinder::BpBinder(int32_t handle)
    : mHandle(handle)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK); //Extend the life of an object
    IPCThreadState::self()->incWeakHandle(handle); //handle corresponding bindle weak reference + 1
}

Creating a BpBinder object will increase the weak reference of the Binder corresponding to the handle by 1

After the creation, we return to the getContextObject() method

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b); 
}

You can see that the javaObjectForIBinder method is used to convert the local IBinder pointer to javaobject. Then it returns to the Java layer.

Keywords: Linux Android Programmer

Added by Cal on Sun, 19 Dec 2021 22:43:42 +0200