首頁 > 軟體

Android 10 啟動之servicemanager原始碼解析

2022-10-12 14:01:58

正文

上一篇文章:

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 DENIEDn",
                   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 %dn", 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 DENIEDn",
             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, OVERRIDEn",
                 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 MEMORYn",
                 str8(s, len), handle, uid);
            return -1;
        }

如果都沒什麼問題,會註冊該service,並加入到svcList連結串列中。

綜上所述,servicemanager主要負責查詢和註冊其他的系統服務,是系統服務的管理者。

文章的最後,留給大家一個問題進行思考:

為什麼Android需要設計servicemanager做中轉來新增和獲取系統服務,而不直接讓使用者端去獲取伺服器端控制程式碼?

更多關於Android 10 啟動之servicemanager的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com