servicemanager的启动简介

Android2023年8月18日 am8:08发布1年前 (2023)更新 91es.com站长
40 0 0
目录

前言

Android源码分析《mediaserver的启动》时涉及到mediaserver服务的添加和获取(之前没有细说),而管理这些服务的就是servicemanager这个类,也就是今天介绍的主角。

涉及文件

frameworks\native\cmds\servicemanager\service_manager.c
frameworks\native\cmds\servicemanager\servicemanager.rc
frameworks\native\cmds\servicemanager\binder.c
frameworks\native\cmds\servicemanager\Android.bp

正文

这里只是简单的走一下流程,知道servicemanager是一个服务管理者,提供服务的注册和查询功能。

servicemanager的启动

根据之前源码分析,知道大部分服务都是在init中解析rc文件启动的。

servicemanager的启动rc文件是servicemanager.rc。

# servicemanager.rc
# 源码中()
frameworks\native\cmds\servicemanager\servicemanager.rc
# Android设备中
/system/etc/init

跟mediaserver一样,也是在init中解析/system/etc/init时启动的。

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
注意

service_manager.c代码会走两次,因为有两个服务公用一套代码。

#@站长
frameworks\native\cmds\servicemanager\Android.bp

会发现

# Android.bp中部分
//略
cc_binary {
    name: "servicemanager",
    defaults: ["servicemanager_flags"],
    srcs: [
        "service_manager.c",
        "binder.c",
    ],
    shared_libs: ["libcutils", "libselinux"],
    init_rc: ["servicemanager.rc"],
}

cc_binary {
    name: "vndservicemanager",
    defaults: ["servicemanager_flags"],
    vendor: true,
    srcs: [
        "service_manager.c",
        "binder.c",
    ],
    cflags: [
        "-DVENDORSERVICEMANAGER=1",
    ],
    shared_libs: ["libcutils", "libselinux"],
    init_rc: ["vndservicemanager.rc"],
}

从上面会发现servicemanager和vndservicemanager公用service_manager.c一套代码,只是参数不一样。

vndservicemanager的.rc文件是vndservicemanager.rc启动的。

service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder
    class core
    user system
    group system readproc
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

PS:后续只关注servicemanager哈

service_manager.c

程序的入口都是main()。

主要做了如下几个工作:

  1. 打开binder驱动

  2. 成为上下文管理者

  3. 进入binder循环,处理客户端发送的请求

int main(int argc, char** argv){
    struct binder_state *bs;
    union selinux_callback cb;
    char *driver;
   //vndservicemanager的启动带参数的(argc > 1),servicemanager的启动不带参数(argc == 1)
    if (argc > 1) {
        driver = argv[1]; //是/dev/vndbinder
    } else {
        driver = "/dev/binder";
    }
    //打开binder驱动,申请128k字节大小的内存空间
    bs = binder_open(driver, 128*1024);
    if (!bs) {
#ifdef VENDORSERVICEMANAGER
        while (true) {
            sleep(UINT_MAX);
        }
#else
        ALOGE("failed to open binder driver %s\n", driver);
#endif
        return -1;
    }
    //成为上下文管理者
    if (binder_become_context_manager(bs)) {
        return -1;
    }
    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);
    //获取sehandle,这里分vndservicemanager和servicemanager
#ifdef VENDORSERVICEMANAGER
    sehandle = selinux_android_vendor_service_context_handle();
#else
    sehandle = selinux_android_service_context_handle();
#endif
    selinux_status_open(true);
    //无法获取sehandle
    if (sehandle == NULL) {
        abort();
    }
    //无法获取service_manager上下文就退出
    if (getcon(&service_manager_context) != 0) {
        abort();
    }
    //进入无限循环,处理client端发来的请求
    binder_loop(bs, svcmgr_handler);
    return 0;
}
打开binder驱动
//125啦站长
//打开binder驱动,申请128k字节大小的内存空间
//driver="/dev/binder"
bs = binder_open(driver, 128*1024);
binder_open()

这里主要工作

  1. 给bs分配内存

  2. 通过open打开设备驱动,返回文件描述符并赋值给bs->fd

  3. 内核空间和用户空间的binder版本比较

  4. mmap内存映射

涉及kernel层代码的方法,推荐看末尾参考文章,我这略过

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;
    }
    //打开Binder设备驱动,返回文件描述符并赋值给bs->fd
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    if (bs->fd < 0) {
        //打开失败就进入fail_open
        goto fail_open;
    }
    //获取binder版本信息并对比
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        //内核空间与用户空间的binder不是同一版本
        goto fail_open;
    }
    //传入的值=128k
    bs->mapsize = mapsize;
    //mmap内存映射
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        //binder设备内存无法映射
        goto fail_map;
    }
    return bs;
fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}
成为上下文管理者
//成为上下文管理者
if (binder_become_context_manager(bs)) {
    return -1;
}
binder_become_context_manager()

成为上下文的管理者,整个系统中只有一个这样的管理者。

int binder_become_context_manager(struct binder_state *bs){
    //通过ioctl,传递BINDER_SET_CONTEXT_MGR指令
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
进入binder循环
//进入无限循环,处理client端发来的请求
binder_loop(bs, svcmgr_handler);

这里传入的是svcmgr_handler指针函数。

先看binder_loop()

binder_loop()

这里主要如下工作

  1. 通过binder_write通知binder驱动要进入循环

  2. 通过ioctl读取binder中的数据

  3. 通过binder_parse解析数据

void binder_loop(struct binder_state *bs, binder_handler func){
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    readbuf[0] = BC_ENTER_LOOPER;
    //将BC_ENTER_LOOPER命令发送给binder驱动,让Service Manager进入循环
    binder_write(bs, readbuf, sizeof(uint32_t));
    //进入循环
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        //不断地binder读写过程
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {
            break;
        }
        //解析binder信息,这里传入了func哈
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            break;
        }
        if (res < 0) {
            break;
        }
    }
}
binder_write()

对data数据进行封装成binder_write_read,然后通过ioctl()发送给binder

int binder_write(struct binder_state *bs, void *data, size_t len){
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    //传入的是BC_ENTER_LOOPER
    bwr.write_buffer = (uintptr_t) data; 
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    return res;
}
binder_parse()

这里传入了svcmgr_handler,也就是func

这里主要功能

  1. 获取cmd码,根据cmd进行处理对应功能

  2. 处理BR_TRANSACTION事务,主要通过func处理(也就是svcmgr_handler)

这里部分不太懂,后续学习。请看参考文吧。多谢、

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) {
        //获取cmd码
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
        //处理解析到的cmd码
        switch(cmd) {
        case BR_NOOP://无操作,退出循环
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
            ptr += sizeof(struct binder_ptr_cookie);
            break;
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*txn)) {
                return -1;
            }
            binder_dump_txn(txn);
            //binder_handler函数不为null
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
                bio_init(&reply, rdata, sizeof(rdata), 4);
                //从txn解析出binder_io信息
                bio_init_from_txn(&msg, txn);
                //通过binder_handler函数进行处理
                res = func(bs, txn, &msg, &reply);
                if (txn->flags & TF_ONE_WAY) {
                    //不需要回复,释放buffer
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                    //需要回复,添加恢复信息并释放buffer
                    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;
            if ((end - ptr) < sizeof(*txn)) {
                return -1;
            }
            binder_dump_txn(txn);
            if (bio) {
                //从txn解析出binder_io信息
                bio_init_from_txn(bio, txn);
                bio = 0;
            } else {
                /* todo FREE BUFFER */
            }
            ptr += sizeof(*txn);
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: {
            // binder死亡消息
            struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;
            ptr += sizeof(binder_uintptr_t);
            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;
}
svcmgr_handler()

servicemanager是个服务管理者,用于保存启动过的服务,并提供查询服务等功能。

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);
        if (s == NULL) {
            return -1;
        }
         //根据名称查找相应handle
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        //调用bio_put_ref(reply, handle),将handle封装到reply.
        bio_put_ref(reply, handle);
        return 0;
    case SVC_MGR_ADD_SERVICE://添加服务
        //服务名
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        dumpsys_priority = bio_get_uint32(msg);
        //添加服务到svclist
        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);
        if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
            return -1;
        }
        si = svclist;
        //遍历
        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;
    }
    default:
        return -1;
    }
    bio_put_uint32(reply, 0);
    return 0;
}
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) {
        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;
    }
    //返回该服务所对应的handle
    return si->handle;
}
do_add_service()
int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
                   uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) {
    struct svcinfo *si;
    if (!handle || (len == 0) || (len > 127))
        return -1;
    //权限检查
    if (!svc_can_register(s, len, spid, uid)) {
        return -1;
    }
     //服务检索 判断是否有存在的服务
    si = find_svc(s, len);
    if (si) {
        if (si->handle) {
            //服务已注册时,释放之前的服务
            svcinfo_death(bs, si);
        }
        //更新handle
        si->handle = handle;
    } else {
        //如果之前没有,就创建并保存
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        if (!si) {
            //内存不足,无法分配足够内存
            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->dumpsys_priority = dumpsys_priority;
        si->next = svclist;
        //svclist保存所有已注册的服务
        svclist = si;
    }
    //以BC_ACQUIRE命令,handle为目标的信息,通过ioctl发送给binder驱动
    binder_acquire(bs, handle);
    //以BC_REQUEST_DEATH_NOTIFICATION命令的信息,通过ioctl发送给binder驱动
    //主要用于清理内存等收尾工作。
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

参考文章

  1. Binder系列3—启动ServiceManager

  2. ServiceManager与binder驱动的交互

 历史上的今天

  1. 2020: 设计模式:装饰模式或包装模式(0条评论)
  2. 2019: ubuntu 安装apache2, php5, mysql, phpmyadmin后无法访问phpmyadmin问题的解决(0条评论)
  3. 2019: 史铁生:合欢树(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com[备用域名]
3、 本站内容: 部分来源于网络,仅供站长学习和参考,若侵权请留言

暂无评论

暂无评论...

随机推荐

舒婷:致橡树

我如果爱你——绝不像攀援的凌霄花,借你的高枝炫耀自己;我如果爱你——绝不学痴情的鸟儿,为绿荫重复单调的歌曲;也不止像泉源,常年送来清凉的慰藉;也不止像险峰,增加你的高度,衬托你的威仪。甚至日光,甚至春雨。不,这些都还不够!我必须是你近旁的一株木棉,作为树的形象和你站在一起...

AAR文件转JAR文件的方法

什么是AARAAR(Android Archive)包是一个Android库项目的二进制归档文件。文件扩展名是.aar,但文件本身是具有以下条目的一个简单zip文件:/AndroidManifest.xml (强制)/classes.jar (强制)/res/ (强制)/R.t...

关于插入部分无线鼠标Android应用重启问题

前言在Android 9.0的项目中,测试工程师验证问题时插入某些USB的无线鼠标,会导致当前的APK会销毁然后重启一次,重新走一次生命周期。Activity为啥会重启?为什么会重启?我们大概率的知道,一般App重启大致有如下原因:APP异常了(微信存在这样的问题,闪退重启)机器状...

C语言之指针函数和函数指针

前言C语言中,除了数组指针和指针数组,还有指针函数和函数指针。很绕,容易记错!因此记录一下,方便自己查阅!正文指针函数指针函数是一个函数的返回值为地址的函数。返回值为指针类型的一个函数一般格式:数据类型 * 函数名(形参){   语句:}比如char name[] =...

FileProvider分享的Uri转成文件

前言简单记录一下FileProvider分享的Uri转出文件。流水账而已,可以跳过。正文之前我们使用FileProvider分享的Uri格式如下:content://com.biumall.file2.fileProvider/root/storage/udisk2/m4a/%E5%87...

Android加载动画常用做法简介

前言Android开发中常用到加载动画,因此今天有空记录一下使用方法和性能对比,方便自己查阅。正文效果就是一直旋转,下面是用到的部分加载图片(忘记保留来源地址了,只知道来自简书面,抱歉)隐藏内容!付费阅读后才能查看!¥2 ¥3多个隐藏块只需支付一次付费阅读参考文章