接口

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