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 will return 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)
必須擴展(繼承自) xy
)。
此外,還提供了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);