Każdy interfejs zdefiniowany w pakiecie HIDL ma własną, automatycznie wygenerowaną klasę C++ w przestrzeni nazw pakietu. Klienci i serwery radzą sobie z interfejsami na różne sposoby:
- Serwery implementują interfejsy.
- Klienci wywołują metody na interfejsach.
Interfejsy mogą być rejestrowane przez serwer według nazwy lub przekazywane jako parametry do metod zdefiniowanych w języku HIDL. Na przykład kod struktury może służyć jako interfejs do odbierania asynchronicznych komunikatów z warstwy HAL i przekazywania tego interfejsu bezpośrednio do warstwy HAL bez jej rejestracji.
Implementacja serwera
Serwer implementujący interfejs IFoo
musi zawierać plik nagłówkowy IFoo
, który został wygenerowany automatycznie:
#include <android/hardware/samples/1.0/IFoo.h>
Nagłówek jest automatycznie eksportowany przez współdzieloną bibliotekę interfejsu IFoo
w celu połączenia. Przykład IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
Przykładowy szkielet serwerowej implementacji interfejsu 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(); } ... };
Aby udostępnić klientowi implementację interfejsu serwera można:
- Zarejestruj implementację interfejsu w
hwservicemanager
(szczegóły poniżej),
LUB - Przekaż implementację interfejsu jako argument metody interfejsu (szczegóły można znaleźć w artykule Asynchroniczne wywołania zwrotne ).
Podczas rejestrowania implementacji interfejsu proces hwservicemanager
śledzi zarejestrowane interfejsy HIDL działające na urządzeniu według nazwy i wersji. Serwery mogą rejestrować implementację interfejsu HIDL według nazwy, a klienci mogą żądać implementacji usług według nazwy i wersji. Proces ten obsługuje interfejs HIDL android.hidl.manager@1.0::IServiceManager
.
Każdy automatycznie wygenerowany plik nagłówkowy interfejsu HIDL (taki jak IFoo.h
) zawiera metodę registerAsService()
, której można użyć do zarejestrowania implementacji interfejsu w hwservicemanager
. Jedynym wymaganym argumentem jest nazwa implementacji interfejsu, ponieważ klienci będą używać tej nazwy do późniejszego pobrania interfejsu z 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
traktuje kombinację [package@version::interface, instance_name]
jako unikatową, aby umożliwić różnym interfejsom (lub różnym wersjom tego samego interfejsu) rejestrację z identycznymi nazwami instancji bez konfliktów. Jeśli wywołasz funkcję registerAsService()
z dokładnie tą samą wersją pakietu, interfejsem i nazwą instancji, hwservicemanager
porzuci odniesienie do wcześniej zarejestrowanej usługi i użyje nowej.
Wdrożenie klienta
Podobnie jak serwer, klient musi #include
każdy interfejs, do którego się odnosi:
#include <android/hardware/samples/1.0/IFoo.h>
Klient może uzyskać interfejs na dwa sposoby:
- Przez
I<InterfaceName>::getService
(przezhwservicemanager
) - Poprzez metodę interfejsu
Każdy automatycznie wygenerowany plik nagłówkowy interfejsu ma statyczną metodę getService
, której można użyć do pobrania instancji usługi z hwservicemanager
:
// getService will return 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ć do niego metody tak, jakby była to implementacja klasy lokalnej. W rzeczywistości wdrożenie może przebiegać w tym samym procesie, w innym procesie, a nawet na innym urządzeniu (przy zdalnym korzystaniu z HAL). Ponieważ klient wywołał getService
na obiekcie IFoo
zawartym w wersji 1.0
pakietu, hwservicemanager
zwraca implementację serwera tylko wtedy, gdy ta implementacja jest kompatybilna z klientami w wersji 1.0
. W praktyce oznacza to tylko implementacje serwerowe w wersji 1.n
(wersja x.(y+1)
interfejsu musi rozszerzać (dziedziczyć po) xy
).
Dodatkowo dostępna jest metoda castFrom
umożliwiająca rzutowanie pomiędzy różnymi interfejsami. Ta metoda polega na wykonaniu wywołania IPC do zdalnego interfejsu, aby upewnić się, że typ bazowy jest taki sam, jak typ żądany. Jeśli żądany typ jest niedostępny, zwracana jest nullptr
.
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, które miały miejsce. Interfejsu HIDL można używać jako asynchronicznego wywołania zwrotnego, ponieważ funkcje interfejsu HIDL mogą przyjmować obiekty interfejsu HIDL jako parametry.
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 przyjmuje 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 serwerem interfejsu IFooCallback
; zapewnia implementację 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 także po prostu przekazać to przez istniejącą instancję interfejsu IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
Serwer implementujący IFoo
odbiera to jako obiekt sp<IFooCallback>
. Może przechowywać wywołanie zwrotne i oddzwaniać do klienta, gdy tylko chce skorzystać z tego interfejsu.
Odbiorcy śmierci
Ponieważ implementacje usług mogą przebiegać w ramach różnych procesów, może się zdarzyć, że proces implementujący interfejs zakończy się, podczas gdy klient pozostanie przy życiu. Wszelkie wywołania obiektu interfejsu hostowanego w procesie, który zakończył działanie, zakończą się niepowodzeniem z powodu błędu transportu ( isOK()
zwróci wartość false). Jedynym sposobem na odzyskanie sprawności po takiej awarii jest zażądanie nowej instancji usługi poprzez wywołanie I<InterfaceName>::getService()
. Działa to tylko wtedy, gdy proces, który uległ awarii, został ponownie uruchomiony i ponownie zarejestrował swoje usługi w servicemanager
(co jest ogólnie prawdą w przypadku implementacji HAL).
Zamiast zajmować się tym reaktywnie, klienci interfejsu mogą również zarejestrować odbiorcę śmierci , aby otrzymać powiadomienie w przypadku śmierci usługi. Aby zarejestrować się w celu otrzymywania takich powiadomień w pobranym interfejsie IFoo
, klient może wykonać następujące czynności:
foo->linkToDeath(recipient, 1481 /* cookie */);
Parametr recipient
musi być implementacją interfejsu android::hardware::hidl_death_recipient
dostarczonego przez HIDL, który zawiera pojedynczą metodę serviceDied()
, która zostanie wywołana z wątku w puli wątków RPC, gdy proces obsługujący interfejs umrze:
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 przekazany za pomocą linkToDeath()
, natomiast parametr who
zawiera słaby wskaźnik do obiektu reprezentującego usługę w kliencie. W przypadku przykładowego wywołania podanego powyżej cookie
wynosi 1481, a who
jest równy foo
.
Możliwe jest także wyrejestrowanie zmarłego po jego zarejestrowaniu:
foo->unlinkToDeath(recipient);