HIDL C++

Android 8 會重新架構 Android 作業系統,以便在裝置獨立的 Android 平台與裝置和供應商專屬程式碼之間定義明確的介面。Android 已以 HAL 介面形式定義許多這類介面,並在 hardware/libhardware 中定義為 C 標頭。HIDL 會將這些 HAL 介面替換為穩定且有版本的介面,這些介面可為 C++ 或 Java 中的用戶端和伺服器端 HIDL 介面 (如下所述)。

本節的頁面說明 HIDL 介面的 C++ 實作方式,包括 hidl-gen 編譯器從 HIDL .hal 檔案自動產生的檔案詳細資料、這些檔案的封裝方式,以及如何將這些檔案與使用這些檔案的 C++ 程式碼整合。

用戶端和伺服器實作

HIDL 介面有用戶端和伺服器實作項目:

  • HIDL 介面的用戶端是指透過呼叫方法使用介面的程式碼。
  • 伺服器是 HIDL 介面的實作項目,可接收來自用戶端的呼叫,並視需要傳回結果。

libhardware HAL 轉換至 HIDL HAL 時,HAL 實作會成為伺服器,而呼叫 HAL 的程序則會成為用戶端。預設實作項目可同時提供傳遞和繫結 HAL,且可能隨時間變更:

圖 1. 舊版 HAL 的開發進度。

建立 HAL 用戶端

請先在 makefile 中加入 HAL 程式庫:

  • 製造商:LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Soong:shared_libs: [ …, android.hardware.nfc@1.0 ]

接著,請納入 HAL 標頭檔案:

#include <android/hardware/nfc/1.0/IFoo.h>

// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

建立 HAL 伺服器

如要建立 HAL 實作,您必須擁有代表 HAL 的 .hal 檔案,並已使用 hidl-gen 上的 -Lmakefile-Landroidbp 為 HAL 產生 Makefile (./hardware/interfaces/update-makefiles.sh 會為內部 HAL 檔案執行此操作,是很好的參考資料)。從 libhardware 轉移 HAL 時,您可以使用 c2hal 輕鬆完成許多這類工作。

如要建立實作 HAL 所需的檔案,請按照下列步驟操作:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

如要讓 HAL 在直通模式下運作,您必須讓函式 HIDL_FETCH_IModuleName 位於 /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so 中,其中 OPTIONAL_IDENTIFIER 是識別直通實作的字串。上述指令會自動符合轉送模式需求,並建立 android.hardware.nfc@1.0-impl 目標,但您也可以使用任何擴充功能。例如 android.hardware.nfc@1.0-impl-foo 使用 -foo 來區分自身。

如果 HAL 是其他 HAL 的次要版本或擴充功能,應使用基礎 HAL 命名此二進位檔。舉例來說,android.hardware.graphics.mapper@2.1 實作項目應仍位於名為 android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER) 的二進位檔中。通常,此處的 OPTIONAL_IDENTIFIER 會包含實際的 HAL 版本。以這種方式命名二進位檔後,2.0 用戶端就能直接擷取,而 2.1 用戶端則可向上轉換實作。

接著,請為這些 Stub 填入功能,並設定 Daemon。守護程式程式碼範例 (支援轉送):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation 會為提供的 -impl 程式庫呼叫 dlopen(),並將其提供為綁定服務。範例 Daemon 程式碼 (適用於純 Binderized 服務):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool never exceeds
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

這個守護程式通常位於 $PACKAGE + "-service-suffix" (例如 android.hardware.nfc@1.0-service),但也可能位於其他位置。特定類別 HAL 的 sepolicy 是屬性 hal_<module> (例如 hal_nfc))。這個屬性必須套用至執行特定 HAL 的守護程序 (如果同一個程序提供多個 HAL,則可套用多個屬性)。