Android Binder inter-process communication mechanism, too realistic

Once a service is implemented, it is not immediately available for others to use. Instead, you need to register the service with the ServiceManager through the Binder driver to indicate that it can be served externally. Let's look at the implementation of the SurfaceFlinger service registration publication code:

/*frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp*/

int main(int, char**) {

    ...

    // When SF is launched in its own process, limit the number of

    // binder threads to 4.

    ProcessState::self()->setThreadPoolMaxThreadCount(4);



    // 1. Start the Binder thread pool

    // start the thread pool

    sp<ProcessState> ps(ProcessState::self());

    ps->startThreadPool();



    // instantiate surfaceflinger

    sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();

    setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);

    set_sched_policy(0, SP_FOREGROUND);



    // initialize before clients can connect

    flinger->init();



    // 2. Register a service in ServiceManager through IServiceManager::addService, where the name of the service is "SurfaceFlinger"

    // publish surface flinger

    sp<IServiceManager> sm(defaultServiceManager());

    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,

                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);



    // run surface flinger in this thread

    flinger->run();

    return 0;

}



Thus, publishing a Binder service requires starting the Binder thread pool, then registering the service in the ServiceManager through IServiceManager::addService.

[](

) 4.4.3 Client Implementation

BpSurfaceComposer needs to implement all interfaces in ISurfaceComposer. Let's take the createDisplay interface mentioned above as an example to see how the BpSurfaceComposer::createDisplay method code is implemented:

    /*frameworks/native/libs/gui/ISurfaceComposer.cpp*/

    virtual sp<IBinder> createDisplay(const String8& displayName, bool secure)

    {

        Parcel data, reply;

        // 1. Fill in the unique description information descriptor for the service interface

        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());

        // 2. Write send parameters through Parcel

        data.writeString8(displayName);

        data.writeInt32(secure ? 1 : 0);

        // 3\.Call remote()->transact to send the request

        remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply);

        return reply.readStrongBinder();

    }



This code is simple, and the logic is to package the call parameters by writing them to Parcel, then call remote ()->transact to send the request.

[](

) 4.4.4 Access to Services

Clients need to query the ServiceManager for a handle to access the service based on the name of the service before they can use the service on the server to get the proxy to the remote service through the BpSurfaceComposer::remote() function and then call the transact method to complete a Binder data send. The detailed code is as follows:

/*framworks/native/libs/gui/SurfaceComposerClient.cpp*/

void ComposerService::connectLocked() {

    // 1\. The name of the remote Binder service to find is SurfaceFlinger

    const String16 name("SurfaceFlinger");

    // 2. Call IServiceManager:: getServicePackaging Interface to get the remote service proxy and encapsulate it in BpSurfaceComposer

    while (getService(name, &mComposerService) != NO_ERROR) {

        usleep(250000);

    }

    ...

}



/*frameworks/native/include/binder/IServiceManager.h*/

template<typename INTERFACE>

status_t getService(const String16& name, sp<INTERFACE>* outService)

{   // 1\. Get a proxy to the ServiceManager to access the ServiceManager

    const sp<IServiceManager> sm = defaultServiceManager();

    if (sm != nullptr) {

        // 2\. Get specific service handles through getService and use interface_cast conversion to BpSurfaceComposer object

        *outService = interface_cast<INTERFACE>(sm->getService(name));

        if ((*outService) != nullptr) return NO_ERROR;

    }

    return NAME_NOT_FOUND;

}



/*frameworks/native/libs/binder/IServiceManager.cpp*/

sp<IServiceManager> defaultServiceManager()

{

    std::call_once(gSmOnce, []() {

        sp<AidlServiceManager> sm = nullptr;

        while (sm == nullptr) {

            //ProcessState::getContextObject(nullptr) Gets access handle to ServiceManager

            sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));

            ...

            }

        }

    });

    return gDefaultServiceManager;

}



/*frameworks/native/libs/binder/ProcessState.cpp*/

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)

{    

    // The parameter "0" here refers to the ServiceManager service, which is a special address used to identify the ServiceManager

    sp<IBinder> context = getStrongProxyForHandle(0);

    ...

    return context;

}



Via interface_cast This method obtains the interface object of a service and automatically determines whether to return a local or remote Binder based on whether it is in the same process or not. interface_cast is a template method with the following source code:

/*frameworks/native/libs/binder/include/binder/IInterface.h*/

template<typename INTERFACE>

inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)

{

    return INTERFACE::asInterface(obj);

}



For ISurfaceComposer, it is actually ISurfaceComposer::asInterface(obj), which is defined as follows:

   /*frameworks/native/libs/binder/include/binder/IInterface.h*/

    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \

            const ::android::sp<::android::IBinder>& obj)               \

    {                                                                   \

        ::android::sp<I##INTERFACE> intr;                               \

        if (obj != nullptr) {                                           \

            //1. First use queryLocalInterface to try to get Binder objects in the same process locally

            intr = static_cast<I##INTERFACE*>(                          \

                obj->queryLocalInterface(                               \

                        I##INTERFACE::descriptor).get());               \

            //2. Failure creates and returns a remote Binder object encapsulated as a BpSurfaceComposer

            if (intr == nullptr) {                                      \

                intr = new Bp##INTERFACE(obj);                          \

            }                                                           \

        }                                                               \

        return intr;                                                    \

    }   



Since they are all template definitions, replace'##'with SurfaceComposer when you understand them.

[](

5 ServiceManager - Binder Service "Housekeeper"

=============================================================================================

ServerManager (SM) can be analogous to DNS servers in the Internet, with an IP address of 0. Each Binder service needs a unique name, and SM manages the registration and lookup of these services. In addition, SM is a standard Binder Server just as the DNS server itself is, and the Binder driver accesses the ServiceManager through a specific handle = 0 location. Therefore, analyzing SM gives us a complete view of how a top-level application can build a Binder Server driven by Binder. Below we analyze the implementation of the SM module.

[](

) 5.1 Startup of ServiceManager process

SM, as the "steward" of Binder services, must ensure that all Binder services in the system are started and working properly before they can be started. So SM starts directly when the init program resolves the init.rc load at startup. As follows:

/*framework/native/cmds/servicemanager/servicemanager.rc*/

service servicemanager /system/bin/servicemanager// 1. Service manager is a stand-alone executable

    class core animation

    user system

    group system readproc

    critical

    onrestart restart healthd

    onrestart restart zygote// 2. Restarting service manager will cause zygote to restart

    onrestart restart audioserver

    onrestart restart media

    onrestart restart surfaceflinger// 3. Restarting service manager will cause surfaceflinger to restart

    onrestart restart inputflinger

    onrestart restart drm

    onrestart restart cameraserver

    onrestart restart keystore

    onrestart restart gatekeeperd

    onrestart restart thermalservice

    writepid /dev/cpuset/system-background/tasks

    shutdown critical



As can be seen from above, servicemanager is a stand-alone executable file written in C++, a native process that runs independently and is pulled up directly by the init process at system startup. The source path is as follows:

/framework/native/cmds/servicemanager/*

The main logic of its main function is as follows:

/*framework/native/cmds/servicemanager/main.cpp*/

int main(int argc, char** argv) {

    if (argc > 2) {

        LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";

    }



    const char* driver = argc == 2 ? argv[1] : "/dev/binder";

    //1. The binder driver and mmap memory mapping actions under open/dev/binder will be completed by initWithDriver

    sp<ProcessState> ps = ProcessState::initWithDriver(driver);

    ps->setThreadPoolMaxThreadCount(0);

    ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);

    //2. Create a ServiceManager object and register yourself as a Binder Server named "manager" through the addService interface

    sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());

    if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {

        LOG(ERROR) << "Could not self register servicemanager";

    }



    IPCThreadState::self()->setTheContextObject(manager);

    ps->becomeContextManager(nullptr, nullptr);// 3. Notify the binder driver to register itself as the binder service steward



    sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);



    BinderCallback::setupTo(looper);

    ClientCallbackCallback::setupTo(looper, manager);



    while(true) {

        looper->pollAll(-1);// 4. Loop waiting for other modules to request service

    }



    // should not be reached

    return EXIT_FAILURE;

}



There are four main things to do:

  1. By calling the initWithDriver static interface of ProcessState, which creates the ProcessState object of the SM process and completes the Binder initialization of the process: call open_first The driver action opens the Binder driver and then calls the mmap action to notify the Binder driver to complete the binder_mmap physical memory allocation and virtual memory mapping operations;

  2. Creating a ServiceManager object and registering SM itself as a Binder Server service named "manager" through the addService interface further illustrates that SM itself is essentially a Binder Server service process written in C++;

  3. Call the becomeContextManager interface of ProcessState through binder_ioctl protocol command BINDER_SET_CONTEXT_MGR notifies the Binder device driver to register SM as the "steward" of the binder service;

  4. Enter a loop waiting for client process access requests from the Binder Client process.

[](

) 5.2 ServiceManager Process Binder Service Interface

Let's look at the code definition for the ServiceManager.h interface file:

/*framework/native/cmds/servicemanager/ServiceManager.h*/

class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {

public:

    ServiceManager(std::unique_ptr<Access>&& access);

    ~ServiceManager();

    ...

    // getService will try to start any services it cannot find

    // 1. Query the Binder Server service by name

    binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override;

    binder::Status checkService(const std::string& name, sp<IBinder>* outBinder) override;

     // 2. Register Binder Server Service

    binder::Status addService(const std::string& name, const sp<IBinder>& binder,

                              bool allowIsolated, int32_t dumpPriority) override;

    // 3. Traverse to list all registered Binder Server services

    binder::Status listServices(int32_t dumpPriority, std::vector<std::string>* outList) override;

    ...



};



From the analysis of the Binder framework C++ layer in the previous section, we can see that ServiceManager inherits from BnServiceManager and is a Binder Server server-side implementation. Provides getService Query Service Service, addServiceRegistered Service Service, and listServices to clients to iterate through all registered services. Its internal implementation maintains the mapping relationship between the service name and the specific service proxy through a global variable record of the ServiceMap type named mNameToService (which is essentially a collection of map types). Take the implementation code of the addService registration service as an example:

/*framework/native/cmds/servicemanager/ServiceManager.cpp*/

Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {

    ...

    auto entry = mNameToService.emplace(name, Service {

        .binder = binder,

        .allowIsolated = allowIsolated,

        .dumpPriority = dumpPriority,

        .debugPid = ctx.debugPid,

    });

    ...

    return Status::ok();

}



[](

) Encapsulation of Access ServiceManager Interface in 5.3 libbinder

Source path:

frameworks/native/include/binder/IServiceManager.h frameworks/native/libs/binder/IServiceManager.cpp

This logic corresponds to the encapsulation of the libbinder Library of the Binder framework C++ layer as the Binder client's access to the Binder service-side interface provided by the ServiceManager process.

The C++ interface of IServiceManager is defined as follows:

/*frameworks/native/include/binder/IServiceManager.h*/

class IServiceManager : public IInterface

{

public:

    ...

    // 1. Query the Binder Server service by name

    /**

     * Retrieve an existing service, blocking for a few seconds

     * if it doesn't yet exist.

     */

    virtual sp<IBinder>         getService( const String16& name) const = 0;



    /**

     * Retrieve an existing service, non-blocking.

     */

    virtual sp<IBinder>         checkService( const String16& name) const = 0;

     // 2. Register Binder Server Service

    /**

     * Register a service.

     */

    // NOLINTNEXTLINE(google-default-arguments)

    virtual status_t addService(const String16& name, const sp<IBinder>& service,

                                bool allowIsolated = false,

                                int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0;

    // 3. Traverse to list all registered Binder Server services

    /**

     * Return list of all existing services.

     */

    // NOLINTNEXTLINE(google-default-arguments)

    virtual Vector<String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0;



    /**

     * Efficiently wait for a service.

     *

     * Returns nullptr only for permission problem or fatal error.

     */

    virtual sp<IBinder> waitForService(const String16& name) = 0;

    ...

};



Ordinary Binder services require an interface to be acquired through the ServiceManager before they can be invoked, so how can the ServiceManager interface be acquired? In libbinder, a defaultServiceManager method is provided to get the proxy for ServiceManager, and this method does not require an incoming parameter. The reason we've already covered in the driver section is that in the implementation of Binder, a special location is reserved for ServiceManager, and it doesn't need to be identified as a normal service. The defaultServiceManager code is as follows:

/*frameworks/native/libs/binder/IServiceManager.cpp*/

sp<IServiceManager> defaultServiceManager()

{

    std::call_once(gSmOnce, []() {

        sp<AidlServiceManager> sm = nullptr;

        while (sm == nullptr) {

            sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));

            if (sm == nullptr) {

                ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());

                sleep(1);

            }

        }



        gDefaultServiceManager = new ServiceManagerShim(sm);

    });



    return gDefaultServiceManager;

}



[](

) 6 Binder Framework Java Layer

======================================================================================

[](

) 6.1 Main Structure

Android applications are developed in the Java language, and the Binder Framework framework naturally provides Java interfaces as well. We have previously analyzed the full implementation of the Binder Framework C++ layer. Therefore, instead of repeating the implementation at all, the Java layer reuses the implementation of the C++ layer directly through the JNI mechanism provided by the virtual machine. The following illustrates the call relationship from the Binder Framework Java layer to the C++ layer using a diagram.

[](

) 6.2 JNI Join Call

The full name of JNI is Java Native Interface, a mechanism provided by a Java virtual machine. This mechanism allows native code to communicate with Java code. Simply put, we can call Java code on the C/C++ side or C/C++ code on the Java side. In fact, many services or mechanisms in Android are implemented in the C/C++ layer. To reuse these implementations in the Java layer, they must be connected through JNI. In fact, this is to solve the call problem in two directions:

  1. How does the Java-side code call the C++ method in libbinder? Taking the transact method in BinderProxy.java as an example, its function is to implement a Binder client in the Java layer to complete a Binder data transmission using a remote proxy on the server side, simplifying the code as follows:
    /*frameworks/base/core/java/android/os/BinderProxy.java*/

    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

        ..

        try {

            return transactNative(code, data, reply, flags);

        } finally {

            ...

        }

    }



     /**

     * Native implementation of transact() for proxies

     */

    public native boolean transactNative(int code, Parcel data, Parcel reply,

            int flags) throws RemoteException;



You can see that the methods transactNative are modified with the native keyword, and there is no method implementation body, these methods are actually implemented in C++. In android_ Util_ The following section of code in the Binder.cpp file defines the relationship between Java methods and C++ methods, defines the true implementation, and simplifies the code implementation as follows:

/*frameworks/base/core/jni/android_util_Binder.cpp*/

// The corresponding relationship between Java methods and C++ methods is defined in the collection

static const JNINativeMethod gBinderProxyMethods[] = {

     /* name, signature, funcPtr */

     ...

    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},

     ...

};



// True method implementation

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,

        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException

{

    ...

    // 1. Get the BpBinder object

    IBinder* target = getBPNativeData(env, obj)->mObject.get();

    ...

    // 2. Call the transact interface of BpBinder in libbinder for true Binder transmission

    status_t err = target->transact(code, *data, reply, flags);

    ...

    return JNI_FALSE;

}



  1. How can libbinder notify calls to the Java tier? The following is an example of how an onTransact calls a Binder::onTransact in Java: This logic is in android_ Util_ JavaBBinder::onTransact (JavaBBinder is a subclass of BBinder) in Binder.cpp implements a native method that calls a method implementation on Java Object using the CallBooleanMethod interface provided by the virtual machine. The simplified code is implemented as follows:
/*frameworks/base/core/jni/android_util_Binder.cpp*/

// JavaBBinder is a subclass of BBinder

class JavaBBinder : public BBinder

{

protected:

    ...

    status_t onTransact(

        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override

    {

        ...

        // 1\. CallBooleanMethod This method is an implementation native method provided by the virtual machine that calls a method on a Java Object. This line of code actually calls a method on mObject that offset is mExecTransact.

        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,

            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);

        ...

    }

    ...

};



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



static int int_register_android_os_Binder(JNIEnv* env)

{

    ...

    jclass clazz = FindClassOrDie(env, kBinderPathName);

    // 2\. Find the execTransact method in android/os/Binder.java

    gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");

    ...

    return RegisterMethodsOrDie(

        env, kBinderPathName,

        gBinderMethods, NELEM(gBinderMethods));

}



/*frameworks/base/core/java/android/os/Binder.java*/

 // Entry point from android_util_Binder.cpp's onTransact

    @UnsupportedAppUsage

    private boolean execTransact(int code, long dataObj, long replyObj,

            int flags) {

        ...

        try {

            return execTransactInternal(code, dataObj, replyObj, flags, callingUid);

        } finally {

            ThreadLocalWorkSource.restore(origWorkSource);

        }

    }



    private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,

            int callingUid) {

        ...

        try {

            ...

            if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) {

                ...

            } else {

                // Call onTransact implementation to java layer

                res = onTransact(code, data, reply, flags);

            }

        } catch (RemoteException|RuntimeException e) {

           ...

        } finally {

           ...

        }

        ...

    }



[](

) 6.3 Java Binder Service Implementation Example

Like the C++ layer, here's a specific example of how the Java layer's Binder service is implemented. With system framework system_ For example, the core service ActivityManager of the server process is the class diagram structure it implements:

The IActivityManager interface defines the functional interfaces that ActivityManager provides to the outside world, and its subclasses inherit these interfaces.

  • IActivityManager.Stub.Proxy is a remote interface that provides client calls, obtains a handle to the service implementer BinderProxy, and sends the specific request through the transact method.

  • IActivityManager.Stub is a server-side implementation with only one onTransact method that docks each request based on the request code and directly calls the corresponding method in the ActivityManagerService.

  • ActivityManagerService is a true implementation of the service-side interface functionality.

You can see that this organizational structure is basically the same as the Binder C++ layer SurfaceFlinger service in the 4.4.4 summary. For developers of Android applications, these classes are encapsulated, so instead of directly touching the classes in the diagram above, we use the interfaces in android.app.ActivityManager. What is the relationship between the interface in the Activity Manager and the implementation of the diagram above? Let's take a look at one of these methods:

/*frameworks/base/core/java/android/app/ActivityManager.java*/

  public List<RunningAppProcessInfo> getRunningAppProcesses() {

        try {

            return getService().getRunningAppProcesses();

        } catch (RemoteException e) {

            throw e.rethrowFromSystemServer();

        }

    }



The implementation of this method calls the method in getService(), so let's see what getService() returns to.

/*frameworks/base/core/java/android/app/ActivityManager.java*/

    @UnsupportedAppUsage

    public static IActivityManager getService() {

        return IActivityManagerSingleton.get();

    }



    @UnsupportedAppUsage

    private static final Singleton<IActivityManager> IActivityManagerSingleton =

            new Singleton<IActivityManager>() {

                @Override

                protected IActivityManager create() {

                    // 1. Obtain a proxy IBinder object for a remote service by accessing the ServiceManager to follow the service name "activity" query

                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);

                    // 2. Call asInterface to encapsulate the acquired remote service proxy IBinder object transformation into client IActivityManager interface object

                    final IActivityManager am = IActivityManager.Stub.asInterface(b);

                    return am;

                }

            };



This is actually done first through IBinder b = ServiceManager.getService("activity"); Query the ServiceManager for a remote service proxy Binder object for the ActivityManager (the AMS system core service is registered in the ServiceManager Binder "Housekeeper" process ahead of time during system startup, and the service name is "activity"); Next, let's look at the implementation of asInterface(b):

/*gen/android/app/IActivityManager.java*/

public static android.app.IActivityManager asInterface(android.os.IBinder obj)

    {

      if ((obj==null)) {

        return null;

      }

      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

      if (((iin!=null)&&(iin instanceof android.app.IActivityManager))) {

        return ((android.app.IActivityManager)iin);

      }

      return new android.app.IActivityManager.Stub.Proxy(obj);

    }



First determine whether there is a local Binder through queryLocalInterface and return directly if there is one, otherwise create an android.app.IActivityManager.Stub.Proxy client proxy object.

[](

) 6.4 AIDL mechanism

Android Learning Notes Summary + Mobile Architecture Video + Factory Interview True Topics + Project Actual Source
Disk: pan.baidu.com/s/12WbFc8cmVXhdgFkOLEfS5g
Extract code: wecw (copy link to browser open)

End

Some students in the review had questions about how to learn the material design control. My suggestion is to go to GitHub and there are many peer-provided examples. These chestnuts are enough to get started.

Some friends said that if you are serious, you need knowledge of NDK and JVM. First, ** NDK is not a mystery, ** You can follow the official steps once to know what is going on, just some code formats and native/JAVA memory interaction, advanced native/JAVA thread interaction, thread interaction is a bit painful, but it's good to avoid it in general. Besides, for beginners, what NDK does matter? According to my previous experience, I only used it in two projects: audio-video communication and an embedded signal processing (offline). Embedded signal processing is the code of my original MATLAB called by JAVA->NDK->.SO->MATLAB. Most of the others are used in games. Ordinary Internet companies will have SO packages for your company.
As for JVM, the part you should master, trust me, you will. If you don't, someone who specializes in JVM will do it. It's better to not worry about taking a look at computer systems and compiling principles.

In a word, writing and practicing more often is the basic quality of programmers. Try to squeeze time and read theoretical books. JVM is not the only virtual machine in the next 30 years. JAVA will no longer be popular in the industry in the next 30 years. Other systems and languages will spring up, but your solid theory will make you understand and learn a language or framework quickly. What you usually write will make you skilled at applying new things to practice very quickly.
Beginners, one sentence, practice more.

**[CodeChina Open Source Project: Android Learning Notes Summary + Mobile Architecture Video + Factory Interview True Topics + Project Actual Source](

)**

Tags: C++ Android Design Pattern Programmer

Posted on Sat, 20 Nov 2021 19:15:58 -0500 by pmaiorana