Associated series Android AOSP Basic series Android system startup series Application process startup series Android In-depth understanding of the four major components of Android Context series Android in-depth understanding of JNI series WMS series AMS series Android package management mechanism series Android input system series

This article was first published on the wechat public account “Houchangcun Mannong”

preface

In the last article, we learned what you need to know about A Binder before you learn about it. One of the things you need to know about Binder is the three parts of the Binder mechanism: Java Binder, Native Binder, Kernel Binder, among which Java Binder and Native Binder are all required for application development. Java Binder needs to work with Native Binder, so it is necessary to understand Native Binder first. The prototype of Native Binder architecture is C/S architecture based on Binder communication, so we start with it first. Source code is based on Android 9.0.

1. C/S architecture based on Binder communication

Binder is widely used for interprocess communication on Android. In the final chapter of the first Android Advanced trilogy, I explained the MediaPlayer framework, which is based on the C/S architecture and uses Binder for interprocess communication, as shown below.

As can be seen from the figure, in addition to the Client and Server sides of the conventional C/S architecture, ServiceManager is also included, which is used to manage services in the system. First, the Server process registers some services with the ServiceManager. To use a Service, the Client needs to query information about the Service from the ServiceManager. Then, a communication channel is established with the Server process of the Service based on the information about the Service, so that the Client can use the Service.

2.MediaServer’s main function

The interaction of Client, Server and ServiceManager is based on the communication with Binder, so the interaction of any two can explain the communication principle of Binder. It can be said that the core principle of Native Binder is the principle of ServiceManager. To better understand the ServiceManager, take the MediaPlayer framework as an example, which is also a must for learning multimedia.

A simple diagram of the MediaPlayer framework is shown below.

frameworks/av/media/mediaserver/main_mediaserver.cpp

int main(int argc __unused, char **argv __unused)
{
    signal(SIGPIPE, SIG_IGN);
    // Get ProcessState instance
    sp<ProcessState> proc(ProcessState::self());/ / 1
    sp<IServiceManager> sm(defaultServiceManager());/ / 2
    ALOGI("ServiceManager: %p", sm.get());
    InitializeIcuOrDie();
    / / MediaPlayerService registration
    MediaPlayerService::instantiate();/ / 3
    ResourceManagerService::instantiate();
    registerExtensions();
    // Start a Binder thread pool
    ProcessState::self()->startThreadPool();
    // The current thread joins the thread pool
    IPCThreadState::self()->joinThreadPool();
}
Copy the code

Note 1 is used to get the ProcessState instance, in which the /dev/binder device is opened and mmap is used to assign a virtual address space for the Binder driver to receive data. Note 2 is used to get an IServiceManager through which other processes can interact with the current ServiceManager, using the Binder. Note 3 registers MediaPlayerService. Note 1 and 2 will be introduced separately in this article, starting with an example of ProcessState.

3. Unique ProcessState for each process

ProcessState, as its name suggests, is used to represent the state of the process. Start by looking at the self function ProcessState in the previous section. 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;
}
Copy the code

The singleton pattern is used to ensure that there is only one instance of ProcessState per process. Note 1 is used to create an instance of ProcessState with /dev/binder. Next, look at the constructor for ProcessState, as shown below. frameworks/native/libs/binder/ProcessState.cpp

ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
    , mDriverFD(open_driver(driver))/ / 1
    , 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) {
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);/ / 2
        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.");
}
Copy the code

Many functions are called in the constructor of ProcessState. Note comment 1, which opens the /dev/binder device. Note 2 the mmap function, it is the kernel in the virtual address space to apply for a virtual memory with users of the same size of memory, and then apply for physical memory, the same piece of physical memory, respectively is mapped to a kernel virtual address space and user’s virtual memory space, realize the virtual address space and kernel data on the user’s virtual memory space synchronization, memory mapping. The Mmap function is used to do memory mapping for Binder devices, along with the open and IOCtl functions, to see what they do. The code for the open_driver function at comment 1 is shown below. 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;
}
Copy the code

Note 1 opens the /dev/binder device and returns the file operator fd so that the binder drivers of the kernel can be manipulated. The ioctl function in Note 2 is used to pass parameters to the Binder device. The ioctl function is used to set the maximum number of threads supported by the Binder to 15 (maxThreads is 15). Finally, the open_driver function returns the file operator fd.

ProcessState analyzes this, and in general it does several important things: 1. Open the /dev/binder device and set the maximum number of threads supported by binder. 2. Mmap is used to allocate a virtual address space for binder to achieve memory mapping.

4. Binder mechanism in ServiceManager

Returning to the MediaServer entry function in section 1, the defaultServiceManager function is called in comment 2. frameworks/native/libs/binder/IServiceManager.cpp

sp<IServiceManager> defaultServiceManager()
{
    if(gDefaultServiceManager ! =NULL) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));/ / 1
            if (gDefaultServiceManager == NULL)
                sleep(1); }}return gDefaultServiceManager;
}
Copy the code

IServiceManager from the file path can know that ServiceManager not only uses Binder communication, it also belongs to the Binder system. DefaultServiceManager also uses singletons. The interface_cast function at comment 1 generates gDefaultServiceManager, which internally calls the getContextObject function of ProcessState, The code is shown below. 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;
    AutoMutex _l(mLock);
    handle_entry* e = lookupHandleLocked(handle);/ / 1
    if(e ! =NULL) {
        IBinder* b = e->binder;
        if (b == NULL| |! e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL.0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
            b = BpBinder::create(handle);/ / 2
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            result.force_set(b);
            e->refs->decWeak(this); }}return result;
}

Copy the code

GetStrongProxyForHandle is directly called in getContextObject. Note that its parameter value is 0, so handle is 0. Handle is a resource identifier. At comment 1, check whether the resource identifier (HANDLE_ENTRY) exists. If not, create BpBinder at comment 2 and assign to HandLE_Entry binder at comment 3. The value of result returned is BpBinder.

4.1 BpBinder and BBinder

When it comes to BpBinder, bbinders are binaries of Binder communication, both of which inherit from IBinder. BpBinder is the proxy class for client-server interaction, while BBinder represents the Server side. BpBinder and BBinder are one-to-one correspondence, BpBinder will find the corresponding BBinder through handle. We know that BpBinder is created in the ServiceManager, and the corresponding BBinder can be found through handle(value 0).

Having analyzed the getContextObject function for ProcessState, return to the interface_cast function:

 gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
Copy the code

The concrete implementation of interface_cast is shown below. frameworks/native/libs/binder/include/binder/IInterface.h

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
Copy the code

In the current scenario, the value of INTERFACE is IServiceManager, so the code after replacement is as follows.

inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj)
{
    return IServiceManager::asInterface(obj);
}
Copy the code

Let’s move on to the IServiceManager.

4.2 the decryption IServiceManager

BpBinder and BBinder are responsible for communication with Binder, while IServiceManager is used to handle the business of ServiceManager. IServiceManager is C++ code, so it is defined in iservicemanager.h. frameworks/native/libs/binder/include/binder/IServiceManager.h

class IServiceManager : public IInterface
{
public:
    DECLARE_META_INTERFACE(ServiceManager)/ / 1.// Some functions that operate on Service
    virtual sp<IBinder>         getService( const String16& name) const = 0;
    virtual sp<IBinder>         checkService( const String16& name) const = 0;
    virtual status_t addService(const String16& name, const sp<IBinder>& service,
                                bool allowIsolated = false.int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0;
    virtual Vector<String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0;
    enum {
        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        CHECK_SERVICE_TRANSACTION,
        ADD_SERVICE_TRANSACTION,
        LIST_SERVICES_TRANSACTION,
    };
};
Copy the code

You can see that IServiceManager inherits IInterface and internally defines constants and functions that operate on Service. In comment 1, it calls the DECLARE_META_INTERFACE macro, which is defined in iInterface.h. frameworks/native/libs/binder/include/binder/IInterface.h

#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const ::android::String16 descriptor;                        \
    static ::android::sp
       
         asInterface( \
       ##interface>
            const ::android::sp<::android::IBinder>& obj);              \
    virtual const ::android::String16& getInterfaceDescriptor(a) const;  \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();     
Copy the code

Where the value of INTERFACE is ServiceManager, the code after the substitution is shown below.

    static const ::android::String16 descriptor;       
    // Define the asInterface function
    static ::android::sp<IServiceManager> asInterface(                    
            const ::android::sp<::android::IBinder>& obj);            
    virtual const ::android::String16& getInterfaceDescriptor(a) const;  
    // Define IServiceManager constructor
    IServiceManager();          
    // Define the IServiceManager destructor
    virtual ~IServiceManager();     
Copy the code

From the name of the DECLARE_META_INTERFACE macro and the code above, you can see that it mainly declares functions and a variable. So where are the implementations of these functions and variables? Answer or in the IInterface. H, called IMPLEMENT_META_INTERFACE macro, as shown in the following code/frameworks/native/libs/binder/include/binder/IInterface. H

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const ::android::String16 I##INTERFACE::descriptor(NAME);           \
    const ::android::String16&                                          \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    ::android::sp
       
         I##INTERFACE::asInterface( \
       ##interface>
            const ::android::sp<::android::IBinder>& obj)               \
    {                                                                   \
        ::android::sp<I##INTERFACE> intr;                               \
        if(obj ! =NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = newBp##INTERFACE(obj); \} \} \return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
Copy the code

The DECLARE_META_INTERFACE macro and the IMPLEMENT_META_INTERFACE macro are used together, and many system services use them, IServiceManager uses the IMPLEMENT_META_INTERFACE macro with only one line of code, As shown below. frameworks/native/libs/binder/IServiceManager.cpp

IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
Copy the code

IMPLEMENT_META_INTERFACE macros for ServiceManager value of the INTERFACE, the NAME value is “android. OS. IServiceManager”, after replacing the code as shown below.

    const ::android::String16 IServiceManager::descriptor("android.os.IServiceManager");          
    const ::android::String16&                                          
            IServiceManager::getInterfaceDescriptor() const {              
        return IServiceManager::descriptor;                                
    } 
     // Implement the asInterface function
    ::android::sp<IServiceManager> IServiceManager::asInterface(              
            const ::android::sp<::android::IBinder>& obj)               
    {                                                                   
        ::android::sp<IServiceManager> intr;                               
        if(obj ! =NULL) {                                              
            intr = static_cast<IServiceManager>(                          
                obj->queryLocalInterface(                               
                        IServiceManager::descriptor).get());               
            if (intr == NULL) {                                         
                intr = new BpServiceManager(obj);/ / 1}}return intr;                                                    
    }                                                                   
    IServiceManager::IServiceManager() { }                                    
    IServiceManager::~IServiceManager() { }                                   
Copy the code

The key point is in comment 1, which creates a BpServiceManager and passes in the parameter obj with the value BpBinder. The asInterface function creates BpServiceManager with BpBinder, and the interface_cast function creates BpServiceManager, and then the interface_cast function creates BpServiceManager. IServiceManager’s defaultServiceManager function returns BpServiceManager. The BpServiceManager constructor is a constructor for BpServiceManager. frameworks/native/libs/binder/IServiceManager.cpp

class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    explicit BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
...
}
Copy the code

The value of the IMPL is actually BpBinder, and the BpServiceManager constructor calls the constructor of the base BpInterface class. frameworks/native/libs/binder/include/binder/IInterface.h

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
...
};
Copy the code

BpInterface inherits BpRefBase, whose implementation is shown below. frameworks/native/libs/binder/Binder.cpp

BpRefBase::BpRefBase(const sp<IBinder>& o)
    : mRemote(o.get()), mRefs(NULL), mState(0)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);

    if (mRemote) {
        mRemote->incStrong(this);           
        mRefs = mRemote->createWeak(this); }}Copy the code

MRemote is an IBinder* pointer that ultimately points to BpBinder, that is, BpServiceManager’s mRemote points to BpBinder. Then the role of BpServiceManager is known, that is, it implements IServiceManager, and through BpBinder to achieve communication.

4.3 IServiceManager family

The above may make you a little dizzy, this is because the relationship between the various classes is not clear, you may be enlightened by the following figure.

1. Both BpBinder and BBinder are related to communication and both inherit from IBinder. 2.BpServiceManager is derived from IServiceManager, both of which are related to services. 3.BpRefBase contains mRemote, and through successive derivations, BpServiceManager also contains mRemote, which points to BpBinder, through which communication is implemented.

5. Section

In this article, we have learned about the C/S architecture of Binder communication and the core principle of Native Binder is actually the principle of ServiceManager. In order to explain the principle of ServiceManager, we need a framework for example, that is MediaPlayer framework. In the explanation of MediaServer entry function, we encountered three problems, including the first two problems related to the knowledge point ProcessState and IServiceManager are explained, the next article will explain the third problem, MediaPlayerService is how to register.


Here not only share big front end, Android, Java and other technologies, but also programmer growth class articles.