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 的新命名空间中,并且使用这些内容的应用(主要是特权应用)必须拥有自己的单独副本。使用 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 中找到自动为接口众多的项目执行此操作的脚本。本示例中的路径是相对路径;硬件/接口可能是代码树下的一个临时目录,让您能够先开发 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.
}