---
# System prepended metadata

title: Binder - 物件參考
tags: [Binder]

---

---
title: 'Binder - 物件參考'
disqus: kyleAlien
---

Binder - 物件參考
===

## OverView of Content

[TOC]

## Binder service 計數

* 當 BinderService 計數從 `0->1`、`1->0` 時，BinderDriver 會對目標 BinderService 發送 **BINDER_WORK_NODE 命令**，該命令會增減 `binder_node` 的計數，之後再由 BinderService 去增減自身 `RefBase` 的計數

    ```c=
    // binder.c

    static int binder_thread_read(
                      struct binder_proc *proc,    // Service 進程
                      struct binder_thread *thread,    // Service 線程
                      binder_uintptr_t binder_buffer, // IPCThreadState 的 mIn 數據空間
                      size_t size,
                      binder_size_t *consumed, 
                      int non_block)
    {
        // 使用者空間的指針 
        void __user *buffer = (void __user *)(uintptr_t)binder_buffer;

        // 取得開頭結尾指針
        void __user *ptr = buffer + *consumed;    // 數據的起點
        void __user *end = buffer + size;        // 數據的終點

        int ret = 0;
        int wait_for_proc_work;

        ...

        while (1) {
            uint32_t cmd;
            // 創建 binder_transaction_data_secctx
            struct binder_transaction_data_secctx tr;

            // 取 tr#transaction_data
            struct binder_transaction_data *trd = &tr.transaction_data;

            // 當前需要執行的 binder_work
            struct binder_work *w = NULL;
            // 任務 header
            struct list_head *list = NULL;

            // 要對外傳輸的訊息
            struct binder_transaction *t = NULL;
            struct binder_thread *t_from;
            size_t trsize = sizeof(*trd);

            ...

            // 判斷 binder_work#type
            switch (w->type) {
            // Server 透過 SM 註冊 Server 服務時會調用
            case BINDER_WORK_NODE: {
                ...

                int has_weak_ref;
                int has_strong_ref;
                void __user *orig_ptr = ptr;

                // Server 的計數
                // 強計數
                strong = node->internal_strong_refs ||
                        node->local_strong_refs;
                // 弱計數
                weak = !hlist_empty(&node->refs) ||
                        node->local_weak_refs ||
                        node->tmp_refs || strong;
                // 是否有強引用
                has_strong_ref = node->has_strong_ref;
                // 是否有弱引用
                has_weak_ref = node->has_weak_ref;

                // 有弱引用，但沒有 ref
                if (weak && !has_weak_ref) {
                    node->has_weak_ref = 1;    // 設為 1
                    node->pending_weak_ref = 1;
                    node->local_weak_refs++;
                }

                // 有強引用，但沒有 ref
                if (strong && !has_strong_ref) {
                    node->has_strong_ref = 1;    // 設為 1
                    node->pending_strong_ref = 1;
                    node->local_strong_refs++;
                }

                if (!strong && has_strong_ref)
                    node->has_strong_ref = 0;    // 設為 0 (清除)
                if (!weak && has_weak_ref)
                    node->has_weak_ref = 0;    // 設為 0 (清除)

                if (!weak && !strong) {
                    ... 釋放 server node
                } else {
                    ...
                }

                if (weak && !has_weak_ref)
                    // @ 查看 binder_put_node_cmd
                    ret = binder_put_node_cmd(
                            proc, thread, &ptr, node_ptr,
                            node_cookie, node_debug_id,
                            BR_INCREFS, "BR_INCREFS");
                ... 省略部分

                if (ret)
                    return ret;
            } break;

            }
        }
    }
    ```
    :::info
    這段邏輯在 Binder Kernel 中處理，接下來準備進入 BinderServer 的處理
    :::

### Binder service - Native BBinder 增、減計數

* 從 Binder 驅動命令 `BINDER_WORK_NODE` 可以看到：

    1. **一個 BinderServer 代表的是一個 BinderNode**，並且 **一個 BinderNode 也對應多個強、弱 ref 引用**

        > ![](https://i.imgur.com/my6dH3p.png)

    2. `binder_put_node_cmd`：將數據寫數 Server 提供的一個使用者空間緩衝區
        ```c=
        // binder.c

        static int binder_put_node_cmd(struct binder_proc *proc,
                           struct binder_thread *thread,
                           void __user **ptrp,
                           binder_uintptr_t node_ptr,
                           binder_uintptr_t node_cookie,
                           int node_debug_id,
                           uint32_t cmd, const char *cmd_name)
        {
            void __user *ptr = *ptrp;

            // 寫入 Server 使用者空間
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);

            if (put_user(node_ptr, (binder_uintptr_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);

            if (put_user(node_cookie, (binder_uintptr_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t); 

            ...
            *ptrp = ptr;
            return 0;
        }
        ```
    3. 對應處理加、減計數的則是 IPCThreadState#[**executeCommand**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp) 函數：
        :::info
        * BBinder 的最終父類就是 RefBase
        * 增加參考計數是緊急的事情必須先做，相反減少計數並非僅及，可以等到 `BINDER_WRITE_READ` 再去執行
        :::
        ```cpp=
        // IPCThreadState.cpp

        status_t IPCThreadState::executeCommand(int32_t cmd)
        {
            BBinder* obj;
            RefBase::weakref_type* refs;
            status_t result = NO_ERROR;

            switch ((uint32_t)cmd) {

                ...

            case BR_ACQUIRE:
                refs = (RefBase::weakref_type*)mIn.readPointer();
                obj = (BBinder*)mIn.readPointer();

                ... log

                // 增加強記數
                obj->incStrong(mProcess.get());

                ... log

                mOut.writeInt32(BC_ACQUIRE_DONE);
                mOut.writePointer((uintptr_t)refs);
                mOut.writePointer((uintptr_t)obj);
                break;

            case BR_RELEASE:
                refs = (RefBase::weakref_type*)mIn.readPointer();
                obj = (BBinder*)mIn.readPointer();

                ... log

                // 不急著處理
                mPendingStrongDerefs.push(obj);
                break;

            case BR_INCREFS:
                refs = (RefBase::weakref_type*)mIn.readPointer();
                obj = (BBinder*)mIn.readPointer();

                // 增加弱記數
                refs->incWeak(mProcess.get());
                mOut.writeInt32(BC_INCREFS_DONE);
                mOut.writePointer((uintptr_t)refs);
                mOut.writePointer((uintptr_t)obj);
                break;

            case BR_DECREFS:
                refs = (RefBase::weakref_type*)mIn.readPointer();

                obj = (BBinder*)mIn.readPointer(); // consume
                // 不急著處理
                mPendingWeakDerefs.push(refs);
                break;

            ...
            }
        }
        ```

## Binder node 計數

在 BinderKernel 中，**BinderServer 是以一個 `binder_node` 結構表示**

### Binder node 增加計數

```c=
// binder.c

static int binder_inc_node(struct binder_node *node, int strong, int internal,
               struct list_head *target_list)
{
    int ret;

    // 鎖住目標 Service node
    binder_node_inner_lock(node);

    // @ 分析 binder_inc_node_nilocked
    ret = binder_inc_node_nilocked(node, strong, internal, target_list);
    binder_node_inner_unlock(node);

    return ret;
}
```
    
* **`binder_inc_node_nilocked` 函數**：首先要知道 
    1. **BinderDriver** 在請求一個 BinderServer 處理事務時就會 **增加 `binder_node`#`local_weak_refs`、`binder_node`#`local_strong_refs` 計數 (內部)**

    2. **BinderClient** 在請求一個 BinderServer 處理事務時就會 **增加 `binder_node`#`internal_strong_refs` 計數 (外部)**

        :::success
        [**Binder Kernel 結構介紹**](https://hackmd.io/TtT34BAwRP2sZ3vALt4Drg)
        :::
    
        接下來請看程式註解

        ```c=
        // binder.c

        static int binder_inc_node_nilocked(struct binder_node *node, // Binder 實體物件
                            int strong,    // 是否曾加強計數
                            int internal,    // 是否是內部 (是否需要 BinderServer 操作計數
                            struct list_head *target_list)    // 目標處理程序的 TODO List
        {
            struct binder_proc *proc = node->proc;    // 取得 Server 運行的 proc

            ...

            if (strong) {    // 增加強記數
                
                if (internal) {    // 內部 (Client 請求
                    
                    // 對於 ServiceManager 來說不用增加，因為 ServiceManager 不會被回收
                    if (target_list == NULL &&
                        node->internal_strong_refs == 0 &&
                        !(node->proc &&
                          node == node->proc->context->binder_context_mgr_node &&
                          node->has_strong_ref)) {
                        pr_err("invalid inc strong node for %d\n",
                            node->debug_id);
                        return -EINVAL;
                    }
                    // 增加內部引用計數
                    node->internal_strong_refs++;

                } else    // 外部 (BinderDriver 引用
                    // 增加外部引用計數
                    node->local_strong_refs++;

                // 增加強引用計數，避免 Server 被銷毀
                if (!node->has_strong_ref && target_list) {
                    struct binder_thread *thread = container_of(target_list,
                                    struct binder_thread, todo);
                    
                    binder_dequeue_work_ilocked(&node->work);

                    BUG_ON(&thread->todo != target_list);
                    
                    // 要求 BinderServer 添加強引用計數
                    // 對 thread#todo 列表新增
                    binder_enqueue_deferred_thread_work_ilocked(thread,
                                           &node->work);
                }
            } else {
                if (!internal)
                    node->local_weak_refs++;

                if (!node->has_weak_ref && list_empty(&node->work.entry)) {
                    // 目標 Proc 無添加列表則失敗
                    if (target_list == NULL) {
                        pr_err("invalid inc weak node for %d\n",
                            node->debug_id);
                        return -EINVAL;
                    }
                    /*
                     * See comment above
                     */
                    // 要求 BinderServer 添加一個弱引用計數
                    binder_enqueue_work_ilocked(&node->work, target_list);
                }
            }
            return 0;
        }
        ```

### Binder node 減少計數

```c=
// binder.c

static void binder_dec_node(struct binder_node *node, int strong, int internal)
{
	bool free_node;

    // 鎖住目標 Service node
	binder_node_inner_lock(node);
    
    // @ 分析 binder_dec_node_nilocked
	free_node = binder_dec_node_nilocked(node, strong, internal);
    
	binder_node_inner_unlock(node);
    
    // 是否需要釋放 Node 資源
	if (free_node)
		binder_free_node(node);
}
```

* **`binder_dec_node_nilocked` 函數**：首先要知道 
    1. **BinderDriver** 在請求一個 BinderServer 釋放時就會 **減少 `binder_node`#`local_weak_refs`、`binder_node`#`local_strong_refs` 計數 (內部)**

    2. **BinderClient** 在請求一個 BinderServer 釋放時就會 **減少 `binder_node`#`internal_strong_refs` 計數 (外部)**

        :::success
        [**Binder Kernel 結構介紹**](https://hackmd.io/TtT34BAwRP2sZ3vALt4Drg)
        :::

        ```c=
        // binder.c

        static bool binder_dec_node_nilocked(struct binder_node *node, // BinderServer
                             int strong,     // 減少強還是弱指針
                             int internal)    // 是否是內部 (是否需要 BinderServer 操作計數
        {
            struct binder_proc *proc = node->proc;

            ...

            if (strong) {    // 強計數操作
                if (internal)
                    node->internal_strong_refs--;    // 目標 Service 減少計數
                else
                    node->local_strong_refs--;        // BinderDriver 內部減少強計數
                
                
                // 仍有值，直接返回，結束操作
                if (node->local_strong_refs || node->internal_strong_refs)
                    return false;

            } else {
                if (!internal)
                    node->local_weak_refs--;        // BinderDriver 內部減少弱計數

                // 仍有值，直接返回，結束操作
                if (node->local_weak_refs || node->tmp_refs ||
                        !hlist_empty(&node->refs))
                    return false;
            }

            
            // 接下來代表 強計數 or 弱計數其中一個為 0
            // proc 存在
            // 並且判斷是否有強 or 弱引用
            if (proc && (node->has_strong_ref || node->has_weak_ref)) {

                // 判斷是否增加一個 `BINDER_WORK_NODE` 到目標進程
                if (list_empty(&node->work.entry)) {
                    // 添加 todo 任務給 BinderService
                    binder_enqueue_work_ilocked(&node->work, &proc->todo);
                    
                    // 喚醒目標 proc 進行處理任務 !
                    binder_wakeup_proc_ilocked(proc);
                }

            } else {
                // 準備 release 物件
                if (hlist_empty(&node->refs) && !node->local_strong_refs &&
                    !node->local_weak_refs && !node->tmp_refs) {
                    
                    // 判斷 node 的 proc 對象
                    if (proc) {    // 還在 BinderService 的紅黑樹中
                        binder_dequeue_work_ilocked(&node->work);
                        
                        // 從紅黑樹中移除
                        rb_erase(&node->rb_node, &proc->nodes);
                        ... deug

                    } else {    // 存在死亡 Binder 清單內
                        ... deug
                            
                        spin_lock(&binder_dead_nodes_lock);
                        /*
                         * tmp_refs could have changed so
                         * check it again
                         */
                        if (node->tmp_refs) {
                            spin_unlock(&binder_dead_nodes_lock);
                            return false;
                        }
                        
                        // 刪除 node
                        hlist_del(&node->dead_node);
                        spin_unlock(&binder_dead_nodes_lock);
                        ... deug
                    }
                    return true;
                }
            }
            return false;
        }
        ```

### Binder node & BBinder 關係

* **設計為 1 個 `binder_node` 對應多個 `binder_ref`；這樣好的好處是 BinderDriver 不會多次與 BinderServer 通訊**
    > 也就是不會 BinderDriver 不會多次發出 `BR_INCREFS`、`BR_ACQUIRE`... 等等命令要求 BinderServer 修改計數

    > ![](https://i.imgur.com/ZFxQ7EK.png)


## Binder ref 計數

* Binder ref (參考物件) 就是 **binder_ref**：
    1. `binder_ref` 在 Kernel space 被創建
    
    2. 被 User space 空間中的` Binder 代理物件` (BpBinder) 所參考 

* BinderClient & BinderDriver 通訊順序圖如下 (注意 Space)

    > ![](https://i.imgur.com/0GKTLw1.png)


### Binder ref - 計數入口

:::info
目前以 Client 的角度來對 BinderDriver 傳送命令
:::

* 主要是透過 `BC_INCREFS`、`BC_ACQUIRE`、`BC_RELEASE`、`BC_DECREFS` 這四個指令來控制 Binder 參考物件的生命週期
    
    1. 使用者 Client 使用一個 **控制碼** 傳送到 BinderDriver 空間，找到對應的 binder_ref

        :::info
        * **控制碼** (句柄)

            這個控制碼是 BinderDriver 為了 BinderClient 創建的，可以對應到一個 `binder_ref`；**其中 ==最特別的是控制碼 0，它是 ServierManager 的控制碼==**
        :::
    
    ```c=
    // binder.c

    static int binder_thread_read(
                      struct binder_proc *proc,
                      struct binder_thread *thread,
                      binder_uintptr_t binder_buffer, // IPCThreadState 的 mIn 數據空間
                      size_t size,
                      binder_size_t *consumed, 
                      int non_block)
    {
        // 使用者空間的指針 
        void __user *buffer = (void __user *)(uintptr_t)binder_buffer;

        // 取得開頭結尾指針
        void __user *ptr = buffer + *consumed;    // 數據的起點
        void __user *end = buffer + size;        // 數據的終點

        int ret = 0;
        int wait_for_proc_work;
	    
        struct binder_context *context = proc->context;

        ...

        while (1) {
            uint32_t cmd;
            // 創建 binder_transaction_data_secctx
            struct binder_transaction_data_secctx tr;

            // 取 tr#transaction_data
            struct binder_transaction_data *trd = &tr.transaction_data;

            // 當前需要執行的 binder_work
            struct binder_work *w = NULL;
            // 任務 header
            struct list_head *list = NULL;

            // 要對外傳輸的訊息
            struct binder_transaction *t = NULL;
            struct binder_thread *t_from;
            size_t trsize = sizeof(*trd);

            ...

            // 判斷 binder_work#type
            switch (cmd) {
            case BC_INCREFS:
            case BC_ACQUIRE:
            case BC_RELEASE:
            case BC_DECREFS: {
                
                uint32_t target;
                const char *debug_string;
                bool strong = cmd == BC_ACQUIRE || cmd == BC_RELEASE;
                bool increment = cmd == BC_INCREFS || cmd == BC_ACQUIRE;
                struct binder_ref_data rdata;

                // 將控制碼儲存到 target 變數中
                if (get_user(target, (uint32_t __user *)ptr))
                    return -EFAULT;

                ptr += sizeof(uint32_t);
                ret = -1;
                
                // 判斷控制碼是否是 0 (是否是 ServiceManager)
                if (increment && !target) {
                    struct binder_node *ctx_mgr_node;

                    // 鎖住當前 proc 中的 context
                    mutex_lock(&context->context_mgr_node_lock);
                    
                    ctx_mgr_node = context->binder_context_mgr_node;
                    
                    if (ctx_mgr_node) {
                        
                        // 不可以有控制碼為 0 的 proc (0 只能設定一次)
                        if (ctx_mgr_node->proc == proc) {
                            binder_user_error("%d:%d context manager tried to acquire desc 0\n",
                                      proc->pid, thread->pid);
                            mutex_unlock(&context->context_mgr_node_lock);
                            return -EINVAL;
                        }
                        
                        // 增加引用
                        // @ 分析 binder_inc_ref_for_node
                        ret = binder_inc_ref_for_node(
                                proc, ctx_mgr_node,
                                strong, NULL, &rdata);
                    }
                    mutex_unlock(&context->context_mgr_node_lock);
                }
                
                if (ret)
                    ret = binder_update_ref_for_handle(
                            proc, target, increment, strong,
                            &rdata);
                
                // 無法取得控制碼對應的 binder node
                if (!ret && rdata.desc != target) {
                    binder_user_error("%d:%d tried to acquire reference to desc %d, got %d instead\n",
                        proc->pid, thread->pid,
                        target, rdata.desc);
                }
                
                switch (cmd) {
                case BC_INCREFS:
                    debug_string = "IncRefs";
                    break;
                case BC_ACQUIRE:
                    debug_string = "Acquire";
                    break;
                case BC_RELEASE:
                    debug_string = "Release";
                    break;
                case BC_DECREFS:
                default:
                    debug_string = "DecRefs";
                    break;
                }
                if (ret) {
                    binder_user_error("%d:%d %s %d refcount change on invalid ref %d ret %d\n",
                        proc->pid, thread->pid, debug_string,
                        strong, target, ret);
                    break;
                }
                
                ...debug 訊息
                break;
            }
                    
            ...
            }
        }
    }
    ```
* **`binder_inc_ref_for_node` 函數**：
    1. 透過 `binder_get_ref_for_node_olocked` 取得 (創建) 相對應的 `binder_ref`

        > 在這裡就是取得 ServiceManger 的 `binder_node`

    2. 透過 `binder_inc_ref_olocked` 增加計數

        ```c=
        // binder.c

        static int binder_inc_ref_for_node(struct binder_proc *proc,
                    struct binder_node *node,
                    bool strong,
                    struct list_head *target_list,
                    struct binder_ref_data *rdata)
        {
            struct binder_ref *ref;
            struct binder_ref *new_ref = NULL;
            int ret = 0;

            // 鎖住 proc 對象
            binder_proc_lock(proc);

            // 取得 node 對應的引用
            // @ 查看 binder_get_ref_for_node_olocked
            ref = binder_get_ref_for_node_olocked(proc, node, NULL);
            if (!ref) {
                binder_proc_unlock(proc);

                // 創建新 node (`binder_ref`) 空間
                new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
                if (!new_ref)
                    return -ENOMEM;
                
                binder_proc_lock(proc);
                // 再次取得 binder_ref
                ref = binder_get_ref_for_node_olocked(proc, node, new_ref);
            }
            // @ 查看 binder_inc_ref_olocked
            // return 0 代表成功
            ret = binder_inc_ref_olocked(ref, strong, target_list);
            
            *rdata = ref->data;
            if (ret && ref == new_ref) {
                // 清除失敗的引用
                binder_cleanup_ref_olocked(new_ref);
                ref = NULL;
            }

            binder_proc_unlock(proc);
            if (new_ref && ref != new_ref)
                // 其他 thread 已創建，那就是放已分配的 new_ref
                kfree(new_ref);
            return ret;
        }
        ```
        * **`binder_get_ref_for_node_olocked` 函數**：**取得 BinderServer 對應的 binder_ref 引用**

            ```c=
            // binder.c

            static struct binder_ref *binder_get_ref_for_node_olocked(
                                struct binder_proc *proc,
                                struct binder_node *node,
                                struct binder_ref *new_ref)
            {
                struct binder_context *context = proc->context;
                struct rb_node **p = &proc->refs_by_node.rb_node;
                struct rb_node *parent = NULL;
                struct binder_ref *ref;
                struct rb_node *n;

                // 從 proc 中取得 `refs_by_node` (BinderServer 的參考)
                while (*p) {
                    parent = *p;
                    ref = rb_entry(parent, struct binder_ref, rb_node_node);

                    if (node < ref->node)
                        p = &(*p)->rb_left;
                    else if (node > ref->node)
                        p = &(*p)->rb_right;
                    else
                        return ref;
                }
                if (!new_ref)
                    return NULL;

                // 以下創建新的 `binder_ref`
                binder_stats_created(BINDER_STAT_REF);
                new_ref->data.debug_id = atomic_inc_return(&binder_last_id);
                new_ref->proc = proc;
                new_ref->node = node;
                rb_link_node(&new_ref->rb_node_node, parent, p);
                rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);

                new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1;
                for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
                    ref = rb_entry(n, struct binder_ref, rb_node_desc);
                    if (ref->data.desc > new_ref->data.desc)
                        break;
                    new_ref->data.desc = ref->data.desc + 1;
                }

                p = &proc->refs_by_desc.rb_node;
                while (*p) {
                    parent = *p;
                    ref = rb_entry(parent, struct binder_ref, rb_node_desc);

                    if (new_ref->data.desc < ref->data.desc)
                        p = &(*p)->rb_left;
                    else if (new_ref->data.desc > ref->data.desc)
                        p = &(*p)->rb_right;
                    else
                        BUG();
                }
                rb_link_node(&new_ref->rb_node_desc, parent, p);
                rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);

                binder_node_lock(node);
                hlist_add_head(&new_ref->node_entry, &node->refs);

                ... debug

                binder_node_unlock(node);
                return new_ref;
            }
            ```

### Binder ref - 增加計數

* `binder_inc_ref_olocked` 函數：請看註解，主要是增加 `binder_ref` or BinderServer 強、弱指標計數

    ```c=
    // binder.c

    static int binder_inc_ref_olocked(struct binder_ref *ref, // Binder 參考物件
                      int strong,    // 增加強 還是 弱 計數
                      struct list_head *target_list)    // 目標 BinderServer 的 TODO List 
    {
        int ret;

        if (strong) {    // 增加強計數
            if (ref->data.strong == 0) {
                // 增加強計數，並要求 BinderServer 也增加計數 (internal_strong_refs 設為 1)
                ret = binder_inc_node(ref->node, 1, 1, target_list);
                if (ret)
                    return ret;
            }
            ref->data.strong++;    // 增加引用強計數

        } else {    // 增加弱計數
            if (ref->data.weak == 0) {
                // 增加外部弱計數
                ret = binder_inc_node(ref->node, 0, 1, target_list);
                if (ret)
                    return ret;
            }
            ref->data.weak++;    // 增加引用弱計數
        }
        return 0;
    }
    ```
    
### Binder ref & node 關係

多個 binder_ref 對一個 binder_noed

> ![](https://i.imgur.com/O3C7WvD.png)


## Binder proxy - 計數

* **Binder proxy 就是 Client 端的 ++BpBinder 實現++**：
    1. Proxy 在使用者空間建立，並執行在使用者空間
    
    2. **跟 Kernel 不同行程，所以不能直接使用指標操控 Kernel 的 Ref 物件**
    
    3. Proxy 必須透過核心取得 ref 的控制碼，**透過控制碼訪問核心的 Ref 物件**
    
    4. **Ref 物件再找到對應的 Binder node**

    
        概念流程圖如下
        
        > ![](https://i.imgur.com/kNEcEdC.png)

### Binder proxy - [BpBinder](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/BpBinder.h) 儲存控制碼

* BpBinder (也就是 Client) 會透過 Kernel 提供的控制碼與 binder_ref 連結；

* 並且 Client 物件會透過 [**ProcessState**](https://cs.android.com/android/platform/superproject/+/master:system/libhwbinder/include/hwbinder/ProcessState.h) 內的一個 `handle_entry` 型態的 Binder 代理清單來取得 BpBinder 物件；
    * `handle_entry`　使用 Key/Value 來儲存控制碼、BpBinder

        > Key：Kernel 提供的控制碼
        > Value：使用者空間的 BpBinder 物件
    
    ```cpp=
    // ProcessState.h


    class ProcessState : public virtual RefBase
    {
    public:
        ...
        
    private:
        ...
        
        
        // 串列
        struct handle_entry {
            IBinder* binder;
            RefBase::weakref_type* refs;
        };

        // 透過 lookupHandleLocked 來找到對應的 BpBinder
        handle_entry*       lookupHandleLocked(int32_t handle);
    }
    ```
    :::info
    * 快取的好處是可以減少 BpBinder 的創建
    :::
    
### Binder proxy - 增加 ref 弱計數

:::success
增加計數的時機在 **BpBinder 建構函數**
:::


* 接著看 [**BpBinder**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/BpBinder.cpp) 的實現：從建構函數中可以看到，**++BpBinder (也就是 Proxy) 的生命週期改為使用弱計數控制++**

    ```cpp=
    // BpBinder.cpp

    BpBinder::BpBinder(Handle&& handle)
          : mStability(0),
            mHandle(handle),
            mAlive(true),
            mObitsSent(false),
            mObituaries(nullptr),
            mTrackedUid(-1) {

        // 由弱計數控制生命週期
        extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    }

    BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : 
            // 呼叫自身的建構函數
            BpBinder(Handle(handle)) {
        ...

        mTrackedUid = trackedUid;

        ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle());

        // 準備添加弱計數
        // @ 追蹤 incWeakHandle 函數
        IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);
    }

    ```
    
    * [**IPCThreadState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp)#`incWeakHandle` 函數：不會立刻新增弱引用次數，會 **等待 ++下次 `BINDER_WRITE_READ` 命令時++，再請求 Binder Kernel 對對應的 binder_ref 添加弱參考計數**

        ```cpp=
        // IPCThreadState.cpp

        void IPCThreadState::incWeakHandle(int32_t handle, BpBinder *proxy)
        {
            ...
            
            // 增加弱引用計數
            mOut.writeInt32(BC_INCREFS);
            // 指定 handle 找到對應的 ref
            mOut.writeInt32(handle);
            ....
        }
        ```

### Binder proxy - 減少 ref 弱計數

:::success
增加計數的時機在 **BpBinder 解構函數**
:::


* 接著看 [**BpBinder**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/BpBinder.cpp) 的解構：

    ```cpp=
    // BpBinder.cpp

    BpBinder::~BpBinder() {
        ...

        IPCThreadState* ipc = IPCThreadState::self();

        ...

        if (ipc) {
            ...
            // 減少弱計數
            ipc->decWeakHandle(binderHandle());
        }
    }
    ```
    * [**IPCThreadState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp)#`decWeakHandle` 函數：不會立刻減少弱引用次數，會 **等待 ++下次 `BINDER_WRITE_READ` 命令時++，再請求 Binder Kernel 對對應的 binder_ref 減少弱參考計數**

        ```cpp=
        // IPCThreadState.cpp

        void IPCThreadState::decWeakHandle(int32_t handle)
        {
            ...

            // 減少弱引用計數
            mOut.writeInt32(BC_DECREFS);
            // 指定 handle 找到對應的 ref
            mOut.writeInt32(handle);
            ...
        }
        ```

### Binder proxy - 增加強記數

* 由於 RefBase 的設計，^1.^ Binder proxy 被其他強引用參考時，就會 ^2.^ 增加 proxy 強引用計數，並 ^3.^ 呼叫 `onFirstRef` 函數；現在就來看看 BpBinder#`onFirstRef` 函數

    ```cpp=
    // BpBinder.cpp

    void BpBinder::onFirstRef() {
        ...

        IPCThreadState* ipc = IPCThreadState::self();
        // 準備增加 ref 的強引用計數
        if (ipc) ipc->incStrongHandle(binderHandle(), this);
    }
    ```
    * [**IPCThreadState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp)#`incStrongHandle` 函數：不會立刻新增強引用次數，會 **等待 ++下次 `BINDER_WRITE_READ` 命令時++，再請求 Binder Kernel 對對應的 binder_ref 新增強參考計數**

        ```cpp=
        // IPCThreadState.cpp

        void IPCThreadState::incStrongHandle(int32_t handle, BpBinder *proxy)
        {
            ...

            // 增加強引用計數
            mOut.writeInt32(BC_ACQUIRE);
            // 指定 handle 找到對應的 ref
            mOut.writeInt32(handle);
            ...
        }
        ```


### Binder proxy - 減少強記數

* 由於 RefBase 的設計，^1.^ Binder proxy 不再被強引用參考時，就會 ^2.^ 減少 proxy 強引用計數，並 ^3.^ 呼叫 `onLastStrongRef` 函數；現在就來看看 BpBinder#`onLastStrongRef` 函數

    ```cpp=
    // BpBinder.cpp

    void BpBinder::onLastStrongRef(const void* /*id*/) {
        ...

        IPCThreadState* ipc = IPCThreadState::self();
        
        // 準備減少對應的 ref 強記數
        if (ipc) ipc->decStrongHandle(binderHandle());

        ...
    }

    ```
    * [**IPCThreadState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp)#`decStrongHandle` 函數：不會立刻減少強引用次數，會 **等待 ++下次 `BINDER_WRITE_READ` 命令時++，再請求 Binder Kernel 對對應的 binder_ref 減少強參考計數**

        ```cpp=
        // IPCThreadState.cpp

        void IPCThreadState::decStrongHandle(int32_t handle)
        {
            ...

            // 減少強引用計數
            mOut.writeInt32(BC_RELEASE);
            // 指定 handle 找到對應的 ref
            mOut.writeInt32(handle);
            ...
        }
        ```
        
### Binder proxy & ref 關係

一個 Binder proxy 只有在創建時才會增加 Binder ref 的弱計數，相對減少也是在銷毀時才會減少計數

> ![](https://i.imgur.com/5LARWxo.png)


## Appendix & FAQ

:::info
:::

###### tags: `Binder`
