HAL 適用的 AIDL

Android 11 引入了在 Android 中使用 AIDL 進行 HAL 的功能。如此一來 可讓您在不採用 HIDL 的情況下實作 Android 部分。轉換 HAL 以使用 AIDL 僅在可能的情況下 (上游 HAL 使用 HIDL 時,必須使用 HIDL)。

使用 AIDL 相互通訊架構元件的 HAL system.img 以及硬體元件 (例如 vendor.img 中的元件) 必須使用 穩定的 AIDL。不過,如要透過不同可用區相互通訊 HAL 只能使用 HAL,使用 IPC 機制沒有限制。

動機

AIDL 存在的時間比 HIDL 長,並在其他許多地方使用,例如 以及應用程式之間的交易現在 AIDL 具有穩定性 可支援,您可以使用單一處理序間通訊 (IPC) 執行階段實作整個堆疊。 AIDL 也比 HIDL 更優異的版本管理系統。

  • 使用單一處理序間通訊 (IPC) 語言,意味著只有一個學習、偵錯和 最佳化與安全性
  • AIDL 支援介面擁有者的就地版本管理:
    • 擁有者可以在介面結尾新增方法,或為 Parcelable 新增欄位。 這意味著,多年來越容易版本代碼版本更新 的費用比去年更低 (類型可修改,而且 每個介面版本都需要額外的程式庫)。
    • 您可以在執行階段附加擴充功能介面,不必在類型中附加 系統,因此不需要將下游擴充功能重新納入較新的版本 介面的每個版本
  • 擁有者選擇使用現有的 AIDL 介面時 保持穩定度。以前,整個介面副本都必須 在 HIDL 中建立的。

根據 AIDL 執行階段建構

AIDL 有三種不同的後端:Java、NDK、CPP。如要使用穩定版 AIDL,您必須 一律使用 system/lib*/libbinder.so 的系統副本並說話 在 /dev/binder。對於供應商圖片上的程式碼,這表示 libbinder 無法使用 (來自 VNDK):這個程式庫有不穩定的 C++ API,以及 內部不穩定的內部元件。原生供應商程式碼必須使用 AIDL,連結至 libbinder_ndk (由系統 libbinder.so 支援)、 並連結至由 aidl_interface 項目建立的 NDK 程式庫。適用對象 查看確切模組名稱,詳情請參閱 模組命名規則

編寫 AIDL HAL 介面

如要讓系統和供應商之間使用 AIDL 介面,介面需求 兩項變更:

  • 每個類型定義都必須使用 @VintfStability 註解。
  • aidl_interface 宣告必須包含 stability: "vintf",

只有介面擁有者才能進行這些變更。

如要進行這些變更,介面必須位在 VINTF 資訊清單。測試這個 (以及 例如驗證釋出的介面是否凍結) VTS 測試 vts_treble_vintf_vendor_test。您可以使用 @VintfStability 直接呼叫 AIBinder_forceDowngradeToLocalStability 後端 android::Stability::forceDowngradeToLocalStability 時, 或 Java 後端中的 android.os.Binder#forceDowngradeToSystemStability 才會傳送到其他程序。降級服務 Java 不支援供應商穩定性,因為所有應用程式都是在系統中執行 相關資訊

此外,為了提高程式碼可攜性並避免潛在問題, 停用不必要的額外程式庫時,請停用 CPP 後端。

請注意,在以下程式碼範例中,backends 的使用方式正確無誤, 分別為 Java、NDK 和 CPP。以下程式碼說明如何選取 CPP 後端以停用該後端。

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

尋找 AIDL HAL 介面

HAL 的 Android 開放原始碼計畫穩定版 AIDL 介面位於與 HIDL 介面,位於 aidl 資料夾。

  • 硬體/介面
  • 架構/硬體/介面
  • 系統/硬體/介面

您應該將擴充功能介面放入其他 hardware/interfaces vendorhardware 中的子目錄。

擴充功能介面

每次發布時,Android 都會提供一組官方 AOSP 介面。Android 裝置 合作夥伴想要為這些介面新增功能,則不應出現任何變更 因為這就代表他們的 Android 執行階段 。針對 GMS 裝置,避免變更 這些介面也可確保 GSI 映像檔能繼續運作。

擴充功能可以透過兩種方式註冊:

不過,只要註冊特定供應商 (意即不是 上游 AOSP) 元件使用介面,但無法合併 衝突。但是,當下游修改上游 Android 開放原始碼計畫元件時 可能會發生合併衝突,建議採用下列策略:

  • 下一版中,新增的介面可向上串流至 Android 開放原始碼計畫
  • 增加介面的彈性,而不會合併衝突。 就能在下一個版本中進行向上串流

Extension parcelables:ParcelableHolder

ParcelableHolder 是可包含另一個 ParcelableParcelableParcelableHolder 的主要用途是將 Parcelable 設為可擴充。 例如,裝置實作人員預期能夠 加入 Android 開放原始碼計畫定義的 ParcelableAospDefinedParcelable,納入其增值值 接著介紹網際網路通訊層 包括兩項主要的安全防護功能

先前沒有 ParcelableHolder,裝置實作者無法修改 加入 Android 開放原始碼計畫定義的穩定版 AIDL 介面,因為加入其他 欄位:

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

如以上程式碼所示,這種做法已毀損,因為 可能會發生衝突,因為當 Parcelable 的 通過下一階段的 Android 修正。

透過 ParcelableHolder,Parcelable 的擁有者可以定義擴充功能 在 Parcelable 中輸入提示點。

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

接著,裝置實作人員可以為Parcelable

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

最後,新的 Parcelable 可附加至原始 Parcelable,並使用 ParcelableHolder欄位。


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

AIDL HAL 伺服器執行個體名稱

按照慣例,AIDL HAL 服務會使用 $package.$type/$instance。例如,震動器 HAL 的執行個體 已註冊為 android.hardware.vibrator.IVibrator/default

編寫 AIDL HAL 伺服器

@VintfStability AIDL 伺服器必須在 VINTF 資訊清單中宣告, 範例如下:

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

否則應正常註冊 AIDL 服務。執行 VTS 時 測試,所有宣告的 AIDL HAL 應該都能正常使用。

編寫 AIDL 用戶端

AIDL 用戶端必須在相容性矩陣中宣告,例如 輸入:

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

將現有的 HAL 從 HIDL 轉換為 AIDL

使用 hidl2aidl 工具將 HIDL 介面轉換為 AIDL。

hidl2aidl 功能:

  • 根據指定套件的 .hal 檔案建立 .aidl 檔案
  • 為具備所有後端的新建 AIDL 套件建立建構規則 已啟用
  • 在 Java、CPP 和 NDK 後端建立翻譯方法,以便進行翻譯 從 HIDL 類型到 AIDL 類型
  • 為具有必要依附元件的程式庫建立建構規則
  • 建立靜態斷言,確保 HIDL 和 AIDL 列舉器具有 CPP 和 NDK 後端中的相同值

請按照下列步驟將 .hal 檔案套件轉換成 .aidl 檔案:

  1. 建構位於 system/tools/hidl/hidl2aidl 中的工具。

    透過最新的原始碼建構這項工具, 無須專人管理您可以在舊版上使用最新版本轉換介面 多個分支版本

    m hidl2aidl
    
  2. 以輸出目錄結尾,然後附加要發布的套件,以執行此工具 已產生轉換

    您也可以選擇使用 -l 引數來新增授權檔案的內容 新增至所有產生的檔案頂端請務必使用正確的執照和日期。

    hidl2aidl -o <output directory> -l <file with license> <package>
    

    例如:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
    
  3. 請詳閱系統產生的檔案,並修正所有轉換問題。

    • conversion.log 包含任何未處理的問題,需要先修正。
    • 產生的 .aidl 檔案可能含有警告和建議, 需要採取行動這些註解的開頭為 //
    • 請花點時間清理及改進套件。
    • 查看 @JavaDerive 為可能需要的功能加上註解,例如 toStringequals
  4. 僅建構您需要的目標。

    • 停用未使用的後端。優先使用 NDK 後端,而非 CPP 請參閱選擇執行階段
    • 移除翻譯程式庫或其產生的任何未使用的程式碼。
  5. 請參閱 AIDL/HIDL 的主要差異

    • 使用 AIDL 內建的 Status 和例外狀況通常能改善 介面,不再需要使用其他介面專屬狀態類型。
    • 根據預設,方法中的 AIDL 介面引數並不是 @nullable 的引數 都位於 HIDL

AIDL HAL 的 SEPolicy

供應商程式碼可查看的 AIDL 服務類型必須具有 hal_service_type 屬性。否則,這項政策設定 做為任何其他 AIDL 服務 (但 HAL 有特殊屬性)。這裡 是 HAL 服務內容的定義範例:

    type hal_foo_service, service_manager_type, hal_service_type;

以平台定義的大多數服務來說,服務內容都具有正確的 類型已新增 (例如,android.hardware.foo.IFoo/default 會 已標示為 hal_foo_service)。但如果架構用戶端支援 多個執行個體名稱,必須新增額外的執行個體名稱 裝置專屬的 service_contexts 檔案。

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

建立新類型的 HAL 時,必須新增 HAL 屬性。特定 HAL 屬性可能與多種服務類型相關聯 (每個服務可能 有多個執行個體)。如果是 HAL、foo hal_attribute(foo)。這個巨集會定義 hal_foo_clienthal_foo_server。針對特定網域,hal_client_domainhal_server_domain 巨集可將網域與特定 HAL 屬性建立關聯。適用對象 舉例來說,這個 HAL 的用戶端就是系統伺服器 hal_client_domain(system_server, hal_foo)。HAL 伺服器同樣包括 hal_server_domain(my_hal_domain, hal_foo)。通常,對於指定的 HAL 屬性,我們也會建立 hal_foo_default 等網域做為參考 HAL 範例不過,有些裝置會將這些網域用於自己的伺服器。 您必須先區分多個伺服器的網域, 多個伺服器使用相同的介面,且需要不同的權限 所用的設定在所有這些巨集中,hal_foo 實際上並非 政策物件而是這些巨集用於參照 與用戶端伺服器配對相關的屬性群組。

但目前為止,我們尚未將 hal_foo_servicehal_foo 建立關聯 (hal_attribute(foo) 的屬性組合)。這個 HAL 屬性相關聯 與採用 hal_attribute_service 巨集的 AIDL HAL 服務 (HIDL HAL 使用) hal_attribute_hwservice 巨集)。例如: hal_attribute_service(hal_foo, hal_foo_service)。也就是說 hal_foo_client 程序可取得 HAL 和 hal_foo_server 註冊 HAL這些註冊規則的強制執行 由結構定義管理員 (servicemanager) 執行。請注意,服務名稱 不一定會對應至 HAL 屬性舉例來說,我們可能會看到 hal_attribute_service(hal_foo, hal_foo2_service)。一般而言 這代表服務會經常搭配使用,因此我們可考慮移除 《hal_foo2_service》,並針對所有服務使用 hal_foo_service 定義。設定多個 hal_attribute_service 的 HAL 大多是因為 原始 HAL 屬性名稱不夠廣泛,無法變更。

綜合以上條件,HAL 範例如下:

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

附加的擴充功能介面

擴充功能可附加至任何繫結器介面 (無論是頂層) 直接向服務管理員註冊的介面,或是屬於子介面。 取得擴充功能時,您必須確認擴充功能的類型為 。只能透過提供繫結器的程序設定擴充功能。

每當擴充功能修改了 現有 HAL 的功能如果需要全新的功能 不需要使用這個機制 但也可以附加擴充功能介面 已直接向服務管理員註冊。附加的擴充功能介面 最適合放置在子介面上 階層可以是深層或多實體使用全域擴充功能建立鏡像 另一項服務的繫結器介面階層 為直接附加的額外資訊提供同等功能。

如要在繫結器上設定擴充功能,請使用下列 API:

  • 在 NDK 後端中:AIBinder_setExtension
  • 在 Java 後端中:android.os.Binder.setExtension
  • 在 CPP 後端中:android::Binder::setExtension
  • 在 Rust 後端中:binder::Binder::set_extension

如要在繫結器上取得擴充功能,請使用下列 API:

  • 在 NDK 後端中:AIBinder_getExtension
  • 在 Java 後端中:android.os.IBinder.getExtension
  • 在 CPP 後端中:android::IBinder::getExtension
  • 在 Rust 後端中:binder::Binder::get_extension

如要進一步瞭解這些 API,請參閱 getExtension 函式。使用範例 擴充功能位於 hardware/interfaces/tests/extension/vibrator

AIDL 和 HIDL 的主要差異

使用 AIDL HAL 或使用 AIDL HAL 介面時,請留意差異 相較於寫入 HIDL HAL

  • AIDL 語言的語法更接近 Java。HIDL 語法與 C++ 類似。
  • 所有 AIDL 介面皆內建錯誤狀態。不建立自訂 狀態類型,在介面檔案中建立常數狀態 int CPP/NDK 後端中的 EX_SERVICE_SPECIFICServiceSpecificException 建立 HTTP 流量請參閱「錯誤 處理
  • 傳送繫結器物件時,AIDL 不會自動啟動執行緒集區。 您必須手動啟動 (詳情請參閱 執行緒 管理)。
  • 在未勾選的傳輸錯誤時,AIDL 不會取消 (HIDL Return 會在以下日期取消) 而已取消勾選的錯誤)。
  • AIDL 每個檔案只能宣告一種類型。
  • 除了輸出內容之外,還可以將 AIDL 引數指定為輸入/輸出中 參數 (沒有「同步回呼」)。
  • AIDL 使用 fd 做為原始類型,而非控制代碼。
  • HIDL 使用主要版本進行不相容的變更,並為 相容的變更。在 AIDL 中,系統會進行回溯相容的變更。 AIDL 沒有主要版本的明確概念;也就是 整合到套件名稱中舉例來說,AIDL 可能會使用套件名稱 bluetooth2
  • 根據預設,AIDL 不會沿用即時優先順序。setInheritRt 每個繫結器都必須使用函式,才能啟用即時優先順序繼承。