接口和软件包

HIDL 是围绕接口进行编译的,接口是面向对象的语言使用的一种用来定义行为的抽象类型。每个接口都是软件包的一部分。

软件包

软件包名称可以具有子级,例如 package.subpackage。已发布的 HIDL 软件包的根目录是 hardware/interfacesvendor/vendorName(例如 Pixel 设备的 vendor/google)。软件包名称在根目录下形成一个或多个子目录;定义软件包的所有文件都位于同一目录下。例如,package android.hardware.example.extension.light@2.0 可以在 hardware/interfaces/example/extension/light/2.0 下找到。

下表列出了软件包前缀和位置:

软件包前缀 位置
android.hardware.* hardware/interfaces/*
android.frameworks.* frameworks/hardware/interfaces/*
android.system.* system/hardware/interfaces/*
android.hidl.* system/libhidl/transport/*

软件包目录种包含扩展名为 .hal 的文件。每个文件均必须包含一个指定文件所属的软件包和版本的 package 语句。文件 types.hal(如果存在)并不定义接口,而是定义软件包中每个接口可以访问的数据类型。

接口定义

除了 types.hal 之外,其他 .hal 文件均定义一个接口。例如,接口的定义通常如下所示:

interface IBar extends IFoo { // IFoo is another interface
    // embedded types
    struct MyStruct {/*...*/};

    // interface methods
    create(int32_t id) generates (MyStruct s);
    close();
};

不含显式 extends 声明的接口会从 android.hidl.base@1.0::IBase(类似于 Java 中的 java.lang.Object)隐式扩展。隐式导入的 IBase 接口声明了多种不应也不能在用户定义的接口中重新声明或以其他方式使用的预留方法。这些方法包括:

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

导入

import 语句是用于访问其他软件包中的软件包接口和类型的 HIDL 机制。import 语句本身涉及两个实体:

  • 导入实体:可以是软件包或接口;
  • 被导入实体:也可以是软件包或接口。

导入实体由 import 语句的位置决定。当该语句位于软件包的 types.hal 中时,导入的内容对整个软件包是可见的;这是软件包级导入。当该语句位于接口文件中时,导入实体是接口本身;这是接口级导入。

被导入实体由 import 关键字后面的值决定。该值不必是完全限定名称;如果某个组成部分被删除了,系统会自动使用当前软件包中的信息填充该组成部分。 对于完全限定值,支持的导入情形有以下几种:

  • 完整软件包导入。如果该值是一个软件包名称和版本(语法见下文),则系统会将整个软件包导入至导入实体中。
  • 部分导入
    • 如果该值是一个接口,则系统会将该软件包的 types.hal 和该接口导入至导入实体中。
    • 如果该值是一个在 types.hal 定义的 UDT,则系统仅会将该 UDT 导入至导入实体中(不导入 types.hal 中的其他类型)。
  • 仅类型导入。如果该值将上文所述的“部分导入”的语法与关键字 types 而不是接口名称配合使用,则系统仅会导入指定软件包的 types.hal 中的 UDT。

导入实体可以访问以下各项的组合:

  • types.hal 中定义的被导入的软件包的常见 UDT;
  • 被导入的软件包的接口(完整软件包导入)或指定接口(部分导入),以便调用它们、向其传递句柄和/或从其继承句柄。

导入语句使用完全限定类型名称语法来提供被导入的软件包或接口的名称和版本:

import android.hardware.nfc@1.0;            // import a whole package
import android.hardware.example@1.0::IQuux; // import an interface and types.hal
import android.hardware.example@1.0::types; // import just types.hal

接口继承

接口可以是之前定义的接口的扩展。扩展可以是以下三种类型中的一种:

  • 接口可以向其他接口添加功能,并按原样纳入其 API。
  • 软件包可以向其他软件包添加功能,并按原样纳入其 API。
  • 接口可以从软件包或特定接口导入类型。

接口只能扩展一个其他接口(不支持多重继承)。具有非零 Minor 版本号的软件包中的每个接口必须扩展一个以前版本的软件包中的接口。例如,如果 4.0 版本的软件包 derivative 中的接口 IBar 是基于(扩展了)1.2 版本的软件包 original 中的接口 IFoo,并且您又创建了 1.3 版本的软件包 original,则 4.1 版本的 IBar 不能扩展 1.3 版本的 IFoo。相反,4.1 版本的 IBar 必须扩展 4.0 版本的 IBar,因为后者是与 1.2 版本的 IFoo 绑定的。 如果需要,5.0 版本的 IBar 可以扩展 1.3 版本的 IFoo

接口扩展并不意味着生成的代码中存在代码库依赖关系或跨 HAL 包含关系,接口扩展只是在 HIDL 级别导入数据结构和方法定义。HAL 中的每个方法必须在相应 HAL 中实现。

供应商扩展

在某些情况下,供应商扩展会作为以下基础对象的子类予以实现:代表其扩展的核心接口的基础对象。同一对象会同时在基础 HAL 名称和版本下,以及扩展的(供应商)HAL 名称和版本下注册。

版本编号

软件包分版本,且接口的版本和其软件包的版本相同。版本用两个整数表示:major.minor。

  • Major 版本不向后兼容。递增 Major 版本号会将 Minor 版本重置为 0。
  • Minor 版本向后兼容。如果递增 Minor 版本号,则意味着较新版本完全向后兼容之前的版本。您可以添加新的数据结构和方法,但不能更改现有的数据结构或方法签名。

为实现与框架的更广泛的兼容性,可同时在一台设备上提供 HAL 的多个 Major 版本。虽然同时也可以在一台设备上提供多个 Minor 版本,但是由于 Minor 版本向后兼容,因此不必在最新 Minor 版本以外,为各个 Major 版本提供其他额外支持。

有关版本编号和供应商扩展的更多详细信息,请参阅 HIDL 版本编号