Android HIDL 官方文檔(四)—— 服務與數據轉換(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_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, 42);

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 的包中提供一個或更多的接口,並且從每個進程中提供適當的接口。字段 clientserver 就用於使調用遵守接口的方向(比如一個 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 類型 MQDescriptorSyncMQDescripotrUnsync 的參數傳遞到 RPC 的對象。這個對象可以被接收進程用於設置隊列的另一端:
    • 同步隊列不允許溢出,且只能有一個讀取者。
    • 異步隊列允許溢出,可以有多個讀者,每個讀者必須及時讀取或丟棄數據。
    • 兩種隊列都不允許下溢(對空隊列讀取將返回失敗信息),並且只能有一個寫入者。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章