Android 框架学习(4)—— HIDL

一、HIDL定义

        HIDL是用于指定HAL与其用户之间接口的一个接口描述语言(Interface Description Language,发音为“hide-l”)。HIDL允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL是用于在可以独立编译的代码库之间进行通信的系统。
        HIDL旨在用于进程间通信 (IPC)。进程之间的通信经过Binder化。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。
        HIDL的设计目的:主要是将Framework和HAL隔离开,Framework可以单独覆盖、更新而不用对HAL做改动。引入HIDL,可以将HAL从system.img中移除出去,方便Android版本的升级。

二、HIDL架构模式

        HIDL的演化历程:
在这里插入图片描述

  • Passthrough模式
  • Binder化的Passthrough HALs

        什么是Binder化?
        一直以来,供应商进程都使用Binder进程间通信(IPC)技术进行通信。在Android O中,/dev/binder设备节点成为了框架进程的专属节点,这意味着供应商进程将无法再访问该节点。供应商进程可以访问/dev/hwbinder,但必须将其AIDL接口转为使用HIDL。

三、软件包

软件包前缀 位置
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声明的接口会从[email protected]::IBase(类似于Java中的java.lang.Object)隐式扩展。

五、导入

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

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

        导入实体由import语句的位置决定。当该语句位于软件包的types.hal中时,导入的内容对整个软件包是可见的;这是软件包级导入。当该语句位于接口文件中时,导入实体是接口本身;这是接口级导入。
        被导入实体由import关键字后面的值决定。该值不必是完全限定名称;如果某个组成部分被删除了,系统会自动使用当前软件包中的信息填充该组成部分。
        对于完全限定值,支持的导入情形有以下几种:

  • 完整软件包导入

        如果该值是一个软件包名称和版本(语法见下文),则系统会将整个软件包导入至导入实体。

import android.hardware.nfc@1.0;            // import a whole package
  • 部分导入

        如果值为:

  1. 一个接口,则系统会将该软件包的types.hal和该接口导入至导入实体中。
  2. types.hal中定义的UDT,则系统仅会将该UDT导入至导入实体中(不导入types.hal中的其他类型)。
  • 仅类型导入

        如果该值将上文所述的“部分导入”的语法与关键字types而不是接口名称配合使用,则系统仅会导入指定软件包的types.hal中的UDT。

import android.hardware.example@1.0::types; // import just types.hal

六、接口继承

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

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

        接口只能扩展一个其他接口(不支持多重继承)。

七、接口哈希

        哈希是一种旨在防止意外更改接口并确保接口更改经过全面审查的机制。这种机制是必需的,因为HIDL接口带有版本编号,也就是说,接口一经发布便不得再更改,但不会影响应用二进制接口(ABI)的情况(例如更正备注)除外。
        每个软件包根目录(即映射到 hardware/interfacesandroid.hardware或映射到vendor/foo/hardware/interfacesvendor.foo)都必须包含一个列出所有已发布HIDL接口文件的current.txt文件。

# current.txt files support comments starting with a ‘#' character
# this file, for instance, would be vendor/foo/hardware/interfaces/current.txt

# Each line has a SHA-256 hash followed by the name of an interface.
# They have been shortened in this doc for brevity but they are
# 64 characters in length in an actual current.txt file.
d4ed2f0e...995f9ec4 vendor.awesome.foo@1.0::IFoo # comments can also go here

# types.hal files are also noted in types.hal files
c84da9f5...f8ea2648 vendor.awesome.foo@1.0::types

# Multiple hashes can be in the file for the same interface. This can be used
# to note how ABI sustaining changes were made to the interface.
# For instance, here is another hash for IFoo:

# Fixes type where "FooCallback" was misspelled in comment on "FooStruct"
822998d7...74d63b8c vendor.awesome.foo@1.0::IFoo

        hidl-gen是安卓架构HIDL编译工具。它的源码目录为system/tools/hidl,编译生成的可执行文件路径为out/host/linux-x86/bin/hidl-gen,可以手动将哈希添加到current.txt文件中,也可以使用hidl-gen添加。以下代码段提供了可与hidl-gen搭配使用来管理current.txt文件的命令示例(哈希已缩短):

$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport vendor.awesome.nfc@1.0::types
9626fd18...f9d298a6 vendor.awesome.nfc@1.0::types
$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport vendor.awesome.nfc@1.0::INfc
07ac2dc9...11e3cf57 vendor.awesome.nfc@1.0::INfc
$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport vendor.awesome.nfc@1.0
9626fd18...f9d298a6 vendor.awesome.nfc@1.0::types
07ac2dc9...11e3cf57 vendor.awesome.nfc@1.0::INfc
f2fe5442...72655de6 vendor.awesome.nfc@1.0::INfcClientCallback
$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport vendor.awesome.nfc@1.0 >> vendor/awesome/hardware/interfaces/current.txt

        hidl-gen生成的每个接口定义库都包含哈希,通过调用IBase::getHashChain可检索这些哈希。

八、注册服务

        HIDL接口服务器(实现接口的对象)可注册为已命名的服务。注册的名称不需要与接口或软件包名称相关。如果没有指定名称,则使用名称“默认”;这应该用于不需要注册同一接口的两个实现的 HAL。例如,在每个接口中定义的服务注册的C++调用是:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if need

        HIDL接口的版本包含在接口本身中。版本自动与服务注册关联,并可通过每个HIDL接口上的方法调用 android::hardware::IInterface::getInterfaceVersion())进行检索。服务器对象不需要注册,并可通过HIDL方法参数传递到其他进程,相应的接收进程会向服务器发送HIDL方法调用。

九、发现服务

        客户端代码按名称和版本请求指定的接口,并对所需的HAL类调用getService

// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = 1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService; service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService; alternateService = 1_1.IFooService.getService("another", true /* retry */);

        每个版本的HIDL接口都会被视为单独的接口。因此,IFooService版本1.1和IFooService版本2.2都可以注册为foo_service,并且两个接口上的getService("foo_service")都可获取该接口的已注册服务。因此,在大多数情况下,注册或发现服务均无需提供名称参数(也就是说名称为“默认”)。

十、服务终止通知

        想要在Service终止时收到通知的客户端会接收到框架传送的终止通知。要接收通知,客户端必须:

  • 将HIDL类/接口hidl_death_recipient(位于C++代码中,而非HIDL中)归入子类。
  • 替换其serviceDied()方法。
  • 实例化hidl_death_recipient子类的对象。
  • 在要监控的服务上调用linkToDeath()方法,并传入IDeathRecipient的接口对象。请注意,此方法并不具备在其上调用它的终止接收方或代理的所有权。

        伪代码示例:

class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);
原创文章 45 获赞 51 访问量 5442

猜你喜欢

转载自blog.csdn.net/hezhanran/article/details/105700450