HAL 的 AIDL

Android 11 引入了在 Android 中将 AIDL 用于 HAL 的功能。这使得在没有 HIDL 的情况下实现部分 Android 成为可能。尽可能将 HAL 转换为专门使用 AIDL(当上游 HAL 使用 HIDL 时,必须使用 HIDL)。

使用 AIDL 在框架组件(例如system.img中的组件)和硬件组件(例如vendor.img中的组件)之间进行通信的 HAL 必须使用稳定的 AIDL。但是,要在一个分区内进行通信,例如从一个 HAL 到另一个,对使用的 IPC 机制没有限制。

动机

AIDL 的存在时间比 HIDL 长,并且在许多其他地方使用,例如在 Android 框架组件之间或在应用程序中。现在 AIDL 具有稳定性支持,可以使用单个 IPC 运行时实现整个堆栈。 AIDL 还具有比 HIDL 更好的版本控制系统。

  • 使用单一 IPC 语言意味着只有一件事需要学习、调试、优化和保护。
  • AIDL 支持接口所有者的就地版本控制:
    • 所有者可以将方法添加到接口的末尾,或将字段添加到 parcelables。这意味着多年来版本代码更容易,而且年复一年的成本更小(类型可以就地修改,不需要为每个接口版本添加额外的库)。
    • 扩展接口可以在运行时而不是在类型系统中附加,因此无需将下游扩展重新定位到较新版本的接口上。
  • 现有的 AIDL 接口可以在其所有者选择稳定它时直接使用。以前,必须在 HIDL 中创建接口的完整副本。

编写 AIDL HAL 接口

对于要在系统和供应商之间使用的 AIDL 接口,该接口需要进行两个更改:

  • 每个类型定义都必须用@VintfStability注释。
  • aidl_interface声明需要包含stability: "vintf", .

只有接口的所有者才能进行这些更改。

当您进行这些更改时,接口必须在VINTF 清单中才能工作。使用 VTS 测试vts_treble_vintf_vendor_test进行测试(以及相关要求,例如验证已发布的接口是否已冻结)。您可以在没有这些要求的情况下使用@VintfStability接口,方法是在 NDK 后端中调用AIBinder_forceDowngradeToLocalStability ,在 C++ 后端中调用android::Stability::forceDowngradeToLocalStability ,或者在 Java 后端中android.os.Binder#forceDowngradeToSystemStability ,然后再发送活页夹对象到另一个进程。 Java 不支持将服务降级到供应商稳定性,因为所有应用程序都在系统上下文中运行。

此外,为了最大程度地提高代码可移植性并避免不必要的附加库等潜在问题,请禁用 CPP 后端。

请注意,以下代码示例中backends的使用是正确的,因为存在三个后端(Java、NDK 和 CPP)。下面的代码说明了如何具体选择 CPP 后端以禁用它。

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

查找 AIDL HAL 接口

用于 HAL 的 AOSP 稳定 AIDL 接口与 HIDL 接口位于相同的基本目录中,位于aidl文件夹中。

  • 硬件/接口
  • 框架/硬件/接口
  • 系统/硬件/接口

您应该将扩展接口放入vendorhardware中的其他hardware/interfaces子目录中。

扩展接口

Android 的每个版本都有一组官方 AOSP 接口。当 Android 合作伙伴想要向这些接口添加功能时,他们不应直接更改这些接口,因为这意味着他们的 Android 运行时与 AOSP Android 运行时不兼容。对于 GMS 设备,避免更改这些接口也是确保 GSI 映像可以继续工作的原因。

扩展可以通过两种不同的方式注册:

  • 在运行时,请参阅附加的扩展
  • 独立,在全球和 VINTF 中注册。

然而,当特定于供应商(意味着不是上游 AOSP 的一部分)组件使用该接口时,注册了一个扩展,就不存在合并冲突的可能性。但是,当下游对上游 AOSP 组件进行修改时,可能会导致合并冲突,建议采用以下策略:

  • 接口添加可以在下一个版本中上游到 AOSP
  • 允许进一步灵活性的接口添加,没有合并冲突,可以在下一个版本中上游

扩展 Parcelables:ParcelableHolder

ParcelableHolder是一个Parcelable ,它可以包含另一个ParcelableParcelableHolder的主要用例是使Parcelable可扩展。例如,设备实施者希望能够扩展 AOSP 定义的ParcelableAospDefinedParcelable以包含其增值功能的图像。

以前没有ParcelableHolder ,设备实现者无法修改 AOSP 定义的稳定 AIDL 接口,因为添加更多字段会出错:

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

从前面的代码中可以看出,这种做法被打破了,因为在 Android 的下一版本中修改 Parcelable 时,设备实现者添加的字段可能会发生冲突。

使用ParcelableHolder ,parcelable 的所有者可以在Parcelable中定义扩展点。

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

然后设备实现者可以为他们的扩展定义他们自己的Parcelable

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

最后,新的Parcelable可以通过ParcelableHolder字段附加到原始Parcelable


// 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 运行时构建

AIDL有三种不同的后端:Java、NDK、CPP。要使用 Stable AIDL,您必须始终在system/lib*/libbinder.so上使用 libbinder 的系统副本,并在/dev/binder上进行对话。对于供应商映像上的代码,这意味着无法使用libbinder (来自 VNDK):该库具有不稳定的 C++ API 和不稳定的内部结构。相反,本地供应商代码必须使用 AIDL 的 NDK 后端,链接到libbinder_ndk (由系统libbinder.so支持),并链接到由aidl_interface条目创建的-ndk_platform库。

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属性。否则,sepolicy 配置与任何其他 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实际上并不是一个 sepolicy 对象。相反,这些宏使用此令牌来引用与客户端服务器对关联的属性组。

但是,到目前为止,我们还没有关联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

要获取活页夹的扩展,请使用以下 API:

  • 在 NDK 后端: AIBinder_getExtension
  • 在 Java 后端: android.os.IBinder.getExtension
  • 在 CPP 后端: android::IBinder::getExtension

您可以在相应后端的getExtension函数的文档中找到有关这些 API 的更多信息。可以在hardware/interfaces/tests/extension/vibrator中找到有关如何使用扩展的示例。

AIDL/HIDL 的主要区别

使用 AIDL HAL 或使用 AIDL HAL 接口时,请注意与编写 HIDL HAL 相比的差异。

  • AIDL 语言的语法更接近 Java。 HIDL 语法类似于 C++。
  • 所有 AIDL 接口都有内置的错误状态。不要创建自定义状态类型,而是在接口文件中创建常量状态整数,并在 CPP/NDK 后端中使用EX_SERVICE_SPECIFIC并在 Java 后端中使用ServiceSpecificException 。请参阅错误处理
  • 发送活页夹对象时,AIDL 不会自动启动线程池。它们必须手动启动(参见线程管理)。
  • AIDL 不会因未经检查的传输错误而中止(HIDL Return aborts on unchecked errors)。
  • AIDL 只能为每个文件声明一种类型。
  • 除了输出参数之外,AIDL 参数还可以指定为 in/out/inout(没有“同步回调”)。
  • AIDL 使用 fd 作为原始类型而不是句柄。
  • HIDL 使用主要版本进行不兼容的更改,使用次要版本进行兼容的更改。在 AIDL 中,向后兼容的更改是就地完成的。 AIDL 没有明确的主要版本概念;相反,它被合并到包名称中。例如,AIDL 可能使用包名bluetooth2
  • AIDL 默认不继承实时优先级。 setInheritRt函数必须用于每个绑定器以启用实时优先级继承。
,

Android 11 引入了在 Android 中将 AIDL 用于 HAL 的功能。这使得在没有 HIDL 的情况下实现部分 Android 成为可能。尽可能将 HAL 转换为专门使用 AIDL(当上游 HAL 使用 HIDL 时,必须使用 HIDL)。

使用 AIDL 在框架组件(例如system.img中的组件)和硬件组件(例如vendor.img中的组件)之间进行通信的 HAL 必须使用稳定的 AIDL。但是,要在一个分区内进行通信,例如从一个 HAL 到另一个,对使用的 IPC 机制没有限制。

动机

AIDL 的存在时间比 HIDL 长,并且在许多其他地方使用,例如在 Android 框架组件之间或在应用程序中。现在 AIDL 具有稳定性支持,可以使用单个 IPC 运行时实现整个堆栈。 AIDL 还具有比 HIDL 更好的版本控制系统。

  • 使用单一 IPC 语言意味着只有一件事需要学习、调试、优化和保护。
  • AIDL 支持接口所有者的就地版本控制:
    • 所有者可以将方法添加到接口的末尾,或将字段添加到 parcelables。这意味着多年来版本代码更容易,而且年复一年的成本更小(类型可以就地修改,不需要为每个接口版本添加额外的库)。
    • 扩展接口可以在运行时而不是在类型系统中附加,因此无需将下游扩展重新定位到较新版本的接口上。
  • 现有的 AIDL 接口可以在其所有者选择稳定它时直接使用。以前,必须在 HIDL 中创建接口的完整副本。

编写 AIDL HAL 接口

对于要在系统和供应商之间使用的 AIDL 接口,该接口需要进行两个更改:

  • 每个类型定义都必须用@VintfStability注释。
  • aidl_interface声明需要包含stability: "vintf", .

只有接口的所有者才能进行这些更改。

当您进行这些更改时,接口必须在VINTF 清单中才能工作。使用 VTS 测试vts_treble_vintf_vendor_test进行测试(以及相关要求,例如验证已发布的接口是否已冻结)。您可以在没有这些要求的情况下使用@VintfStability接口,方法是在 NDK 后端中调用AIBinder_forceDowngradeToLocalStability ,在 C++ 后端中调用android::Stability::forceDowngradeToLocalStability ,或者在 Java 后端中android.os.Binder#forceDowngradeToSystemStability ,然后再发送活页夹对象到另一个进程。 Java 不支持将服务降级到供应商稳定性,因为所有应用程序都在系统上下文中运行。

此外,为了最大程度地提高代码可移植性并避免不必要的附加库等潜在问题,请禁用 CPP 后端。

请注意,以下代码示例中backends的使用是正确的,因为存在三个后端(Java、NDK 和 CPP)。下面的代码说明了如何具体选择 CPP 后端以禁用它。

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

查找 AIDL HAL 接口

用于 HAL 的 AOSP 稳定 AIDL 接口与 HIDL 接口位于相同的基本目录中,位于aidl文件夹中。

  • 硬件/接口
  • 框架/硬件/接口
  • 系统/硬件/接口

您应该将扩展接口放入vendorhardware中的其他hardware/interfaces子目录中。

扩展接口

Android 的每个版本都有一组官方 AOSP 接口。当 Android 合作伙伴想要向这些接口添加功能时,他们不应直接更改这些接口,因为这意味着他们的 Android 运行时与 AOSP Android 运行时不兼容。对于 GMS 设备,避免更改这些接口也是确保 GSI 映像可以继续工作的原因。

扩展可以通过两种不同的方式注册:

  • 在运行时,请参阅附加的扩展
  • 独立,在全球和 VINTF 中注册。

然而,当特定于供应商(意味着不是上游 AOSP 的一部分)组件使用该接口时,注册了一个扩展,就不存在合并冲突的可能性。但是,当下游对上游 AOSP 组件进行修改时,可能会导致合并冲突,建议采用以下策略:

  • 接口添加可以在下一个版本中上游到 AOSP
  • 允许进一步灵活性的接口添加,没有合并冲突,可以在下一个版本中上游

扩展 Parcelables:ParcelableHolder

ParcelableHolder是一个Parcelable ,它可以包含另一个ParcelableParcelableHolder的主要用例是使Parcelable可扩展。例如,设备实施者希望能够扩展 AOSP 定义的ParcelableAospDefinedParcelable以包含其增值功能的图像。

以前没有ParcelableHolder ,设备实现者无法修改 AOSP 定义的稳定 AIDL 接口,因为添加更多字段会出错:

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

从前面的代码中可以看出,这种做法被打破了,因为在 Android 的下一版本中修改 Parcelable 时,设备实现者添加的字段可能会发生冲突。

使用ParcelableHolder ,parcelable 的所有者可以在Parcelable中定义扩展点。

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

然后设备实现者可以为他们的扩展定义他们自己的Parcelable

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

最后,新的Parcelable可以通过ParcelableHolder字段附加到原始Parcelable


// 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 运行时构建

AIDL有三种不同的后端:Java、NDK、CPP。要使用 Stable AIDL,您必须始终在system/lib*/libbinder.so上使用 libbinder 的系统副本,并在/dev/binder上进行对话。对于供应商映像上的代码,这意味着无法使用libbinder (来自 VNDK):该库具有不稳定的 C++ API 和不稳定的内部结构。相反,本地供应商代码必须使用 AIDL 的 NDK 后端,链接到libbinder_ndk (由系统libbinder.so支持),并链接到由aidl_interface条目创建的-ndk_platform库。

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属性。否则,sepolicy 配置与任何其他 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实际上并不是一个 sepolicy 对象。相反,这些宏使用此令牌来引用与客户端服务器对关联的属性组。

但是,到目前为止,我们还没有关联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

要获取活页夹的扩展,请使用以下 API:

  • 在 NDK 后端: AIBinder_getExtension
  • 在 Java 后端: android.os.IBinder.getExtension
  • 在 CPP 后端: android::IBinder::getExtension

您可以在相应后端的getExtension函数的文档中找到有关这些 API 的更多信息。可以在hardware/interfaces/tests/extension/vibrator中找到有关如何使用扩展的示例。

AIDL/HIDL 的主要区别

使用 AIDL HAL 或使用 AIDL HAL 接口时,请注意与编写 HIDL HAL 相比的差异。

  • AIDL 语言的语法更接近 Java。 HIDL 语法类似于 C++。
  • 所有 AIDL 接口都有内置的错误状态。不要创建自定义状态类型,而是在接口文件中创建常量状态整数,并在 CPP/NDK 后端中使用EX_SERVICE_SPECIFIC并在 Java 后端中使用ServiceSpecificException 。请参阅错误处理
  • 发送活页夹对象时,AIDL 不会自动启动线程池。它们必须手动启动(参见线程管理)。
  • AIDL 不会因未经检查的传输错误而中止(HIDL Return aborts on unchecked errors)。
  • AIDL 只能为每个文件声明一种类型。
  • 除了输出参数之外,AIDL 参数还可以指定为 in/out/inout(没有“同步回调”)。
  • AIDL 使用 fd 作为原始类型而不是句柄。
  • HIDL 使用主要版本进行不兼容的更改,使用次要版本进行兼容的更改。在 AIDL 中,向后兼容的更改是就地完成的。 AIDL 没有明确的主要版本概念;相反,它被合并到包名称中。例如,AIDL 可能使用包名bluetooth2
  • AIDL 默认不继承实时优先级。 setInheritRt函数必须用于每个绑定器以启用实时优先级继承。