In my last blog post, I shared with Binder how the communication process is implemented, which is shown below:

Binder Client, Binder Server, Binder Driver and Service Manager are the main objects of Binder mechanism. The complete process is as follows :(here is a picture from Gityuan’s blog)

Comparing the two, or taking a closer look at my previous blog post, shows that the role of Service Manager is neglected in the introduction communication process, so this is the purpose of this article.

  • ServerManagerandBinderThe relationship?
  • ServerManagerThe role of
  • ServerManagerThe general working process of

Service Manager and real-name Binder

What is the role of the ServiceManager?

ServiceManager is an important class in Android. As its name suggests, it manages all the system services and maintains the binder communication between the system services and the clients.

What is the relationship between ServiceManager and Binder?

Simply put, a ServiceManager translates a Binder name as a character into a Client reference to the Binder so that the Client can use the Binder name to obtain a reference to the Binder entity. (Binder is a real name Binder)

Service Manager is a process, Server is a process, and Server registers with Service Manager involves interprocess communication. But now interprocess communication needs interprocess communication, which is like finding a chicken to lay eggs before eggs can hatch. So how do you solve this chicken-and-egg problem?

Clever implementation:

To overcome this problem, a pre-created chicken is the ServiceManager’s Binder entity to lay an egg. ServiceManager provides a special Binder that has no name and does not require registration. Binder drivers automatically create Binder entities for a process that registers itself as a ServiceManager using BINDER_SET_CONTEXT_MGR (this is the pre-built chicken).

Registration mode of the Server

The reference to this Binder entity is fixed to 0 in all clients without any other means of obtaining it. That is, a Server that wants to register its Binder with a ServiceManager must communicate with the ServiceManager’s Binder through the 0 reference.

Like network communication, this 0 reference is like a DNS address that needs to be configured first (as opposed to the DNS server, everything else is a client). Similarly, a process or application, even if it is a Server providing a service, is also a client, as opposed to an SM.

The whole process goes like this:

When a Binder Service is created, they communicate their [name, Binder handle] relationship to SM for registration.

The Client obtains the Service Binder reference

A Server registers a Binder with a ServiceManager, and a Client gets a reference to Binder by name. Since Client also needs a Binder to communicate with SM, Client also obtains a Service Binder from SM through this reference 0.

The Handle of the ServiceManager is fixed to the Client and is 0. Therefore, the ServiceManager service does not need to be queried and can be used directly.

The whole process goes like this:

  1. ClientSend a packet toSMRequesting a nameBinderreference
  2. SMAfter receiving the request, look for the name in the request packet and find the corresponding name in the lookup tableBinderA reference to the
  3. Will find theBinderThe reference is sent to the request as a replyClient

Now Client and Server and SM registration and access to a basic understanding, the specific implementation details will not be elaborated here, you can go to see the source code to understand. Let’s focus on SM

Start and create SM

Rc is started when the init program parses init.rc, and when it restarts, zygote, Media, and SurfaceFlinger for other system services will also be reloaded

//frameworks/native/cmds/servicemanager/servicemanager.rc
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
    writepid /dev/cpuset/system-background/tasks
    shutdown critical
Copy the code

Because SM started early, it was guaranteed to be the first application in the system to register with Binder drivers as a “big butler.”

//frameworks/native/cmds/servicemanager/service_manager.c int main(int argc, char** argv) { struct binder_state *bs; Bs = binder_open(driver, 128*1024); binder_open(driver, 128*1024) //2. Set yourself as a Binder butler, Only one ServiceManager can exist in the system. If (binder_become_context_manager(bs)) {ALOGE(" Cannot become Context Manager (%s)\n", strerror(errno)); return -1; } //3. Enter the loop and wait for and process the client request binder_loop(bs, svcmgr_handler); return 0; }Copy the code

When the preparation is complete, SM waits for requests from clients. This part is an important part of SM.

The working process of SM

Binder_loop (bs, svcmgr_handler); svcmgr_handler (bs, svcmgr_handler);

//frameworks/native/cmds/servicemanager/binder.c void binder_loop(struct binder_state *bs, binder_handler func){ struct binder_write_read bwr; uint32_t readbuf[32]; Readbuf [0] = BC_ENTER_LOOPER; readBuf [0] = BC_ENTER_LOOPER; binder_write(bs, readbuf, sizeof(uint32_t)); for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (uintptr_t) readbuf; // wait for client data res = ioctl(bs->fd, BINDER_WRITE_READ, & BWR); Res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); }}Copy the code

It loops through the request queue, similar to the message management mechanism of Handler, but note that there is no message queue in SM, its messages are fetched by Binder drivers.

It can be divided into three steps:

  1. fromBinder driveRead message in
  2. Process the message
  3. Never quit (unless there is an error)

Processing messages is a more complicated step. How does it work?

When a message is read from the Binder driver, it calls binder_parse() to parse the message. The binder_parse() method works differently for different BR protocols:

int binder_parse(struct binder_state *bs, struct binder_io *bio, uintptr_t ptr, size_t size, binder_handler func) { int r = 1; uintptr_t end = ptr + (uintptr_t) size; while (ptr < end) { switch(cmd) { case BR_NOOP: break; case BR_TRANSACTION_COMPLETE: break; case BR_INCREFS: case BR_ACQUIRE: case BR_RELEASE: case BR_DECREFS: case BR_TRANSACTION: { struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr; binder_dump_txn(txn); if (func) { unsigned rdata[256/4]; struct binder_io msg; struct binder_io reply; int res; bio_init(&reply, rdata, sizeof(rdata), 4); bio_init_from_txn(&msg, txn); Res = func(bs, TXN, &msg, &reply); if (txn->flags & TF_ONE_WAY) { binder_free_buffer(bs, txn->data.ptr.buffer); } else {// Response message processing result binder_send_reply(bs, &reply, TXN ->data.ptr.buffer, res); } } ptr += sizeof(*txn); break; } case BR_REPLY: { struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr; binder_dump_txn(txn); if (bio) { bio_init_from_txn(bio, txn); bio = 0; } ptr += sizeof(*txn); r = 0; break; } case BR_DEAD_BINDER: case BR_FAILED_REPLY: case BR_DEAD_REPLY: default: } } return r; }Copy the code

As you can see from the code, there are a number of BR protocols, two of which should be highlighted: one is BR_TRANSCATION and BR_REPLY, especially BR_TRANSCATION

BR_TRANSCATION

Processing of the BR_TRANSCATION command is done primarily by the func function, which then returns the result to the Binder driver, which receives the message and passes it to the client.

Svcmgr_handler: binder_loop(bs, svcmgr_handler);

int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data *txn, struct binder_io *msg, struct binder_io *reply) { switch(txn->code) { case SVC_MGR_GET_SERVICE: case SVC_MGR_CHECK_SERVICE: S = bio_get_string16(MSG, &len); Handle = do_find_service(s, len, TXN ->sender_euid, TXN ->sender_pid); if (! handle) break; bio_put_ref(reply, handle); return 0; case SVC_MGR_ADD_SERVICE: s = bio_get_string16(msg, &len); handle = bio_get_ref(msg); allow_isolated = bio_get_uint32(msg) ? 1:0; dumpsys_priority = bio_get_uint32(msg); If (do_add_service(bs, s, len, handle, TXN ->sender_euid, allow_ISOLATED, dumpsys_priority, txn->sender_pid)) return -1; break; case SVC_MGR_LIST_SERVICES: { uint32_t n = bio_get_uint32(msg); uint32_t req_dumpsys_priority = bio_get_uint32(msg); while (si) { if (si->dumpsys_priority & req_dumpsys_priority) { if (n == 0) break; n--; } si = si->next; } if (si) { bio_put_string16(reply, si->name); return 0; } return -1; } bio_put_uint32(reply, 0); return 0; }Copy the code

As you can clearly see, the function of this method is to do different things according to different codes, such as query services, register services, and enumerate all services.

BR_REPLY and BR_TRANSCATION implementations are similar.

Take a chestnut

WindowManager wm = (WindowManager)getSystemService(getApplication().window_service); Public view= layoutinflater.from (getApplication()).inflate(r.layout.float_layout, null); // addView to wm wm. AddView (view, layoutParams);Copy the code

GetSystemService (getApplication().window_service) gets a reference to a WindowManager object, and then calls the addView function of WindowManager. Add a View to the window.

The code looks simple, but we know that the system services run in the SystemServer process, so the whole process involves cross-process communication.

How is it implemented from a cross-process perspectiveaddViewWhat about the process?

Based on the knowledge above, The internal principle of the getSystemService(getApplication().window_service) function is to query the ServiceManager for a reference to a remote object with the identifier getApplication().window_service. A reference to a WindowManager object.

However, the actual implementation of this reference is some WindowManager agent. With this reference, the real implementation is in the proxy when addView is called, which packages the parameters into a Parcel object, calls the Transact function, and triggers a series of calls driven by the Binder.