Table of contents
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_link_to_death
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.