---
# System prepended metadata

title: Binder - Native 開發
tags: [Binder]

---

---
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`
