寫一個自己的 Android Hidl Service

HIDL

HAL 接口定義語言(簡稱 HIDL,發音爲“hide-l”)是用於指定 HAL 和其用戶之間的接口的一種接口描述語言 (IDL)。HIDL 允許指定類型和方法調用(會彙集到接口和軟件包中)。從更廣泛的意義上來說,HIDL 是用於在可以獨立編譯的代碼庫之間進行通信的系統。

HIDL 旨在用於進程間通信 (IPC)。進程之間的通信採用 Binder 機制。對於必須與進程相關聯的代碼庫,還可以使用直通模式(在 Java 中不受支持)。

HIDL 可指定數據結構和方法簽名,這些內容會整理歸類到接口(與類相似)中,而接口會彙集到軟件包中。儘管 HIDL 具有一系列不同的關鍵字,但 C++ 和 Java 程序員對 HIDL 的語法並不陌生。此外,HIDL 還使用 Java 樣式的註釋。

HIDL 設計

HIDL 的目標是,可以在無需重新構建 HAL 的情況下替換框架。HAL 將由供應商或 SOC 製造商構建,並放置在設備的 /vendor 分區中,這樣一來,就可以在框架自己的分區中通過 OTA 替換框架,而無需重新編譯 HAL。

HIDL 設計在以下方面之間保持了平衡:

  • 互操作性。 在可以使用各種架構、工具鏈和編譯配置來編譯的進程之間創建可互操作的可靠接口。HIDL 接口是分版本的,發佈後不得再進行更改。
    -效率。 HIDL 會嘗試儘可能減少複製操作的次數。HIDL 定義的數據以 C++ 標準佈局數據結構傳遞至 C++ 代碼,無需解壓,可直接使用。此外,HIDL 還提供共享內存接口;由於 RPC 本身有點慢,因此 HIDL 支持兩種無需使用 RPC 調用的數據傳輸方法:共享內存和快速消息隊列 (FMQ)。
    -直觀。 通過僅針對 RPC 使用 in 參數,HIDL 避開了內存所有權這一棘手問題(請參閱 Android 接口定義語言 (AIDL));無法通過相應方法高效返回的值將通過回調函數返回。無論是將數據傳遞到 HIDL 中以進行傳輸,還是從 HIDL 接收數據,都不會改變數據的所有權,也就是說,數據所有權始終屬於調用函數。數據僅需要在函數被調用期間保留,可在被調用的函數返回數據後立即清除。

HIDL 語法

根據設計,HIDL 語言與 C 語言類似(但前者不使用 C 預處理器)。下面未描述的所有標點符號(用途明顯的 = 和 | 除外)都是語法的一部分。

注意:如需詳細瞭解 HIDL 代碼樣式,請參閱代碼樣式指南。

  • /** */ 表示文檔註釋。此樣式只能應用於類型、方法、字段和枚舉值聲明。
  • /* */ 表示多行註釋。
  • // 表示註釋一直持續到行尾。除 // 之外,換行符與任何其他空格一樣。
  • 在以下示例語法中,從 // 到行尾的文本不是語法的一部分,而是對語法的註釋。
  • [empty] 表示該字詞可能爲空。
  • ? 跟在文本或字詞後,表示它是可選的。
  • … 表示包含零個或多個項、使用指定的分隔符號分隔的序列。HIDL 中不含可變參數。
  • 逗號用於分隔序列元素。
  • 分號用於終止各個元素,包括最後的元素。
  • 大寫字母是非終止符。
  • italics 是一個令牌系列,如 integer 或 identifier(標準 C 解析規則)。
  • constexpr 是 C 樣式的常量表達式(例如 1 + 1 和 1L << 3)。
  • import_name 是軟件包或接口名稱,按 HIDL 版本編號中所述的方式加以限定。
  • 小寫 words 是文本令牌。

例如

ROOT =
    PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... }  // not for types.hal
  | PACKAGE IMPORTS ITEM ITEM...  // only for types.hal; no method definitions

ITEM =
    ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
  |  safe_union identifier { UFIELD; UFIELD; ...};
  |  struct identifier { SFIELD; SFIELD; ...};  // Note - no forward declarations
  |  union identifier { UFIELD; UFIELD; ...};
  |  enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
  |  typedef TYPE identifier;

VERSION = integer.integer;

PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;

PREAMBLE = interface identifier EXTENDS

EXTENDS = <empty> | extends import_name  // must be interface, not package

GENERATES = generates (FIELD, FIELD ...)

// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
   [empty]
  |  IMPORTS import import_name;

TYPE =
  uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
 float | double | bool | string
|  identifier  // must be defined as a typedef, struct, union, enum or import
               // including those defined later in the file
|  memory
|  pointer
|  vec<TYPE>
|  bitfield<TYPE>  // TYPE is user-defined enum
|  fmq_sync<TYPE>
|  fmq_unsync<TYPE>
|  TYPE[SIZE]

FIELD =
   TYPE identifier

UFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SIZE =  // Must be greater than zero
     constexpr

ANNOTATIONS =
     [empty]
  |  ANNOTATIONS ANNOTATION

ANNOTATION =
  |  @identifier
  |  @identifier(VALUE)
  |  @identifier(ANNO_ENTRY, ANNO_ENTRY  ...)

ANNO_ENTRY =
     identifier=VALUE

VALUE =
     "any text including \" and other escapes"
  |  constexpr
  |  {VALUE, VALUE ...}  // only in annotations

ENUM_ENTRY =
     identifier
  |  identifier = constexpr

以上摘自Google 官網

寫自己的Hidl service

創建自己的代碼工作目錄

創建自己的service目錄
例如 vendor/lee/interfaces/leeservice

mkdir -p vendor/lee/interfaces/leeservice

創建service版本1.0目錄可以根據需要創建1.1/1.2或者2.0版本

mkdir -p vendor/lee/interfaces/leeservice/1.0

創建默認service實現目錄

mkdir -p vendor/lee/interfaces/leeservice/1.0/default/impl

指定package-root
在vendor/lee/interfaces/leeservice創建Android.bp 然後寫入

subdirs = [
    "1.0",
	"1.0/default",
]

hidl_package_root {
    name: "vendor.lee.lee",
    path: "vendor/lee/interfaces/leeservice",
}

創建自己的Hal文件

參照如下,可以根據自己的需要適當的增刪
在logservice目錄創建自己的hal文件

  • 接口hal文件
    vendor/lee/interfaces/leeservice/1.0/ILeeManager.hal
  • callback的hal文件
    vendor/lee/interfaces/leeservice/1.0/ILeeCallback.hal
  • 類型定義文件
    vendor/lee/interfaces/leeservice/1.0/typers.hal

在接口hal文件中創建自己的接口
例如

package [email protected];

import ILeeCallback;

interface ILeeManager {
    setLevel(int32_t level);
    getLevel() generates(int32_t level);
    requestString(string s);
    registerCallback(ILeeCallback callback);
    unregisterCallback(ILeeCallback callback);
};

在callback的hal文件創建回調函數
例如

package [email protected];

interface ILeeCallback {

    OnLeeEvent(LeeEvent event,  LeeExtra extra);
};

在類型定義的hal中添加自己的定義
例如

package [email protected];

enum LeeEvent : uint32_t
{
    LEE_ADD = 0,
    LEE_DEL = 1,
};

struct LeeExtra {
    uint32_t extraSize;
    string extraPath;
};

生成CPP文件和bp文件

在vendor/lee/interfaces/leeservice/目錄執行

LOC=1.0/default/impl
PKG=[email protected]
hidl-gen  -Landroidbp  -rvendor.lee.lee:vendor/lee/interfaces/leeservice $PKG
hidl-gen -o $LOC -Lc++-impl  -rvendor.lee.lee:vendor/lee/interfaces/leeservice $PKG
hidl-gen -o $LOC -Landroidbp-impl  -rvendor.lee.lee:vendor/lee/interfaces/leeservice $PKG

然後你就發現在impl目錄生成了cpp文件及bp文件,這是自動生成發的空實現
現在文件樹形結構如下

vendor/lee/interfaces/leeservice/
|-- 1.0
|   |-- Android.bp
|   |-- ILeeCallback.hal
|   |-- ILeeManager.hal
|   |-- default
|   |   `-- impl
|   |       |-- Android.bp
|   |       |-- LeeCallback.cpp
|   |       |-- LeeCallback.h
|   |       |-- LeeManager.cpp
|   |       `-- LeeManager.h
|   `-- types.hal
`-- Android.bp

3 directories, 10 files

註釋掉vendor/lee/interfaces/leeservice/1.0/default/impl/Android.bp中
// relative_install_path: “hw”,
並添加如下兩句

    local_include_dirs: [
       ".",
   ],

   export_include_dirs: [
       ".",
   ],

然後你就可以在LeeManager.cpp及其他文件中添加自己的實現了

添加service及rc

在vendor/lee/interfaces/leeservice/1.0/default添加service.cpp
添加如下代碼

#include <LeeManager.h>
#include <android/log.h>
#include <hidl/HidlTransportSupport.h>

using namespace android;
using namespace vendor::lee::lee::V1_0::implementation;

int main(int /* argc */, char* /* argv */[]) {

    android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */);

    sp<LeeManager> mLeeManager = new LeeManager();
    status_t status = mLeeManager->registerAsService();

    if (status != OK) {
        ALOGE("Unable to register log service (%d)", status);
        return 1;
    }

    android::hardware::joinRpcThreadpool();

    return 1;
}

在vendor/lee/interfaces/leeservice/1.0/default創建[email protected]並添加如下代碼

service lee-hal-1.0 /vendor/bin/hw/[email protected]
    class hal
    user system
    group system

on post-fs-data
    start lee-hal-1.0

自此hidlservice就創建完成了
但是此時上層應用是拿不到service的
需要註冊service

註冊halservice

修改自己board目錄的manifest.xml
例如
device/google/wahoo/manifest.xml
添加如下代碼

    <hal format="hidl" optional="true">
        <name>vendor.lee.lee</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>ILeeManager</name>
            <instance>default</instance>
        </interface>
    </hal>

並在vendor_framework_compatibility_matrix.xml添加如下代碼

    <hal format="hidl" optional="true">
        <name>vendor.lee.lee</name>
        <version>1.0</version>
        <interface>
            <name>ILeeManager</name>
             <instance>default</instance>
        </interface>
    </hal>

至此hidl service部分已經全部完成
剩下的就是根據需要配置selinux的權限了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章