Android 10 启动之servicemanager源码解析

目录
  • 正文
  • 获取服务
  • 注册服务

正文

上一篇文章:

Android 10 启动分析之Init篇 (一)

在前文提到,init进程会在在Trigger 为init的Action中,启动servicemanager服务,这篇文章我们就来具体分析一下servicemanager服务,它到底做了哪些事情。

servicemanager服务的源码位于/frameworks/native/cmds/servicemanager/service_manager.c,我们将从这个类的入口开始看起。

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";
    }
    //打开binder驱动,并设置mmap的内存大小为128k
    bs = binder_open(driver, 128*1024);
    ...
   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已封装如下步骤:
    while (1)
    {
        /* read data */
        /* parse data, and process */
        /* reply */
    }
    */
    binder_loop(bs, svcmgr_handler);
    return 0;
}

从main函数中可以看出,它主要做了三件事情:

  • 打开/dev/binder设备,并在内存中映射128K的空间。
  • 通知Binder设备,把自己变成context_manager,其他用户进程都通过0号句柄访问ServiceManager。
  • 进入循环,不停的去读Binder设备,看是否有对service的请求,如果有的话,就去调用svcmgr_handler函数回调处理请求。

我们再来看看svcmgr_handler函数的实现:

int svcmgr_handler(struct binder_state *bs,
                  struct binder_transaction_data_secctx *txn_secctx,
                  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;
   uint32_t dumpsys_priority;
   struct binder_transaction_data *txn = &txn_secctx->transaction_data;
   if (txn->target.ptr != BINDER_SERVICE_MANAGER)
       return -1;
   if (txn->code == PING_TRANSACTION)
       return 0;
   ...
   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,
                                (const char*) txn_secctx->secctx);
       if (!handle)
           break;
       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);
       if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
                          txn->sender_pid, (const char*) txn_secctx->secctx))
           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, (const char*) txn_secctx->secctx, txn->sender_euid)) {
           ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
                   txn->sender_euid);
           return -1;
       }
       si = svclist;
       // walk through the list of services n times skipping services that
       // do not support the requested priority
       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:
       ALOGE("unknown code %d\n", txn->code);
       return -1;
   }
   bio_put_uint32(reply, 0);
   return 0;
}

我们先来认识一下binder的数据传输载体binder_transaction_data:

struct binder_transaction_data {
  union {
  /* 当binder_transaction_data是由用户空间的进程发送给Binder驱动时,
 handle是该事务的发送目标在Binder驱动中的信息,即该事务会交给handle来处理;
  handle的值是目标在Binder驱动中的Binder引用。*/
    __u32 handle;
    /* 当binder_transaction_data是有Binder驱动反馈给用户空间进程时,
   ptr是该事务的发送目标在用户空间中的信息,即该事务会交给ptr对应的服务来处理;
   ptr是处理该事务的服务的服务在用户空间的本地Binder对象。*/
    binder_uintptr_t ptr;
  } target;  // 该事务的目标对象(即,该事务数据包是给该target来处理的)
  // 只有当事务是由Binder驱动传递给用户空间时,cookie才有意思,它的值是处理该事务的ServerC++层的本地Binder对象
  binder_uintptr_t cookie;
  // 事务编码。如果是请求,则以BC_开头;如果是回复,则以BR_开头。
  __u32 code;
  /* General information about the transaction. */
  __u32 flags;
  //表示事务发起者的pid和uid。
  pid_t sender_pid;
  uid_t sender_euid;
  // 数据大小
  binder_size_t data_size;
  //数据偏移量
  binder_size_t offsets_size;
  //data是一个共用体,当通讯数据很小的时,可以直接使用buf[8]来保存数据。当够大时,只能用指针buffer来描述一个申请的数据缓冲区。
  union {
    struct {
       /* transaction data */
      binder_uintptr_t buffer;
      binder_uintptr_t offsets;
    } ptr;
    __u8 buf[8];
  } data;
};

可以看到,svcmgr_handler函数中对binder data的事务编码进行了判断,并分别对SVC_MGR_GET_SERVICE(SVC_MGR_CHECK_SERVICE)SVC_MGR_ADD_SERVICESVC_MGR_LIST_SERVICES三种类型的事务编码做了业务处理。

获取服务

  case SVC_MGR_CHECK_SERVICE:
          s = bio_get_string16(msg, &len);
          ptr = do_find_service(bs, s, len);
          if (!ptr)
              break;
          bio_put_ref(reply, ptr);
          return 0;

do_find_service函数中主要执行service的查找,并把找到的服务句柄写入reply,返回给客户端。

uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid)
{
    struct svcinfo *si = find_svc(s, len);
    ...
    return si->handle;
}

我们继续看find_svc函数:

struct svcinfo *find_svc(const uint16_t *s16, size_t 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 NULL;
}

svclist 是一个单向链表,储存了所有向servicemanager注册的服务信息。find_svc遍历svclist链表,通过服务名称作为索引条件,最终找到符合条件的服务。

注册服务

    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);
        if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
        txn->sender_pid, (const char*) txn_secctx->secctx))
            return -1;

我们继续看do_add_service函数中做了哪些事情。

在该函数中,首先会去检查客户端是否有权限注册service,如果没有权限就直接返回,不能注册。

 if (!svc_can_register(s, len, spid, sid, uid)) {
        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
             str8(s, len), handle, uid);
        return -1;
    }

然后会去检查该service是否已经注册过了,如果已经注册过,那么就不能再注册了。

 si = find_svc(s, len);
    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;
    }

再判断内存是否足够。

 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;
        }

如果都没什么问题,会注册该service,并加入到svcList链表中。

综上所述,servicemanager主要负责查询和注册其他的系统服务,是系统服务的管理者。

文章的最后,留给大家一个问题进行思考:

为什么Android需要设计servicemanager做中转来添加和获取系统服务,而不直接让客户端去获取服务端句柄?

更多关于Android 10 启动之servicemanager的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android实现在ServiceManager中加入自定义服务的方法详解

    本文实例讲述了Android实现在ServiceManager中加入自定义服务的方法.分享给大家供大家参考,具体如下: 当我们要使用android的系统服务时,一般都是使用Context.getSystemService方法.例如我们要获取AudioManager,我们可以: AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 获取的服务,其实是在ServiceManager中注册的Binder服务,

  • android开机自动启动app的解决方法

    经过多次尝试之后,终于找到了开机自动启动App的解决方法 开机后会停留在锁屏页面,且短时间内如果没有进行解锁操作,屏幕会进入休眠状态,所以启动APP时需要先唤醒屏幕和解锁屏幕 定义一个广播类BootBroadcastReceiver.java public class BootBroadcastReceiver extends BroadcastReceiver {     static final String ACTION = "android.intent.action.BOOT_COMP

  • Android实现app开机自启动功能

    本文实例为大家分享了Android实现app开机自启动的具体代码,供大家参考,具体内容如下 最近要做个大屏的开发板程序,需要长期稳定运行,并开机自启运行此软件. 废话不多说,上代码 开机自启需要广播检测,权限 android.permission.RECEIVE_BOOT_COMPLETED 1.AndroidManifest.xml中加入两行代码,红色代码 <?xml version="1.0" encoding="utf-8"?> <manif

  • android studio开发实现APP开机自启动

    最近在做个APP,需要开启自启功能,通过在网上查找资料,实现了自启功能,非常简单,步骤如下: 1.创建广播接收器broadcastReceiver 2.在AndroidManifest.xml中配置自启权限和注册接收器接收的广播消息类型 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <receiver     android:name=".reveiv

  • Android ServiceManager的启动和工作原理

    ServiceManager启动 所有的系统服务都是需要在ServiceManager中进行注册的,而ServiceManager作为一个起始的服务,是通过init.rc来启动的. //system\core\rootdir\init.rc //启动的服务,这里是用的服务名称.服务名称是在对应的rc文件中注册并启动的 start servicemanager 具体的服务信息是在servicemanger.rc命名并定义的 //\frameworks\native\cmds\servicemana

  • Android 10 启动之servicemanager源码解析

    目录 正文 获取服务 注册服务 正文 上一篇文章: Android 10 启动分析之Init篇 (一) 在前文提到,init进程会在在Trigger 为init的Action中,启动servicemanager服务,这篇文章我们就来具体分析一下servicemanager服务,它到底做了哪些事情. servicemanager服务的源码位于/frameworks/native/cmds/servicemanager/service_manager.c,我们将从这个类的入口开始看起. int ma

  • Android okhttp的启动流程及源码解析

    前言 这篇文章主要讲解了okhttp的主要工作流程以及源码的解析. 什么是OKhttp 简单来说 OkHttp 就是一个客户端用来发送 HTTP 消息并对服务器的响应做出处理的应用层框架. 那么它有什么优点呢? 易使用.易扩展. 支持 HTTP/2 协议,允许对同一主机的所有请求共用同一个 socket 连接. 如果 HTTP/2 不可用, 使用连接池复用减少请求延迟. 支持 GZIP,减小了下载大小. 支持缓存处理,可以避免重复请求. 如果你的服务有多个 IP 地址,当第一次连接失败,OkHt

  • Android Handler,Message,MessageQueue,Loper源码解析详解

    本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文< Android中Handler的使用>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功能.但

  • SpringBoot 启动方法run()源码解析

    入口 通常一个简单的SpringBoot基础项目我们会有如下代码 @SpringBootApplication @RestController @RequestMapping("/") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 值得关注的有SpringApplication.run以及注解@

  • Android开发中线程池源码解析

    线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间任务时创建与销毁线程的代价.线程池不仅能够保证内核的充分利用,还能防止过分调度.可用线程数量应该取决于可用的并发处理器.处理器内核.内存.网络sockets等的数量. 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销.----摘自维基百科 我们在Android或者Java开发中

  • Android SwipeRefreshLayout下拉刷新源码解析

    本文实例为大家分享了SwipeRefreshLayout下拉刷新源码,供大家参考,具体内容如下 1.SwipeRefreshLayout是Google在support v4 19.1版本的library更新的一个下拉刷新组件,实现刷新效果更方便. 弊端:只有下拉 //设置刷新控件圈圈的颜色 swipe_refresh_layout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_orang

  • java中break和continue源码解析

    在自己学习java语言的过程中,很容易把break和continue的用法混淆.为了便于以后快速查阅及温习,在此特留学习笔记一份. 简述 在任何迭代语句的主体部分,都可以用break和continue控制循环的流程.其中,break用于强行退出循环,不执行循环中剩余的语句.而continue则停止执行当前迭代,然后退回循环起始处,开始下一次迭代. 源码 下面这个程序向大家展示了break和continue在for和while循环中的例子: package com.mufeng.thefourth

  • Android10 启动Zygote源码解析

    目录 app_main ZygoteInit preload preloadClasses preloadResources preloadSharedLibraries forkSystemServer app_main 上一篇文章: # Android 10 启动分析之servicemanager篇 (二) 在init篇中有提到,init进程会在在Trigger 为late-init的Action中,启动Zygote服务,这篇文章我们就来具体分析一下Zygote服务,去挖掘一下Zygote负

  • Android 10 启动Init进程解析

    目录 按下电源键时,android做了啥? init进程解析 FirstStageMain SetupSelinux SecondStageMain init.rc 解析 按下电源键时,android做了啥? 当我们按下电源键时,手机开始上电,并从地址0x00000000处开始执行,而这个地址通常是Bootloader程序的首地址. bootloader是一段裸机程序,是直接与硬件打交道的,其最终目的是“初始化并检测硬件设备,准备好软件环境,最后调用操作系统内核”.除此之外,bootloader

  • Android10 App 启动分析进程创建源码解析

    目录 正文 RootActivityContainer ActivityStartController 调用startActivityUnchecked方法 ActivityStackSupervisor 启动进程 RuntimeInit.applicationInit这个方法 正文 从前文# Android 10 启动分析之SystemServer篇 (四)中可以得知,系统在完成所有的初始化工作后,会通过 mAtmInternal.startHomeOnAllDisplays(currentU

随机推荐