天天看點

Android HIDL 官方文檔(四)—— 服務與資料轉換(Services & Data Transfer)1. 注冊服務2. 發現服務3. 服務死亡通知4. 資料傳輸

  • 注冊服務
  • 發現服務
  • 服務死亡通知
  • 資料傳輸
    • 1 回調
    • 2 每次事務的限制
    • 3 方法實作
    • 4 非遠端過程調用的資料傳輸
  • 對應的官方文檔位址:HIDL(General) - 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

檔案中的接口向服務發送資料。

1. 注冊服務

(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:

       HIDL 接口服務端(實作接口的對象)可被注冊為一個命名的服務。注冊的名稱不需要與接口名或者包名相關。如果沒有指定名稱,則會使用

default

作為名稱。這應使用于不需要注冊相同接口的兩個已實作的 HALs。舉個例子,在每個接口中定義的服務注冊的 C++ 調用:

registerAsService();
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 接口的版本包含在接口本身之中。它自動關聯到注冊的服務,并且可以通過調用方法

android::hardware::IInterface::getInterfaceVersion()

對每個 HIDL 接口進行檢索。服務端對象不需要進行注冊,并且它可以通過 HIDL 方法的參數傳遞給另一個程序,這将使 HIDL 方法調用到服務端。

2. 發現服務

(Discovering services)

Requests by client code are made for a given interface by name and by version, calling

getService

on the desired HAL class:

       用戶端的代碼請求是按照給定接口的名稱與版本進行的,在所需要的 HAL 類上調用

getService

方法:

sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = _1::IFooService::getService("another_foo_service"); 
           
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”).

       一個 HIDL 接口的每個不同的版本分别被視為一個單獨的接口。是以,1.1 版本的

IFooService

與 2.2 版本的

IFooService

都可以被注冊為

foo_service

,并且

getService("foo_service")

方法在任一接口上都可以獲得該接口的注冊服務。這就是為什麼在大多數情況下,不需要為注冊或發現提供參數名(此時為

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 returned interface by

IFoo::getService

always use 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.

       供應商接口對象也在傳回接口的傳輸方法中起到一定的作用。

[email protected]

包中的接口

IFoo

,如果條目存在,通過

IFoo::getService

傳回的接口常常使用在裝置清單中

android.hardware.foo

的傳輸方法。如果傳輸方法是不可用的,則會傳回空指針。

3. 服務死亡通知

(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.

       需要被通知的用戶端在一個服務死亡時,可以收到由架構所傳遞的死亡通知。為了接收通知,用戶端需要滿足以下條件:

  • HIDL 類 / 接口

    hidl_death_recipient

    的子類。(在 C++ 代碼中,而非 HIDL)
  • 重寫它的

    serviceDied()

    方法。
  • 執行個體化一個

    hidl_death_recipient

    子類對象。
  • 調用服務的方法

    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, );
           
The same death recipient may be registered on multiple different services.

       同樣的死亡接收者可以是在不同的服務上注冊的。

4. 資料傳輸

(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.

       通過調用

.hal

檔案中定義的接口,資料可以被傳輸到服務中。有兩種類型的方法:

  • 阻塞的方法将會等待服務端産生一個需要的結果。
  • 單向的方法隻在一個方向上傳輸資料,并且不會阻塞。如果 RPC 内部的資料量超過了實作的限制,則本次調用可以阻塞或者傳回錯誤提示(行為未确定)。

       一個沒有值傳回,但不定義為單向的方法,仍然是阻塞的。

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

       在 HIDL 接口中聲明的所有方法都是單向調用的,無論是從 HAL 調用還是調用到 HAL。接口沒有特别指定調用是在哪個方向上的。需要調用源自 HAL 的架構應在 HAL 的包中提供一個或更多的接口,并且從每個程序中提供适當的接口。字段

client

server

就用于使調用遵守接口的方向(比如一個 HAL 可以是一個接口的

server

同時是另一個接口的

client

)。

4.1 回調

(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.

       同步回調應用于一些需要傳回資料的 HIDL 方法。對于一個需要傳回超過一個值(或者傳回一個非基本類型的值)的 HIDL 方法,它會通過回調函數來傳回這些結果資料。如果僅僅需要傳回一個值,并且這個值屬于基本類型,則不需要回調,直接通過該方法傳回即可。服務端實作 HIDL 方法,而用戶端則實作回調函數。

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++.

       異步回調允許一個 HIDL 接口服務端發起調用。這是通過第一個接口傳遞第二個接口的執行個體實作的。第一個接口的用戶端必須作為第二個接口的服務端。第一接口的服務端可以調用第二接口對象中的方法。舉個例子,一個 HAL 實作可以異步地将資訊發送回正在使用它的程序,這是通過調用由該程序建立并服務的一個接口來運作的。用于異步回調的接口中的方法可以是阻塞的(并且也可以傳回值給調用者),也可以是單向的。在 HIDL C++ 的

Asynchronous callbacks

小節中有比較具體的介紹。

To simplify memory ownership, method calls and callbacks take only

in

parameters and do not support

out

or

inout

parameters.

       為了簡化記憶體所有權,方法的調用與回調隻需要參數

in

,而不需要

out

或者

inout

參數。

4.2 每次事務的限制

(Per-transaction limits)

Per-transaction limits may be imposed on the amount of data sent in HIDL methods and callbacks. The limits are yet to be determined but may be as small as 4K. Calls exceeding these limits return failure immediately. 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.

       對每次事務的限制條件可能會施加到 HIDL 方法或回調中所傳輸的資料量上。這些限制尚未确定,但可能隻會有

4K

。當調用超過了這些限制,則會直接傳回失敗資訊。另一個限制是關于處理多個同時事務時的 HIDL 基礎結構的可用資源量。多個事務可以同時進行,是因為多個線程或程序向一個程序發送調用,或者多個單向調用未被所接收的程序快速地處理完畢。

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.

       在一個設計良好的接口中,超過資源限制的這些情況不應發生。如果發生了,則超出這些限制的調用可能會阻塞直到可用資源足夠為止,或者發送一個傳輸錯誤信号。

4.3 方法實作

(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.

       HIDL 生成的目智語言(C++ 或者 Java)頭檔案中定義了必要的類型,方法與回調。HIDL 定義的方法與回調的原型對于服務端與用戶端代碼是相同的。HIDL 系統在組織資料進行 IPC 傳輸的調用端提供了代理的實作,并且在傳遞資料到開發者實作的方法中的被調用端提供了樁的代碼。

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 中,代碼會接收到資料(一個普通 Java 對象)的本地拷貝,它可以儲存和修改,或者允許被垃圾回收機制處理。

4.4 非遠端過程調用的資料傳輸

(Non-RPC data transfer)

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.

       在 RPC 傳輸方式之外,HIDL 還有兩種資料傳輸的方法:共享記憶體與快速消息隊列,它們都隻在 C++ 語言中支援。

  • 共享記憶體。HIDL 内建的類型

    memory

    是用于傳遞一個表示已配置設定的共享記憶體的對象的。可以在接收程序中使用它來映射共享記憶體。
  • 快速消息隊列。HIDL 提供了一個實作了不用等待的消息傳遞的模闆消息隊列類型。它并沒有使用到 Passthrough 模式或者 Binder 化模式(互聯裝置之間的互動不會擁有這些特性)的核心與排程器。通常,HAL 會設定它的隊列的末端,并建立可以通過内置 HIDL 類型

    MQDescriptorSync

    MQDescripotrUnsync

    的參數傳遞到 RPC 的對象。這個對象可以被接收程序用于設定隊列的另一端:
    • 同步隊列不允許溢出,且隻能有一個讀取者。
    • 異步隊列允許溢出,可以有多個讀者,每個讀者必須及時讀取或丢棄資料。
    • 兩種隊列都不允許下溢(對空隊列讀取将傳回失敗資訊),并且隻能有一個寫入者。