HIDL Java

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

HIDL 介面主要用於原生程式碼,因此 HIDL 著重於在 C++ 中自動產生高效程式碼。不過,HIDL 介面也必須可直接從 Java 使用,因為某些 Android 子系統 (例如 Telephony) 具有 Java HIDL 介面。

本節的頁面說明 HIDL 介面的 Java 前端,詳細說明如何建立、註冊及使用服務,並說明以 Java 編寫的 HAL 和 HAL 用戶端如何與 HIDL RPC 系統互動。

用戶端範例

這是範例,說明包裝函式 android.hardware.foo@1.0 中介面 IFoo 的用戶端,已註冊為服務名稱 default,以及使用自訂服務名稱 second_impl 的額外服務。

新增程式庫

如要使用對應的 HIDL 虛設程式庫,您必須新增依附元件。通常是靜態資料庫:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

如果您知道自己已提取這些程式庫的相依項目,也可以使用共用連結:

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

在 Android 10 中新增程式庫的其他考量

如果您有指定 Android 10 以上版本為目標版本的系統或供應商應用程式,可以靜態加入這些程式庫。您也可以使用 (僅) 從裝置上安裝的自訂 JAR 檔案使用 HIDL 類別,並透過現有的 uses-library 機制為系統應用程式提供穩定的 Java API。後者可節省裝置空間。詳情請參閱「實作 Java SDK 程式庫」。對於較舊的應用程式,系統會保留舊的行為。

從 Android 10 開始,這些程式庫也提供「淺層」版本。這些類別包括有問題的類別,但不包括任何依附類別。舉例來說,android.hardware.foo-V1.0-java-shallow 會納入 foo 套件中的類別,但不會納入 android.hidl.base-V1.0-java 中的類別,後者包含所有 HIDL 介面的基礎類別。如果您建立的程式庫已將偏好的介面基本類別設為依附元件,可以使用以下方式:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java-shallow", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow

應用程式的啟動 classpath 也無法再使用 HIDL 基礎和管理程式庫 (先前由於 Android 的委派第一類載入程式,因此有時會將這些做為隱藏 API 使用)。而是已移至含有 jarjar 的新命名空間,而使用這些 (必須是私密應用程式) 的應用程式必須擁有各自的獨立副本。使用 HIDL 的啟動類別路徑中的模組,必須使用這些 Java 程式庫的淺層變化版本,並將 jarjar_rules: ":framework-jarjar-rules" 新增至其 Android.bp,以便使用啟動類別路徑中存在的這些程式庫版本。

修改 Java 來源

這個服務只有一個版本 (@1.0),因此這段程式碼只會擷取該版本。如要瞭解如何處理多個不同版本的服務,請參閱介面擴充功能

import android.hardware.foo.V1_0.IFoo;
...
// retry to wait until the service starts up if it is in the manifest
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IFoo anotherServer = IFoo.getService("second_impl", true /* retry */);
server.doSomething(…);

提供服務

Java 中的架構程式碼可能需要提供介面,才能接收來自 HAL 的非同步回呼。

針對 android.hardware.foo 套件 1.0 版中的 IFooCallback 介面,您可以按照下列步驟在 Java 中實作介面:

  1. 在 HIDL 中定義介面。
  2. 開啟 /tmp/android/hardware/foo/IFooCallback.java 做為參考。
  3. 為 Java 實作建立新模組。
  4. 檢查抽象類別 android.hardware.foo.V1_0.IFooCallback.Stub,然後編寫新的類別來擴充該類別,並實作抽象方法。

查看自動產生的檔案

如要查看自動產生的檔案,請執行:

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

這些指令會產生目錄 /tmp/android/hardware/foo/1.0。針對檔案 hardware/interfaces/foo/1.0/IFooCallback.hal,這會產生檔案 /tmp/android/hardware/foo/1.0/IFooCallback.java,該檔案會封裝 Java 介面、Proxy 程式碼和 Stub (Proxy 和 Stub 都符合介面)。

-Lmakefile 會產生規則,在建構期間執行此指令,並允許您加入 android.hardware.foo-V1.0-java,並連結至適當的檔案。hardware/interfaces/update-makefiles.sh 提供的指令碼可自動為充滿介面的專案執行這項操作。本例中的路徑為相對路徑;「hardware/interfaces」可以是程式碼樹狀結構底下的臨時目錄,方便您在發布 HAL 前先行開發。

執行服務

HAL 會提供 IFoo 介面,該介面必須透過 IFooCallback 介面向架構發出非同步回呼。IFooCallback 介面並未以名稱註冊為可偵測的服務;IFoo 必須包含 setFooCallback(IFooCallback x) 等方法。

如要從 android.hardware.foo 套件的 1.0 版設定 IFooCallback,請將 android.hardware.foo-V1.0-java 新增至 Android.mk。執行服務的程式碼如下:

import android.hardware.foo.V1_0.IFoo;
import android.hardware.foo.V1_0.IFooCallback.Stub;
....
class FooCallback extends IFooCallback.Stub {
    // implement methods
}
....
// Get the service from which you will be receiving callbacks.
// This also starts the threadpool for your callback service.
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
....
// This must be a persistent instance variable, not local,
//   to avoid premature garbage collection.
FooCallback mFooCallback = new FooCallback();
....
// Do this once to create the callback service and tell the "foo-bar" service
server.setFooCallback(mFooCallback);

介面擴充功能

假設特定服務在所有裝置上實作 IFoo 介面,在特定裝置上,服務可能會提供在介面擴充功能 IBetterFoo 中實作的其他功能,如下所示:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

呼叫瞭解擴充介面的程式碼可使用 castFrom() Java 方法,將基本介面安全地轉換為擴充介面:

IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IBetterFoo extendedService = IBetterFoo.castFrom(baseService);
if (extendedService != null) {
  // The service implements the extended interface.
} else {
  // The service implements only the base interface.
}