介面

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