Android10.0 Binder通信原理(三)-ServiceManager篇

摘要:本節主要來講解Android10.0 Binder中守護進程ServiceManager是如何啓動、註冊、獲取服務

閱讀本文大約需要花費35分鐘。

文章首發微信公衆號:IngresGe

專注於Android系統級源碼分析,Android的平臺設計,歡迎關注我,謝謝!

[Android取經之路] 的源碼都基於Android-Q(10.0) 進行分析

[Android取經之路] 系列文章:

《系統啓動篇》

  1. Android系統架構
  2. Android是怎麼啓動的
  3. Android 10.0系統啓動之init進程
  4. Android10.0系統啓動之Zygote進程
  5. Android 10.0 系統啓動之SystemServer進程
  6. Android 10.0 系統服務之ActivityMnagerService
  7. Android10.0系統啓動之Launcher(桌面)啓動流程
  8. Android10.0應用進程創建過程以及Zygote的fork流程
  9. Android 10.0 PackageManagerService(一)工作原理及啓動流程
  10. Android 10.0 PackageManagerService(二)權限掃描
  11. Android 10.0 PackageManagerService(三)APK掃描
  12. Android 10.0 PackageManagerService(四)APK安裝流程

《日誌系統篇》

  1. Android10.0 日誌系統分析(一)-logd、logcat 指令說明、分類和屬性
  2. Android10.0 日誌系統分析(二)-logd、logcat架構分析及日誌系統初始化
  3. Android10.0 日誌系統分析(三)-logd、logcat讀寫日誌源碼分析
  4. Android10.0 日誌系統分析(四)-selinux、kernel日誌在logd中的實現​

《Binder通信原理》

  1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  2. Android10.0 Binder通信原理(二)-Binder入門篇
  3. Android10.0 Binder通信原理(三)-ServiceManager篇
  4. Android10.0 Binder通信原理(四)-Native-C\C++實例分析
  5. Android10.0 Binder通信原理(五)-Binder驅動分析
  6. Android10.0 Binder通信原理(六)-Binder數據如何完成定向打擊
  7. Android10.0 Binder通信原理(七)-Framework binder示例
  8. Android10.0 Binder通信原理(八)-Framework層分析
  9. Android10.0 Binder通信原理(九)-AIDL Binder示例
  10. Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub設計模式​​​​​​​​​​​​​​
  11. Android10.0 Binder通信原理(十一)-Binder總結

 

1.概述

在Android中,系統提供的服務被包裝成一個個系統級service,這些service往往會在設備啓動之時添加進Android系統。當我們某個應用想要調用系統某個服務的功能時,往往是向系統發出請求,調用該服務的外部接口。在上一節我們也瞭解到,這種外部接口,我們通常稱爲代理接口,也就是我們要拿到目標服務對應的代理對象。

但是Android中有很多個服務,如果都是直接向目標服務拿對象也不太現實,開銷會很大。因此在Android中,引入了一個ServiceManager的系統級服務--也可以稱之爲Binder的守護進程來管理這些服務對象。

上一節我們大體的瞭解了Binder的設計思路,瞭解了Binder機制的四要素:Client、Server、ServiceManager、Binder驅動。其中ServiceManager主要用來管理Server的名稱和對象。

ServiceManager本身的工作很簡單:註冊服務、查詢服務、列出所有服務,啓動一個死循環來解析Binder驅動讀寫動作,進行事務處理。

 

 

2.Binder架構

Binder通信流程如下:

1)首先服務端需要向ServiceManager進行服務註冊,ServiceManager有一個全局的service列表svcinfo,用來緩存所有服務的handler和name。

2)客戶端與服務端通信,需要拿到服務端的對象,由於進程隔離,客戶端拿到的其實是服務端的代理,也可以理解爲引用。客戶端通過ServiceManager從svcinfo中查找服務,ServiceManager返回服務的代理。

3)拿到服務對象後,我們需要向服務發送請求,實現我們需要的功能。通過 BinderProxy 將我們的請求參數發送給 內核,通過共享內存的方式使用內核方法 copy_from_user() 將我們的參數先拷貝到內核空間,這時我們的客戶端進入等待狀態。然後 Binder 驅動向服務端的 todo 隊列裏面插入一條事務,執行完之後把執行結果通過 copy_to_user() 將內核的結果拷貝到用戶空間(這裏只是執行了拷貝命令,並沒有拷貝數據,binder只進行一次拷貝),喚醒等待的客戶端並把結果響應回來,這樣就完成了一次通訊。

 

3.servicemanager的啓動

init進程啓動時,加載servicemanager.rc,啓動serviecmanager:

servicemanager.rc 文件如下:

/frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
        ...

從上面的rc文件可知道,servicemanager編譯後,位於手機的/system/bin中,這是一個Native C/C++的進程,C/C++的進程都會有一個main()的入口,順藤摸瓜,我們找到了servicemanager的入口:service_manager.c 的main()。

 

4.service_manager調用棧:

我們在看源碼或者調試死機的時候,最喜歡看的就是上下文的調用關係,在此,我列出了service_manager.c 的調用棧信息,暫時先忽略細節部分

 

5. 源碼分析

   servicemanager的整體流程和 service管理的結構我們都有了一個整體認識,那麼細節部分就得靠看源碼來深入理解了。

 

5.1 主程序啓動main()

前面我們知道,servicemanager 是一個Native C/C++的程序,那麼會存在一個main()的入口,代碼如下:


int main(int argc, char** argv)
{
    struct binder_state *bs;
    union selinux_callback cb;
    char *driver;

    //0. servicemanager 和vndservicemanager 共享一套代碼,vndservicemanager傳入參數"/dev/vndbinder"
    if (argc > 1) {
        driver = argv[1];   //vndservicemanager 的driver爲 "/dev/vndbinder"
    } else {
        driver = "/dev/binder"; //servicemanager 訪問的driver爲"/dev/binder"
    }

    //1.通過系統調用 open "/dev/binder", mmap 申請一塊128K的內存空間
    bs = binder_open(driver, 128*1024);
    if (!bs) {
#ifdef VENDORSERVICEMANAGER
        ALOGW("failed to open binder driver %s\n", driver);
        while (true) {
            sleep(UINT_MAX);    //如果是"/dev/vndbinder" 打開失敗,陷入死循環繼續等待
        }
#else
        ALOGE("failed to open binder driver %s\n", driver);  //如果是"/dev/binder" 打開失敗,直接跳出
#endif
        return -1;
    }

    //3.註冊servicemanager爲binder 機制守護進程 ,其實就是把0號的handler給servicemanager使用,以後只要訪問0號的handler,binder驅動就知道是與servicemanager進行交互
    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);   //註冊selinux的callback操作

#ifdef VENDORSERVICEMANAGER
    sehandle = selinux_android_vendor_service_context_handle();
#else
    sehandle = selinux_android_service_context_handle();
#endif
    selinux_status_open(true);  //啓動selinux的檢查

    if (sehandle == NULL) {
        ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
        abort();
    }

    //如果selinux檢查不過,則不允許進行binder的操作
    if (getcon(&service_manager_context) != 0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort();
    }

    //4. 啓動loop循環機制,等待"/dev/binder" 發來讀寫信息,解析這些內容,執行相應的操作,例如:註冊服務、獲取服務、列出服務信息
    binder_loop(bs, svcmgr_handler);

    return 0;
}

在Android8.0後,谷歌引入Treble機制,binder機制增加了hwbinder和vndbinder,其中vndbinder的守護進程爲vndservicemanager。

vndservicemanager和service共用同一份代碼,只是傳入的參數和宏控制的流程有部分差異。

vndservicemanager會傳入參數"/dev/vndbinder", servicemanager使用默認的"/dev/binder".

servicemanager主要做了以下幾件事:

1.打開binder驅動,申請了128K的內存空間

2.然後調用binder_become_context_manager()讓自己成爲整個系統中唯一的上下文管理器,其實也就是service管理器

3.調用binder_loop()進入無限循環,不斷監聽並解析binder驅動發來的命令。

4.binder_loop()通過binder_parse()進行命令解析,然後調用回調函數svcmgr_handler()進行處理,例如:註冊服務、獲取服務、列出服務信息

 

下面我們按步來看看各個回合工作的細節部分。

 

5.2 binder_open()

servicemanager啓動後,先通過binder_open()來打開"/dev/binder", 代碼如下:

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    ...
    //1.打開Binder設備驅動,陷入內核
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    ...

    //2.獲取Binder的版本信息,比較協議版本是否相同,不同則跳出
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || 
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION))
        ...
    }

    bs->mapsize = mapsize;
    //3.mmap內存映射128K內存空間,mmap必須是page的整數倍
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    ...

    return bs;
    ...
}

binder_open()的工作也比較簡單,分爲以下幾步

1.通過系統調用open()來打開"/dev/binder",獲得一個句柄信息,在Binder驅動中對應的是函數binder_open()

2.通過ioctl 獲取binder的版本信息,比較binder協議版本是否相同,不同則跳出,在Binder驅動中對應的是函數binder_ioctl()

3.通過mmap內存映射128K的內存空間,即把binder驅動文件的128K字節映射到了內存空間,這128K內存空間爲servicemanager使用。在Binder驅動中對應的是函數binder_mmap()。

其他的binder服務進程會映射 BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) 的內存空間,_SC_PAGE_SIZE表示一個page頁的大小,通常情況下爲4K,即(1M - 4K*2)=(1M-7K)

這個page的大小,不同廠家有時候也會調整大小,一般有1M,64K,4K,1KB,通常爲4K。

ServiceManager 進程mmap的內存大小可以通過adb shell命令得出

其中 0x7457d61000 - 0x7457d41000 = 0x20000, 轉成10進制,即爲128K

ARM32內存映射:

虛擬空間的低3GB部分從0-0XBFFFFFFF的虛擬線性地址,用戶態和內核態都可以尋址,這部分也是每個進程的獨立空間。

虛擬空間的高1G部分從0XC0000000到0XFFFFFFFF的虛擬地址,只有內核態的進程才能訪問,這種限制由頁目錄和頁表描述符的權限標誌位決定,通過MMU自動控制。

虛擬地址空間爲4G,0-3G是用戶地址空間(用戶態和內核態都可以尋址,這也是每個進行的獨立空間),3G-4G內核地址空間,每個進程都是擁有4G地址空間,只是用戶態下面無法訪問高1G空間;內核空間是被所有進程共享的

ARM64內存映射:

默認情況下,32位系統默認只能支持4G的內存,在打開PAE後,最大可以擴展到64G的內存。隨着物理硬件的不斷升級,現在的內存越來越大,因此基本上都切換到了64位系統。

理論上講,64位的地址總線可以支持高達16EB(2^64)的內存。

2^64 次方太大了,Linux 內核只採用了 64 bits 的一部分(開啓 CONFIG_ARM64_64K_PAGES 時使用 42 bits,頁大小是 4K 時使用 39 bits),該文假設使用的頁大小是 4K(VA_BITS = 39)

ARM64 有足夠的虛擬地址,用戶空間和內核空間可以有各自的 2^39 = 512GB 的虛擬地址。

需要注意到,32 位應用仍然擁有 512GB 的內核虛擬地址空間,並且不與內核共享自己的 4GB 空間。但在 ARM32 上,32 位應用只有 3GB 的地址空間。

ARM32和ARM64內存地址比較:

ARM32

ARM64

32位應用虛擬地址空間

3G

4G

64位應用虛擬地址空間

不支持

512G

內核虛擬空間

1G

512G

 

5.3 binder_become_context_manager()

binder_become_context_manager()的作用是讓ServiceManager成爲整個系統中唯一的上下文管理器,其實也就是service管理器,這樣我們就可以把ServiceManager稱之爲守護進程。

int binder_become_context_manager(struct binder_state *bs)
{
    struct flat_binder_object obj;
    memset(&obj, 0, sizeof(obj));
    obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX;

    //Android10.0 中引入BINDER_SET_CONTEXT_MGR_EXT,用來把ServiecManager設置成爲安全的上下文,
    int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj);

    // fallback to original method
    if (result != 0) {
        android_errorWriteLog(0x534e4554, "121035042");
        //如果安全上下文設置失敗,繼續使用原有的BINDER_SET_CONTEXT_MGR來進行控制
        result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
    }
    return result;
}

對應的binder驅動中操作如下:

從用戶空間拷貝ioctl的參數,調用binder_ioctl_set_ctx_mgr()進行設置

BINDER_SET_CONTEXT_MGR_EXT帶參數,BINDER_SET_CONTEXT_MGR不帶參數

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    ...
    switch (cmd) {
        case BINDER_SET_CONTEXT_MGR_EXT: {
            struct flat_binder_object fbo;

            if (copy_from_user(&fbo, ubuf, sizeof(fbo))) {
                ret = -EINVAL;
                goto err;
            }
            ret = binder_ioctl_set_ctx_mgr(filp, &fbo);
            if (ret)
                goto err;
            break;
        }
        case BINDER_SET_CONTEXT_MGR:
            ret = binder_ioctl_set_ctx_mgr(filp, NULL);
            if (ret)
                goto err;
            break;
    }
    ...
}

static int binder_ioctl_set_ctx_mgr(struct file *filp,
        struct flat_binder_object *fbo)
{
    int ret = 0;
    //進程的binder_proc, 這裏是ServiceManager的 binder_proc,之前通過open("/dev/binder")得來
    struct binder_proc *proc = filp->private_data;
    struct binder_context *context = proc->context;
    struct binder_node *new_node;
    // 線程的uid
    kuid_t curr_euid = current_euid();

    //互斥鎖
    mutex_lock(&context->context_mgr_node_lock);
     //正常第一次爲null,如果不爲null則說明該進程已經設置過context mgr則直接退出
    if (context->binder_context_mgr_node) {
        pr_err("BINDER_SET_CONTEXT_MGR already set\n");
        ret = -EBUSY;
        goto out;
    }
    //檢查當前進程是否具有註冊Context Manager的SEAndroid安全權限
    ret = security_binder_set_context_mgr(proc->tsk);
    if (ret < 0)
        goto out;
    if (uid_valid(context->binder_context_mgr_uid)) {
          //讀取binder_context_mgr_uid和當前的比,如果不一樣,報錯。
        if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
            pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
                   from_kuid(&init_user_ns, curr_euid),
                   from_kuid(&init_user_ns,
                     context->binder_context_mgr_uid));
            ret = -EPERM;
            goto out;
        }
    } else {
         //將當前進程的uid賦值給context的binder_context_mgr_uid變量以作保存
        context->binder_context_mgr_uid = curr_euid;
    }
    // 創建binder_node對象
    new_node = binder_new_node(proc, fbo);
    if (!new_node) {
        ret = -ENOMEM;
        goto out;
    }
    binder_node_lock(new_node);
    // 增加強弱引用計數
    new_node->local_weak_refs++;
    new_node->local_strong_refs++;
    new_node->has_strong_ref = 1;
    new_node->has_weak_ref = 1;
    context->binder_context_mgr_node = new_node; //把新創建的node對象賦值給context->binder_context_mgr_node,成爲serviceManager的binder管理實體
    binder_node_unlock(new_node);
    binder_put_node(new_node);
out:
    //釋放鎖
    mutex_unlock(&context->context_mgr_node_lock);
    return ret;
}

binder_ioctl_set_ctx_mgr()的流程也比較簡單

1.先檢查當前進程是否具有註冊Context Manager的SEAndroid安全權限

2.如果具有SELinux權限,會爲整個系統的上下文管理器專門生成一個binder_node節點,使該節點的強弱應用加1

3.新創建的binder_node 節點,記入context->binder_context_mgr_node,即ServiceManager 進程的context binder節點,使之成爲serviceManager的binder管理實體

 

5.4 binder_loop()

下一步進行守護進程的循環處理,binder_loop()會先向binder驅動發出了BC_ENTER_LOOPER命令,告訴binder驅動"本線程要進入循環狀態了",接着進入一個for循環不斷調用ioctl()讀取發來的數據,接着解析這些數據

[frameworks/native/cmds/servicemanager/service_manager.c]

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;   //binder 讀寫結構,記錄buffer中讀和寫的數據信息
    uint32_t readbuf[32];   //準備128= 32*4字節的讀取buffer

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    // 向binder驅動發送命令協議BC_ENTER_LOOPER,告訴binder驅動"本線程要進入循環狀態了".
    //binder_write 中把 binder_write_read的read的信息置空,這樣在binder_ioctl中,只會處理write的流程
    binder_write(bs, readbuf, sizeof(uint32_t));

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);   //進入循環,不斷地binder讀寫過程

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
         // 解析binder信息,傳入的回調func 函數爲 svcmgr_handler()
        res = binder_parse(bs, 0, (uintptr_t) 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;
        }
    }
}

其中最重要的一個結構體是binder_write_read,它用來記錄Binder buffer中讀和寫的數據信息結構體如下:

struct binder_write_read {
    binder_size_t       write_size;     //要寫入的字節數,write_buffer的總字節數
    binder_size_t       write_consumed; //驅動程序佔用的字節數,write_buffer已消費的字節數
    binder_uintptr_t    write_buffer;   //寫緩衝數據的指針
    binder_size_t       read_size;      //要讀的字節數,read_buffer的總字節數
    binder_size_t       read_consumed;  //驅動程序佔用的字節數,read_buffer已消費的字節數
    binder_uintptr_t    read_buffer;    //讀緩存數據的指針
};

5.5 binder_parse()

在binder_loop()進入for循環之後,核心處理流程就是ioctl和binder_parse(), 即不停的從Binder驅動接收讀寫數據,進行binder解析後,進行處理.

[frameworks/native/cmds/servicemanager/binder.c]
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;

    //每個命令中可能包含多個BR碼,因此需要用while循環來解析buffer中所有的BR_XX碼
    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr; //從ptr指向的地址讀取cmd(協議)命令
        ptr += sizeof(uint32_t);        //cmd佔用 readbuf的4個字節
#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,"  %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *)));
#endif
            ptr += sizeof(struct binder_ptr_cookie);
            break;
        case BR_TRANSACTION_SEC_CTX:
        case BR_TRANSACTION: {
               struct binder_transaction_data_secctx txn;
               ...
               binder_dump_txn(&txn.transaction_data);
               if (func) {
                       ...
                       res = func(bs, &txn, &msg, &reply);
                       ...
               }
        case BR_REPLY: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*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);
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: {
                  //獲取binder_death數據
            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;
}

在binder_loop()中聲明瞭一個128字節的棧內存-readbuf, 用BINDER_WRITE_READ命令從驅動讀取一些內容,並傳入binder_parse(),binder_parse()根據binder驅動傳來的 "BR_XXX"協議碼,進行相關的邏輯處理,最主要的有三個"BR_XXX"協議

BR_TRANSACTION  : 事務處理,解析binder_transaction_data的數據,調用回調函數svcmgr_handler() 進行服務的註冊、獲取等操作

BR_REPLY       : 消息回覆

BR_DEAD_BINDER  : 死亡通知

只要binder_parse()解析正常,binder_loop()就會一直執行下去,ServiceManager進程不退出。

binder_parse()解析binder驅動傳來的readbuf的內存,readbuf擁有128字節的棧內存,每次可以只處理一個cmd,也可以有多個cmd,所以存在一個while循環,可以同時解析多個cmd,多個cmd的結構如下圖所示:

 

5.5.1 BR_XXX 協議碼分析

BR_XXX碼,也成爲Binder響應碼,這裏介紹了ServiceManager處理的一些響應碼的作用:

5.5.2 BR_TRANSACTION 解析

我們這裏單獨分析下 BR_TRANSACTION 的流程,這也是我們常用的一個流程,這是Binder驅動向Server端發送請求數據。

從readbuf中解析出binder_transaction_data的數據,最後對接收和發送數據進行了封裝,傳遞給svcmgr_handler()做詳細處理


int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    case BR_TRANSACTION_SEC_CTX:
    case BR_TRANSACTION: {
        //binder_transaction_data_secctx 比 binder_transaction_data 多一個 secctx成員,主要用來指向安全上下文
        struct binder_transaction_data_secctx txn;
        if (cmd == BR_TRANSACTION_SEC_CTX) {
            if ((end - ptr) < sizeof(struct binder_transaction_data_secctx)) {
                ALOGE("parse: txn too small (binder_transaction_data_secctx)!\n");
                return -1;
            }
            memcpy(&txn, (void*) ptr, sizeof(struct binder_transaction_data_secctx));
            ptr += sizeof(struct binder_transaction_data_secctx);
        } else /* BR_TRANSACTION */ {
            if ((end - ptr) < sizeof(struct binder_transaction_data)) {
                ALOGE("parse: txn too small (binder_transaction_data)!\n");
                return -1;
            }
            //readbuf 偏移4個自己的cmd後,拷貝一個binder_transaction_data 結構的數據給transaction_data
            memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data));
            ptr += sizeof(struct binder_transaction_data);  //readbuf偏移 binder_transaction_data的內容,這樣readbuf可以指向下一個BR_XX結構

            txn.secctx = 0;
        }
        //打印binder_transaction_data數據
        binder_dump_txn(&txn.transaction_data);
        if (func) {
            unsigned rdata[256/4];
            struct binder_io msg;   //收到數據封裝
            struct binder_io reply; //發送數據封裝
            int res;

            bio_init(&reply, rdata, sizeof(rdata), 4);     //reply初始化
            bio_init_from_txn(&msg, &txn.transaction_data); //從txn.transaction_data 解析出binder_io的信息,存入msg
            res = func(bs, &txn, &msg, &reply); //svcmgr_handler 回調處理
            if (txn.transaction_data.flags & TF_ONE_WAY) {
                 //如果是TF_ONE_WAY處理,則釋放txn->data的數據
                binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);
            } else {
                 //如果不是TF_ONE_WAY處理,給binder驅動回覆數據
                binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);
            }
        }
        break;
    }
}

從上面的邏輯看,我們重點關注的是 binder_transaction_data 這個結構,binder_transaction_data說明了transaction到底在傳輸什麼語義,而語義碼就記錄在其code成員中。不同語義碼需要攜帶的數據也是不同的,這些數據由data指定.

結構體說明如下:


struct binder_transaction_data {
       union {
         __u32        handle;       //binder_ref(即handle)對於發送數據包的一方,該成員指明發送目的地。
                                      //由於目的是在遠端,所以這裏填入的是對Binder實體的引用,存放在target.handle中。如前述,Binder的引用在代碼中也叫句柄(handle)
         binder_uintptr_t ptr;    //Binder_node的內存地址,當數據包到達接收方時,驅動已將該成員修改成Binder實體,即指向Binder對象內存的指針,使用target.ptr來獲得
       } target;
       binder_uintptr_t        cookie;   //BBinder指針, 發送方忽略該成員;接收方收到數據包時,該成員存放的是創建Binder實體時由該接收方自定義的任意數值,做爲與Binder指針相關的額外信息存放在驅動中
       __u32                code;                   //RPC代碼,代表Client與Server雙方約定的命令碼,例如:ADD_SERVICE_TRANSACTION
       __u32                flags;       //標誌位,比如TF_ONE_WAY代表異步,即不等待Server端回覆
       pid_t                sender_pid;   //發送端進程的pid
       uid_t                sender_euid;  //發送端進程的uid
       binder_size_t        data_size;    //data數據的總大小
       binder_size_t        offsets_size; //IPC對象的大小

       union {
               struct {
                       binder_uintptr_t        buffer; //數據區起始地址
                       binder_uintptr_t        offsets;//數據區IPC對象偏移量
               } ptr;
               __u8        buf[8]; 
       } data;//RPC數據
}

從上面binder_transaction_data的結構可以看出,data可存入的數據很少,主要採用了數據其實地址和數據偏移量,根據代碼上下文可知,調用了bio_init_from_txn(),從txn.transaction_data 解析出binder_io的信息,存入msg

 

5.5.2.1 bio_init_from_txn()

   bio_init_from_txn()的作用就是把 binder_transaction_data 的“數據起始地址”、“偏移量”、“data數據的總大小”和“偏移數組中可用的條目”:

void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn)
{
    bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer;
    bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets;
    bio->data_avail = txn->data_size;
    bio->offs_avail = txn->offsets_size / sizeof(size_t);        //計算 偏移數組中可用的條目
    bio->flags = BIO_F_SHARED;                //標誌需要釋放緩衝區
}

binder_io 這個結構從字面意思我們就可以看出要用它來讀取binder傳遞來的數據,結構如下:
struct binder_io
{
    char *data;            //指向數據 讀/寫的指針
    binder_size_t *offs;   //數據偏移量
    size_t data_avail;     //數據緩衝區中可用的字節數
    size_t offs_avail;     //偏移數組中可用的條目

    char *data0;           //數據緩衝區的起始地址
    binder_size_t *offs0;  //緩衝區偏移量的起始位置
    uint32_t flags;
    uint32_t unused;
};

binder_transaction_data 和  binder_io的關聯

初始化完 binder_io 的replay,並把transaction_data 轉換成了binder_io 的msg後,調用回調函數svcmgr_handler()進行最終邏輯處理

 

5.6 svcmgr_handler()

在BR_TRANSACTION的命令解析後,就把binder_transaction_data_secctx的數據傳給回調函數svcmgr_handler()進行處理。

根據不同的傳輸語義碼(txn->code) 來進行相應的操作:查詢服務,註冊服務,以及列舉所服務

源碼如下:


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;

    //獲取binder_parse 過來的binder_transaction_data 數據
    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;


    //從 msg的 binder_io.data的起始地址讀取4個字節的內容,存入strict_policy,strict_policy現在不需要使用,可以直接忽略
    //然後msg->data 往後偏移4個字節,即忽略了開頭的strict_policy;msg->data_avail 緩衝區的剩餘可用字節數減去4個字節。
    //msg->data0一直不變,指向數據緩衝區的起始地址
    strict_policy = bio_get_uint32(msg);
    bio_get_uint32(msg);  // 繼續偏移4個字節,忽略了header

     //先讀取msg->data接下來的4個字節,即爲length,然後讀取((len + 1) * sizeof(uint16_t))字節的內容存入s。這裏len+1的目的:字符串都要包含最後一個’\0’,需要把這個偏移量加入
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }

    if ((len != (sizeof(svcmgr_id) / 2)) ||
        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
        fprintf(stderr,"invalid id %s\n", str8(s, len));
        return -1;
    }

    //檢查SELinux環境是否有任何改變
    if (sehandle && selinux_status_updated() > 0) {
#ifdef VENDORSERVICEMANAGER
        struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle();
#else
        struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
#endif
        if (tmp_sehandle) {
            selabel_close(sehandle);
            sehandle = tmp_sehandle;
        }
    }

    //根據不同code進行邏輯處理
    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        //獲取服務名字和長度,分別存在s和len
        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_io 數據獲取的方法要注意:

1) uint32_t bio_get_uint32(struct binder_io *bio)

  作用:獲取bio->data開始後的4個字節。

  說明:bio->data 往後偏移4個字節,bio-->data_avail 緩衝區的剩餘可用字節數減去4個字節。bio-->data0一直不變,指向數據緩衝區的起始地址

 

2) uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz)

  作用:在svcmgr_handler()主要是用來獲取服務名和length

  說明:先讀取一個32bits的整數,這個整數值就是字符串的長度。然後再讀取((len + 1) * sizeof(uint16_t))字節。

  這裏len+1的目的:字符串都要包含最後一個’\0’,需要把這個偏移量加入

 

3) static void *bio_get(struct binder_io *bio, size_t size)

  作用:根據傳入的size,獲取對應的ptr內容

  說明:bio->data 往後偏移4個字節,bio-->data_avail 緩衝區的剩餘可用字節數減去4個字節。bio-->data0一直不變,指向數據緩衝區的起始地址

 

4) uint32_t bio_get_ref(struct binder_io *bio)

  作用:獲取一個服務端的handle

  說明:從bio->data 中讀出一個flat_binder_object的結構數據,返回 flat_binder_object->handle

 

5) void bio_put_ref(struct binder_io *bio, uint32_t handle)

  作用:在svcmgr_handler中主要是把handler存入reply

  說明:bio->data 偏移size(flat_binder_object), 其中存入 flat_binder_object obj的數據,並把hanlder存入obj

 

6) void bio_put_string16(struct binder_io *bio, const uint16_t *str)

  作用:把service的名稱 存入reply

 

svcmgr_handler() 對binder驅動傳來的binder_transaction_data進行解析讀取後,根據不同的code,執行不同的操作,下面一起看看“註冊服務”和“查找服務”是如何操作的。

 

5.7 ServiceManager 是如何管理service信息的?

通過前面的一些信息,我們瞭解到,ServiceManager用來管理服務信息,那麼它是如何進行管理的呢?

在ServiceMnager的進程裏有一個全局性的svclist變量,服務信息都存在於這裏

struct svcinfo *svclist = NULL;

結構:

struct svcinfo
{
    struct svcinfo *next;        // 下一個"服務的信息"
    uint32_t handle;                //實際上記錄的就是系統service對應的binder句柄值
    struct binder_death death;
    int allow_isolated;
    uint32_t dumpsys_priority;
    size_t len;                        //服務名的長度
    uint16_t name[0];                //Service的名字,最後要保留一個\0的位置,buffer長度爲 (len + 1) * sizeof(uint16_t)
};

 

5.8 註冊服務

根據傳入的code:SVC_MGR_ADD_SERVICE得知,本次binder流程想要進行服務註冊。

步驟:

  1. 從binder_io  msg中獲取服務名稱和長度
  2. 從binder_io msg 中獲取handle
  3. 檢查該服務是否有註冊的selinx權限
  4. 查詢服務列表svclist 是否存在該handle,如果有handle,就更新該服務的handle信息,通過這個handle我們最終就能找到遠端的service實體
  5. 如果svclist不存在該服務,申請一個svcinfo的空間,把服務名、長度、handle等信息存入其中
  6. 把svcinfo 加入svclist的鏈表中
  7. 再以BC_ACQUIRE命令,handle爲目標的信息,通過ioctl發送給binder驅動
  8. 最後以BC_REQUEST_DEATH_NOTIFICATION命令的信息,通過ioctl發送給binder驅動,主要用於清理內存等收尾工作
int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data_secctx *txn_secctx,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    ...
    case SVC_MGR_ADD_SERVICE:
        //獲取服務的名字s和長度len
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        //獲取handle,通過這個handle我們最終就能找到遠端的service實體
        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;
    ...
}


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, const char* sid) {
    struct svcinfo *si;

     //service的name長度不能大於127
    if (!handle || (len == 0) || (len > 127))
        return -1;

    //最終調用selinux_check_access() 進行selinux的權限檢查,檢查服務是否有進行服務註冊
    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;
    }
    //查詢是否包含該name的svcinfo
    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); //服務已註冊時,釋放相應的服務
        }
        //更新服務 handle
        si->handle = handle;
    } else {
        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;
        }

        // 對svcinfo進行初始化賦值
        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;
}

 

5.9 查找服務

根據傳入的code:SVC_MGR_GET_SERVICE、SVC_MGR_CHECK_SERVICE得知,本次binder流程想要進行服務獲取。

步驟:

  1. 獲取服務的名字s和長度len
  2. 從svclist中根據名字查到一個svcinfo的結構
  3. 進行selinx檢查
  4. 檢查通過,返回該服務的handle
  5. 把handle放入binder_io 的reply的data中
  6. 最後在binder_parse()中調用binder_send_reply(),以BC_REPLY命令, 通過ioctl發送給binder驅動,最終轉到client進程

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data_secctx *txn_secctx,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        //獲取服務的名字s和長度len
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        //從svclist中查找服務名稱對應的handle
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
                                 (const char*) txn_secctx->secctx);
        if (!handle)
            break;
        //把handle存入binder_io reply的信息中
        bio_put_ref(reply, handle);
        return 0;
}

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

    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;
        }
    }
     //調用selinux_check_access()進行檢查服務是否滿足查詢條件,是否有selinx權限問題
    if (!svc_can_find(s, len, spid, sid, uid)) {
        return 0;
    }
    //返回服務的handle
    return si->handle;
}

查到服務的handle後,存入binder_io reply的data中,其中存入的type爲HANDLE: BINDER_TYPE_HANDLE

void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
    struct flat_binder_object *obj;

    if (handle)
        obj = bio_alloc_obj(bio);
    else
        obj = bio_alloc(bio, sizeof(*obj));

    if (!obj)
        return;

    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    obj->hdr.type = BINDER_TYPE_HANDLE;
    obj->handle = handle;
    obj->cookie = 0;
}

服務的handle被放入到binder_io的reply中後,我們回到binder_parse(),當svcmgr_handler()處理完成後,執行binder_send_reply()的過程。

binder_send_reply()主要是將BC_FREE_BUFFER和BC_REPLY命令協議發送給Binder驅動,向client端發送reply. 其中data的數據區中保存的是TYPE爲HANDLE

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;

    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    data.cmd_reply = BC_REPLY;        // reply命令
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {  //svcmgr_handler執行成功,把reply的數據組裝進入txn
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
       //向Binder驅動通信
    binder_write(bs, &data, sizeof(data));
}

 

6.總結

ServiceManager 註冊成爲上下文管理者-守護進程,負責管理系統中的所有服務。ServiceManger進程跟所有向其註冊服務的死亡通知建立聯繫, 那麼當服務所在進程死亡後, 會只需告知ServiceManager.每個Client通過查詢ServiceManager可獲取Server進程的情況,降低所有Client進程直接檢測會導致負載過重。

ServiceManager的整體流程如下:

  1. 打開設備,mmap一個128K的內存空間,進行用戶空間和內核空間的內存綁定
  2. 註冊成爲上下文的管理者--守護進程
  3. 陷入死循環,標註線程的looper狀態爲enter
  4. 不停的操作binder讀寫過程
  5. 解析binder數據,進行查詢服務、註冊服務、列出服務的核心操作
  6. 由於在進行服務註冊的時候,把svcinfo_death()註冊到了binder_death的中,當收到BR_DEAD_BINDER時,表明應用層向Binder驅動發送Binder調用時,Binder應用層的另一個端已經死亡,進行死亡通知操作,調用svcinfo_death(),最終調用binder_release()發送BC_RELEASE 到內核進行處理。

7.代碼路徑:

frameworks/native/cmds/servicemanager/binder.c

frameworks/native/cmds/servicemanager/service_manager.c

frameworks/native/cmds/servicemanager/servicemanager.rc

 

我的微信公衆號:IngresGe

參考:

《啓動ServiceManager》

《Binder(ServiceManager篇)》

《淺談Service Manager成爲Android進程間通信(IPC)機制Binder守護進程之路》

《SEAndroid安全機制對Binder IPC的保護分析》 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章