--- title: 'Binder - Native 開發' disqus: kyleAlien --- Binder - Native 開發 === ## OverView of Content 在這裡我們先定義了一個硬體驅動 (可在模擬器看到 `/dev/hello`),可以參考 [**Android Device Drivers**](https://hackmd.io/BwoSTTC1TW64dHNEs2uNWg?view#Android-Device-Drivers) 這篇 > ![](https://i.imgur.com/AKk1bxk.png) [TOC] ## 開發 Native Binder 這裡是要透過在 Source Code 建立一個 C++ 的 **BinderServer**、**BinderClient**;資料夾目錄如下 > ![](https://i.imgur.com/kZst8Y6.png) :::info * 使用 `Android 10_r1` Source code ::: ## Native - 基礎設置 :::info common 共用資料 這裡會定義代理 (`BpHelloService`)、本地 (`BnHelloService`) 的基礎類 ::: * 抽象介面:**定義共同介面 `IHelloService`:該介面繼承於 ==IInterface==** * 實現自己需要的方法,這邊簡單實現 setter / getter 兩個方法 * 使用 **`DECLARE_META_INTERFACE` 宏**,這個宏主要功能是 **宣告共同方法**,像是 `asInterface`、`getInterfaceDescriptor`... 等等方法 :::success 詳細請見 [**IInterface 分析**](https://hackmd.io/_8ne10VrSK-mK6nlyxL5RQ#IInterface---%E5%85%B1%E5%90%8C%E6%8E%A5%E5%8F%A3) ::: ```c= // IHelloService.h #ifndef I_HELLO_SERVICE_H #define I_HELLO_SERVICE_H #include <utils/RefBase.h> #include <binder/IInterface.h> #include <binder/Parcel.h> #define HELLO_SERVICE "sean.pan.HelloService" using namespace android; class IHelloService : public IInterface { public: // 記得使用宏 DECLARE_META_INTERFACE(HelloService); virtual int32_t getVal() = 0; virtual void setVal(int32_t val) = 0; }; ... #endif ``` ### Native - Bn 系列 Service * **定義 Service 類的基礎類 `BnHelloService`**:該類繼承於 **==BnInterface==**,模板類型為 `IHelloService` ```c= // IHelloService.h #ifndef I_HELLO_SERVICE_H #define I_HELLO_SERVICE_H #include <utils/RefBase.h> #include <binder/IInterface.h> #include <binder/Parcel.h> #define HELLO_SERVICE "sean.pan.HelloService" ... class BnHelloService: public BnInterface<IHelloService> { public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; #endif ``` * **Binder Server** 實作:**重點是在實現 `onTransact` 方法**,去判斷 Code 再做出不同行為 ```c= // IHelloService.cpp #include "IHelloService.h" using namespace android; enum ACTION { GET_VAL = IBinder::FIRST_CALL_TRANSACTION, SET_VAL }; status_t BnHelloService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch (code) { case GET_VAL: { // 這個括號是必要的 CHECK_INTERFACE(IHelloService, data, reply); // 檢查傳入的是否是 IHelloService 介面 int32_t val = getVal(); reply->writeInt32(val); return NO_ERROR; } case SET_VAL: { // 這個括號是必要的 CHECK_INTERFACE(IHelloService, data, reply); int32_t val_2 = data.readInt32(); setVal(val_2); return NO_ERROR;; } default: { return BBinder::onTransact(code, data, reply, flags); } } } ``` ### Native - Bp 系列 Proxy * 實作 **Binder 代理**:**繼承於 BpInterface,模板為 IHelloService**;**實現 Proxy 透過 ==remote#transact== 傳遞給 BinderService 的方法** * **使用 `IMPLEMENT_META_INTERFACE` 宏**:實現代理類的共同方法,與 `DECLARE_META_INTERFACE` 相互對應 :::warning * IMPLEMENT_META_INTERFACE 宏 第二個參數必須使用 雙引號`""` 來傳入,它代表了 BinderService 的 Descriptor ::: ```c= // IHelloService.cpp #include "IHelloService.h" using namespace android; class BpHelloService : public BpInterface<IHelloService> { public: BpHelloService(const sp<IBinder>& impl) : BpInterface<IHelloService>(impl) { } int32_t getVal() { Parcel data; data.writeInterfaceToken(IHelloService::getInterfaceDescriptor()); Parcel reply; // remote (遠端代理) remote()->transact(GET_VAL, data, &reply); int32_t val = reply.readInt32(); return val; } void setVal(int32_t val) { Parcel data; data.writeInterfaceToken(IHelloService::getInterfaceDescriptor()); data.writeInt32(val); Parcel reply; // remote (遠端代理) remote()->transact(SET_VAL, data, &reply); } }; // 對應 DECLARE_META_INTERFACE // "sean.pan.HelloService" 是 BinderService 的標誌 IMPLEMENT_META_INTERFACE(HelloService, "sean.pan.HelloService"); ``` ## Native 應用 有了上面 Native 建立的基礎後,就可以透過基礎來建立 Server、Client ### Native - Server 服務端 * 我們知道 Binder 是一個 C/S 架構,所以我們要在這裡創建一個服務端 (繼承基礎類的 `BnHelloService`) 1. 創建 HelloService:它代表了服務的實體 * 使用 open 開啟 `/dev/hello`:取得檔案描述 FD,之後就可以透過該 FD 來控制 Android Driver Deivce 2. main: * 初始化 Service 物件,並透過 `addService` 添加到 ServiceManager 中 * 使用 `ProcessState::self()->startThreadPool()` 開啟 Binder Thread Pool * 使用 `IPCThreadState::self()->joinThreadPool()` 將當前 Thread 加入 Binder Thread Pool ```c= #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include "../common/IHelloService.h" #define HELLO_DEVICE_NAME "/dev/hello" class HelloService : public BnHelloService { public: HelloService() { fd = open(HELLO_DEVICE_NAME, O_RDWR); if(fd == -1) { printf("Failed to open device %s.\n", HELLO_DEVICE_NAME); } } virtual ~HelloService() { if(fd != -1) { close(fd); } } static void instantiate() { // 向 ServiceManager 添加服務 defaultServiceManager()->addService(String16(HELLO_SERVICE), new HelloService()); } int32_t getVal() { int32_t val = 0; if(val != -1) { read(fd, &val, sizeof(val)); } return val; } void setVal(int32_t val) { if(val != -1) { write(fd, &val, sizeof(val)); } } private: int fd; }; int main(int argc, char** argv) { (void) argc; (void) argv; HelloService::instantiate(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; } ``` * **創建 `Android.mk`**:編譯 `HelloService.cpp` 產生一個可執行檔案 ```shell= LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := ../common/IHelloService.cpp \ HelloService.cpp LOCAL_SHARED_LIBRARIES := libcutils libutils libbinder LOCAL_MODULE := HelloService include $(BUILD_EXECUTABLE) ``` * mmm 編譯 Server ```shell= mmm -j12 external/binder/server/ ``` > ![](https://i.imgur.com/AYNIA3X.png) ### Native - Client 客戶端 * 我們知道 Binder 是一個 C/S 架構,所以我們要在這裡創建一個客戶端,它要使用 Hello Service 服務 1. 使用 `defaultServiceManager` 取得,ServiceManager 的代理 2. 使用 `IHelloService::asInterface` 取得 IHelloService 的代理 3. 透過取得的代理來操作遠端 BinderService ```c= #include <stdio.h> #include <utils/Log.h> #include <binder/IServiceManager.h> #include "../common/IHelloService.h" int main() { // 1, 取得 ServiceManager 代理 sp<IBinder> binder = defaultServiceManager()->getService(String16(HELLO_SERVICE)); if(binder == NULL) { printf("Failed to get hello service: %s.\n", HELLO_SERVICE); return -1; } // 取得 IHelloService 的代理 sp<IHelloService> service = IHelloService::asInterface(binder); if(service == NULL) { printf("Failed to get hello service interface.\n"); return -2; } printf("Read original value from HelloService:\n"); int32_t val = service->getVal(); printf(" %d.\n", val); int target_val = 5; printf("Use HelloService add value %d to hello device.\n", target_val); val += target_val; service->setVal(val); printf("After add value, read device again:\n"); int32_t val_2 = service->getVal(); printf(" %d.\n", val_2); return 0; } ``` * **創建 `Android.mk`**:編譯 `HelloClient.cpp` 產生一個可執行檔案 ```shell= LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := ../common/IHelloService.cpp \ HelloClient.cpp LOCAL_SHARED_LIBRARIES := libcutils libutils libbinder LOCAL_MODULE := HelloClient include $(BUILD_EXECUTABLE) ``` * mmm 編譯 Client ```shell= mmm -j12 external/binder/client/ ``` > ![](https://i.imgur.com/c6JDTay.png) ## 運行測試 Server & Client * 將編譯好的檔案複製進模擬器 `/data` 區 ```shell= adb push ./out/target/product/generic_x86_64/system/bin/HelloService adb push ./out/target/product/generic_x86_64/system/bin/HelloClient ``` 1. 先背景執行 `HelloService` ```shell= ./data/HelloService & ``` 2. 再運行 `HelloClient`:可以發現 Service 從 0 每次運行都 + 5 ```shell= ./data/HelloClient ``` > ![](https://i.imgur.com/yjzgXvy.png) ## Appendix & FAQ :::info ::: ###### tags: `Binder`