HIDL 软件包中定义的每个接口在其软件包的命名空间内都有自己的自动生成的 C++ 类。客户端和服务器会通过不同的方式处理接口:
- 服务器实现接口。
- 客户端在接口上调用方法。
接口可以由服务器按名称注册,也可以作为参数传递到以 HIDL 定义的方法。例如,框架代码可设定一个从 HAL 接收异步消息的接口,并将该接口直接传递到 HAL(无需注册该接口)。
服务器实现
实现 IFoo
接口的服务器必须包含自动生成的 IFoo
头文件:
#include <android/hardware/samples/1.0/IFoo.h>
该标头由 IFoo
接口的共享库自动导出以进行链接。IFoo.hal
示例:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
IFoo 接口的服务器实现的示例框架:
// From the IFoo.h header using android::hardware::samples::V1_0::IFoo; class FooImpl : public IFoo { Return<void> someMethod(foo my_foo, someMethod_cb _cb) { vec<uint32_t> return_data; // Compute return_data _cb(return_data); return Void(); } ... };
如需使服务器接口的实现可供客户端使用,您可以:
- 向
hwservicemanager
注册接口实现(详情见下文)
或者
- 将接口实现作为接口方法的参数进行传递(详情见异步回调部分)。
注册接口实现时,hwservicemanager
进程会按名称和版本号跟踪设备上正在运行的已注册 HIDL 接口。服务器可以按名称注册 HIDL 接口实现,而客户端则可以按名称和版本号请求服务实现。该进程可提供 HIDL 接口 android.hidl.manager@1.0::IServiceManager
。
每个自动生成的 HIDL 接口头文件(例如 IFoo.h
)都有一个 registerAsService()
方法,可用于向 hwservicemanager
注册接口实现。唯一一个必需的参数是接口实现的名称,因为稍后客户端会使用此名称从 hwservicemanager
检索接口:
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
hwservicemanager
会将 [package@version::interface, instance_name]
组合视为唯一,以使不同的接口(或同一接口的不同版本)能够采用完全相同的实例名称无冲突地注册。如果您调用的 registerAsService()
具有完全相同的软件包版本、接口和实例名称,则 hwservicemanager
将丢弃对先前注册的服务的引用,并使用新的服务。
客户端实现
和服务器一样,客户端也必须 #include
其引用的每个接口:
#include <android/hardware/samples/1.0/IFoo.h>
客户端可以通过两种方式获取接口:
- 通过
I<InterfaceName>::getService
(通过hwservicemanager
) - 通过接口方法
每个自动生成的接口头文件都有一个静态 getService
方法,可用于从 hwservicemanager
检索服务实例:
// getService returns nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
现在,客户端有一个 IFoo
接口,并可以向其(将其当作本地类实现)调用方法。实际上,实现可以在同一个进程中运行,也可以在不同的进程中运行,甚至还可以在另一个设备上运行(通过 HAL 远程处理)。由于客户端在 1.0
版软件包中包含的 IFoo
对象上调用 getService
,因此仅当服务器实现与 1.0
客户端兼容时,hwservicemanager
才会返回该实现。实际上,这意味着系统只会返回版本为 1.n
的服务器实现(x.(y+1)
版本的接口必须扩展(继承自)x.y
)。
此外,您也可以使用 castFrom
方法,在不同的接口之间进行类型转换。该方法会通过以下方式发挥作用:对远程接口进行 IPC 调用,以确保底层类型与正在请求的类型相同。如果请求的类型不可用,则返回 nullptr
。
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
异步回调
很多现有的 HAL 实现会与异步硬件通信,这意味着它们需要以异步方式通知客户端已发生的新事件。HIDL 接口可以用作异步回调,因为 HIDL 接口函数可以将 HIDL 接口对象用作参数。
接口文件 IFooCallback.hal
示例:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
IFoo
中采用 IFooCallback
参数的新方法示例:
package android.hardware.samples@1.0; interface IFoo { struct Foo { int64_t someValue; handle myHandle; }; someMethod(Foo foo) generates (int32_t ret); anotherMethod() generates (vec<uint32_t>); registerCallback(IFooCallback callback); };
使用 IFoo
接口的客户端是 IFooCallback
接口的服务器;它会提供 IFooCallback
的实现:
class FooCallback : public IFooCallback { Return<void> sendEvent(uint32_t event_id) { // process the event from the HAL } Return<void> sendData(const hidl_vec<uint8_t>& data) { // process data from the HAL } };
它也可以简单地通过 IFoo
接口的现有实例来传递该实现:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
实现 IFoo
的服务器会将此作为 sp<IFooCallback>
对象进行接收。它可以存储该回调,而且只要它想使用此接口,均可回调到客户端。
服务终止通知接收方
由于服务实现可以在不同的进程中运行,因此可能会出现实现接口的进程已终止但客户端仍保持活动状态的情况。对托管在已终止进程中的接口对象的任何调用都将失败,并会返回相应的传输错误(isOK()
返回 false
)。如需从这类故障中恢复正常,唯一的方法是通过调用 I<InterfaceName>::getService()
来请求服务的新实例。仅当崩溃的进程已重新启动且已向 servicemanager
重新注册其服务时,这种方法才有效(对 HAL 实现而言通常如此)。
接口的客户端也可以注册为服务终止通知接收方,以便在服务终止时收到通知,而不是被动地应对这种情况。如需在检索的 IFoo
接口上注册此类通知,客户端可执行以下操作:
foo->linkToDeath(recipient, 1481 /* cookie */);
recipient
参数必须是由 HIDL 提供的 android::hardware::hidl_death_recipient
接口的实现,该接口中包含在托管接口的进程终止时从 RPC 线程池中的线程调用的单个 serviceDied()
方法:
class MyDeathRecipient : public android::hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) { // Deal with the fact that the service died } }
cookie
参数包含通过 linkToDeath()
传入的 Cookie,而 who
参数则包含一个弱指针,它指向表示客户端中的服务的对象。在上面给出的调用示例中,cookie
等于 1481,who
等于 foo
。
您也可以在注册服务终止通知接收方后将其取消注册:
foo->unlinkToDeath(recipient);