Android底层:通熟易懂的分析binder--4.ServiceManager
本篇内容
ServiceManager的作用
启动servicemanager进程
ServiceManager的准备工作
注册服务
获取服务
总结
以下代码的分析是基于7.0.0代码分析的
01
—
ServiceManager的作用
世间的万事万物都是有因果关系的,事物不会凭白无故的产生,它总是有缘由的。只有我们弄清楚缘由才能更好的理解事物。
ServiceManager也是如此,为啥android中要有它呢?它肯定是来解决某类问题的,那我们来看下存在什么样的问题?再来看下ServiceManager是怎么样解决的?
存在的问题
binder进程通信是一个c/s模式,就如socket通信一样,clientSocket需要先知道serverSocket的ip地址和端口号,这样clientSocket才能与serverSocket建立连接进行通信,那binder进程通信的c端同样也需要拿到s端的“引用”(指BinderProxy或BpBinder)才能进行通信,那怎么样拿到这个引用呢?这就是问题所在。
解决问题
解决思路与域名解析的过程很像。在浏览器中输入网址后会到设备上设置的DNS服务器处获取域名对应的ip地址;同理binder的c端会携带一个name值(对应s端)到某个地方获取s端的"引用",这里的某个地方就是ServiceManager,那c端怎么获取ServiceManager服务的引用呢?好像进入一个无限循环里面,在域名解析过程中需要提前在设备上设置好DNS服务器的地址,同理要想获取ServiceManager引用,在android中约定凡是BpBinder(native的一个类,代表binder服务的引用)中的handle值为0,则代表是ServiceManager的"引用"。
即c端要想获取到s端的"引用"需要先去ServiceManager获取,要想从ServiceManager获取到信息,总不能ServiceManager这里空空如也,那就有一个前提需要s端把自己的"引用"提前注册到ServiceManager中。
ServiceManager在binder进程通信中起一个获取/保存各种服务(比如AMS,WMS,PMS等)控制中心的作用,并且它是binder进程通信的服务端。
02
—
启动servicemanager进程
ServiceManager为啥要单独的放在一个进程里面呢?为啥不能放在system_server进程中呢?
我觉得主要原因应该是:ServiceManager中保存的系统服务不单单是只有system_server进程中的系统服务,还有别的进程中的系统服务。它作为一个单独的进程存在与其他的系统服务实现解耦,并且只要保证自己在系统启动时能最先启动就可以了(完全不需要去关系其他的进程是否启动完成等操作)。
在android系统启动时会先启动init进程,进而去读取init.rc(关于rc文件的内容大家可以去搜索下,这里不赘述了)文件,同时还会读取其他的rc文件,这里的其他rc文件包含servicemanager.rc文件,看下这文件的内容
/frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
class core
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
writepid /dev/cpuset/system-background/tasks
service后面的servicemanager是服务的名称,后面的/system/bin/servicemanager最终会执行 /frameworks/native/cmds/servicemanager/service_manager.c,onrestart代表的是servicemanager进程重启的化会重启的进程,system_server进程是在zygote进程中启动的。
init进程读取了servicemanager.rc文件后就会启动servicemanager进程。
03
—
ServiceManager的准备工作
ServiceManager是为binder进程通信提供服务的,因此在它所在的进程启动后,它需要做一些准备工作就如 Android底层:通熟易懂分析binder:1.binder准备工作 中提到的一样,准备工作主要有:
open driver
mmap
become context manager
binder_loop
ServiceManager的流程要相对简单很多,没有使用ProcessState,IPCThreadState那套代码,而是重新写了一套代码(代码位于/frameworks/native/cmds/servicemanager/ ),并且没有启用多线程,只用了一个线程处理所有的请求。
servicemanager进程启动后,会执行/frameworks/native/cmds/servicemanager/service_manager.c的main方法,准备工作主要是在main方法中进行的,那来看下相关代码
int main()
{
struct binder_state *bs;
//打开driver
bs = binder_open(*);
省略代码...
// 设置变为contextmanager
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -;
}
//selinux相关代码
selinux_enabled = is_selinux_enabled();
sehandle = selinux_android_service_context_handle();
selinux_status_open(true);
if (selinux_enabled > ) {
if (sehandle == NULL) {
ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
abort();
}
if (getcon(&service_manager_context) != ) {
ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
abort();
}
}
union selinux_callback cb;
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
cb.func_log = selinux_log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
//进入loop循环不断的取命令
binder_loop(bs, svcmgr_handler);
return ;
}
main方法中主要做了以下几件事:
open driver 和 mmap
调用binder_open方法 执行了open driver和mmap工作,这已经在 Android底层:通熟易懂分析binder:1.binder准备工作 分析过了,就不赘述了。
become context manager
become context manager的主要作用是通知driver层它是context manager,这样别的进程就能调用到ServiceManager了。
具体设置代码如下:
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
是不是很简单,其实就是通过ioctl发送BINDER_SET_CONTEXT_MGR的cmd
给driver层,driver层的关键代码在下面:
static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
省略代码...
binder_context_mgr_node = binder_new_node(proc, 0, 0);
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto out;
}
binder_context_mgr_node->local_weak_refs++;
binder_context_mgr_node->local_strong_refs++;
binder_context_mgr_node->has_strong_ref = 1;
binder_context_mgr_node->has_weak_ref = 1;
out:
return ret;
}
会先构造一个binder_node赋给binder_context_mgr_node(注意它是一个静态属性),到此ServiceManager就变为context manager了。
binder_loop
从loop这个词就能断定出肯定是起了一个循环,不断的读取别的进程的命令,那来看下相关代码:
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
uint_t readbuf[];
bwr.write_size = ;
bwr.write_consumed = ;
bwr.write_buffer = ;
readbuf[] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = ;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < ) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, , (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == ) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < ) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
上面代码主要做了以下几件事:
发送BC_ENTER_LOOPER给driver层
启动一个for循环,在循环中不断重复:发送消息给driver层,和读取driver层返回的消息,调用binder_parse解析返回的消息。
具体关于driver层是怎么处理BINDER_WRITE_READ类型的消息可以查看前面几篇文章。
binder_parse
binder_loop已经启动起来,不断的监听driver层的数据了,先来简单看下都有哪些数据,后面还会进行详细分析,那来看下binder_parse方法吧:
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
int r = ;
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
uint_t cmd = *(uint_t *) ptr;
省略代码...
switch(cmd) {
case BR_NOOP:
break;
case BR_TRANSACTION_COMPLETE:
break;
case BR_INCREFS:
case BR_ACQUIRE:
case BR_RELEASE:
case BR_DECREFS:
省略代码...
break;
case BR_TRANSACTION: {
省略代码
break;
}
case BR_REPLY: {
省略代码...
break;
}
case BR_DEAD_BINDER: {
省略代码...
break;
}
case BR_FAILED_REPLY:
r = -;
break;
case BR_DEAD_REPLY:
r = -;
break;
default:
ALOGE("parse: OOPS %d\n", cmd);
return -;
}
}
return r;
}
上面的代码只把cmd贴出来了,其中的cmd有BINDER_NOOP,BINDER_TRANSACTION,BINDER_TRANSACTION_COMPLETE,BINDER_REPLY等是不是是曾相识。关于binder_parse的内容先介绍到这,后面还会详细介绍。
到此ServiceManager的准备工作结束,它可以等待driver层的数据了。
04
—
注册服务
ServiceManager既然有保存服务的功能,那服务是需要提前注册的,那来分析下服务的注册流程。
要想向ServiceManager添加服务,需要先获取到ServiceManager的"引用"。
4.1 获取ServiceManager的“引用”
下面来分析下这个流程,从ServiceManager.java代码分析起,先看下相关代码
ServiceManager.java
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
ServiceManager类中的静态属性sServiceManager保存了ServiceManager服务的“引用”值,若为null,则去 BinderInternal.getContextObject() 中获取,看下相关代码
BinderInternal.java
public static final native IBinder getContextObject();
getContextObject是一个native方法,那进入jni层看下它的实现,
android_util_binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
最终调用了ProcessState的getContextObject方法,ProcessState是咱们的老朋友了,看下相关代码
ProcessState.cpp
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
return getStrongProxyForHandle(0);
}
getContextObject会调用getStrongProxyForHandle(0),注意这时候的参数是0,进入这个方法看下相关代码
sp<IBinder> ProcessState::getStrongProxyForHandle(int_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
//查找handle_entry
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
// We need to create a new BpBinder if there isn't currently one, OR we
// are unable to acquire a weak reference on this current one. See comment
// in getWeakProxyForHandle() for more info about this.
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
//发现handle==0,
if (handle == 0) {
// Special case for context manager...
// The context manager is the only object for which we create
// a BpBinder proxy without already holding a reference.
// Perform a dummy transaction to ensure the context manager
// is registered before we create the first local reference
// to it (which will occur when creating the BpBinder).
// If a local reference is created for the BpBinder when the
// context manager is not present, the driver will fail to
// provide a reference to the context manager, but the
// driver API does not return status.
//
// Note that this is not race-free if the context manager
// dies while this code runs.
//
// TODO: add a driver API to wait for context manager, or
// stop special casing handle for context manager and add
// a driver API to get a handle to the context manager with
// proper reference counting.
Parcel data;
//去ping下ServiceManager是否活着,没活着则返回null
status_t status = IPCThreadState::self()->transact(
, IBinder::PING_TRANSACTION, data, NULL, );
if (status == DEAD_OBJECT)
return NULL;
}
// 根据handle来构造BpBinder,这时候的handle是0
b = new BpBinder(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
// This little bit of nastyness is to allow us to add a primary
// reference to the remote proxy when this team doesn't have one
// but another team is sending the handle to us.
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
若ServiceManager没有死掉,会返回一个handle为0的BpBinder,BinderInternal.getContextObject() 方法会获得一个BinderProxy(它的mObject指向handle为0的BpBinder)
到此ServiceManager的“引用”获取成功,可以与ServiceManager通信了。
4.2 注册服务
注册服务的起始方法是在ServiceManager.java的addService方法中,看下它的代码
public static void addService(String name, IBinder service) {
try {
getIServiceManager().addService(name, service, false);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
上面的方法其实就是binder进程通信的过程,关于binder进程请求的流程可以看下 Android底层:通熟易懂的分析binder--3. 探究binder全流程通信之请求篇 内容,
最终的请求最终到达 2 节中的binder_parse方法中,来看下相关代码
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
int r = ;
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
uint_t cmd = *(uint_t *) ptr;
省略代码...
switch(cmd) {
省略代码...
case BR_TRANSACTION: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if ((end - ptr) < sizeof(*txn)) {
ALOGE("parse: txn too small!\n");
return -;
}
binder_dump_txn(txn);
if (func) {
unsigned rdata[/];
struct binder_io msg;
struct binder_io reply;
int res;
bio_init(&reply, rdata, sizeof(rdata), );
bio_init_from_txn(&msg, txn);
//调用func进行具体消息处理
res = func(bs, txn, &msg, &reply);
//异步则告知driver层释放buffer
if (txn->flags & TF_ONE_WAY) {
binder_free_buffer(bs, txn->data.ptr.buffer);
} else {
//同步则发送回复消息,回复消息在reply中
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
}
}
ptr += sizeof(*txn);
break;
}
省略代码...
}
}
return r;
}
上面代码会调用func(bs,txn,&msg,&reply)方法,func是service_manager.c中的svcmgr_handler方法,来看下这方法(只展示了与注册服务相关代码)
service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint_t *s;
size_t len;
uint_t handle;
uint_t strict_policy;
int allow_isolated;
if (txn->target.ptr != BINDER_SERVICE_MANAGER)
return -;
if (txn->code == PING_TRANSACTION)
return ;
// 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_uint(msg);
s = bio_get_string(msg, &len);
if (s == NULL) {
return -;
}
if ((len != (sizeof(svcmgr_id) / )) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
fprintf(stderr,"invalid id %s\n", str(s, len));
return -;
}
if (sehandle && selinux_status_updated() > ) {
struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
if (tmp_sehandle) {
selabel_close(sehandle);
sehandle = tmp_sehandle;
}
}
switch(txn->code) {
省略代码...
case SVC_MGR_ADD_SERVICE:
s = bio_get_string(msg, &len);
if (s == NULL) {
return -;
}
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint(msg) ? : ;
if (do_add_service(bs, s, len, handle, txn->sender_euid,
allow_isolated, txn->sender_pid))
return -;
break;
省略代码...
}
bio_put_uint(reply, );
return ;
}
注册服务的code值是SVC_MGR_ADD_SERVICE,因此进入这个case中,从msg中解析出s和handle,进而在调用do_add_service方法,看下相关代码
service_manager.c
int do_add_service(struct binder_state *bs,
const uint16_t *s, size_t len,
uint32_t handle, uid_t uid, int allow_isolated,
pid_t spid)
{
struct svcinfo *si;
if (!handle || (len == 0) || (len > 127))
return -1;
//判断是否有权限注册,
if (!svc_can_register(s, len, spid, uid)) {
ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
str8(s, len), handle, uid);
return -1;
}
//查找svcinfo,它是一个封装了服务信息的结构体
si = find_svc(s, len);
//存在,则给si的handle赋新的handle值
if (si) {
if (si->handle) {
ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
str8(s, len), handle, uid);
svcinfo_death(bs, si);
}
si->handle = handle;
} else {
//不存在则构建si
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
if (!si) {
ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
str8(s, len), handle, uid);
return -1;
}
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->next = svclist;
//把si放入svclist链表中
svclist = si;
}
binder_acquire(bs, handle);
binder_link_to_death(bs, handle, &si->death);
return 0;
}
上面代码主要是做了添加当前的service信息到svclist链表中的过程:若查找到si(封装了服务信息的结构体),则修改它的handle值;否则构造一个si,放入svclist链表中。
svcinfo
来看下svcinfo包含的数据有哪些,下面是它的代码
struct svcinfo
{
//指向下个节点
struct svcinfo *next;
//handle值
uint32_t handle;
//死亡相关d的结构体
struct binder_death death;
int allow_isolated;
//下面是服务d的名称
size_t len;
uint16_t name[0];
};
svclist
svclist是svcinfo的链表,注册的服务存储在svcinfo中后,最终会以链表的形式存在。
注册服务成功后,会给添加服务的进程发送reply信息,到此整个流程结束,不知大家是否发现ServiceManager中保存的服务,服务的信息主要是name和handle(BpBinder)值,并没有保存服务的真正对象引用。
05
—
获取服务
有了上面注册服务的基础,在分析获取服务的过程会非常容易,获取服务也是从ServiceManager.java开始,获取ServiceManager的“引用” 4节中已经讲过,就不分析了,看下相关代码
ServiceManager.java
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
ServiceManager中会有一个静态sCache来保存所有的服务,不存在则通过binder进程通信去ServiceManager中获取,获取服务的binder请求最终会到service_manager.c中的svcmgr_handler方法,看下相关代码
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
size_t len;
uint32_t handle;
uint32_t strict_policy;
int allow_isolated;
省略代码...
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);
return 0;
省略代码...
}
bio_put_uint32(reply, 0);
return 0;
}
获取服务的code值是SVC_MGR_GET_SERVICE,因此进入这个case,会调用do_find_service方法,来看下这个方法
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
{
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;
if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
return 0;
}
}
if (!svc_can_find(s, len, spid, uid)) {
return 0;
}
return si->handle;
}
这方法很简单就是从svclist中查找到svcinfo后把svcinfo的handle返回,进而在调用 bio_put_ref(reply, handle) 把handle放入reply中,在给获取服务的进程发送reply信息。获取服务的进程最终获取到的是一个(BinderProxy或BpBinder),BpBinder中的handle在driver层是会被再次转换为当前进程的handle值的。
到此获取服务的流程分析完毕。
06
—
总结
ServiceManger是binder进程通信的基石,没有它binder进程通信就不可以进行,甚至整个android系统不可以运行。在系统启动过程中,init进程启动后会启动servicemanager进程,ServiceManager执行open driver,mmap准备工作,最终进入loop状态等待driver层发送的数据。
ServiceManager提供了获取/保存服务的功能,服务在ServiceManager中是以svcinfo(主要是handle和name)的结构体存在的,并将svcinfo连接到svclist的链表上。
别的进程与ServiceManager通信需要先获取ServiceManager的“引用”(handle为0的BpBinder),java层需要通过ServiceManager.java类来获取。