Android_Architecture_HIDL(General)--Services & Data Transfer(服务和数据转移)

说明:转载自官方英文+中文版https://source.android.com/devices/architecture/hidl/services


Services & Data Transfer 服务和数据转移

This section describes how to register and discover services and how to send data to a service by calling methods defined in interfaces in .hal files.

本部分介绍了如何注册和发现服务,以及如何通过调用 .hal 文件内的接口中定义的方法将数据发送到服务。

Registering services 注册服务


HIDL interface servers (objects implementing the interface) can be registered as named services. The registered name need not be related to the interface or package name. If no name is specified, the name “default” is used; this should be used for HALs that do not need to register two implementations of the same interface. For example, the C++ call for service registration defined in each interface is:

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

The version of a HIDL interface is included in the interface itself. It is automatically associated with service registration and can be retrieved via a method call (android::hardware::IInterface::getInterfaceVersion()) on every HIDL interface. Server objects need not be registered and can be passed via HIDL method parameters to another process that will make HIDL method calls into the server.

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

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

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

Discovering services 发现服务


Requests by client code are made for a given interface by name and by version, calling getService on the desired HAL class:

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

Each version of a HIDL interface is treated as a separate interface. Thus, IFooService version 1.1 and IFooService version 2.2 can both be registered as “foo_service” and getService("foo_service") on either interface gets the registered service for that interface. This is why, in most cases, no name parameter needs to be supplied for registration or discovery (meaning name “default”).


The Vendor Interface Object also plays a part in the transport method of the returned interface. For an interface IFoo in package [email protected], the interface returned by IFoo::getService always uses the transport method declared for android.hardware.foo in the device manifest if the entry exists; and if the transport method is not available, nullptr is returned.


In some cases, it may be necessary to continue immediately even without getting the service. This can happen (for instance) when a client wants to manage service notifications itself or in a diagnostic program (such as atrace) which needs to get all hwservices and retrieve them. In this case, additional APIs are provided such as tryGetService in C++ or getService("instance-name", false) in Java. The legacy API getService provided in Java also must be used with service notifications. Using this API does not avoid the race condition where a server registers itself after the client requests it with one of these no-retry APIs.

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

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

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

供应商接口对象还会影响所返回接口的传输方法。对于软件包 [email protected] 中的接口 IFooIFoo::getService 返回的接口始终使用设备清单中针对 android.hardware.foo 声明的传输方法(如果相应条目存在的话);如果该传输方法不存在,则返回 nullptr。

在某些情况下,即使没有获得相关服务,也可能需要立即继续。例如,当客户端希望自行管理服务通知或者在需要获取所有 hwservice 并检索它们的诊断程序(例如 atrace)中时,可能会发生这种情况。在这种情况下,可以使用其他 API,例如 C++ 中的 tryGetService 或 Java 中的 getService("instance-name", false)。Java 中提供的旧版 API getService 也必须与服务通知一起使用。使用此 API 不会避免以下竞态条件:当客户端使用某个非重试 API 请求服务器后,该服务器对自身进行了注册。

Service death notifications 服务终止通知


Clients who want to be notified when a service dies can receive death notifications delivered by the framework. To receive notifications, the client must:

  1. Subclass the HIDL class/interface hidl_death_recipient (in C++ code, not in HIDL).
  2. Override its serviceDied() method.
  3. Instantiate an object of the hidl_death_recipient subclass.
  4. Call the linkToDeath() method on the service to monitor, passing in the IDeathRecipient's interface object. Note that this method does not take ownership of the death recipient or the proxy on which it is called.


A pseudocode example (C++ and Java are similar):

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

The same death recipient may be registered on multiple different services.

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

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

伪代码示例(C++ 和 Java 类似):

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

同一终止接收方可能已在多个不同的服务上注册。

Data transfer 数据转移


Data may be sent to a service by calling methods defined in interfaces in .hal files. There are two kinds of methods:

  • Blocking methods wait until the server has produced a result.
  • Oneway methods send data in only one direction and do not block. If the amount of data in-flight in RPC calls exceeds implementation limits, the calls may either block or return an error indication (behavior is not yet determined).


A method that does not return a value but is not declared as oneway is still blocking.


All methods declared in a HIDL interface are called in a single direction, either from the HAL or into the HAL. The interface does not specify which direction it will be called in. Architectures that need calls to originate from the HAL should provide two (or more) interfaces in the HAL package and serve the appropriate interface from each process. The words client and server are used with respect to the calling direction of the interface (i.e. the HAL can be a server of one interface and a client of another interface).

可通过调用 .hal 文件内的接口中定义的方法将数据发送到服务。具体方法有两类:

  • 阻塞方法会等到服务器产生结果。
  • 单向方法仅朝一个方向发送数据且不阻塞。如果 RPC 调用中正在传输的数据量超过实现限制,则调用可能会阻塞或返回错误指示(具体行为尚不确定)。
    不返回值但未声明为 oneway 的方法仍会阻塞。

在 HIDL 接口中声明的所有方法都是单向调用,要么从 HAL 发出,要么到 HAL。该接口没有指定具体调用方向。需要从 HAL 发起调用的架构应该在 HAL 软件包中提供两个(或更多个)接口并从每个进程提供相应的接口。我们根据接口的调用方向来取名“客户端”或“服务器”(即 HAL 可以是一个接口的服务器,也可以是另一个接口的客户端)。

Callbacks 回调

The word callback refers to two different concepts, distinguished by synchronous callback and asynchronous callback.


Synchronous callbacks are used in some HIDL methods that return data. A HIDL method that returns more than one value (or returns one value of non-primitive type) returns its results via a callback function. If only one value is returned and it is a primitive type, a callback is not used and the value is returned from the method. The server implements the HIDL methods and the client implements the callbacks.


Asynchronous callbacks allow the server of a HIDL interface to originate calls. This is done by passing an instance of a second interface through the first interface. The client of the first interface must act as the server of the second. The server of the first interface can call methods on the second interface object. For example, a HAL implementation may send information asynchronously back to the process that is using it by calling methods on an interface object created and served by that process. Methods in interfaces used for asynchronous callback may be blocking (and may return values to the caller) or oneway. For an example, see “Asynchronous callbacks” in HIDL C++.


To simplify memory ownership, method calls and callbacks take only in parameters and do not support out or inout parameters.

“回调”一词可以指代两个不同的概念,可通过“同步回调”和“异步回调”进行区分。

“同步回调”用于返回数据的一些 HIDL 方法。返回多个值(或返回非基元类型的一个值)的 HIDL 方法会通过回调函数返回其结果。如果只返回一个值且该值是基元类型,则不使用回调且该值从方法中返回。服务器实现 HIDL 方法,而客户端实现回调。

“异步回调”允许 HIDL 接口的服务器发起调用。通过第一个接口传递第二个接口的实例即可完成此操作。第一个接口的客户端必须作为第二个接口的服务器。第一个接口的服务器可以在第二个接口对象上调用方法。例如,HAL 实现可以通过在由该进程创建和提供的接口对象上调用方法来将信息异步发送回正在使用它的进程。用于异步回调的接口中的方法可以是阻塞方法(并且可能将值返回到调用程序),也可以是 oneway 方法。要查看相关示例,请参阅 HIDL C++ 中的“异步回调”。

要简化内存所有权,方法调用和回调只能接受 in 参数,并且不支持 outinout 参数。

Per-transaction limits 每事务限制

Per-transaction limits are not imposed on the amount of data sent in HIDL methods and callbacks. However, calls exceeding 4KB per transaction are considered excessive. If this is seen, re-architecting the given HIDL interface is recommended. Another limitation is the resources available to the HIDL infrastructure to handle multiple simultaneous transactions. Multiple transactions can be in-flight simultaneously due to multiple threads or processes sending calls to a process or multiple oneway calls that are not handled quickly by the receiving process. The maximum total space available for all concurrent transactions is 1MB by default.


In a well-designed interface, exceeding these resource limitations should not happen; if it does, the call which exceeded them may either block until resources become available or signal a transport error. Each occurrence of exceeding per-transaction limits or overflowing HIDL implementation resources by aggregate in-flight transactions is logged to facilitate debugging.

每事务限制不会强制限制在 HIDL 方法和回调中发送的数据量。但是,每事务调用 4KB 以上的数据便被视为过度调用。如果发生这种情况,建议重新设计给定 HIDL 接口的架构。另一个限制是可供 HIDL 基础架构处理多个同时进行的事务的资源。由于多个线程或进程向一个进程发送调用或者接收进程未能快速处理多个 oneway 调用,因此多个事务可能会同时进行。默认情况下,所有并发事务可用的最大总空间为 1MB。

在设计良好的接口中,不应出现超出这些资源限制的情况;如果超出的话,则超出资源的调用可能会阻塞,直到资源可用或发出传输错误的信号。每当因正在进行的总事务导致出现超出每事务限制或溢出 HIDL 实现资源的情况时,系统都会记录下来以方便调试。

Method implementations 方法实现

HIDL generates header files declaring the necessary types, methods, and callbacks in the target language (C++ or Java). The prototype of HIDL-defined methods and callbacks is the same for both client and server code. The HIDL system provides proxy implementations of the methods on the caller side that organize the data for IPC transport, and stub code on the callee side that passes the data into developer implementations of the methods.


The caller of a function (HIDL method or callback) has ownership of the data structures passed into the function, and retains ownership after the call; in all cases the callee does not need to free or release the storage.

  • In C++, the data may be read-only (attempts to write to it may cause a segmentation fault) and are valid for the duration of the call. The client can deep-copy the data to propagate it beyond the call.
  • In Java, the code receives a local copy of the data (a normal Java object), which it may keep and modify or allow to be garbage-collected.

HIDL 生成以目标语言(C++ 或 Java)声明必要类型、方法和回调的标头文件。客户端和服务器代码的 HIDL 定义方法和回调的原型是相同的。HIDL 系统提供调用程序端(整理 IPC 传输的数据)的方法代理实现,并将代码存根到被调用程序端(将数据传递到方法的开发者实现)。

函数的调用程序(HIDL 方法或回调)拥有对传递到该函数的数据结构的所有权,并在调用后保留所有权;被调用程序在所有情况下都无需释放存储。

  • 在 C++ 中,数据可能是只读的(尝试写入可能会导致细分错误),并且在调用期间有效。客户端可以深层复制数据,以在调用期间外传播。
  • 在 Java 中,代码会接收数据的本地副本(普通 Java 对象),代码可以保留和修改此数据或允许垃圾回收器回收。

Non-RPC data transfer 非 RPC 数据转移

HIDL has two ways to transfer data without using an RPC call: shared memory and a Fast Message Queue (FMQ), both supported only in C++.

  • Shared memory. The built-in HIDL type memory is used to pass an object representing shared memory that has been allocated. Can be used in a receiving process to map the shared memory.

  • Fast Message Queue (FMQ). HIDL provides a templated message queue type that implements no-wait message-passing. It does not use the kernel or scheduler in passthrough or binderized mode (inter-device communication will not have these properties). Typically, the HAL sets up its end of the queue, creating an object that can be passed through RPC via a parameter of built-in HIDL type MQDescriptorSync or MQDescriptorUnsync. This object can be used by the receiving process to set up the other end of the queue.

    • Sync queues are not allowed to overflow, and can only have one reader.
    • Unsync queues are allowed to overflow, and can have many readers, each of which must read data in time or lose it.

    Neither type is allowed to underflow (read from an empty queue will fail), and each type can only have one writer.


For more details on FMQ, see Fast Message Queue (FMQ).

HIDL 在不使用 RPC 调用的情况下通过两种方法来转移数据:共享内存和快速消息队列 (FMQ),只有 C++ 同时支持这两种方法。

  • 共享内存。内置 HIDL 类型 memory 用于传递表示已分配的共享内存的对象。 可以在接收进程中使用,以映射共享内存。

  • 快速消息队列 (FMQ)。HIDL 提供了一种可实现无等待消息传递的模板化消息队列类型。它在直通式或绑定式模式下不使用内核或调度程序(设备间通信将不具有这些属性)。通常,HAL 会设置其队列的末尾,从而创建可以借助内置 HIDL 类型 MQDescriptorSyncMQDescriptorUnsync 的参数通过 RPC 传递的对象。接收进程可使用此对象设置队列的另一端。

    • “已同步”队列不能溢出,且只能有一个读取器。
    • “未同步”队列可以溢出,且可以有多个读取器;每个读取器必须及时读取数据,否则数据就会丢失。

    两种队列都不能下溢(从空队列进行读取将会失败),且都只能有一个写入器。

有关 FMQ 的更多详情,请参阅快速消息队列 (FMQ)

发布了60 篇原创文章 · 获赞 27 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/xiaosaerjt/article/details/104588432
今日推荐