HIDL Java

在 Android 8.0 中,Android 操作系统经过重新架构,从而在设备无关的 Android 平台与设备和供应商特定的代码之间定义了清晰的接口。Android 已经在 HAL 接口的形式中定义了许多此类接口,这些接口在 hardware/libhardware 中定义为 C 标头。HIDL 使用稳定、版本化的接口替换了这些 HAL 接口,这些接口可以是 Java(如下所述),也可以是 C++ 中的客户端和服务器端 HIDL 接口。

HIDL 接口主要旨在用于原生代码,因此 HIDL 专注于 C++ 中高效代码的自动生成。但是,HIDL 接口也必须可直接从 Java 使用,因为某些 Android 子系统(例如电话服务)具有 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 或更高版本为目标的系统或供应商应用,则可以静态包含这些库。您还可以使用(仅限)HIDL 类,这些类来自使用现有 uses-library 机制为系统应用提供的稳定 Java API 安装在设备上的自定义 JAR。后一种方法可以节省设备上的空间。如需了解详情,请参阅实现 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 库的浅层变体,并在其 Android.bp 中添加 jarjar_rules: ":framework-jarjar-rules",以使用启动类路径中存在的这些库的版本。

修改您的 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。

运行服务

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