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

HIDL 基礎庫和管理器庫也不再在應用程式的啟動類別路徑上可用(以前,由於 Android 的委託優先類別載入器,它們有時被用作隱藏 API)。相反,它們已被移動到jarjar的新命名空間中,並且使用這些的應用程式(必須是 priv 應用程式)必須擁有自己的單獨副本。使用 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 介面、代理程式碼和存根(兩個代理程式和存根符合接口)。

-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.
}