--- 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 層 [TOC] ## Linux & Binder IPC 通訊原理 基礎的 Linux IPC 通訊機制請參考 [**IPC 通訊機制**](https://hackmd.io/jh6jLfzWQZarUjsQOEQZpw) ### Linux 內核空間 1. 在 Linux 中會將空間分為 ^1.^ User space 使用者空間、^2.^ Kernel space 內核空間,這兩個空間是被隔離的,這樣的好處有 1. 安全性:保護 Kernel space 不讓 User space 隨意訪問 2. 記憶體空間:Kernel space 有專屬的記憶體區塊,其他的空間不可隨意使用 3. 穩定性:即使其他 User space 的應用崩潰也不會影響到 Kernel space 的運作 2. Kernel Space 特色:數據是每個進程共享,但進程與進程之間數據不共享,如下圖 ! (下圖都是在虛擬記憶體中,並非實體記憶體) > ![](https://i.imgur.com/ZBfZLIK.png) 3. System call:系統調用是一種 **統稱**,其中有許多的函數都稱為系統調用,**系統調用是用戶空間訪問內核空間的 ++==唯一手段==++** * 這種行為保證了用戶在訪問內核空間時的一致性,所有的資源由內核親自操控、管理,保證了 **安全性、權限訪問** :::success * 函數取名,何為 User? 站在 **Kernel 的角度看,User 就是各個 ++用戶空間++**,之後分析我們會常常看到兩個函數 1. `copy_from_user`:將數據從 User 拷貝到 kenel space 2. `copy_to_user`:將數據從 kenel 拷貝到 User space ::: > ![](https://i.imgur.com/CZsuLMG.png) ### 傳統 Linux IPC 通訊問題 * 傳統 Linux IPC 通訊模型如下 1. 內核空間:**主動** 開闢一塊配存區塊(**內核緩衝**)放置要傳輸的資料 2. 發送數據方:透過 `copy_from_user` 將 User 資料 **複製到內核緩衝區** 3. 接收方:在自己的使用者空間開闢一塊 **使用者緩衝區** 4. 內核空間:透過 `copy_to_user` 將資料 **複製到使用者緩衝區** > ![](https://i.imgur.com/gykbF8W.png) * 通過以上的流程可以發現 Linux IPC 機制有以下問題 1. 需要經過 **2 次複製**,資料才可以到達使用者空間(copy_from_user、copy_to_user) 2. **使用者接收數據的緩衝區,無法確定大小**,如果只是進可能將空間擴大,則可能會浪費記憶體空間 ### 虛擬記憶體 - 分頁快取 & 內存映射 * 如果對 **虛擬記憶體** 沒有概念的話建議先看看 [**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),分頁快取的概念如下圖 > ![](https://i.imgur.com/oQdNSp4.png) * Linux 系統有提供一種機制 **內存映射**:它可以將指定文件(外部文件)的記憶體映射到使用者進程空間,之後操作該內存就會直接反應到記憶體中 * mmap:也就是 Memory Map,它概念是可以 **在核心分頁中分配到的地址**,會直接 **映射到進程的虛擬內存中** * mmap 的好處是透過 **映射** (想成內核指向文件地址),只需要 **一次性複製**,就可以把資料寫入到文件中(不考慮寫入時機) > ![](https://i.imgur.com/B86mhNS.png) * 如果沒有使用內存映射機制,則需要 **兩次複製**,才可以將資料寫入到目標文件中 > ![](https://i.imgur.com/7U6tluI.png) ## Binder 概述 Binder 是基於 OpenBinder 實現(最早是由 PalmInc 公司開發,後來才轉進 Google) ### Binder 通訊原理 * **Binder 是 ++基於分頁快取 & 內存映射++ 來實現**,但這裡的映射並不是真實文件,而是在 dev 區塊的一個內存區塊(想成虛擬文件),簡單來說內核空間會有以下操作 1. 在 Kernel space 開闢一塊,**數據接收區** (**原本由使用者空間提供,++改為核心提供空間++**) > ![](https://i.imgur.com/I30RwuM.png) 2. 在 Kernel space 開闢一塊,**內存緩衝區** (同內存映射模型) > ![](https://i.imgur.com/Z0xqXif.png) 3. **建立 ++==映射==關係++** (**映射的概念就類似於 Map**),透過**兩次映射**將資料寫到接收進程中,兩次映射如下 * 3-1. 數據接收區 & 內存緩衝區 > ![](https://i.imgur.com/DXi5H8p.png) * 3-2. 內存緩衝區 & 接收進程的虛擬地址 > ![](https://i.imgur.com/9Bv166c.png) 4. 發送資料方,只需要透過 `copy_from_user` 把資料 **複製到內核緩衝區**,之後就可以透過映射,將資料反應到接收方的虛擬記憶體中 > ![](https://i.imgur.com/owZbXbA.png) ### Binder 優點 1. **性能** 傳統 Linux IPC 機制除了共享內存外 (有很多需要注意的部分),大部分都需要兩次複製 (Socket、Pipe、消息對列...) > 共享內存如下 > > ![](https://i.imgur.com/781wiFz.png) 2. **穩定性** Binder 基於 Client/Server 架構,這種架構通常採用兩層數據結構,而內存共享可能會造成資源的危險,需要使用鎖保護內存 (並可能產生死鎖) 3. **安全性** * 傳統 IPC 機制需要在用戶層手動指定遠端進程的 ID ( e.g Socket's Port * 傳統 IPC 機制也無法管理進程的用戶 UID/ 進程 PID,無法判斷遠端進程的安全性,而 Binder 由 Android 系統管理,在系統中會判斷 UID/PID 是否符合訪問權限 ### Android 其他 IPC 機制 * 除了 Binder 以外,Android 仍有使用到其他 IPC 機制 1. Socket:Zygote 進程監聽 AMS 創建新進程使用 Socket 2. Mutex:Kill Process 採用信號 (Mutex) 機制 ## Binder - 介紹 :::info * Binder library 動態連結 Binder 最終會編譯成一個動態連結 Library:`libbinder.so`,方便其他進程複用 ::: * Binder 的是 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 關係 關係圖如下 > ![](https://i.imgur.com/2bkgf6m.png) * [**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` 命令 | :::warning * **getInterfaceDescriptor 方法**:取得 Binder 服務的 **唯一標示** ::: * [**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 的句柄 | | 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 必須相互對應** 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` > ![](https://i.imgur.com/apSwl8t.png) 2. BBinder 代表 **服務端**:主要使用 `onTransact` 方法處理 Client 發送過來的資訊 > ![](https://i.imgur.com/z3tzYwn.png) * 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; } ``` > ![](https://i.imgur.com/ZemBWj3.png) ### [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 服務的基類(使用者定義的共同方法) > ![](https://i.imgur.com/RdTVBIj.png) 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 設備,**取得篇移值** * 使用 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) 創建對象 ```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); } ``` 流程圖如下 > ![](https://i.imgur.com/SCXbCou.png) 2. interface_cast 方法:另外一小節分析 ### 創建 [IServiceManager](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/IServiceManager.h) - BpServiceManager 對象 :::info IServiceManager 功能:主要放有 ServiceManager 的所有函數,對應到 Java 層也會有相同的方法,像是 `getService`、`checkService`、`addService`... 等等 ::: * 在上面我們知道 ProcessState#getContextObject 返回的是一個 IBinder(**BpBinder**)對象,而 IBinder 對象如何返回一個 IServiceManager 呢? 這就需要透過 **`interface_cast` 函數** ```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; } ``` > ![](https://i.imgur.com/BNPPw4E.png) * [**IInterface**](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/IInterface.h)#**interface_cast** 方法使用了 **C++ 的 ==模板設計==** ```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` 文件**,讓其自動生成 > ![](https://i.imgur.com/HV7BRGR.png) * 但這並不影響我們研究,所以這裡我們仍看看宏定義 ::: 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==** * [**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. } } ``` > ![](https://i.imgur.com/pUre6WT.png) * 小結論:^1.^ BpServiceManager#mRemote 其實就是 **指向 BpBinder 對象**,^2.^ BpServiceManager 實現了 IServiceManager 的接口方法,並透過 **調用 mRemote 來實現 BpBinder 通訊** :::success * BpServiceManager 的功能相當於一個 **==OOP 代理模式==**,透過訪問 BpServiceManager 來訪問 BpBinder ::: > ![](https://i.imgur.com/DtVSsjJ.png) ### IServiceManager 關係 UML * 這裡我們提及較為重要的類、還有它們的主要功能 | 類 | 產生 | 功能說明 | | ---- | ---- | -------- | | BpRefBase | 實體 | 存有 IBinder 對象(BpBinder) | | BpServiceManager | 實體、aidl 產生 | 用來代理 BpBinder | | IInterface | 實體 | 定義許多可使用的宏 | | IServiceManager | 宏、aidl 產生 | IServiceManager 透過 `asInterface` 函數來取得 BpBinder | | BpBinder | 實體 | Client 端使用的 Binder | | BBinder | 實體 | Server 端使用的 Binder | UML 關係圖可以看到每個類之間的關係 > ![](https://i.imgur.com/wHTCDbZ.png) ## 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 驅動 - 指令流程如下 > ![](https://i.imgur.com/Pe3tImp.png) ### [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" data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); // 使用者傳入 "media.player" data.writeString16(name); // MediaPlayerService 對象,包裝成 `flat_binder_object` 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` 字符串 ```cpp= // Parcel.cpp status_t Parcel::writeInterfaceToken(const String16& interface) { 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` 結構,主要 **記錄本地 Server、其弱引用地址** ```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 ... 目前是本地 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( uint32_t code, // ADD_SERVICE_TRANSACTION (給目標 Service 的控制命令) 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; } ``` > ![](https://i.imgur.com/CyhOIOV.png) ### [IPCThreadState](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/IPCThreadState.cpp) - TLS 介紹 ```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 中只會有一個實例**,不同 Thread 之間不共享 :::info atomic 類是使用 CAS 機制 ::: ```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; // 尚未創建過,創建對象 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、自身傳入 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 * 每個 Thread 都有 256 byte 的 in/out Parcel Buffer,當容量不足時會自動擴充 ::: ### [IPCThreadState](https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/IPCThreadState.cpp) - transact 傳送命令 * 在了解 IPCThreadState 後,我們接著來看 BpServiceManager#addService 所使用到的 `transact` 函數,這裡有幾個重要函數 | 函數名 | 功能 | | -------- | -------- | | writeTransactionData | **組裝 `binder_transaction_data` 數據** | | waitForResponse | 將數據傳到 BinderDriver 並等待 BinderDriver (kernel) 回覆 | :::danger * Parcel 可以在不同進程間傳輸資訊,請先看 [**Parcel 打包**](https://hackmd.io/Qr4dm2XsSpyhMeB5dIePyA?view#Parcel---Active-Object) IBinder 物件,對 Service 判斷,並設定不同的 Type (這個 Type 會有關於 Binder Kernel 如何判斷轉換) ::: ```cpp= // IPCThreadState.cpp status_t IPCThreadState::transact( int32_t handle, // 0 (目標 handle 0 為 ServiceManager) uint32_t code, // ADD_SERVICE_TRANSACTION const Parcel& data, // 給 BinderDriver 的資料 Parcel* reply, // BinderDriver 回覆的資料 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 回覆命令給使用端 ::: ### 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 // binder 的偏移 (BinderServer 的所有紀錄) 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` 指令位置** ? 由於它在 Parcel 最後被加入,所以 `BC_TRANSACTION` 放置在 `binder_transaction_data` 數據後 > ![](https://i.imgur.com/zeBUYDx.png) ::: * 到目前為止我們可以看到 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 | 目標控制碼為 0,也就是 ServiceManager | | code | ADD_SERVICE_TRANSACTION | 給 ServiceManager 的命令 | | flags | TF_ACCEPT_FDS | | | data_size | sizeof(int32_t) | | | - | strlen("android.os.IServiceManager") | | | - | sizeof(flat_binder_object) | | | - | strlen("media.player") | 要註冊的目標 Service | | offsets_size | sizeof(size_t) | | | data.ptr.buffer | "android.os.IServiceManager" | | | - | flat_binder_object | 下表會再說明內容 | | - | "media.player" | | | data.ptr.offsets | sizeof(int32_t) | | | - | strlen("android.os.IServiceManager") | | | - | strlen("media.player") | | * 再針對 `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 對象 (控制碼) }; binder_uintptr_t cookie; // Local Service 時指向 Binder 的實體位置 }; struct binder_object_header { __u32 type; }; ``` | flat_binder_object | 數據 | 補充 | | -------- | -------- | -------- | | type | BINDER_TYPE_BINDER | 本地 Service | | flag | 0x7f \| FLAT_BINDER_FLAG_ACCEPTS_FDS | | | binder | `localBinder()` | 本地服務的地址 | | cookie | `localBinder()->getWeakRefs` | 本地服務弱計數的地址 | ### talkWithDriver - 與 BinderDriver 通訊 :::info 主要使用結構 `binder_write_read` ::: * **IPCThreadState#`waitForResponse` 函數**:主要是與 BinderDriver 通訊,並處理 Binder kernel 的回覆(處理以 `BR_` 開頭的命令),其中有兩個較重要的函數 > 該函數不只讀,也會寫指令 | 函數名 | 功能 | | - | - | | talkWithDriver | 對 BinderDriver 寫入(mOut) or 讀取(mIn) | | executeCommand | 處理從 BinderDriver (kernel) 回傳的命令 | ```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 傳送資料 ```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 代表有資料 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 // BinderDriver 有資料要讀取 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 函數 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 中) 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** 函數 ::: > ![](https://i.imgur.com/KxiqmYH.png) ## 註冊服務 - 命令過程 * 之前有說過 Binder 是 Clien/Server 架構,在這裡我們所舉的例子 1. Client 是 MediaPlayerService:**註冊過程是發生在 Client 端**,透過 Proxy 去訪問 BinderKernel 2. Server 是 ServiceManager:用於添加系統 簡化 Binder 命令如下,**通過 ++協議命令++** 來完成系統服務註冊 > ![](https://i.imgur.com/evX7uHd.png) 詳細命令流程 > ![](https://i.imgur.com/Pe3tImp.png) * 到這裡你會涉及到許多麼命令,這有點類似於 **網路封包**,指令是一層一層解析 > ![](https://i.imgur.com/kSbYHjX.png) ## Appendix & FAQ :::info 跟沒有借東西數到 10 限制當起跑點 ::: ###### tags: `Binder` `Android 系統`