接口

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();
    }
    ...
};

要使服务器接口的实现可供客户端使用,您可以

  1. 使用 hwservicemanager 注册接口实现(详见下文),

    或者

  2. 传递接口实现作为接口方法的参数(有关详情,请参阅异步回调)。

注册接口实现后,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 接口的实现,该接口包含一个名为 serviceDied() 的方法,当托管接口的进程死亡时,会从 RPC 线程池中的线程调用该方法

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);