Binder 程式示例與分析之C語言篇續

語言: CN / TW / HK

Binder 程式示例與分析之C語言篇中講解了如何完成一個 Binder C 語言示例程式並詳細分析了服務的註冊過程,接下來我們繼續分析以下兩個情景:

  • 服務的獲取過程
  • 服務的使用過程

1. 服務的獲取過程

服務的獲取過程主要有以下幾個流程:

  • 呼叫 binder_open 完成 binder 驅動初始化
  • 呼叫 svcmgr_lookup 獲取到服務的 handle 值
  • 通過 binder_call 發起遠端呼叫

binder_client.c :

```c int main(int argc, char *argv) { int fd; struct binder_state bs; uint32_t svcmgr = BINDER_SERVICE_MANAGER; int ret;

//完成 binder 初始化
bs = binder_open("/dev/binder", 128*1024);
if (!bs) {
    fprintf(stderr, "failed to open binder driver\n");
    return -1;
}

g_bs = bs;

//獲取 hello 服務的 handle 值
g_handle = svcmgr_lookup(bs, svcmgr, "hello");
if (!g_handle) {
    return -1;
}

//呼叫服務
sayhello();

}

void sayhello(void) { unsigned iodata[512/4]; struct binder_io msg, reply;

/* 構造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);


/* 放入引數 */
bio_put_uint32(&msg, 0);  // strict mode header
bio_put_string16_x(&msg, "IHelloService");

/* 呼叫binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, HELLO_SVR_CMD_SAYHELLO))
    return ;

/* 從reply中解析出返回值 */
binder_done(g_bs, &msg, &reply);

} ```

接下來逐一分析程式碼:

binder_open 初始化好原始碼,該函式前面已做分析,這裡就不在細說了。

svcmgr_lookup 用於發起遠端呼叫,查詢服務的 handle 值。

handle 是服務在核心中的索引(有點檔案描述符 fd 的感覺),通過 handle 才能向目標程序的服務傳送資料。

svcmgr_lookup 函式有三個引數: * struct binder_state *bs : binder_open 返回的 binder 狀態值 * target:目標程序的 handle 值,ServiceManager 對應的 handle 值固定為 0 * name:服務的名字

```c / * 根據服務的名字(name),返回服務在核心中的 handle 值 * target:目標程序的 handle 值,ServiceManager 對應的 handle 值固定為 0 * name:服務的名字 / uint32_t svcmgr_lookup(struct binder_state bs, uint32_t target, const char *name) { uint32_t handle; unsigned iodata[512/4]; struct binder_io msg, reply;

bio_init(&msg, iodata, sizeof(iodata), 4);
//第一個資料 0
bio_put_uint32(&msg, 0);  // strict mode header
//"android.os.IServiceManager"
bio_put_string16_x(&msg, SVC_MGR_NAME);
//服務的名字 "hello"
bio_put_string16_x(&msg, name);

//發起遠端過程呼叫
//呼叫的方法是 SVC_MGR_CHECK_SERVICE
//client 端開始休眠
if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
    return 0;

//......

} ```

發起遠端呼叫後,ServiceManager 被喚醒,執行到以下程式碼:

```c 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;
binder_write(bs, readbuf, sizeof(uint32_t));

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

    //ServieManager 從這裡喚醒
    //獲取到 Server 傳過來的資料
    //核心的資料是 
    // SVC_MGR_CHECK_SERVICE 表示要呼叫的方法
    // "hello" 傳入方法的資料 
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

    if (res < 0) {
        ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
        break;
    }

    //解析收到的資料,並回調 func 方法
    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_parse 方法:

這裡會解析我們收到的資料,我們先看看我們收到的資料的具體格式:

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

//可能存在多組資料,通過 while 迴圈將每個資料解析完後再退出迴圈
while (ptr < end) {
    //獲取到命令 cmd,不同的 cmd 執行不同的操作
    uint32_t cmd = *(uint32_t *) ptr;
    ptr += sizeof(uint32_t);
    switch(cmd) {
    //......
    //會走到這個分支
    case BR_TRANSACTION_SEC_CTX:
    case BR_TRANSACTION: {
        struct binder_transaction_data_secctx txn;
        if (cmd == BR_TRANSACTION_SEC_CTX) {
          //......
        } else /* BR_TRANSACTION */ { //程式碼會走這裡
            if ((end - ptr) < sizeof(struct binder_transaction_data)) {
                ALOGE("parse: txn too small (binder_transaction_data)!\n");
                return -1;
            }
            //解析出 binder_transaction_data 結構體
            memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data));
            ptr += sizeof(struct binder_transaction_data);

            txn.secctx = 0;
        }

        //......

        if (func) {
            unsigned rdata[256/4];
            struct binder_io msg;
            struct binder_io reply;
            int res;

            bio_init(&reply, rdata, sizeof(rdata), 4);
            //進一步解析資料
            //解析出 binder_io 結構體
            bio_init_from_txn(&msg, &txn.transaction_data);
            //呼叫回撥函式
            res = func(bs, &txn, &msg, &reply);
            //回覆資料
            if (txn.transaction_data.flags & TF_ONE_WAY) {
                binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);
            } else {
                binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);
            }
        }
        break;
    }
    //......
    default:
        ALOGE("parse: OOPS %d\n", cmd);
        return -1;
    }
}

return r;

} ```

上面的程式碼走到 res = func(bs, &txn, &msg, &reply); 時就會執行到 binder_loop 傳入的回撥函式 svcmgr_handler

```c //已刪減部分不相關程式碼 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;


strict_policy = bio_get_uint32(msg);
bio_get_uint32(msg);  // Ignore worksource header.
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;
}
//根據 code 值呼叫不同的方法
//當前場景下 code == SVC_MGR_CHECK_SERVICE 
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
    //s 是服務的名字 hello
    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 寫入 reply
    bio_put_ref(reply, handle);
    return 0;
//......
default:
    ALOGE("unknown code %d\n", txn->code);
    return -1;
}

bio_put_uint32(reply, 0);
return 0;

} ```

接下來呼叫 binder_send_reply 將 reply 返回給 Client:

```c 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;
//第二個命令給 client 傳送 reply 資料
//傳送 BC_REPLY 接收方會收到 BR_REPLY
data.cmd_reply = BC_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 返回0, 走這裡
    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_write(bs, &data, sizeof(data));

} ```

接下來 Client 被喚醒:

```c int binder_call(struct binder_state bs, struct binder_io msg, struct binder_io *reply, uint32_t target, uint32_t code) { //...... for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (uintptr_t) readbuf;

    //從這裡喚醒
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

    if (res < 0) {
        fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));
        goto fail;
    }
    //解析收到的資料
    res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);
    if (res == 0) return 0;
    if (res < 0) goto fail;
}

//...... } ```

解析資料過程:

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

while (ptr < end) {
    uint32_t cmd = *(uint32_t *) ptr;
    ptr += sizeof(uint32_t);

    switch(cmd) {
    //......
    //程式碼走這裡
    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 中,也就是傳入的 reply
            bio_init_from_txn(bio, txn);
            bio = 0;
        } else {
            /* todo FREE BUFFER */
        }
        ptr += sizeof(*txn);
        r = 0;
        break;
    }
    default:
        ALOGE("parse: OOPS %d\n", cmd);
        return -1;
    }
}

return r;

} ```

解析出 reply 後,程式碼回到 binder_call,binder_call 接著又返回到 svcmgr_lookup:

```c uint32_t svcmgr_lookup(struct binder_state bs, uint32_t target, const char name) { uint32_t handle; unsigned iodata[512/4]; struct binder_io msg, reply;

bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0);  // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);

//從這裡返回
//handle 值已寫入 reply
if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
    return 0;

//從 reply 中解析出 handle
handle = bio_get_ref(&reply);

if (handle)
    binder_acquire(bs, handle);

//告知驅動,binder 呼叫已完成
binder_done(bs, &msg, &reply);

return handle;

} ```

到這裡,client 獲取到 hello 服務的 handle 值,服務請求階段完畢。

2. 服務的使用過程

服務的使用過程就是呼叫客戶端 sayhello 的過程:

```c void sayhello(void) { unsigned iodata[512/4]; struct binder_io msg, reply;

/* 構造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);


/* 放入引數 */
bio_put_uint32(&msg, 0);  // strict mode header
bio_put_string16_x(&msg, "IHelloService");

/* 呼叫binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, HELLO_SVR_CMD_SAYHELLO))
    return ;

/* 從reply中解析出返回值 */
binder_done(g_bs, &msg, &reply);

} ``` 這部分內容與服務獲取過程有 80% 以上的重複,有了前面的基礎,這部分解析就非常簡單了,這部分內容就留給讀者自行分析了。