---
title: 'Binder - Native 開發'
disqus: kyleAlien
---
Binder - Native 開發
===
## OverView of Content
在這裡我們先定義了一個硬體驅動 (可在模擬器看到 `/dev/hello`),可以參考 [**Android Device Drivers**](https://hackmd.io/BwoSTTC1TW64dHNEs2uNWg?view#Android-Device-Drivers) 這篇
> 
[TOC]
## 開發 Native Binder
這裡是要透過在 Source Code 建立一個 C++ 的 **BinderServer**、**BinderClient**;資料夾目錄如下
> 
:::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/
```
> 
### 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/
```
> 
## 運行測試 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
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `Binder`