---
title: 'Binder - Native Binder 原理'
disqus: kyleAlien
---
Binder - Native Binder 原理
===
## OverView of Content
Android 的基礎 IPC 機制請看 [**Android IPC 進程通訊**](https://hackmd.io/dJ_kZLhKQqOWawEphX2Vdg?view),這裡就不特別介紹基礎使用
Android IPC 機制的特色是 Binder,而 Binder 系統相當龐大,有分為 Kernel 層、Native 層、Java 層 這三個面向
這裡著重介紹分析 Native 層… BinderNative 基本上就是從 User Space 角度看 Binder IPC
```shell!
APP / Framework
│
│ Java Binder API
▼
Binder Framework (Java)
│
│ JNI
▼
Binder Native (C++) // 目前的位置
│
│ ioctl()
▼
Binder Driver (Kernel)
```
[TOC]
## Linux & Binder IPC 通訊原理
基礎的 Linux IPC 通訊機制請參考 [**IPC 通訊機制**](https://hackmd.io/jh6jLfzWQZarUjsQOEQZpw)
### Linux 內核空間
1. 在 Linux 中會將空間分為 ^1.^ UserSpace 使用者空間、^2.^ KernelSpace 內核空間,這兩個空間是被隔離的,這樣的好處有
1. 安全性:保護 KernelSpace 不讓 UserSpace 直接訪問,必須透過 SystemCall 才能訪問
2. 記憶體空間:KernelSpace 有專屬的記憶體區塊,其他的空間不可隨意使用
3. 穩定性:即使其他 UserSpace 的應用崩潰也不會影響到 KernelSpace 的運作
2. KernelSpace 特色:KernalSpace 虛擬地址空間 Mapping 到所有的 Process 都相同,但進程與進程之間數據不共享,如下圖 ! (下圖都是在虛擬記憶體中,並非實體記憶體)
進程之間通訊必須透過 `Binder`、`Socket`、`Pipe`、`ShareMemory`… 等等手動
> 
3. SystemCall:系統調用是一種「**統稱**」,其中有許多的函數都稱為系統調用,**系統調用是用戶空間訪問內核空間的 ++==手段==++**… 也可以說 UserSpace 向 KernalSpace 請求服務的標準接口就稱為 SystemCall
> 這種行為保證了用戶在訪問內核空間時的一致性,所有的資源由內核親自操控、管理,保證了 **安全性、權限訪問**
常見的驅動 SystemCall 如下表
| 系統調用 | 功能 |
| - | - |
| open | 打開設備 |
| read | 讀取資料 |
| write | 寫入資料 |
| ioctl | 控制設備 |
| mmap | 記憶體映射 |
:::success
* 函數命名中的 `*_user`,何為 User?
站在 **Kernel 的角度看,User 就是各個 ++用戶空間++**,之後分析我們會常常看到兩個函數
1. `copy_from_user`:將數據從 User 拷貝到 kenel space
2. `copy_to_user`:將數據從 kenel 拷貝到 User space
:::
> 
### 傳統 Linux IPC 通訊問題
* 傳統 Linux IPC 通訊模型如下
1. 內核空間:內核會在 IPC 機制中維護一塊 kernel buffer 用於暫存資料(暫存進程之間傳遞的資料)
2. 發送數據方:透過 `copy_from_user` 將 User 資料 **複製到內核緩衝區**
3. 接收方:在自己的使用者空間開闢一塊 **使用者緩衝區**
4. 內核空間:透過 `copy_to_user` 將資料 **複製到使用者緩衝區**
> 
* 通過以上的流程可以發現 Linux IPC 機制有以下問題
1. 傳統 Linux IPC 大多只是在傳 byte stream / message,缺乏「物件引用」語意
傳統 Linux IPC 的問題:只能傳資料,不能原生表達「遠端物件」;像 `pipe` / `socket` / `message queue`,通常傳的是:bytes、packet、message… 等等
也就是說,核心只知道你在傳一段資料,但 kernel 不知道這代表一個什麼遠端服務物件… 例如
```shell
{ "cmd": "getUser", "id": 123 }
```
所以 **所以傳統 IPC 常常要自己額外處理**,像是要處理協定格式、服務定位、連線管理、錯誤恢復、生命週期管理、權限驗證
2. 每次 IPC 都會做 UserSpace 與 KernalSpace 上下文轉換,所以有較高的 ContextSwitch 開銷
### 虛擬記憶體 - 分頁快取 & 內存映射
* 如果對 **虛擬記憶體** 沒有概念的話建議先看看 [**Linux 記憶體管理**](https://hackmd.io/ptxTEwCzQBuxv61dGL_lvA?view#%E8%A8%98%E6%86%B6%E9%AB%94%E7%AE%A1%E7%90%86)、[**分頁快取**](https://hackmd.io/8qOCfHI0SA6cK3l3gDCtOg?view#%E5%88%86%E9%A0%81%E5%BF%AB%E5%8F%96),分頁快取的概念如下圖(下圖假設是行程 A 讀取 `Hello.text` 文件,這時就會先讀去到核心記憶體,再 Mapping 到行程 A 的 UserSpace 下)
> 
* Linux 系統有提供一種機制 **內存映射**(`Mapping`):它可以將指定文件(外部文件)的記憶體映射到使用者進程空間,之後操作該內存就會直接反應到記憶體中
* mmap:也就是 Memory Map,它概念是可以 **在核心分頁中分配到的地址**,會直接 **映射到進程的虛擬內存中**
* mmap 的好處是透過 **映射** (想成內核指向文件地址),使用者空間可以直接操作自身的內存區塊,就可以把資料寫入到文件中(不考慮寫入時機)
> 
* 如果沒有使用內存映射機制,使用者進程對文件的讀寫通常需要經過
**使用者空間與內核空間之間的資料複製**:
1. 使用者空間 → 內核空間
`copy_from_user` 將資料從 **UserSpace** 複製到 **Kernel Buffer / Page Cache**
2. 內核空間 → 文件系統快取
內核再將資料寫入到 **Page Cache**,之後再由內核機制決定何時同步到磁碟。
因此在傳統 I/O 模型中,資料通常需要經過 **多次記憶體複製** 才能完成讀寫
## Binder 概述
Binder 是基於 OpenBinder 實現(最早是由 PalmInc 公司開發,後來才轉進 Google)
### Binder 通訊原理
* **Binder 是 ++基於分頁快取 & 內存映射++ 來實現**,但這裡的映射並不是真實文件,而是在 `/dev` 區塊的一個內存區塊(想成虛擬文件),簡單來說內核空間會有以下操作(下圖是一個概念圖,並非真實細節)
1. 在 KernelSpace 開闢一塊,**數據接收區** (**原本由使用者空間提供,++改為核心提供空間++**)
> 
2. 在 KernelSpace 開闢一塊,**內存緩衝區** (同內存映射模型)
> 
3. **建立 ++==映射==關係++** (**映射的概念就類似於 Map**),透過**兩次映射**將資料寫到接收進程中,兩次映射如下
* 3-1. 數據接收區 & 內存緩衝區
> 
* 3-2. 內存緩衝區 & 接收進程的虛擬地址
> 
4. 發送資料方,只需要透過 `copy_from_user` 把資料 **複製到內核緩衝區**,之後就可以透過映射,將資料反應到接收方的虛擬記憶體中
> 
### Binder 優點
1. **性能**
傳統 Linux IPC 機制(如 Socket、Pipe、Message Queue 等),
在資料傳輸過程中通常需要經過 **使用者空間與內核空間之間的資料複製**:
```
UserSpace → KernelSpace → UserSpace
```
也就是說資料至少會經過 **兩次記憶體複製** 才能到達另一個進程
Binder IPC 透過 `mmap` 在進程與 Binder Driver 之間建立共享緩衝區,由內核在共享區域中完成資料管理與路由,減少不必要的資料搬移與系統呼叫開銷
> 下圖 Binder 驅動會 Handle 進程 A、B 的資料複製,透過 Binder 驅動替 A、B 進程在核心內存創建對應空間(概念是 A、B 進程共用核心內存)
>
> 
2. **穩定性**
Binder 驅動會在內核中管理進程之間的引用關係,並提供 **Death Notification 機制**,當遠端服務進程異常終止時,Binder 驅動能夠通知其他持有該 Binder 物件引用的進程,使系統可以及時清理資源或重新建立服務連線
相較於傳統 IPC 機制,開發者通常需要自行處理,連線斷開、資源回收與錯誤處理邏輯
3. **安全性**
* 傳統 IPC 機制需要在用戶層手動指定遠端進程的 ID ( e.g Socket's Port
* 傳統 IPC 機制也無法管理進程的用戶 UID/ 進程 PID,無法判斷遠端進程的安全性,而 Binder 驅動由 Android 系統管理,在系統中會判斷 UID/PID 是否符合訪問權限
當一個進程發起 Binder transaction 時,服務端可以透過 Binder API 取得呼叫者的身份資訊,以進行權限驗證
> 相較之下,許多傳統 IPC 機制(如 Socket)只提供通訊管道,本身並不會自動提供呼叫者身份資訊,**權限控制通常需要在應用層自行實現**
### Android 其他 IPC 機制
* 除了 Binder 以外,Android 仍有使用到其他 IPC 機制
1. **Socket**
Zygote 進程透過 Socket 與 ActivityManagerService 溝通,用於建立新的應用進程
2. **Signal**
Linux Signal 機制可用於通知或終止進程,例如 `SIGKILL`
3. **Shared Memory**
在某些高效能場景(如圖形或多媒體系統)中,也可能透過共享記憶體進行資料交換。
## 認識 Binder Native
:::info
* Binder library 動態連結
BinderNative 最終會編譯成一個動態連結 Library:`libbinder.so`,方便其他進程複用
> 
:::
* BinderNative 是 C/S 架構實現分為…
1. **Proxy**:對應到 Client 端,相關處理類帶有 ==`p` 字母==
2. **Native**:對應到 Server 端,相關處理類帶有 ==`n` 字母==
| 類 | 概述 | 說明 |
| ------ | - | -------- |
| BpRefBase | 自動釋放 ref | RefBase 子類,提供 `remote` 方法讓 獲取遠程 Binder |
| IInterface | Server | Binder Server 的基類 |
| BpInterface | Proxy | Proxy 提供 Client 使用的功能接口 |
| BnInterface | Binder Native | Native Server 實現的接口 |
| IBinder | Binder 基類 | BBinder、BpBinder 都繼承於 IBinder |
| BpBinder | Proxy | 提供 `transact` 方法來發送 Client 端請求,會由 Bpxxx 實現 |
| BBinder | Server | Server 實現的基類,提供 `onTransact` 方法,用來接收 Client 端訊息 |
| ProcessState | Process | 一個進程只有一個 ProcessState |
| IPCThreadState | Thread | 一個 Thread 在 C++ 層 |
| Parcel | Binder 包裹 | Binder 中傳遞數據、物件的包裹 |
### IBinder、BpBinder、BBinder 關係
關係圖如下
> 
* [**IBinder**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/Binder.h) 繼承 `RefBase` (代表了繼承 IBinder 類的子類都具有倍操作的強弱引用計數的特性),**用來描述所有在 Binder 上傳遞的物件**;
IBinder 是 BpBinder、BBinder 的父類,主要使用的方法如下
| IBinder 方法 | 說明 |
| - | - |
| localBinder | 取得本地 Binder |
| remoteBinder | 取得遠端 Binder |
| transact | 傳送 Binder 業務 |
| queryLocalInterface | 取得本地 Binder,如果失敗則返回 Null |
| getInterfaceDescriptor | 獲取 Binder Server 描述,**也代表了 binder 服務的唯一標示** |
| isBinderAlive | 檢查 Binder 是否還存活 |
| pingBinder | 對 Binder 發送 `PING_TRANSACTION` 命令 |
> `localBinder()` / `remoteBinder()` 主要用於判斷 Binder 是本地物件還是遠端代理物件
:::warning
* **`getInterfaceDescriptor` 方法**:取得 Binder 服務的 **唯一標示**:回傳的其實就是 AIDL interface name(通常是完整的 package path),像是…
| Service | Descriptor |
|-------|-----------|
| ActivityManagerService | `android.app.IActivityManager` |
| PackageManagerService | `android.content.pm.IPackageManager` |
| WindowManagerService | `android.view.IWindowManager` |
| PowerManagerService | `android.os.IPowerManager` |
| SurfaceFlinger | `android.ui.ISurfaceComposer` |
| MediaPlayerService | `android.media.IMediaPlayerService` |
```java=
IBinder binder = ServiceManager.getService("activity");
// android.app.IActivityManager
String descriptor = binder.getInterfaceDescriptor();
```
> 我至於怎麼知道有哪些服務,可以過 `adb shell service list` 查看 ServiceManager 中的所有服務
>
> 
:::
* [**BpBinder**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/BpBinder.h) 繼承 IBinder:代表 **==客戶端==** (代替客戶端訪問服務)
| IBinder 方法 | 說明 |
| - | - |
| create(int32_t handle) | create 方法會返回一個指向 Binder Server 的句柄(Handler) |
| transact | 提供 transact 的實做,封裝使用者要發送給 Binder Server 的數據 |
* [**BBinder**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/Binder.h;l=30;bpv=1;bpt=1) 繼承 IBinder:代表 **==服務端==**
| IBinder 方法 | 說明 |
| - | - |
| onTransact | **Server 用來接收 Binder Client 的數據** |
:::warning
1. Client `transact` 方法會對應到 Service `onTransact` 方法
2. Client 端會透過 `transact` 傳送一個 code 給 Server 端,這兩個 **code 必須相互對應**;AIDL 編譯器會為每個 Binder 方法生成一個 `TRANSACTION_xxx` 常數,並在 Client `transact` 與 Server `onTransact` 之間建立對應關係
也就是說 transaction code 對應到 method index
> Code 的範圍定義在 `IBinder.h`
```cpp=
// IBinder.h
public:
enum {
FIRST_CALL_TRANSACTION = 0x00000001,
LAST_CALL_TRANSACTION = 0x00ffffff,
...
}
```
:::
### [BpBinder](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/BpBinder.cpp) 與 [BBinder](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/BpBinder.h) - 理解
* BpBinder 與 BBinder 都繼承於 [**IBinder.h**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/IBinder.h) 標頭檔,兩個一一對應,一個作為客戶端角色一個作為服務端角色
1. BpBinder 代表 **客戶端**:用來 **代理客戶端 與 服務端通訊**,重點方法是 `transact`
> 
2. BBinder 代表 **服務端**:主要使用 `onTransact` 方法處理 Client 發送過來的資訊
> 
* BpBinder 可以通過 **handle (資源標識符)** 找到 BBinder 相對應的服務:詳細來說,**handle 這個整數是控制碼 (des)**,**用來找到 Binder 驅動中的 `binder_ref`**
找到 `binder_ref` 後就可以間接找到對應的 BinderService
```cpp=
// BpBinder.h
class BpBinder : public IBinder
{
public:
...
virtual status_t transact(uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0) final;
private:
static sp<BpBinder> create(int32_t handle);
struct BinderHandle {
int32_t handle;
};
// 忽略 PRC
struct RpcHandle {
sp<RpcSession> session;
uint64_t address;
};
using Handle = std::variant<BinderHandle, RpcHandle>;
Handle mHandle;
}
```
> 
### [IInterface](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/IInterface.h) - 共同接口
* 在上面我們知道 Client、Server 端的方法必須相互對應,所以最好是定義一個共用介面接口,**在 Binder 中這個共用接口的 ++基類就是 IInterface++**
* [**IInterface**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/IInterface.h) 是 Client、Server 接口 (使用者設定的接口) 的基類
```cpp=
// IInterface.h
class IInterface : public virtual RefBase
{
public:
IInterface();
static sp<IBinder> asBinder(const IInterface*);
static sp<IBinder> asBinder(const sp<IInterface>&);
protected:
virtual ~IInterface();
virtual IBinder* onAsBinder() = 0;
};
```
**`onAsBinder` 方法**:會根據實現類不同代表不同意義,如果是 ^1.^ Native 實現則返回本地物件,如果是 ^2.^ Proxy 則返回 Proxy 物件
:::info
客戶端使用時就可以透過 `asBinder` 直接獲取 Binder 物件
:::
* **BnInterface、BpInterface**:兩者都是一個模板類,這個模板(`INTERFACE`) 就是 Binder 服務的基類(使用者定義的共同方法)
> 
1. BnInterface 類:BnInterface **又繼承於 BBinder,目的是為了 ++讓 Binder Server 複寫 `onTransact` 方法++**,讓 Server 處理 Client 傳來的訊息
```cpp=
// IInterface.h
template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
virtual const String16& getInterfaceDescriptor() const;
typedef INTERFACE BaseInterface;
protected:
virtual IBinder* onAsBinder();
};
// ----------------------------------------------------------
// Binder.h
class BBinder : public IBinder
{
public:
BBinder();
...
}
// ----------------------------------------------------------
// Binder.h
class Binder : public virtual RefBase
{
...
}
```
2. BpInterface 類:BpInterface 又**繼承於 [BpRefBase](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/Binder.h),目的是為了 ++讓 Client 端透過 `remote` 方法取得 Server 的句柄++**
:::success
* `remote` 成員指向一個 BpBinder 物件
:::
```cpp=
// IInterface.h
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
explicit BpInterface(const sp<IBinder>& remote);
typedef INTERFACE BaseInterface;
protected:
virtual IBinder* onAsBinder();
};
// -------------------------------------------------------------------
// Binder.h
class BpRefBase : public virtual RefBase
{
protected:
explicit BpRefBase(const sp<IBinder>& o);
virtual ~BpRefBase();
...
// 取得遠端 Server 句柄
inline IBinder* remote() const { return mRemote; }
inline sp<IBinder> remoteStrong() const { return sp<IBinder>::fromExisting(mRemote); }
private:
BpRefBase(const BpRefBase& o);
BpRefBase& operator=(const BpRefBase& o);
IBinder* const mRemote;
RefBase::weakref_type* mRefs;
std::atomic<int32_t> mState;
};
```
:::info
* **`BpInterface`、`BnInterface` 兩者的父類都是 RefBase,因此實作這兩個類的 Service、Client 都具有智能被計算引用的功能,讓引用計數可被操作**
:::
## Client 取得訪問 ServiceManager 代理
### MediaServer - Binder 服務
* 由於該篇章是介紹 Native Binder 機制,這裡選擇 MediaServer 服務來介紹,他會直接從 Native 層來向 ServiceManager 註冊
* [**MediaServer**](https://android.googlesource.com/platform/frameworks/av/+/master/media/mediaserver/main_mediaserver.cpp)#main 函數,在這個函數中我們可以看到
1. ProcessState 初始化
* 開啟 `dev/binder` 目錄下的 Binder 設備,**取得 FD**
* 使用 mmap 為 MediaServer 進程在 Binder 驅動分配一個虛擬空間,來接收數據
2. 取得 ServiceManager 實例
* 取得 IServiceManager 物件,並用它與 ServiceManager 戶交
3. MediaServer 向 ServiceManager 註冊服務
* 用個關鍵字向 ServiceManager 註冊服務
```cpp=
// main_mediaserver.cpp
using namespace android;
int main(int argc __unused, char **argv __unused)
{
signal(SIGPIPE, SIG_IGN);
// 1. ProcessState 初始化
sp<ProcessState> proc(ProcessState::self()); // @ ProcessState::self()
// 2. 取得 ServiceManager 實例
sp<IServiceManager> sm(defaultServiceManager()); // defaultServiceManager()
ALOGI("ServiceManager: %p", sm.get());
// 3. MediaServer 向 ServiceManager 註冊服務
MediaPlayerService::instantiate();
ResourceManagerService::instantiate();
registerExtensions();
::android::hardware::configureRpcThreadpool(16, false);
// 啟動 Binder Thread Pool
ProcessState::self()->startThreadPool();
// 將當前 Thread 加入 Binder Thread Pool
IPCThreadState::self()->joinThreadPool();
::android::hardware::joinRpcThreadpool();
}
```
### [ProcessState](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/ProcessState.cpp) - 進程單例
* **ProcessState#self()** 函數是取得 ProcessState 的 **進程單例** 物件,同時是一個進程在 Native 的代表,**==它負責開啟 `/dev/binder` 裝置==,將裝置映射到該進程中**
```cpp=
// main_mediaserver.cpp
using namespace android;
int main(int argc __unused, char **argv __unused)
{
signal(SIGPIPE, SIG_IGN);
// 1. ProcessState 初始化
// @ ProcessState::self()
sp<ProcessState> proc(ProcessState::self());
... 省略
}
```
* ProcessState 有兩個較大重點是,^1.^ 開啟 `/dev/binder` 並設定 `最大 Binder thread 數量`、^2.^ 通過 `mmap` 為 binder 在該進程中分配一塊虛擬地址空間,達到內存映射的目的
> **ProcessState 是進程單例**,每個行程的 ProcessState 所開啟的 **==mmap 大小為 1M==**
* 保證同**一個進程內 ++只有一個 ProcessState 的實例++ 存在**:^1.^ 使用 [**智能指針**](https://hackmd.io/nQWJhCJcQ6K-99dKIT_nmQ?view)、^2.^ 並 [**加鎖**](https://hackmd.io/jh6jLfzWQZarUjsQOEQZpw?view#%E9%80%B2%E7%A8%8B%E9%80%9A%E8%A8%8A---%E9%8E%96)(上面這兩種是不同的功能唷,可以點連結去看文章)^3.^ 使用 [**make**](https://hackmd.io/nQWJhCJcQ6K-99dKIT_nmQ?view#sp---make-%E5%87%BD%E6%95%B8) 創建 ProcessState 物件
```cpp=
// /libs/binder/ProcessState.cpp
[[clang::no_destroy]] static sp<ProcessState> gProcess;
sp<ProcessState> ProcessState::self()
{
return init(kDefaultDriver, false /*requireDefault*/);
}
sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
{
// 1. 使用智能指針
[[clang::no_destroy]] static sp<ProcessState> gProcess;
// 2. 加鎖
[[clang::no_destroy]] static std::mutex gProcessMutex;
if (driver == nullptr) {
// 一般不會進來
std::lock_guard<std::mutex> l(gProcessMutex);
return gProcess;
}
// 單例
[[clang::no_destroy]] static std::once_flag gProcessOnce;
std::call_once(gProcessOnce, [&](){
if (access(driver, R_OK) == -1) {
ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
driver = "/dev/binder"; // binder 驅動
}
std::lock_guard<std::mutex> l(gProcessMutex);
// 3. 創建 ProcessState 物件
gProcess = sp<ProcessState>::make(driver);
});
...
return gProcess;
}
```
### ProcessState - open 開啟 Binder
* 保證只有 **ProcessState 物件創建時,才開啟 Binder 驅動(open) & 內存映射(mmap)** 應對了 **++一個進程只會做一次 Binder 內存映射++**
* **執行 `open_driver` 函數**:透過 `open` 開啟 Binder 驅動後,`/dev/binder` 就映射到內核分頁中,返回檔案描述 (FD) 並使用 `mDriverFD` 存起來
:::info
* open 函數 概述
1. 映射為驅動的 `binder_open` 函數
2. 在應用呼叫 open 開啟 Binder 驅動後,就會在內核創建一個 `binder_proc` 結構,並初始化
3. **這樣 Kernel 就擁有該應用進程的訊息**
:::
* 執行 [**`mmap` 函數**](https://hackmd.io/Rkz2NOVoRdWu5v6Nkb4cxQ#%E9%80%B2%E7%A8%8B%E9%80%9A%E8%A8%8A---mmap-%E5%87%BD%E6%95%B8):將核心開闢一塊空間,將分頁的 Binder 記憶體映射到進程中
```cpp=
// /libs/binder/ProcessState.cpp
// 接近 1M 的空間
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
// binder 支持的最大 Thread 數量設定為 15
#define DEFAULT_MAX_BINDER_THREADS 15
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver)) // 注意 open_driver 函數
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mWaitingForThreads(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS) // MAX Thread 數量
, mStarvationStartTimeMs(0)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
, mCallRestriction(CallRestriction::NONE)
{
// 成功開啟 /dev/binder
if (mDriverFD >= 0) {
// 開始執行 mmap 函數 (執行 Kernal 層)
// sm 也是服務端
mVMStart = mmap(
nullptr,
BINDER_VM_SIZE, // 文件映射內存大小,接近 1M 的空間
PROT_READ,
MAP_PRIVATE | MAP_NORESERVE,
mDriverFD, // 指定該 Binder 的 offect
0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
}
static int open_driver(const char *driver)
{
// 開啟 binder 驅動
// fd 就是 offect
int fd = open(driver, O_RDWR | O_CLOEXEC);
... 省略錯誤處理
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; // 15 個為上限
// 寫入設定 binder thread 的上限
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
... 省略部分
return fd;
}
```
:::success
* `Intel x86 CPU` 提供了 **0 ~ 3 共 4 個特權權限**,**特權數字越小,權限越高** (0 最高、3 最小),而 Linux 將該機制規劃為
1. Kernel 權限至為 0 最高
2. User 權限至為 3 最低
這樣進程在 User 模式下,**不經由系統調用 (system call) 就無法主動訪問 Kernel 空間**,這也就起到了保護作用
:::
### 取得 IServiceManager - 創建 BpBinder 代理
:::info
BpBinder 可以記憶為與 Binder 的代理(Proxy),而 **現在的步驟就是取得與 ServiceManager 通訊的代理物件**
\-----------------------------------------------------------------------
ServiceManager 是一個特別的服務,**它的控制碼 (handle) 一直是 0,因此它也省去了去 BinderDriver 中找 binder_ref 的流程**
:::
* **defaultServiceManager 函數**:MediaServer 中有呼叫這個函數,並透過該函數取得 IServiceManager 實例,現在就來分析這個函數
```cpp=
// main_mediaserver.cpp
#include <binder/IServiceManager.h>
using namespace android;
int main(int argc __unused, char **argv __unused)
{
...
// 2. 取得 ServiceManager 實例
// @ 分析 defaultServiceManager() 函數
sp<IServiceManager> sm(defaultServiceManager());
}
```
* defaultServiceManager 定義在 [**IServiceManager.h**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/IServiceManager.h) 中,實現在 [**IServiceManager.cpp**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/IServiceManager.cpp),在這裡會透過 **==`interface_cast` 函數== 取得 IServiceManager ++代理物件++ 實例**,接著繼續分析
:::success
* 這裡使用全局變量 `gDefaultServiceManager` 保證 **整個進程只有一個 ServiceManager 代理物件**
:::
```cpp=
// IServiceManager.h
sp<IServiceManager> defaultServiceManager();
// 全局變量
[[clang::no_destroy]] static std::once_flag gSmOnce;
[[clang::no_destroy]] static sp<IServiceManager> gDefaultServiceManager;
sp<IServiceManager> defaultServiceManager()
{
std::call_once(gSmOnce, []() {
sp<AidlServiceManager> sm = nullptr;
// @ 分析 getContextObject 方法
// @ 分析 interface_cast
while (sm == nullptr) {
sm = interface_cast<AidlServiceManager>(
// 返回的是 IBinder 物件
// ProcessState::self() 是取得進程單例
ProcessState::self()->getContextObject(nullptr)
);
if (sm == nullptr) {
ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());
sleep(1);
}
}
gDefaultServiceManager = sp<ServiceManagerShim>::make(sm);
});
return gDefaultServiceManager;
}
```
1. [**ProcessState**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/ProcessState.cpp)#getContextObject 方法呼叫 `getStrongProxyForHandle` 並傳入 0(這個 0 其實就是取得 ServiceManager 的標示),分析 `getStrongProxyForHandle` 方法
```cpp=
// ProcessState.cpp
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
// @ 分析 getStrongProxyForHandle
sp<IBinder> context = getStrongProxyForHandle(0);
...
return context;
}
```
1. 查看是否有 ServiceManager 緩存,如果有的話直接返回
2. 透過 `lookupHandleLocked` 方法,搜尋 handle 為 0 的節點,**如果不存在該節點,則會創建節點後返回**
> 節點為 `handle_entry` 結構
:::info
* 每個進程會有一個 `mHandleToObject` 清單,用來儲存 **Key:控制碼、Value:代理物件** 的列表
```cpp=
class ProcessState : public virtual RefBase
{
private:
struct handle_entry {
IBinder* binder; // 指向代理物件
RefBase::weakref_type* refs; // 指向代理物件的計數
};
Vector<handle_entry>mHandleToObject;
}
```
:::
3. 取得 handle_entry 結構內的 IBinder,並且以下有 **兩個狀況會創建新的 IBinder 物件**
* 直接沒有 IBinder 成員
* 無法對該 IBinder 成員增加弱引用次數
> 代表該代理物件已經死亡
:::success
目前要創建的 IBinder 物件是 [**BpBinder**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/BpBinder.h),用來訪問 ServiceManager 進程
:::
```cpp=
// ProcessState.cpp
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
// 1. 緩存 ServiceManager 物件
if (handle == 0 && the_context_object != nullptr) return the_context_object;
// 2. 搜尋 handle 為 0 的節點,**如果不存在該節點,則會創建節點後返回**
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
// 取得節點的內容 IBinder
IBinder* b = e->binder;
// 創建 BpBinder 的條件 兩條件
// a. 找不到對應的 BpBinder(e->binder) 指針
// b. 無法對這個 BpBinder(e->binder) 增加 weakreference
if (b == nullptr || !e->refs->attemptIncWeak(this)) {
if (handle == 0) {
IPCThreadState* ipc = IPCThreadState::self();
CallRestriction originalCallRestriction = ipc->getCallRestriction();
ipc->setCallRestriction(CallRestriction::NONE);
Parcel data;
status_t status = ipc->transact(
0, IBinder::PING_TRANSACTION, data, nullptr, 0);
ipc->setCallRestriction(originalCallRestriction);
if (status == DEAD_OBJECT)
return nullptr;
}
// 創建 BpBinder 物件,handle 為 0,作為指向 ServiceManager 的句柄
sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
e->binder = b.get();
if (b) e->refs = b->getWeakRefs();
result = b;
} else { // binder 成員不為 null
result.force_set(b);
// 取得後減少弱引用次數 (因為前面有測試增加弱引用計數,返回前就需減掉)
e->refs->decWeak(this);
}
}
return result;
}
// 這裡保存了這個進程已建立的 Binder 相關訊息
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
// mHandleToObject 類型 Vector<handle_entry>;
const size_t N=mHandleToObject.size();
if (N <= (size_t)handle) {
handle_entry e;
e.binder = nullptr;
e.refs = nullptr;
status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
if (err < NO_ERROR) return nullptr;
}
// 若是 Vector 表中沒有相應節點,則會自動添加一個
return &mHandleToObject.editItemAt(handle);
}
```
流程圖如下
> 
2. interface_cast 方法:另外一小節分析
### 創建 [IServiceManager](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/IServiceManager.h) - BpServiceManager 物件:從 IBinder 到 Binder 介面代理
:::info
IServiceManager 功能:主要放有 ServiceManager 的所有函數,對應到 Java 層也會有相同的方法,像是 `getService`、`checkService`、`addService`... 等等
:::
* 在上面我們知道,如果訪問的是一個遠端進程 ProcessState#getContextObject 返回的是一個 IBinder(**BpBinder**, `sp<IBinder>`)物件,而 IBinder 物件如何返回一個 IServiceManager 呢?
這就需要透過 **`interface_cast` 函數**,但這裡要注意,這個 `interface_cast` 並不是 C++ RTTI cast 類型轉換,而是呼叫 Binder 介面的規則
```cpp=
// IServiceManager.cpp
using AidlServiceManager = android::os::IServiceManager;
// cache
sp<IServiceManager> defaultServiceManager();
[[clang::no_destroy]] static sp<IServiceManager> gDefaultServiceManager;
sp<IServiceManager> defaultServiceManager()
{
std::call_once(gSmOnce, []() {
sp<AidlServiceManager> sm = nullptr;
while (sm == nullptr) {
// @ 分析 interface_cast 方法
// AidlServiceManager 就是 ++IServiceManager++
sm = interface_cast<AidlServiceManager>(
// 返回的是 IBinder 物件
ProcessState::self()->getContextObject(nullptr)
);
...
}
gDefaultServiceManager = sp<ServiceManagerShim>::make(sm);
});
return gDefaultServiceManager;
}
```
> 
* [**IInterface**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/IInterface.h)#**interface_cast** 方法使用了 **C++ 的 ==模板設計==**,以下我們以 `IServiceManager` 嘗試轉換成模板的概念
```cpp=
// IInterface.h
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
// 傳入 AidlServiceManager
// using AidlServiceManager = android::os::IServiceManager;
return INTERFACE::asInterface(obj);
}
// -------------------------------------------------------
// 目前傳入 INTERFACE => IServiceManager 標示
// 以下我們手動轉換
inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj)
{
return IServiceManager::asInterface(obj);
}
```
* INTERFACE::asInterface 是一個宏方法宣告 & 宏定義
:::danger
* 最新版本有提示 Binder interface 如果 **使用 ++宏定義常常會導致錯誤++**,**最好的方法是定義一個 `.aidl` 文件**,讓其自動生成
> 
但這並不影響我們研究,所以這裡我們仍看看宏定義
:::
1. **`DECLARE_META_INTERFACE` 宏**:透過宏(define)定義在 `IInterface.h` 中
```cpp=
// IInterface.h
#define DECLARE_META_INTERFACE(INTERFACE) \
public: \
// 該介面的唯一描述符號,之後會定義為傳入的 "INTERFACE"
static const ::android::String16 descriptor; \
// 宣告 asInterface 方法
static ::android::sp<I##INTERFACE> asInterface(const ::android::sp<::android::IBinder>& obj); \
// 宣告 getInterfaceDescriptor 方法
virtual const ::android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE(); \
static bool setDefaultImpl(::android::sp<I##INTERFACE> impl); \
static const ::android::sp<I##INTERFACE>& getDefaultImpl(); \
\
private: \
static ::android::sp<I##INTERFACE> default_impl; \
\
```
* 透過 **DECLARE_META_INTERFACE** 可以翻譯成如下
| 標誌 | 傳入 & 轉換 |
| -------- | -------- |
| INTERFACE | IServiceManager |
```cpp=
// IInterface.h
public:
static const ::android::String16 descriptor;
static ::android::sp<IServiceManager> asInterface(const ::android::sp<::android::IBinder>& obj);
virtual const ::android::String16& getInterfaceDescriptor() const;
IServiceManager();
virtual ~IServiceManager();
static bool setDefaultImpl(::android::sp<IServiceManager> impl);
static const ::android::sp<IServiceManager>& getDefaultImpl();
private:
static ::android::sp<IServiceManager> default_impl;
```
2. **`IMPLEMENT_META_INTERFACE` 宏**:這裡就有 **提示不要用宏定義 binder interface**
:::info
* `IMPLEMENT_META_INTERFACE` 與 `DECLARE_META_INTERFACE` 宏對應,**它是 `DECLARE_META_INTERFACE` 的實現**
:::
```cpp=
// IInterface.h
// 查看 DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE 宏
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)
// 查看 DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0 宏
#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
const ::android::StaticString16 I##INTERFACE##_descriptor_static_str16( \
__IINTF_CONCAT(u, NAME)); \
const ::android::String16 I##INTERFACE::descriptor(I##INTERFACE##_descriptor_static_str16); \
DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(I##INTERFACE, I##INTERFACE, Bp##INTERFACE)
// 宏定義
#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(ITYPE, INAME, BPTYPE) \
const ::android::String16& ITYPE::getInterfaceDescriptor() const { return ITYPE::descriptor; } \
::android::sp<ITYPE> ITYPE::asInterface(const ::android::sp<::android::IBinder>& obj) { \
::android::sp<ITYPE> intr; \
// getInterfaceDescriptor 實作方案
if (obj != nullptr) { \
intr = ::android::sp<ITYPE>::cast(obj->queryLocalInterface(ITYPE::descriptor)); \
if (intr == nullptr) { \
intr = ::android::sp<BPTYPE>::make(obj); \
} \
} \
return intr; \
} \
::android::sp<ITYPE> ITYPE::default_impl; \
bool ITYPE::setDefaultImpl(::android::sp<ITYPE> impl) { \
/* Only one user of this interface can use this function */ \
/* at a time. This is a heuristic to detect if two different */ \
/* users in the same process use this function. */ \
assert(!ITYPE::default_impl); \
if (impl) { \
ITYPE::default_impl = std::move(impl); \
return true; \
} \
return false; \
} \
const ::android::sp<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \
ITYPE::INAME() {} \
ITYPE::~INAME() {}
```
* 可以翻譯成如下(有 2 個 define 宏),並可以看到 **asInterface 主要是 ==創建 BpServiceManager 物件==**
| define 宏 | 標誌 | 傳入 & 轉換 |
| - | -------- | -------- |
| IMPLEMENT_META_INTERFACE | INTERFACE | IServiceManager |
| IMPLEMENT_META_INTERFACE | NAME | android.os.IServiceManager |
| - | - | - |
| DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0 | ITYPE | IServiceManager |
| DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0 | INAME | IServiceManager |
| DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0 | BPTYPE | BpServiceManager |
```cpp=
// ServiceManager 轉換的程式如下
const ::android::StaticString16
IServiceManager_descriptor_static_str16(__IINTF_CONCAT(u, NAME));
const ::android::String16 IServiceManager
::descriptor(IServiceManager_descriptor_static_str16);
const ::android::String16& IServiceManager
::getInterfaceDescriptor() const { return IServiceManager::descriptor; }
::android::sp<IServiceManager> IServiceManager::asInterface(const ::android::sp<::android::IBinder>& obj) {
::android::sp<IServiceManager> intr;
if (obj != nullptr) {
intr = ::android::sp<IServiceManager>::cast(obj->queryLocalInterface(IServiceManager::descriptor));
if (intr == nullptr) {
// obj 物件是 BpBinder
intr = ::android::sp<BpServiceManager>::make(obj);
}
}
return intr;
}
::android::sp<IServiceManager> IServiceManager::default_impl; .
bool IServiceManager::setDefaultImpl(::android::sp<IServiceManager> impl) {
assert(!IServiceManager::default_impl);
if (impl) {
IServiceManager::default_impl = std::move(impl);
return true;
}
return false;
}
const ::android::sp<IServiceManager>& IServiceManager::getDefaultImpl() { return IServiceManager::default_impl; }
IServiceManager::IServiceManager() {}
IServiceManager::~IServiceManager() {}
```
* 這時我們可以知道要訪問遠端物件的話 **ServiceManager#asInterface 方法主要是 ++創建 BpServiceManager 物件++**,並且它接收的參數是 ProcessState::getContextObject 返回的的 **==BpBinder==**
`asInterface` 的主要作用是先檢查 `IBinder` 物件是否是本地物件,如果非本地物件就會創建 `BpXXX` 代理物件(例如舊版中的 `BpServiceManager`)
* [**BpServiceManager**](https://android.googlesource.com/platform/frameworks/native/+/jb-dev/libs/binder/IServiceManager.cpp) 的實做在新版本中也被移除,猜測應該也是由 aidl 協助創建,該類會繼承於 BpInterface 接口,接著我們分析 [**BpInterface**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/IInterface.h#133)、[**BpRefBase**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/Binder.cpp)
```java=
// IServiceManager.cpp
// 舊版 BpServiceManager
// @ 分析 BpInterface 類
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
BpServiceManager(const sp<IBinder>& impl)
: BpInterface<IServiceManager>(impl)
{ }
// -----------------------------------------------------------------------
// IInterface.h
// @ 分析 BpRefBase 類
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
explicit BpInterface(const sp<IBinder>& remote);
protected:
typedef INTERFACE BaseInterface;
virtual IBinder* onAsBinder();
};
// -----------------------------------------------------------------------
// Binder.cpp
BpRefBase::BpRefBase(const sp<IBinder>& o) // o => BpServiceManager => BpBinder
: mRemote(o.get()), mRefs(nullptr), mState(0)
{
// 由弱引用計數來控制該物件生命週期
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
if (mRemote) {
mRemote->incStrong(this); // Removed on first IncStrong().
mRefs = mRemote->createWeak(this); // Held for our entire lifetime.
}
}
```
> 
* 小結論:^1.^ BpServiceManager#mRemote 其實就是 **指向 BpBinder 物件**,^2.^ BpServiceManager 實現了 IServiceManager 的接口方法,並透過 **調用 mRemote 來實現 BpBinder 通訊**
:::success
* BpServiceManager 的功能相當於一個 **==OOP 代理模式==**,透過訪問 BpServiceManager 來訪問 BpBinder
:::
> 
### IServiceManager 關係 UML
* 這裡我們提及較為重要的類、還有它們的主要功能
| 類 | 產生 | 功能說明 |
| ---- | ---- | -------- |
| BpRefBase | 實體 | 存有 IBinder 物件(BpBinder) |
| BpServiceManager | 實體、aidl 產生 | 用來代理 BpBinder |
| IInterface | 實體 | 定義許多可使用的宏 |
| IServiceManager | 宏、aidl 產生 | IServiceManager 透過 `asInterface` 函數來取得 BpBinder |
| BpBinder | 實體 | Client 端使用的 Binder |
| BBinder | 實體 | Server 端使用的 Binder |
UML 關係圖可以看到每個類之間的關係
> 
## Client 向 ServiceManager 註冊
```cpp=
// main_mediaserver.cpp
using namespace android;
int main(int argc __unused, char **argv __unused)
{
... 省略部份
// MediaServer 向 ServiceManager 註冊服務
MediaPlayerService::instantiate();
... 省略部份
}
```
`instantiate` 函數註冊在 [**MediaPlayerService**](https://android.googlesource.com/platform/frameworks/av/+/master/media/libmediaplayerservice/MediaPlayerService.cpp) 實做
```cpp=
// MediaPlayerService.cpp
void MediaPlayerService::instantiate() {
// 透過 `defaultServiceManager` 函數
// 取得訪問 ServiceManager 的代理
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
```
* 從 defaultServiceManager 可以知道以下事情
1. 取得 BpServiceManager 物件(訪問 ServiceManager 的代理)
2. 透過 [**BpServiceManager**](https://android.googlesource.com/platform/frameworks/native/+/jb-dev/libs/binder/IServiceManager.cpp) 呼叫 BpBinder#addService 方法
3. 傳入參數 "`media.player`" 與 "`MediaPlayerService 物件`"
> 類似於 Key/Value 完成註冊
* 訪問 Binder 驅動 - 概念指令流程如下
> 
### [IServiceManager](https://android.googlesource.com/platform/frameworks/native/+/jb-dev/libs/binder/IServiceManager.cpp) - addService 註冊
* 由於新版沒有 BpServiceManager 實做,所以我們參考舊版是如何實現
| Parcel 打包方法 | 實際值 |
| -------- | -------- |
| writeInterfaceToken | `android.os.IServiceManager` |
| writeString16 | `media.player` |
| writeStrongBinder | MediaPlayerService 物件 (IBinder 指針) |
```java=
// IServiceManager.cpp
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
BpServiceManager(const sp<IBinder>& impl)
: BpInterface<IServiceManager>(impl)
{ }
// default 抽象實做
virtual status_t addService(
const String16& name,
const sp<IBinder>& service,
bool allowIsolated)
{
Parcel data, reply;
// 1. 寫入 "android.os.IServiceManager"
// @ 查看 writeInterfaceToken
data.writeInterfaceToken(
IServiceManager::getInterfaceDescriptor()
);
// 使用者傳入 "media.player"
data.writeString16(name);
// MediaPlayerService 物件,包裝成 `flat_binder_object`
// 查看 @ writeStrongBinder
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
// 這裡取得的 remote 就是 BpBinder
// @ 下一步追蹤 transact 方法
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
}
```
1. `writeInterfaceToken` 函數:會將 **介面 String 寫入 Parcel 包中**;目前就是寫入 `android.os.IServiceManager` 字符串
也就是該函數會將 Binder 介面的 Descriptor 寫入 `Parcel`,**供 Server 端在 `onTransact()` 中透過 `enforceInterface()` 驗證 Client 與 Server 是否使用相同介面**
```cpp=
// Parcel.cpp
status_t Parcel::writeInterfaceToken(const String16& interface)
{
// @ 查看 writeInterfaceToken 方法
return writeInterfaceToken(interface.string(), interface.size());
}
status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
... 省略部分
// 將描述寫入
return writeString16(str, len);
}
```
:::success
* IServiceManager::[**getInterfaceDescriptor**](https://cs.android.com/android/platform/superproject/+/master:out/soong/.intermediates/frameworks/native/libs/binder/libbinder/android_x86_64_silvermont_shared/gen/aidl/android/os/IServiceManager.cpp) 定義為: `"android.os.IServiceManager"` (系統中唯一描述)
```cpp=
// IServiceManager.cpp
namespace android {
namespace os {
// 宏定義 `DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE`
DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(
ServiceManager,
"android.os.IServiceManager")
} // namespace os
} // namespace android
```
:::
2. `writeStrongBinder` 函數:將 Server 元件(地址) 包裝成 `flat_binder_object` 結構,主要 **記錄本地 BinderServer、其弱引用地址**
> 也就是該函數會把該 Binder 物件的識別資訊 (例如本地 Binder 指標與 weak refs 指標) 交給 Binder 驅動
之所要要這樣紀錄,是因為 Binder 驅動層(C 語言)並不清楚 C++ 中的「物件概念」,所以這裡是讓驅動以不理解的方式直接紀錄 C++ 層(BinderNative)的物件的方式
```cpp=
// Parcel.cpp
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
// @ 查看 flattenBinder
return flattenBinder(val);
}
status_t Parcel::flattenBinder(const sp<IBinder>& binder) {
BBinder* local = nullptr;
// 查看是否是本地 Server
if (binder) local = binder->localBinder();
if (local) local->setParceled();
... 省略 RPC
flat_binder_object obj;
...
if (binder != nullptr) {
if (!local) {
// 非本地 Service ... 會轉為 BINDER_TYPE_HANDLE
// 目前是本地 Service 所以暫不關注
} else {
// 當前狀況 <<<<
... 省略部分
obj.hdr.type = BINDER_TYPE_BINDER; // 本地 Service 標示
// 取得本地 Service 弱引用的地址
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
// 取得本地 Service 地址
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
...
}
...
// @ 查看 writeObject
status_t status = writeObject(obj, false);
...
return finishFlattenBinder(binder);
...
}
```
:::success
* Parcel#`writeObject` 函數詳解請看另一篇 [**Android-Parcel**](https://hackmd.io/Qr4dm2XsSpyhMeB5dIePyA#Android-Parcel) 介紹
:::
* BpServiceManager#`addService` 方法:使用的 **remote** 就是 [**BpBinder 物件**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/BpBinder.cpp),所以其實它是在呼叫 BpBinder#**transact** 方法
```cpp=
// BpBinder.cpp
status_t BpBinder::transact(
// ADD_SERVICE_TRANSACTION (給目標 Service 的控制命令)
uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
... 省略部份
status_t status;
if (CC_UNLIKELY(isRpcBinder())) {
// ... Rpc Binder
} else {
// @ 追蹤 IPCThreadState#transact 方法
// binderHandle 目前返回是 0 (訪問 ServiceManager 的 handle 值)
status = IPCThreadState::selTextf()->
transact(binderHandle(), code, data, reply, flags);
}
... 省略部份
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
```
> 
### [IPCThreadState](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/IPCThreadState.cpp) - TLS 介紹
IPCThreadState 負責:
- 管理保存 thread 與 Binde 驅動的互動狀態
- 打包 outgoing transaction
- 解析 incoming command
- 維護 Binder thread 的 IPC 上下文
- IPCThreadState TLS 的設計也是為了 Binder thread pool
```cpp=
// IPCThreadState.h
class IPCThreadState
{
public:
...
static IPCThreadState* self(); // 取得 Thread 單例
// 與 Binder 驅動互動
status_t transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
private:
...
// 與 Binder 驅動互動
status_t talkWithDriver(bool doReceive=true);
// 進程單例
const sp<ProcessState> mProcess;
}
```
* **IPCThreadState 是使用一種 ++TLS 機制++,它的全名是 Thread local storage,這是一種 ++執行緒(線程)隔離++ 機制**,**每個 Thread 中都會有一個 IPCThreadState 實例**,但不同 Thread 之間不共享
:::info
* 這裡使用 `std::atomic<bool>` 搭配 mutex 互斥鎖,避免 TLS key 初始化時的競態條件,概念程式如下
```shell
if (haveTLS)
fast path
lock
if (!haveTLS)
create key
unlock
```
這段實作採用了類似 Double-Checked Locking 的方式,確保 TLS key 只會被初始化一次,同時避免每次呼叫都進入 mutex
:::
```cpp=
// IPCThreadState.cpp
static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
// atomic 類
static std::atomic<bool> gHaveTLS(false);
static pthread_key_t gTLS = 0;
IPCThreadState* IPCThreadState::self()
{
// 判斷是否已加載過
if (gHaveTLS.load(std::memory_order_acquire)) {
restart:
const pthread_key_t k = gTLS
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
// 已創建過直接返回 cache 物件
if (st) return st;
// 尚未創建過,創建物件
// @ 追蹤 IPCThreadState 建構函數
return new IPCThreadState;
...
// mutex 鎖
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS.load(std::memory_order_relaxed)) {
int key_create_value = pthread_key_create(&gTLS, threadDestructor);
// 判斷是否成功 (0 代表成功)
if (key_create_value != 0) {
pthread_mutex_unlock(&gTLSMutex);
ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",
strerror(key_create_value));
return nullptr;
}
gHaveTLS.store(true, std::memory_order_release);
}
pthread_mutex_unlock(&gTLSMutex);
// 去 restart tag 加載並返回
goto restart;
}
```
* IPCThreadState 建構函數,將 IPCThreadState 物件設置為 TLS、並設定傳輸 & 接收 Binder data 的大小
```cpp=
// IPCThreadState.cpp
static pthread_key_t gTLS = 0;
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()), // ProcessState 進程單例
mServingStackPointer(nullptr),
mServingStackPointerGuard(nullptr),
mWorkSource(kUnsetWorkSource),
mPropagateWorkSource(false),
mIsLooper(false),
mIsFlushing(false),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0),
mCallRestriction(mProcess->mCallRestriction) {
// 設置 TLS,將獲取的 pthread_key_t、自身傳入,綁定至該 thread 的 TLS
pthread_setspecific(gTLS, this);
clearCaller();
// 預設 Binder 傳遞的大小 (單位 byte)
mIn.setDataCapacity(256); // 接收 Binder data 256 byte
mOut.setDataCapacity(256); // 傳輸 Binder data 256 byte
}
// -----------------------------------------------------------------
// IPCThreadState.h
class IPCThreadState
{
... 省略部份
private:
... 省略部份
// Parcel 物件
Parcel mIn;
Parcel mOut;
};
```
:::success
* IPCThreadState 內部的 `mIn` 與 `mOut` Parcel 在初始化時會預設配置 256 bytes 的容量,僅作為 thread 端暫存 transaction data 的初始 buffer,
當資料量超過時會自動擴充
:::
### [IPCThreadState](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/IPCThreadState.cpp) - transact 傳送命令
* 在了解 IPCThreadState 後,我們接著來看 BpServiceManager#addService 所使用到的 `transact` 函數,這裡有幾個重要函數
| 函數名 | 功能 |
| -------- | -------- |
| writeTransactionData | **組裝 `binder_transaction_data` 數據**<br>也就是會將 Parcel 中的資料轉換為 Binder protocol 的 transaction command,並封裝為 `binder_transaction_data`結構,準備寫入 BinderDriver 的 buffer |
| waitForResponse | 將數據傳到 BinderDriver 並等待 BinderDriver (kernel) 回覆 |
:::danger
* Parcel 可以在不同進程間傳輸資訊,請先看 [**Parcel 打包**](https://hackmd.io/Qr4dm2XsSpyhMeB5dIePyA?view#Parcel---Active-Object) IBinder 物件,對 Service 判斷,並設定不同的 Type (這個 Type 會有關於 Binder Kernel 如何判斷轉換)
:::
`IPCThreadState::transact()` 的核心作用是:將高階的 Binder transaction(Parcel)轉換為 Binder Driver 可理解的 **protocol command**(例如 `BC_TRANSACTION`),之後才是與 BinderDriver 的溝通
```cpp=
// IPCThreadState.cpp
status_t IPCThreadState::transact(
int32_t handle, // 0 (目標 handle 0 為 context manager 也就是 ServiceManager)
uint32_t code, // ADD_SERVICE_TRANSACTION
const Parcel& data, // 給 Binder 驅動的資料
Parcel* reply, // Binder 驅動回覆的資料
uint32_t flags) // 是否同步傳輸
{
... log 訊息
status_t err;
flags |= TF_ACCEPT_FDS; // 允許 Server 回傳結果中帶有 FD ( 檔案描述符
... log 訊息
// @ writeTransactionData 函數
// 傳遞 cmd 為 BC_TRANSACTION
err = writeTransactionData(BC_TRANSACTION,
flags,
handle, // 0 (ServiceManager 固定)
code, // ADD_SERVICE_TRANSACTION
data,
nullptr);
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) { // 判斷是否是單向數據(異步呼叫時常使用)
// 需要等待回應
... 省略部份
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
... 省略部份
} else { // 不須等待回應
err = waitForResponse(nullptr, nullptr);
}
return err;
}
```
:::success
* Binder Kernel 指令取名方式:**傳向 Binder Kernel 的指令是有規則的**
`BC_` 開頭的是向 Binder Kernel 發送命令
`BR_` 則是 Binder kernel 回覆命令給使用端
* **為什麼 Binder 要允許回傳 FD?**
FD 是「進程內編號」,不是資源本體,**每個進程對於同一個資源會有不同的 FD 來描述**
> 在進程 A 中取得 ashmem 資源是 FD 7,但在進程 B 中取得同樣的 ashmem 資源則是 FD 13
Binder 傳 FD 的用途是「把已打開的資源交給別人」,也就是直鱉阿以可用的 kernel resource handle 交給需要使用的進程
:::
```mermaid
sequenceDiagram
autonumber
participant Client as Client Thread<br/>IPCThreadState
participant Out as mOut Parcel Buffer
participant Driver as Binder Driver
participant Server as Server Thread
participant In as mIn / reply Parcel
Client->>Out: writeTransactionData(BC_TRANSACTION, ...)
Note over Client,Out: 先把 command 與 binder_transaction_data<br/>寫進 thread-local mOut
Client->>Client: waitForResponse(reply)
Client->>Driver: talkWithDriver()<br/>ioctl(BINDER_WRITE_READ)
Note over Client,Driver: 將 mOut 中的 BC_* 命令寫入 Driver
Driver->>Driver: 解析 BC_TRANSACTION
Driver->>Driver: 找到 target handle / node / thread
Driver->>Server: 喚醒目標 Binder thread
Server->>Driver: 讀取 BR_TRANSACTION
Note over Driver,Server: Server 端收到 transaction 事件
Server->>Server: BBinder::onTransact(...)
Note over Server: 執行對應服務邏輯
Server->>Driver: 回傳結果 / BR_REPLY
Driver->>Client: 返回 BR_REPLY
Client->>In: 將 reply 解析到 reply Parcel
Client-->>Client: transact() 返回
```
### writeTransactionData - 打包數據給 BinderDriver
:::info
主要使用結構 `binder_transaction_data`
:::
* IPCThreadState#**`writeTransactionData` 函數**:首先會準備要傳向 Binder kernel 的數據結構 `(binder_transaction_data)` 結構,對該結構填充數據
| binder_transaction_data 成員 | 功能 | 當前數據 |
| -------- | -------- | - |
| cmd | Binder kernel 需要處理的指令 | `BC_TRANSACTION` |
| handle | 標示要傳遞的目標 | `0` (ServiceManager) |
| code | 接收進程會收到的命令 | 傳遞給 ServiceManager 命令 `ADD_SERVICE_TRANSACTION` |
```cpp=
// IPCThreadState.cpp
// 傳遞 cmd 為 BC_TRANSACTION
status_t IPCThreadState::writeTransactionData(int32_t cmd, // BC_TRANSACTION
uint32_t binderFlags,
int32_t handle, // 0
uint32_t code, // ADD_SERVICE_TRANSACTION
const Parcel& data, // 使用者要傳輸給 BinderDriver 的數據
status_t* statusBuffer)
{
binder_transaction_data tr;
// 不要將未初始化的堆棧數據傳遞給遠程進程
tr.target.ptr = 0;
tr.target.handle = handle; // 目前是 0
tr.code = code; // 目前是 ADD_SERVICE_TRANSACTION
tr.flags = binderFlags; // TF_ACCEPT_FDS
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
const status_t err = data.errorCheck(); // 再次檢查 Parcel 資料結構是否有錯誤
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData(); // 將 data 指標賦予 buffer
// offsets 用來指出 `Parcel` 資料區中哪些位置存放的是 Binder 物件
// (例如 `flat_binder_object`)
// Binder Driver 會依據這些 offset,在 transaction 過程中逐一解析與轉換 Binder object
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
...
} else {
...
}
// 將給 BinderKernel 的命令寫入 mOut 中
mOut.writeInt32(cmd); // 目前是 BC_TRANSACTION
// 將數據複製進 tr 中
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
// --------------------------------------------------------------
// Parcel.cpp
status_t Parcel::write(const void* data, size_t len)
{
...
void* const d = writeInplace(len);
if (d) {
// d 複製到 data 中
memcpy(d, data, len); // 直接使用 memcpy 將數據複製進去
return NO_ERROR;
}
return mError;
}
```
:::info
* **`BC_TRANSACTION` 指令位置** ? 依照對 `mOut` 中的資料順序為…
```c=
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
```
1. `BC_TRANSACTION`
2. `binder_transaction_data`
也就是先寫入 **命令碼**,再寫入該命令對應的參數結構
:::
* 到目前為止我們可以看到 UserSpace 使用者將 Parcel 數據打包進 `binder_transaction_data` 結構中,結構如下
```c=
// binder.h
struct binder_transaction_data {
// 不同狀態下代表了不同含意
union {
__u32 handle; // 控制碼
binder_uintptr_t ptr;
} target;
// User 另外指定的參數
binder_uintptr_t cookie;
// 傳遞給 BinderService 的命令,與 BinderDriver 無關
__u32 code;
// flags 定義如 transaction_flags,代表這次通訊的特徵
__u32 flags;
// 以下兩個是 Binder 驅動程式填寫
__kernel_pid_t sender_pid; // 發起請求的 進程 ID
uid_t sender_euid; // 發起請求的 UID
binder_size_t data_size; // 描述 data 大小
binder_size_t offsets_size; // 描述 data 偏移
union {
// 資料較大時須使用 ptr
struct {
binder_uintptr_t buffer; // 指向緩存空間
binder_uintptr_t offsets;
} ptr;
__u8 buf[8]; // 資料較少時,可以直接使用
} data;
};
```
總結一下在該階段中打包好的 binder_transaction_data (簡稱 TRD) 結構如下
| TRD 成員 | 數據 | 補充 |
| -------- | -------- | -------- |
| target.handle | 0 | `target.handle`:指定本次 transaction 的目標 Binder handle<br>當前值為 `0`,代表目標為 Binder Context Manager,也就是 ServiceManager |
| code | ADD_SERVICE_TRANSACTION | 給 ServiceManager 的命令 |
| flags | TF_ACCEPT_FDS | |
| data_size | sizeof(int32_t)<br>strlen("android.os.IServiceManager")<br>sizeof(flat_binder_object)<strlen("media.player")>(要註冊的目標 Service) | - |
| offsets_size | sizeof(size_t) | - |
| data.ptr.buffer | "android.os.IServiceManager"<br>flat_binder_object<br>"media.player" | `data.ptr.buffer` 指向整塊 Parcel data 區,其中依照 Parcel 打包順序保存了:<br>interface token (`android.os.IServiceManager`)<br>service name (`media.player`)<br>service Binder object (`flat_binder_object`)<br>`allowIsolated` |
| data.ptr.offsets | sizeof(int32_t)<br>strlen("android.os.IServiceManager")<br>strlen("media.player") | 陣列內容只有一個 offset,表示那個 flat_binder_object 在 buffer 中的位置 |
* 再針對 `flat_binder_object` 看看其內容
```c=
// binder.h
struct flat_binder_object {
struct binder_object_header hdr;
__u32 flags; // 只有是 LocalBinder 時才有意義
// 物件 共同體
union {
binder_uintptr_t binder; // Local Service 同 process 物件
__u32 handle; // 遠端跨 process 物件 (控制碼)
};
// Local Service 時指向 BinderNative 的實體位置
// 對於 Binder 驅動沒有特殊含義,而是對於 C++(Native 層才有意義)
binder_uintptr_t cookie;
};
struct binder_object_header {
__u32 type;
};
```
| flat_binder_object 成員 | 數據 | 補充 |
| --------------------- | -------------------------------------- | ------------------------ |
| `hdr.type` | `BINDER_TYPE_BINDER` | 表示本地 Binder 物件 |
| `flags` | `0x7f \| FLAT_BINDER_FLAG_ACCEPTS_FDS` | Binder 物件特性 |
| `binder` | `local->getWeakRefs()` | 本地 Binder weak refs 識別資訊 |
| `cookie` | `local` | 本地 `BBinder` 物件指標 |
### talkWithDriver - 與 BinderDriver 通訊
:::info
主要使用結構 `binder_write_read`
`talkWithDriver()` 函數透過 `ioctl(BINDER_WRITE_READ)`,一次完成 ^1.^ UserSpace → BinderDriver 的 command 寫入,以及 ^2.^ BinderDriver → UserSpace 的回覆事件讀取
:::
* **IPCThreadState#`waitForResponse` 函數**:主要是與 BinderDriver 通訊,並處理 Binder kernel 的回覆(處理以 `BR_` 開頭的命令),其中有兩個較重要的函數
> 該函數不只讀,也會寫指令
| 函數名 | 功能 |
| - | - |
| `waitForResponse` | 進入回應迴圈,等待並解析 BinderDriver 回傳事件 |
| `talkWithDriver` | 呼叫 `ioctl(BINDER_WRITE_READ)`,與 BinderDriver 實際交換資料 |
| `executeCommand` | 處理 BinderDriver 回傳的 `BR_*` 命令 |
```cpp=
// IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
// 與 Binder 通訊
// @ 分析 talkWithDriver 函數
if ((err=talkWithDriver()) < NO_ERROR) break;
// 到這裡就代表 BinderDriver 有回覆 (mIn 有資料)
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = (uint32_t)mIn.readInt32();
... log 訊息
// 有許多 BR_ 開頭的 command,這裡只留下幾個
switch (cmd) {
...
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
... 其他 case
default:
// 處理各種命令
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
...
return err;
}
```
* **IPCThreadState#`talkWithDriver` 函數**:真正與 BinderDriver 通訊的函數;目前要處理的 **命令是 `BC_TRANSACTION`**,通知 BinderDriver 傳送資料
| binder_write_read(`bwr`) 結構的重點欄位 | 意義 |
| - | - |
| `write_size` | 本次要寫給 BinderDriver 的資料大小 |
| `write_buffer` | 指向 userspace 的寫入資料(通常是 `mOut`) |
| `write_consumed` | Driver 已處理的寫入資料大小 |
| `read_size` | 本次希望從 BinderDriver 讀取的資料大小 |
| `read_buffer` | 指向 userspace 的讀取 buffer(通常是 `mIn`) |
| `read_consumed` | Driver 實際回填的資料大小 |
```cpp=
// IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive) // doReceive = true
{
if (mProcess->mDriverFD < 0) {
return -EBADF;
}
binder_write_read bwr
// 判斷 mIn#position 位置 是否有移動 (也就是 BinderDriver 是否有傳資料給 Thread)
// 有移動並大於 dataSize 代表有資料
//
// 目前 mIn 內的資料是否已經讀完,如果讀完了,就需要再向 driver 讀新的資料
// dataPosition < dataSize:mIn 還有未消費資料
// dataPosition >= dataSize:mIn 已經讀完,需要 refill
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// 1. doReceive 是否接收 BinderDriver 資料
// 2. needRead 在讀取 BinderDriver 資料時我們不想寫入任何 mOut 資料
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
// 如果 outAvail 為 0,就不會傳送資料給 BinderDriver
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data(); // 寫入資料
// 目前狀況是 doReceive = true
if (doReceive && needRead) {
// needRead = true
// 若 `doReceive && needRead` 為真,則表示本次 ioctl 希望從 BinderDriver 讀取資料,
// 因此會將 `mIn` 作為接收 buffer 傳入 `binder_write_read`
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
// 不讀 BinderDriver 資料
bwr.read_size = 0;
bwr.read_buffer = 0;
}
... log 訊息
// 如果沒有任何事情要做(不須 read | write)則即時返回
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
// consumed 設定為 0
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
... log 訊息
#if defined(__ANDROID__)
// 呼叫 Kernel#binder_ioctl 函數
// `BINDER_WRITE_READ` 是 Binder Driver 最核心的 ioctl 之一,
// 它允許 userspace 在 **同一次系統呼叫中**
// 同時提交待處理的 `BC_*` 命令,並接收 Driver 回傳的 `BR_*` 事件
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
if (mProcess->mDriverFD < 0) {
err = -EBADF;
}
... log 訊息
} while (err == -EINTR);
if (err >= NO_ERROR) { // 以下為沒錯誤
if (bwr.write_consumed > 0) { //
if (bwr.write_consumed < mOut.dataSize())
... BinderDriver 尚未處理完資料
else {
mOut.setDataSize(0);
processPostWriteDerefs();
}
}
if (bwr.read_consumed > 0) {
// 有資料要讀取,更新 DataPosition & buf 指標
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
... log msg
return NO_ERROR;
}
return err;
}
```
* 這裡就會接觸到 BinderDriver (kernel 層) 的 ioctl 函數,到這裡就超過 Binder Native 的領域,我們先知道 BinderDriver 會紀錄傳入的 **`handle 標示` & `name 服務名稱`** ... 等等資訊
1. 當前 handle 是 0 (裝在 mOut 中);Binder 驅動會用這個數值來找目標進程
2. 當前服務名 name 是 "`android.os.IServiceManager`" (裝在 mOut 中)
3. 並且帶有兩個命令
| 命令 | 處理物件 |
| - | - |
| ADD_SERVICE_TRANSACTION | ServiceManager |
| BC_TRANSACTION | BinderDriver |
:::success
* 後續請參考 [**Binder - Kernel 驅動分析**](https://hackmd.io/8E1AyMAgRzym3g6ktNV37Q#Binder---Kernel-%E9%A9%85%E5%8B%95%E5%88%86%E6%9E%90),會說明 BinderDriver#**binder_ioctl** 函數
:::
> 
## 註冊服務 - 命令過程
* 之前有說過 Binder 是 Clien/Server 架構,在這裡我們所舉的例子
1. Client 是 MediaPlayerService:**註冊過程是發生在 Client 端**,透過 Proxy 去訪問 BinderKernel
2. Server 是 ServiceManager:用於添加系統
簡化 Binder 命令如下,**通過 ++協議命令++** 來完成系統服務註冊
> 
詳細命令流程
> 
* 到這裡你會涉及到許多麼命令,這有點類似於 **網路封包**,指令是一層一層解析
> 
## Appendix & FAQ
:::info
:::
###### tags: `Binder` `Android 系統`