Android system-ServiceManager1

Table of contents

introduction

concept

start up

flow chart

main

binder_open

binder_become_context_manager

binder_ioctl

binder_ioctl_set_ctx_mgr

binder_new_node

binder_loop

binder_write

binder_ioctl

binder_ioctl_write_read

binder_thread_write

binder_parse

bio_init

bio_init_from_txn

svcmgr_handler

svcinfo

query service

do_find_service

find_svc

bio_put_ref

bio_alloc_obj

bio_alloc

do_add_service

registration service

svc_can_register

svcinfo_death

bio_get_ref

binder_ioctl_write_read

binder_thread_write

binder_thread_read


introduction

ServiceManager is a daemon process in Binder IPC communication, which serves for the realization of Binder IPC communication. So ServiceManager itself is a service, and it is a binder service.

ServiceManager has two characteristics:

1. Instead of using the multi-thread model in libbinder to communicate with the Binder driver, it communicates directly with the Binder driver through the binder.c written confidently, and there is only one loop binder_loop to read and process transactions, so as to keep it simple and efficient

2. There are two main functions of ServiceManager: registration service and query service.

concept

ServiceManager startup process (simplified version):

1) Open the binder driver

2) Register as a context manager

3) Enter the loop cycle and process transactions

The two core functions of ServiceManager:

Register service: record service name and handler, save to svclist list

Query service: query the corresponding handler information according to the service name

start up

flow chart

framework/native/cmds/servicemanager/

- service_manager.c

- binder.c

kernel/drivers/ (different Linux branch paths are slightly different)

- android/binder.c

ServiceManager is created by the init process

service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    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
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

main

int main(int argc, char** argv)
{
    struct binder_state *bs;
    union selinux_callback cb;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }

    bs = binder_open(driver, 128*1024);
    if (!bs) {
#ifdef VENDORSERVICEMANAGER
        ALOGW("failed to open binder driver %s\n", driver);
        while (true) {
            sleep(UINT_MAX);
        }
#else
        ALOGE("failed to open binder driver %s\n", driver);
#endif
        return -1;
    }

    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
#ifdef VENDORSERVICEMANAGER
    cb.func_log = selinux_vendor_log_callback;
#else
    cb.func_log = selinux_log_callback;
#endif
    selinux_set_callback(SELINUX_CB_LOG, cb);

#ifdef VENDORSERVICEMANAGER
    sehandle = selinux_android_vendor_service_context_handle();
#else
    sehandle = selinux_android_service_context_handle();
#endif
    selinux_status_open(true);

    if (sehandle == NULL) {
        ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
        abort();
    }

    if (getcon(&service_manager_context) != 0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort();
    }


    binder_loop(bs, svcmgr_handler);

    return 0;
}

ServiceManager startup process (common version):

1) Open the binder driver and call the mmap method to allocate 128K of memory mapping space: binder_open

2) Register as a context manager: binder_become_context_manager

3) Verify selinux permissions to determine whether the process has permission to register or view specified services

4) Enter the loop and wait for the client's request: bind_loop

binder_open

struct binder_state
{
    int fd;
    void *mapped;
    size_t mapsize;
};

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
   struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }

    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open %s (%s)\n",
                driver, strerror(errno));
        goto fail_open;
    }

    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;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

void binder_close(struct binder_state *bs)
{
    munmap(bs->mapped, bs->mapsize);
    close(bs->fd);
    free(bs);
}

The user space will call the driver's open method through the open method, create a binder_proc structure object, and then assign the binder_proc object to fd->private_data, and put it into the global linked list binder_procs

The binder version of the driver can be obtained through ioctl(bs->fd, BINDER_VERSION, &vers).

Call mmap() for memory mapping. Similarly, the mmap() method is called by the system and corresponds to the binder_mmap() method of the Binder driver layer. This method will create a Binder_buffer object in the Binder driver layer and put it into the proc->buffers of the current binder_proc linked list.

int fd; // file descriptor of dev/binder

void *mapped; //point to the memory address of mmap

size_t mapsize; //The allocated memory size, the default is 128KB

binder_become_context_manager

Be the manager of the context, and there is only one such manager in the whole system. Through the system call through the ioctl() method, it corresponds to the binder_ioctl() method of the Binder driver layer.

binder_ioctl

According to the parameter BINDER_SET_CONTEXT_MGR, the binder_ioctl_set_ctx_mgr() method is finally called, and this process will hold binder_main_lock.

binder_ioctl_set_ctx_mgr

The global binder_node object binder_context_mgr_node is created, and the strong and weak references of binder_context_mgr_node are added by 1.

binder_new_node

Create a binder_node structure object in the Binder driver layer, and add the current binder_proc to the node->proc of binder_node. And create two queues of binder_node async_todo and binder_work.

binder_loop

Enter the loop read and write operation, the parameter func passed by the main() method points to svcmgr_handler.

binder_write sends the BC_ENTER_LOOPER command to the binder driver through ioctl(). At this time, bwr only has data in write_buffer, and enters the binder_thread_write() method. Next, enter the for loop and execute ioctl(). At this time, only read_buffer has data in bwr, then enter the binder_thread_read() method.

binder_write

According to the parameters passed in, initialize bwr, where write_size is 4, write_buffer points to the starting address of the buffer, and its content is the BC_ENTER_LOOPER request protocol number. Send the bwr data to the binder driver through ioctl, then call its binder_ioctl method, as follows:

binder_ioctl

binder_ioctl_write_read

binder_thread_write

Take out the cmd data from bwr.write_buffer, here is BC_ENTER_LOOPER. It can be seen that the upper layer calls the binder_write() method this time, mainly to complete the setting of the looper state of the current thread as BINDER_LOOPER_STATE_ENTERED.

binder_parse

Parse the binder information, where the parameter ptr points to BC_ENTER_LOOPER, and func points to svcmgr_handler. Therefore, when a request comes, svcmgr_handler is called.

bio_init

bio_init_from_txn

svcmgr_handler

Functions of this method: query services, register services, and list all services

svcinfo

Each service is represented by the svcinfo structure, and the handle value is determined by the end of the process where the service is located during the process of registering the service.

query service

do_find_service

Query the target service and return the handle corresponding to the service

find_svc

From the svclist service list, check whether it has been registered according to the service name. When the service already exists in svclist, it returns the corresponding service name, otherwise it returns NULL.

When the handle of the service is found, call bio_put_ref(reply, handle) to encapsulate the handle into the reply.

bio_put_ref

bio_alloc_obj

bio_alloc

do_add_service

registration service

The registration service is divided into the following 3 parts:

  • svc_can_register: Check permissions, check whether selinux permissions are satisfied;
  • find_svc: service retrieval, query the matching service according to the service name;
  • svcinfo_death: Release the service. When a service with the same name is found, the service information will be cleared first, and then the current service will be added to the service list svclist;

svc_can_register

svcinfo_death

bio_get_ref

binder_write, after entering the Binder driver, directly call and enter binder_thread_write, and process the BC_REQUEST_DEATH_NOTIFICATION command

binder_ioctl_write_read

binder_thread_write

The proc and thread in this method refer to the information of the current servicemanager process. At this time, if there is data in the TODO queue, enter binder_thread_read.

So which scenarios will add BINDER_WORK_DEAD_BINDER transactions to the queue? That is, when the process where the binder is located dies, the binder_release method will be called, and then binder_node_release will be called. This process will send a callback for the death notification.

binder_thread_read

Write the command BR_DEAD_BINDER to the user space, where the cookie is the svcinfo_death passed earlier. The message will be processed when binder_loop executes binder_parse next time.

Guess you like

Origin blog.csdn.net/haigand/article/details/132248247