Interfejsy

Każdy interfejs zdefiniowany w pakiecie HIDL ma własną, automatycznie generowaną klasę C++ w przestrzeni nazw pakietu. Klienty i serwery obsługują interfejsy w różne sposoby:

  • Serwery implementują interfejsy.
  • Metody wywołań klientów w interfejsach.

Interfejsy mogą być rejestrowane przez serwer według nazwy lub przekazywane jako do metod określonych przez HIDL. Na przykład kod platformy może obsługiwać do odbierania komunikatów asynchronicznych z HAL i przekazywania tego interfejsu bezpośrednio do HAL bez jego rejestrowania.

Implementacja serwera

Serwer implementujący interfejs IFoo musi zawierać element Automatycznie wygenerowany plik nagłówka IFoo:

#include <android/hardware/samples/1.0/IFoo.h>

Nagłówek jest automatycznie eksportowany przez zasoby wspólne IFoo interfejs, do którego ma być nadany link. Przykładowy element IFoo.hal:

// IFoo.hal
interface IFoo {
    someMethod() generates (vec<uint32_t>);
    ...
}

Przykładowy szkielet implementacji interfejsu IFoo na serwerze:

// 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();
    }
    ...
};

Aby udostępnić klientowi implementację interfejsu serwera, mogą:

  1. Zarejestruj implementację interfejsu w hwservicemanager (szczegóły poniżej),

    LUB

  2. Przekaż implementację interfejsu jako argument interfejs (szczegóły znajdziesz w sekcji Asynchroniczna, ).

Przy rejestrowaniu implementacji interfejsu funkcja Proces hwservicemanager śledzi zarejestrowane interfejsy HIDL działające na urządzeniu według nazwy i wersji. Serwery mogą zarejestrować interfejs HIDL wdrożenie według nazwy, a klienci mogą prosić o implementacje usług po nazwie i jej wersji. Ten proces służy do obsługi interfejsu HIDL android.hidl.manager@1.0::IServiceManager

Każdy automatycznie wygenerowany plik nagłówka interfejsu HIDL (np. IFoo.h) ma metodę registerAsService(), której można użyć do zarejestrowania implementacji interfejsu za pomocą interfejsu hwservicemanager. Jedyna wymagany argument to nazwa implementacji interfejsu jako klientów użyj tej nazwy do pobrania interfejsu z hwservicemanager później:

::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 traktuje połączenie tych parametrów: [package@version::interface, instance_name] jako unikalną wartość, którą można włączyć za pomocą różnych interfejsów (lub różnych wersji tego samego interfejsu) w celu rejestracji z identycznymi nazwami instancji bez konfliktów. Jeśli dzwonisz registerAsService() z dokładnie taką samą wersją pakietu, interfejsem i nazwy instancji, hwservicemanager usuwa odwołanie do funkcji wcześniej zarejestrowanej usługi i nowej.

Implementacja u klienta

Podobnie jak serwer, klient musi #include każdy interfejs. Dotyczy to:

#include <android/hardware/samples/1.0/IFoo.h>

Klient może uzyskać dostęp do interfejsu na dwa sposoby:

  • Przez: I<InterfaceName>::getService (przez: hwservicemanager).
  • Za pomocą interfejsu

Każdy automatycznie wygenerowany plik nagłówka interfejsu zawiera statyczny plik getService której można użyć do pobrania instancji usługi z hwservicemanager:

// getService returns nullptr if the service can't be found
sp<IFoo> myFoo = IFoo::getService();
sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");

Teraz klient ma interfejs IFoo i może wywoływać metody tak jakby była to implementacja klas lokalnych. W rzeczywistości wdrożenie mogą funkcjonować w ramach tego samego procesu, innego procesu, a nawet na innym urządzeniu. (z działaniem zdalnym HAL). Ponieważ klient zadzwonił do firmy getService IFoo obiekt z wersji 1.0 pakietu, hwservicemanager zwraca implementację serwera tylko wtedy, gdy to implementacja jest zgodna z klientami 1.0. W praktyce oznacza tylko implementacje serwera z wersją 1.n (wersja x.(y+1) interfejsu musi stanowić rozszerzenie (dziedziczenie z) x.y).

Dodatkowo podana jest metoda castFrom do rzutowania między przy użyciu różnych interfejsów. Ta metoda polega na wywołaniu IPC do pilota i upewnij się, że bazowy typ jest taki sam jak typ poproszono o dostęp. Jeśli żądany typ jest niedostępny, nullptr jest .

sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService();
sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);

Asynchroniczne wywołania zwrotne

Wiele istniejących implementacji HAL komunikuje się ze sprzętem asynchronicznym, co oznacza, że potrzebują asynchronicznego sposobu powiadamiania klientów o nowych zdarzeniach, . Interfejs HIDL może być używany jako asynchroniczne wywołanie zwrotne, ponieważ HIDL funkcje interfejsu mogą przyjmować jako parametry obiekty interfejsu HIDL.

Przykładowy plik interfejsu IFooCallback.hal:

package android.hardware.samples@1.0;
interface IFooCallback {
    sendEvent(uint32_t event_id);
    sendData(vec<uint8_t> data);
}

Przykładowa nowa metoda w IFoo, która wymaga Parametr 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);
};

Klient korzystający z interfejsu IFoo jest serwer interfejsu IFooCallback; zapewnia implementacja 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
    }
};

Może również po prostu przesłać ją nad istniejącą instancją Interfejs IFoo:

sp<IFooCallback> myFooCallback = new FooCallback();
myFoo.registerCallback(myFooCallback);

Serwer implementujący interfejs IFoo odbiera to jako plik sp<IFooCallback> obiekt. Może zapisać wywołanie zwrotne i wywołać z powrotem do klienta za każdym razem, gdy zechce użyć danego interfejsu.

Adresaci śmierci

Implementacje usług mogą działać w ramach innego procesu, więc może się zdarzyć, że proces wdrażania interfejsu wygasa, gdy klient przeżyje. Wszystkie wywołania obiektu interfejsu hostowanego w procesie, które zakończyły się, kończą się niepowodzeniem z błędem transportu (isOK() zwraca false). Jedynym sposobem na w przypadku takiej awarii polega na zażądaniu nowej instancji usługi przez Wywołuję: I<InterfaceName>::getService(). Ta funkcja działa tylko wtedy, gdy proces, który uległ awarii, uruchomił się ponownie i ponownie zarejestrował usługi przy użyciu servicemanager (zwykle dotyczy to implementacji HAL).

Zamiast reagować na to, klienci interfejsu mogą również zarejestrować odbiorcę śmierci, aby otrzymywać powiadomienia o umowie usługi. Aby zarejestrować takie powiadomienia w pobranym interfejsie IFoo, Klient może wykonać te czynności:

foo->linkToDeath(recipient, 1481 /* cookie */);

Parametr recipient musi być implementacją funkcji Interfejs android::hardware::hidl_death_recipient udostępniany przez HIDL, który zawiera pojedynczą metodę serviceDied() o nazwie z wątku w puli wątków RPC, gdy proces hostujący interfejs się kończy:

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
    }
}

Parametr cookie zawiera plik cookie, który został przekazany z linkToDeath(), a parametr who zawiera słaby wskaźnik do obiektu reprezentującego usługę w kliencie. Za pomocą przykładowe wywołanie podane powyżej, cookie równa się 1481 i who wynosi foo.

Możesz też wyrejestrować odbiorcę zgonu po jego zarejestrowaniu:

foo->unlinkToDeath(recipient);