Android Process Communication --- Service Manager (1), android --- service
The Bind mechanism consists of four parts. Bind driver, Client, ServiceManager & Service
1. Bind is actually a linux-based driver for Memory Sharing.
The bind driver is biased towards the kernel, and the content of the bind mechanism is very large, so we will skip this part temporarily.
2. ServiceManager
As the name implies, Service Manager is a "Manager ". More specifically, it is the manager of all system services.
Start with service_manager.c \ frameworks \ native \ cmds \ servicemanager \ service_manager.c
static struct { unsigned uid; const char *name;} allowed[] = { { AID_MEDIA, "media.audio_flinger" }, { AID_MEDIA, "media.log" }, { AID_MEDIA, "media.player" }, { AID_MEDIA, "media.camera" }, { AID_MEDIA, "media.audio_policy" }, { AID_DRM, "drm.drmManager" }, { AID_NFC, "nfc" }, { AID_BLUETOOTH, "bluetooth" }, { AID_RADIO, "radio.phone" }, { AID_RADIO, "radio.sms" }, { AID_RADIO, "radio.phonesubinfo" }, { AID_RADIO, "radio.simphonebook" },/* TODO: remove after phone services are updated: */ { AID_RADIO, "phone" }, { AID_RADIO, "sip" }, { AID_RADIO, "isms" }, { AID_RADIO, "iphonesubinfo" }, { AID_RADIO, "simphonebook" }, { AID_MEDIA, "common_time.clock" }, { AID_MEDIA, "common_time.config" }, { AID_KEYSTORE, "android.security.keystore" },};
The above is a part of the system service. These are all registered in servicemanager for management.
The service manager does the following:
I. Provide the IBind object, that is, the reference of each service for each process, and the Ibind object is unique for each process.
II. Register various system services in servicemanager.
Here, the binder driver is not the driver concept in our general operating system structure. It can be understood as a medium for communication between the client and ServiceManager.
The essence of the binder driver is Memory Sharing.
In fact, this is the front part of the entire bind mechanism, that is, from the client to servicemanager, so that the client can get the Ibind object and then directly "operate servie ".
For example:
AlarmManager alarmManager = context.getSystemService(Context.ALARM_SERVICE); alarmManager.setExact(AlarmManager.ELAPSED_REALTIME, elapsedRealtime, pendingIntent);
Obtain the alaram service bind object and then operate the "service" provided by the service ".
And this operation is synchronized!
It is like operating the same process.
Next, let's take a look at how service Manager implements the above.
2.1 Start Service Manager:
Since SM is an administrator, it should be the most diligent, that is, it must be started as early as possible.
Yes, its startup is defined in init. rc: \ system \ core \ rootdir \ init. rc
# adbd on at boot in emulatoron property:ro.kernel.qemu=1 start adbdservice servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart healthd onrestart restart zygote onrestart restart media onrestart restart surfaceflinger onrestart restart drm
What is Service Manager doing after it is started?
Or in service_manager.c:
int main(int argc, char **argv){ struct binder_state *bs; void *svcmgr = BINDER_SERVICE_MANAGER; bs = binder_open(128*1024); if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } svcmgr_handle = svcmgr; binder_loop(bs, svcmgr_handler); return 0;}
Binder_open open the bind driver and allocate the size of KB.
Binder_become_context_manager (bs ):
int binder_become_context_manager(struct binder_state *bs){ return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}
Register yourself as a Service manager.
void binder_loop(struct binder_state *bs, binder_handler func){ int res; struct binder_write_read bwr; unsigned readbuf[32]; bwr.write_size = 0; bwr.write_consumed = 0; bwr.write_buffer = 0; readbuf[0] = BC_ENTER_LOOPER; binder_write(bs, readbuf, sizeof(unsigned)); for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (unsigned) readbuf; res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); break; } res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func); if (res == 0) { ALOGE("binder_loop: unexpected reply?!\n"); break; } if (res < 0) { ALOGE("binder_loop: io error %d %s\n", res, strerror(errno)); break; } }}
Start to enter the loop, which is similar to the previously analyzed andorid thread message driving mechanism.
Read the message queues, parse them, and know that an exception occurs.
Next, let's look at bind_parse:
int binder_parse(struct binder_state *bs, struct binder_io *bio, uint32_t *ptr, uint32_t size, binder_handler func){ int r = 1; uint32_t *end = ptr + (size / 4); while (ptr < end) { uint32_t cmd = *ptr++;#if TRACE fprintf(stderr,"%s:\n", cmd_name(cmd));#endif switch(cmd) { case BR_NOOP: break; case BR_TRANSACTION_COMPLETE: break; case BR_INCREFS: case BR_ACQUIRE: case BR_RELEASE: case BR_DECREFS:#if TRACE fprintf(stderr," %08x %08x\n", ptr[0], ptr[1]);#endif ptr += 2; break; case BR_TRANSACTION: { struct binder_txn *txn = (void *) ptr; if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) { ALOGE("parse: txn too small!\n"); return -1; } 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); binder_send_reply(bs, &reply, txn->data, res); } ptr += sizeof(*txn) / sizeof(uint32_t); break; } case BR_REPLY: { struct binder_txn *txn = (void*) ptr; if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) { ALOGE("parse: reply too small!\n"); return -1; } binder_dump_txn(txn); if (bio) { bio_init_from_txn(bio, txn); bio = 0; } else { /* todo FREE BUFFER */ } ptr += (sizeof(*txn) / sizeof(uint32_t)); r = 0; break; } case BR_DEAD_BINDER: { struct binder_death *death = (void*) *ptr++; death->func(bs, death->ptr); break; } case BR_FAILED_REPLY: r = -1; break; case BR_DEAD_REPLY: r = -1; break; default: ALOGE("parse: OOPS %d\n", cmd); return -1; } } return r;}
The key is analysis: BR_TRANSACTION, BR_REPLY.
BR_TRANSACTION is initialized, and then
res = func(bs, txn, &msg, &reply);binder_send_reply(bs, &reply, txn->data, res);
The func function is passed in service_manager.c.
int svcmgr_handler(struct binder_state *bs, struct binder_txn *txn, struct binder_io *msg, struct binder_io *reply)
Therefore, the bind_loop function for final analysis is the input function!
Now the entire service_manager process is clear.
Event-driven mechanism:
1. Read messages from the bind driver
2. process messages
3. log on to logoff and never exit until a fatal error occurs.
Int struct (struct binder_state * bs, struct binder_txn * txn, struct binder_io * msg, struct binder_io * reply) {struct svcinfo * si; uint16_t * s; unsigned len; void * ptr; uint32_t strict_policy; int allow_isolated; // ALOGI ("target = % p code = % d pid = % d uid = % d \ n", // txn-> target, txn-> code, txn-> sender_pid, txn-> sender_euid); if (txn-> target! = Svcmgr_handle) return-1; // Equivalent to Parcel: enforceInterface (), reading the RPC // header with the strict mode policy mask and the interface name. // Note that we ignore the strict_policy and don't propagate it // further (since we do no outbound RPCs anyway ). strict_policy = bio_get_uint32 (msg); s = bio_get_string16 (msg, & len); if (len! = (Sizeof (svcmgr_id)/2) | memcmp (svcmgr_id, s, sizeof (svcmgr_id) {fprintf (stderr, "invalid % s \ n ", str8 (s); return-1;} switch (txn-> code) {case SVC_MGR_GET_SERVICE: case SVC_MGR_CHECK_SERVICE: s = bio_get_string16 (msg, & len ); ptr = do_find_service (bs, s, len, txn-> sender_euid); if (! Ptr) break; bio_put_ref (reply, ptr); return 0; case SVC_MGR_ADD_SERVICE: s = bio_get_string16 (msg, & len); ptr = bio_get_ref (msg ); allow_isolated = bio_get_uint32 (msg )? 1: 0; if (do_add_service (bs, s, len, ptr, txn-> sender_euid, allow_isolated) return-1; break; case SVC_MGR_LIST_SERVICES: {unsigned n = bio_get_uint32 (msg); si = svclist; while (n --> 0) & si) si = si-> next; if (si) {bio_put_string16 (reply, si-> name); return 0;} return-1;} default: ALOGE ("unknown code % d \ n", txn-> code ); return-1;} bio_put_uint32 (reply, 0); return 0 ;}Svcmgr_handler
Switch statement to query and obtain the service or registration.
Check whether the service with the same name exists in the svclist.
Svclist is a linked list, which is the same as the thread message queue!
struct svcinfo *find_svc(uint16_t *s16, unsigned len){ struct svcinfo *si; for (si = svclist; si; si = si->next) { if ((len == si->len) && !memcmp(s16, si->name, len * sizeof(uint16_t))) { return si; } } return 0;}
Next let's take a look at void * do_find_service (struct binder_state * bs, uint16_t * s, unsigned len, unsigned uid)
What is return?
Registration Service: SVC_MGR_ADD_SERVICE:
Int do_add_service (struct binder_state * bs, uint16_t * s, unsigned len, void * ptr, unsigned uid, int allow_isolated) {struct svcinfo * si; // ALOGI ("add_service ('% s', % p, % s) uid = % d \ n", str8 (s), ptr, // allow_isolated? "Allow_isolated ":"! Allow_isolated ", uid); if (! Ptr | (len = 0) | (len> 127) return-1; if (! Svc_can_register (uid, s) {ALOGE ("add_service ('% s', % p) uid = % d-permission denied \ n", str8 (s), ptr, uid); return-1;} si = find_svc (s, len); if (si) {if (si-> ptr) {ALOGE ("add_service ('% s ', % p) uid = % d-already registered, OVERRIDE \ n ", str8 (s), ptr, uid); svcinfo_death (bs, si );} si-> ptr = ptr;} else {si = malloc (sizeof (* si) + (len + 1) * sizeof (uint16_t); if (! Si) {ALOGE ("add_service ('% s', % p) uid = % d-out of memory \ n", str8 (s), ptr, uid ); return-1;} si-> ptr = ptr; si-> len = len; memcpy (si-> name, s, (len + 1) * sizeof (uint16_t )); si-> name [len] = '\ 0'; si-> death. func = svcinfo_death; si-> death. ptr = si; si-> allow_isolated = allow_isolated; si-> next = svclist; svclist = si;} binder_acquire (bs, ptr); binder_link_to_death (bs, ptr, & si-> death); return 0 ;}Do_add_service
Int svc_can_register (unsigned uid, uint16_t * name)
Determine whether it is in the allowed table.
First, check whether it is in the list?
Si = find_svc (s, len );
If no longer, register a new si to svclist.
Now service_manager is started.