介面

在 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 returns 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,因此 hwservicemanager 只會在該實作與 1.0 用戶端相容時,傳回伺服器實作。實際上,這表示只有版本為 1.n 的伺服器實作項目 (介面版本 x.(y+1) 必須擴充 (繼承自) x.y)。

此外,系統會提供 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 介面物件做為參數,因此可用於非同步回呼。

介面檔案 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);