Usługi & Transfer danych

W tej sekcji opisano, jak rejestrować i odkrywać usługi oraz jak wysyłać dane do usługi poprzez wywoływanie metod zdefiniowanych w interfejsach w plikach .hal .

Rejestracja usług

Serwery interfejsu HIDL (obiekty implementujące interfejs) można rejestrować jako nazwane usługi. Zarejestrowana nazwa nie musi być powiązana z nazwą interfejsu lub pakietu. Jeśli nie określono żadnej nazwy, używana jest nazwa „domyślna”; należy tego używać w przypadku warstw HAL, które nie muszą rejestrować dwóch implementacji tego samego interfejsu. Na przykład wywołanie rejestracji usługi w języku C++ zdefiniowane w każdym interfejsie to:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if needed

Wersja interfejsu HIDL jest zawarta w samym interfejsie. Jest automatycznie powiązany z rejestracją usługi i można go pobrać za pomocą wywołania metody ( android::hardware::IInterface::getInterfaceVersion() ) na każdym interfejsie HIDL. Obiekty serwera nie muszą być rejestrowane i mogą być przekazywane poprzez parametry metody HIDL do innego procesu, który będzie wywoływał metodę HIDL na serwerze.

Odkrywanie usług

Żądania za pomocą kodu klienta są wysyłane dla danego interfejsu według nazwy i wersji, wywołując getService na żądanej klasie HAL:

// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);

Każda wersja interfejsu HIDL jest traktowana jako oddzielny interfejs. Zatem IFooService w wersji 1.1 i IFooService w wersji 2.2 można zarejestrować jako „foo_service”, a getService("foo_service") na każdym interfejsie pobiera zarejestrowaną usługę dla tego interfejsu. Dlatego w większości przypadków do rejestracji lub wykrywania nie jest konieczne podanie parametru nazwy (co oznacza nazwę „domyślną”).

Obiekt interfejsu dostawcy odgrywa również rolę w sposobie transportu zwracanego interfejsu. W przypadku interfejsu IFoo w pakiecie android.hardware.foo@1.0 , interfejs zwrócony przez IFoo::getService zawsze używa metody transportu zadeklarowanej dla android.hardware.foo w manifeście urządzenia, jeśli wpis istnieje; a jeśli metoda transportu nie jest dostępna, zwracana jest wartość nullptr.

W niektórych przypadkach może być konieczne natychmiastowe kontynuowanie usługi, nawet bez skorzystania z usługi. Może się to zdarzyć (na przykład), gdy klient chce samodzielnie zarządzać powiadomieniami o usługach lub w programie diagnostycznym (takim jak atrace ), który musi pobrać wszystkie usługi hwservice i je pobrać. W tym przypadku dostępne są dodatkowe API, takie jak tryGetService w C++ lub getService("instance-name", false) w Javie. W przypadku powiadomień serwisowych należy także używać starszego interfejsu API getService dostępnego w języku Java. Korzystanie z tego interfejsu API nie pozwala uniknąć sytuacji wyścigu, w której serwer rejestruje się po zażądaniu tego przez klienta za pomocą jednego z tych interfejsów API bez konieczności ponawiania próby.

Powiadomienia o śmierci usługi

Klienci, którzy chcą otrzymywać powiadomienia o śmierci usługi, mogą otrzymywać powiadomienia o śmierci dostarczane przez platformę. Aby otrzymywać powiadomienia Klient musi:

  1. Podklasa klasy/interfejsu HIDL hidl_death_recipient (w kodzie C++, a nie w HIDL).
  2. Zastąp metodę serviceDied() .
  3. Utwórz instancję obiektu podklasy hidl_death_recipient .
  4. Wywołaj metodę linkToDeath() w usłudze do monitorowania, przekazując obiekt interfejsu IDeathRecipient . Należy pamiętać, że ta metoda nie przejmuje własności odbiorcy śmierci ani serwera proxy, na którym jest wywoływana.

Przykład pseudokodu (C++ i Java są podobne):

class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);

Ten sam odbiorca śmierci może być zarejestrowany w wielu różnych usługach.

Transfer danych

Dane można przesłać do usługi wywołując metody zdefiniowane w interfejsach w plikach .hal . Istnieją dwa rodzaje metod:

  • Metody blokowania czekają, aż serwer wygeneruje wynik.
  • Metody jednokierunkowe wysyłają dane tylko w jednym kierunku i nie blokują. Jeśli ilość danych przesyłanych w wywołaniach RPC przekracza limity implementacji, wywołania mogą blokować lub zwracać informację o błędzie (zachowanie nie zostało jeszcze określone).

Metoda, która nie zwraca wartości, ale nie jest zadeklarowana jako oneway nadal blokuje.

Wszystkie metody zadeklarowane w interfejsie HIDL są wywoływane w jednym kierunku, albo z warstwy HAL, albo do warstwy HAL. Interfejs nie określa, w którym kierunku będzie wywoływany. Architektury wymagające wywołań pochodzących z warstwy HAL powinny zapewniać dwa (lub więcej) interfejsy w pakiecie HAL i obsługiwać odpowiedni interfejs z każdego procesu. Słowa klient i serwer są używane w odniesieniu do kierunku wywoływania interfejsu (tj. warstwa HAL może być serwerem jednego interfejsu i klientem innego interfejsu).

Oddzwonienia

Słowo wywołanie zwrotne odnosi się do dwóch różnych pojęć, różniących się od wywołania synchronicznego i wywołania zwrotnego asynchronicznego .

Synchroniczne wywołania zwrotne są używane w niektórych metodach HIDL, które zwracają dane. Metoda HIDL, która zwraca więcej niż jedną wartość (lub zwraca jedną wartość typu innego niż pierwotne), zwraca swoje wyniki poprzez funkcję wywołania zwrotnego. Jeśli zwrócona zostanie tylko jedna wartość i jest ona typu pierwotnego, wywołanie zwrotne nie jest używane, a wartość jest zwracana z metody. Serwer implementuje metody HIDL, a klient implementuje wywołania zwrotne.

Asynchroniczne wywołania zwrotne umożliwiają serwerowi interfejsu HIDL inicjowanie wywołań. Odbywa się to poprzez przepuszczenie instancji drugiego interfejsu przez pierwszy interfejs. Klient pierwszego interfejsu musi działać jako serwer drugiego. Serwer pierwszego interfejsu może wywoływać metody na obiekcie drugiego interfejsu. Na przykład implementacja HAL może asynchronicznie wysyłać informacje z powrotem do procesu, który ich używa, wywołując metody obiektu interfejsu utworzonego i obsługiwanego przez ten proces. Metody w interfejsach używane do asynchronicznego wywołania zwrotnego mogą blokować (i mogą zwracać wartości do osoby wywołującej) lub oneway . Na przykład zobacz „Asynchroniczne wywołania zwrotne” w HIDL C++ .

Aby uprościć posiadanie pamięci, wywołania metod i wywołania zwrotne in tylko parametry i nie obsługują parametrów out ani inout .

Limity na transakcję

Limity transakcyjne nie są narzucane na ilość danych przesyłanych w metodach HIDL i wywołaniach zwrotnych. Jednakże połączenia przekraczające 4 KB na transakcję są uważane za nadmierne. Jeśli zostanie to zauważone, zaleca się zmianę architektury danego interfejsu HIDL. Kolejnym ograniczeniem są zasoby dostępne dla infrastruktury HIDL do obsługi wielu jednoczesnych transakcji. Wiele transakcji może być realizowanych jednocześnie, ponieważ wiele wątków lub procesów wysyła wywołania do procesu lub wiele oneway wywołań, które nie są szybko obsługiwane przez proces odbierający. Maksymalna łączna przestrzeń dostępna dla wszystkich jednoczesnych transakcji wynosi domyślnie 1 MB.

W dobrze zaprojektowanym interfejsie przekroczenie tych ograniczeń zasobów nie powinno mieć miejsca; jeśli tak się stanie, wywołanie, które je przekroczyło, może zostać zablokowane do czasu udostępnienia zasobów lub zasygnalizować błąd transportu. Każde wystąpienie przekroczenia limitów transakcji lub przepełnienia zasobów implementacji HIDL przez zagregowane transakcje w locie jest rejestrowane w celu ułatwienia debugowania.

Implementacje metod

HIDL generuje pliki nagłówkowe deklarujące niezbędne typy, metody i wywołania zwrotne w języku docelowym (C++ lub Java). Prototyp metod i wywołań zwrotnych zdefiniowanych w HIDL jest taki sam zarówno dla kodu klienta, jak i serwera. System HIDL zapewnia implementacje proxy metod po stronie wywołującej, które organizują dane do transportu IPC, oraz kod pośredniczący po stronie wywoływanej, który przekazuje dane do implementacji metod programisty.

Osoba wywołująca funkcję (metoda HIDL lub wywołanie zwrotne) jest właścicielem struktur danych przekazanych do funkcji i zachowuje ją po wywołaniu; we wszystkich przypadkach odbiorca nie musi zwalniać ani zwalniać pamięci.

  • W C++ dane mogą być tylko do odczytu (próby zapisu do nich mogą spowodować błąd segmentacji) i są ważne przez czas trwania połączenia. Klient może głęboko skopiować dane i rozpropagować je poza połączeniem.
  • W Javie kod otrzymuje lokalną kopię danych (zwykły obiekt Java), którą może przechowywać i modyfikować lub pozwolić na usunięcie śmieci.

Transfer danych inny niż RPC

HIDL ma dwa sposoby przesyłania danych bez użycia wywołania RPC: pamięć współdzielona i szybka kolejka komunikatów (FMQ), oba obsługiwane tylko w C++.

  • Wspólna pamięć . Wbudowana memory typu HIDL służy do przekazywania obiektu reprezentującego pamięć współdzieloną, która została przydzielona. Można go użyć w procesie odbierania do mapowania pamięci współdzielonej.
  • Szybka kolejka wiadomości (FMQ) . HIDL zapewnia szablonowy typ kolejki komunikatów, który implementuje przekazywanie komunikatów bez oczekiwania. Nie używa jądra ani harmonogramu w trybie przejściowym ani powiązanym (komunikacja między urządzeniami nie będzie miała tych właściwości). Zwykle warstwa HAL ustawia swój koniec kolejki, tworząc obiekt, który można przekazać przez RPC za pomocą parametru wbudowanego typu HIDL MQDescriptorSync lub MQDescriptorUnsync . Obiekt ten może zostać wykorzystany przez proces odbierający do skonfigurowania drugiego końca kolejki.
    • Kolejki synchronizacji nie mogą się przepełniać i mogą mieć tylko jeden czytnik.
    • Kolejki niezsynchronizowane mogą się przepełniać i mogą mieć wielu czytników, z których każdy musi odczytać dane na czas, w przeciwnym razie je stracą.
    Żaden typ nie może mieć niedomiaru (odczyt z pustej kolejki zakończy się niepowodzeniem), a każdy typ może mieć tylko jednego pisarza.

Aby uzyskać więcej informacji na temat FMQ, zobacz Szybka kolejka wiadomości (FMQ) .