The ServiceManager starts and works

ServiceManager start

All system services need to be registered with ServiceManager, which is started with init.rc as a starting service.

    //system\core\rootdir\init.rc	
	// Start the service, here is the service name used. The service name is registered and started in the corresponding RC file
    start servicemanager
Copy the code

The specific service information is named and defined in Servicemanger.rc

//\frameworks\native\cmds\servicemanager\servicemanager.rc
service servicemanager /system/bin/servicemanager
    class core animation
    user system// Specify the usersystemrungroup system readproc/ / thatservicemanagerIs a critical service in the system, // the critical service does not exit, if exit, the system will restart, when the system restartsonrestartKeyword modification process, // for examplezygote,media,surfaceflingerAnd so on.critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart audioserver
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart inputflinger
    onrestart restart drm
    onrestart restart cameraserver
    onrestart restart keystore
    onrestart restart gatekeeperd
    onrestart restart thermalservice.Copy the code

The entry function for servicemanager is in service_manager.c

//frameworks\native\libs\binder\ndk\service_manager.cpp
int main(int argc, char** argv)
{
	// Binder_state structure, which stores three bits of information about binder
    struct binder_state *bs;
	// Open the binder driver and request 125K bytes of memory space
    bs = binder_open(driver, 128*1024); .// Register yourself as a manager of Binder mechanisms
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n".strerror(errno));
        return - 1; }...// Start a loop to wait for and process requests from the client
    binder_loop(bs, svcmgr_handler);

    return 0;
}

Copy the code

There are three main things you do in the main function.

  • Open the driver and apply for 128K bytes of memory space
  • Register yourself as administrator of the Binder mechanism
  • The loop starts and waits for and processes requests from the Client
binder_open
//frameworks\native\cmds\servicemanager\binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;
    // Apply for corresponding memory space
    bs = malloc(sizeof(*bs));
	// Open the binder device file, which is a device driver operation
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
	// Get binder version numbers from IOCtl
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == - 1) || (vers.protocol_version ! = BINDER_CURRENT_PROTOCOL_VERSION)) {fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }
    bs->mapsize = mapsize;
	Binder device files are mapped to the corresponding address space of the process. The size of the address space is 128K
	// After mapping, the starting address and size of the address space are stored in the structure,
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    return bs;
}
Copy the code

Binder_opende opens Binder driver files, mmaps them, and stores the corresponding address space in the structure.

binder_become_context_manager
//frameworks\native\cmds\servicemanager\binder.c
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
Copy the code

Ioctl calls binder_ioctl, a Binder driven function, to register as the administrator.

binder_loop

By registering servicemanger as a context manager with Binder, it becomes the “master” of the Binder mechanism, handling Client requests during system runtime, using an infinite loop because of the time uncertainty of requests. Namely 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;
	// The current thread is registered as a Binder directive
    readbuf[0] = BC_ENTER_LOOPER;
	// Write BC_ENTER_LOOPER to Binder driver,
	// Register the current ServiceManager thread as a Binder thread (note that ServiceManager is itself a Binder thread).
	// Register as a Binder thread to handle interprocess requests
    binder_write(bs, readbuf, sizeof(uint32_t));
	// Keep iterating
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
		// Use BINDER_WRITE_READ to check for requests in Binder drivers.
		// If there is a request, it goes to the binder_parse section below. If not, the current ServiceManager thread waits in the Binder driver for a new interprocess request
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
		// There is a request for information. The requested information is processed with binder_parse, func
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); }}Copy the code
  • The Servicemanager registers itself as a Binder thread first. Interprocess requests can only be received after being registered as a Binder service. The directive to register as a Binder service is BC_ENTER_LOOPER. It is then written to the binder driver using the **binder_write()** method.
//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_WRITE_READ can be read or written. The key is read_size and write_size.
	// If write_size>0. Is written. If read_size>0, it is read.
	// If both are greater than 0, write first, then 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

System Service Registration

In Android, each process can obtain various system services (AMS, PMS, WMS, etc.) provided by the system through ServiceManager. These system services must be registered with Binder to obtain the ServiceManager service. As we discussed earlier, the ServiceManager registers itself as a Binder service.

Here we use SurfaceFling to fetch the ServiceManager service as an example.

//frameworks\native\services\surfaceflinger\main_surfaceflinger.cpp
#include <binder/IServiceManager.h>
int main(int.char* *) {...// Get an SM object, equivalent to new BpServiceManager(new BpBinder(0))
    sp<IServiceManager> sm(defaultServiceManager());
	// Register the SurfaceFling service with the ServiceManager
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
	// When SurfaceFlinger calls init, it initializes Display
    startDisplayService(a);// dependency on SF getting registered above.return 0;
}

Copy the code

There are two main points in the system service registration process

  • Obtain Binder objects for ServiceManager.
  • Register as a system service through addService.

Obtain the Binder object of ServiceManager

The defaultServiceManager()** method is the Binder object used to get the ServiceManager service.

defaultServiceManager
//frameworks\native\libs\binder\IServiceManager.cpp
sp<IServiceManager> defaultServiceManager(a)
{
    if(gDefaultServiceManager ! =nullptr) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
		/* * 1. ProcessState::self()->getContextObject(NULL): Return a BpBinder. ServiceManager's desc defaults to 0. * 2. Interface_cast encapsulates BpBinder as IServiceManager, which can be called directly IServiceManager interface */
		
		DefaultServiceManager instantiates BpBinder first. * interface_cast instantiates BpXXX and gives BpBinder to manage. * * Users on the Proxy side cannot directly see BpBinder, which is held by BpXXX. Users themselves do not care about the capabilities of BpBinder, only the interface defined by IXXX

        while (gDefaultServiceManager == nullptr) {// If the value is not empty, the setting is complete
        	// Try to get the ServiceManager object continuously, if not, then sleep (1),
        	// This is because the ServiceManager and some services started by init.rc are started at the same time, so there is no guarantee that the ServiceManager can be started first.
        	// So there will be access to ServiceManager when not available.
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self() - >getContextObject(nullptr));
            if (gDefaultServiceManager == nullptr)
                sleep(1); }}return gDefaultServiceManager;
}
Copy the code

**ProcessState::self()->getContextObject(Nullptr)** is called directly to get the corresponding service.

  • ProcessState::self()->getContextObject(NULL): Returns a BpHwBinder. The default value of desc for ServiceManager is 0.
  • Interface_cast encapsulates BpBinder as IServiceManager
ProcessState::self()
//system\libhwbinder\ProcessState.cpp
// Return a ProcessState
sp<ProcessState> ProcessState::self(a)
{
    Mutex::Autolock _l(gProcessMutex);
    if(gProcess ! =nullptr) {
        return gProcess;
    }
    gProcess = new ProcessState(kDefaultDriver);
    return gProcess;
}

Copy the code

A ProcessState object is returned.

getContextObject
//system\libhwbinder\ProcessState.cpp
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    // The argument passed in is handle. 0,
    return getStrongProxyForHandle(0);
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    handle_entry* e = lookupHandleLocked(handle);
    if(e ! =nullptr) {
        IBinder* b = e->binder;
        if (b == nullptr| |! e->refs->attemptIncWeak(this)) {
			// If b is empty, create a BpHwBinder
            b = new BpHwBinder(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs(a); result = b; }else {
            result.force_set(b);
            e->refs->decWeak(this); }}return result;
}
Copy the code

A BpHwBinder object is created when it does not exist. So it’s understandable that we end up returning a BpBinder object

Here is a design idea:

  1. DefaultServiceManager first instantiates BpBinder.

  2. Interface_cast instantiates BpXXX and assigns BpBinder to its management.

Users on the Proxy side cannot directly see BpBinder, which is held by BpXXX. Users themselves do not care about the capabilities of BpBinder, only the interfaces defined by IXXX. So there’s a good encapsulation here.

Go back to the defaultServiceManger method, substitute the return value, and get

// Note that the handle passed in to the method is 0, so the BpBinder parameter is 0
gDefaultServiceManager = interface_cast<IServiceManager>(new BpBinder(0));
Copy the code
interface_cast
//frameworks\native\include\binder\IInterface.h
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
//INTERFACE is put into IServiceManager, the resulting code is
inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj)
{
    return IServiceManager::asInterface(obj);// Static methods are called directly
}
Copy the code

The IServiceManager interface member function asInterface is called to encapsulate a Binder proxy object with a handle value of 0 as a ServiceManger proxy object. Encapsulate a Binder proxy object with a handle value of 0 as a ServiceManger proxy object.

The IServiceManager interface member function asInterface is implemented by the macro IMPLEMENT_META_INTERFACE, as shown below:

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       
    DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)    
#endif

#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\
    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
            const ::android::sp<::android::IBinder>& obj)               \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                               \
        if(obj ! = nullptr) { \ intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if(intr == nullptr) { \ intr = new Bp##INTERFACE(obj); \ } \ } \ return intr; \} \Copy the code

Into IServiceManager after the code is:

android::sp<IServiceManager> IIServiceManager::asInterface(const android::sp<android::IBinder>& obj)                                 
{                                                                                     
	android::sp<IServiceManager> intr;                                                    
	if(obj ! =NULL) {                                                                     
		intr = static_cast<IIServiceManager*>(                                                  
                    obj->queryLocalInterface(IServiceManager::descriptor).get());/ / returns NULL
		if (intr == NULL) {                
			intr = new BpServiceManager(obj);  // A ServiceManager proxy object is created}}return intr;                                  
}  
Copy the code

At this point, we have created a BpIServiceManager object and returned its interface IServiceManager to the caller.

The overall logic can be understood as new BpServiceManager(New BpBinder()). Of course, this is just simplified code, and the complex logic behind it can be ignored for now. The overall process is as follows:

Add the Service

Client request

When the ServiceManager service is obtained, the addService method can be used to register the service. When retrieving a service, the BpServiceManager object is ultimately returned, so here we can directly find the corresponding method to add a service

    virtual status_t addService(const String16& name, const sp<IBinder>& service,
                                bool allowIsolated, int dumpsysPriority) {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        data.writeInt32(dumpsysPriority);
        status_t err = remote() - >transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
Copy the code

The BpServiceManager constructor passes in a BpBinder object, where the remote() method is actually a BpBinder object.

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // If binder has died, no data is returned
    if (mAlive) {
        ...
		// Call the transact method of IPCThreadState.
        status_t status = IPCThreadState::self() - >transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}
Copy the code

The transact method of IPCThreadState is called to write the corresponding data to the Binder driver. When the Binder driver receives information about registering a service, it registers the corresponding service with the ServiceManager. We can look at the arguments passed:

  • MHandle: 0: indicates the process ID to process the request. When the ServiceManager is registered, its process ID is 0. So the ServiceManager process handles the request.
  • Code: The parameter is ADD_SERVICE_TRANSACTION.
  • Data: Contains information about the process to be added, including the name, whether to run separately, and so on
The ServiceManager handles requests

When the client sends the request, our ServiceManger can receive the message and process it. When ServiceManger is started, binder_looper is called to check whether the corresponding data is received.

This function is implemented by svcmgr_handler in the input argument to the binder_loop() method.

//frameworks\native\libs\binder\ndk\service_manager.cpp
int main(int argc, char** argv){...// Start a loop to wait for and process requests from the client
    binder_loop(bs, svcmgr_handler); . }Copy the code

Svcmgr_handler is our specific request handling method.

//frameworks\native\cmds\servicemanager\service_manager.c
int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data_secctx *txn_secctx, struct binder_io *msg, struct binder_io *reply)
{...struct binder_transaction_data *txn =&txn_secctx->transaction_data; .// Depending on the type of transport.
    switch(txn->code) {
        case SVC_MGR_ADD_SERVICE:// Add a service
            // Add the service
            do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
                               txn->sender_pid, (const char*) txn_secctx->secctx)
            ... 
}

    
int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
                   uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid, const char* sid) {
    struct svcinfo *si;

    //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
    // allow_isolated ? "allow_isolated" : "! allow_isolated", uid);
    The length of the service name cannot exceed 127 bytes
    if(! handle || (len ==0) || (len > 127))
        return - 1;
	// The selinux_check_access method is called to check whether the service has been registered
    if (!svc_can_register(s, len, spid, sid, uid)) {
        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n".str8(s, len), handle, uid);
        return - 1;
    }
	// Check whether there is already svcinfo containing name
    si = find_svc(s, len);
    if (si) {
        if (si->handle) {
            ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n".str8(s, len), handle, uid);
			// The service has been registered
            svcinfo_death(bs, si);
        }
		// Update the handle of service
        si->handle = handle;
    } else {
		// Request memory
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        si->handle = handle;
        si->len = len;
        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;
        si->dumpsys_priority = dumpsys_priority;
		// Register it in the list of services svclist, the linked list used here to hold data
        si->next = svclist;
        svclist = si;
    }
	// Target handle with BC_ACQUIRE command.
    binder_acquire(bs, handle);
	// Target handle with the BC_REQUEST_DEATH_NOTIFICATION directive.
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}
Copy the code

After receiving the request information, the ServiceManager generates the corresponding SvCInfo object and stores it in the service list svclist.

The overall process is as follows:

Binder can also be looked at in another dimension

Obtaining System Services

For Servie service access, in fact, the answer is the same idea. Binder objects are shown to fetch ServiceManager, and then the server sends a request for a service to be processed by the ServiceManager.

Here we’ll just look at the mechanism by which the ServiceManager receives a service acquisition. Also in **svcmgr_handler()**.

//frameworks\native\cmds\servicemanager\service_manager.c
int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data_secctx *txn_secctx, struct binder_io *msg, struct binder_io *reply)
{...struct binder_transaction_data *txn =&txn_secctx->transaction_data; .// Depending on the type of transport.
    switch(txn->code) {
        case SVC_MGR_GET_SERVICE:// Get the service
        case SVC_MGR_CHECK_SERVICE:
            s = bio_get_string16(msg, &len);
            if (s == NULL) {
                return - 1;
            }
            // Obtain the handle value corresponding to the service according to the PID and uid
            handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
                                     (const char*) txn_secctx->secctx);
            if(! handle)break;
            // Return the handle corresponding to the service
            bio_put_ref(reply, handle);
            return 0;
}

Copy the code

There are two main operations:

  1. Obtain the handle of the corresponding service from the service list
  2. Write handle to the reply data to be returned.
do_find_service
//frameworks\native\cmds\servicemanager\service_manager.c  
// Obtain the corresponding service
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid)
{
	// Obtain the corresponding service
    struct svcinfo *si = find_svc(s, len);

    if(! si || ! si->handle) {return 0;
    }

    if(! si->allow_isolated) {// If this service doesn't allow access from isolated processes,
        // then check the uid to see if it is isolated.
        uid_t appid = uid % AID_USER;
		// Check whether the service is allowed to exist independently of the process
        if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
            return 0; }}// Check if you have selinx permission.
    if (!svc_can_find(s, len, spid, sid, uid)) {
        return 0;
    }
	// Return the handle of the service
    return si->handle;
}

Copy the code
bio_put_ref

When a handle is acquired, the **bio_put_ref()** method is called to write the handle corresponding to the service into the returned data.

//frameworks\native\cmds\servicemanager\service_manager.c
void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
    struct flat_binder_object *obj;
	// Apply for the corresponding address space
    if (handle)
        obj = bio_alloc_obj(bio);
    else
        obj = bio_alloc(bio, sizeof(*obj));

    if(! obj)return;

    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
	// Type is BINDER_TYPE_HANDLE
    obj->hdr.type = BINDER_TYPE_HANDLE;
	//记录handle
    obj->handle = handle;
    obj->cookie = 0;
}
Copy the code

For service acquisition, the data of reply must be written back to the process requesting the service. This is where we come back to our binder_loop() function. In this function, there is a binder_parse() method that processes the request information and sends the reply information to the client through the Binder driver.

binder_parse
//frameworks\native\cmds\servicemanager\binder.c
int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{...case BR_TRANSACTION: {
           ...
				// Call func
                res = func(bs, txn, &msg, &reply);
                if (txn->flags & TF_ONE_WAY) {
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                	// Send protocol instructions to Binder drivers and send reply to clients
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res); }...return r;
}
Copy the code

When the func function is called and the corresponding message is returned, the reply data is sent to the client through the binder_send_reply() method.

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;

    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
	// Return instruction
    data.cmd_reply = BC_REPLY;
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {// Svcmgr_handler executes successfully to assemble the REPLY data into TXN
        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;
    }
	// Send data
    binder_write(bs, &data, sizeof(data));
}
Copy the code

conclusion

The ServiceManager is a daemon process that manages all service information in the system. All registered information is stored in a linked list. It is also a service itself, and after registering as a daemon with Binder drivers, it registers itself as a service to be invoked by other services.

reference

www.jianshu.com/p/a90c697d6…

www.jianshu.com/p/c5110f71a…

Blog.csdn.net/jltxgcy/art…

www.jianshu.com/p/dafbc4751…

Source code analysis project address: Android-29-framwork

Synchronous public number [open Ken]