---
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 引用**
> 
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 修改計數
> 
## Binder ref 計數
* Binder ref (參考物件) 就是 **binder_ref**:
1. `binder_ref` 在 Kernel space 被創建
2. 被 User space 空間中的` Binder 代理物件` (BpBinder) 所參考
* BinderClient & BinderDriver 通訊順序圖如下 (注意 Space)
> 
### 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
> 
## Binder proxy - 計數
* **Binder proxy 就是 Client 端的 ++BpBinder 實現++**:
1. Proxy 在使用者空間建立,並執行在使用者空間
2. **跟 Kernel 不同行程,所以不能直接使用指標操控 Kernel 的 Ref 物件**
3. Proxy 必須透過核心取得 ref 的控制碼,**透過控制碼訪問核心的 Ref 物件**
4. **Ref 物件再找到對應的 Binder node**
概念流程圖如下
> 
### 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 的弱計數,相對減少也是在銷毀時才會減少計數
> 
## Appendix & FAQ
:::info
:::
###### tags: `Binder`