Język definicji interfejsu HAL lub HIDL to język opisu interfejsu (IDL) służący do określania interfejsu między warstwą HAL a jej użytkownikami. HIDL umożliwia określanie typów i wywołań metod, zebranych w interfejsy i pakiety. Mówiąc szerzej, HIDL to system komunikacji między bazami kodu, które mogą być kompilowane niezależnie. Począwszy od Androida 10, HIDL jest przestarzały, a Android migruje, aby używać AIDL wszędzie.
HIDL jest przeznaczony do komunikacji międzyprocesowej (IPC). Warstwy HAL utworzone za pomocą HDL są nazywane powiązanymi warstwami HAL, ponieważ mogą komunikować się z innymi warstwami architektury za pomocą wywołań komunikacji międzyprocesowej (IPC) powiązania. Powiązane warstwy HAL są uruchamiane w oddzielnym procesie od klienta, który ich używa. W przypadku bibliotek, które muszą być połączone z procesem, dostępny jest również tryb przekazywania (nieobsługiwany w Javie).
HIDL określa struktury danych i sygnatury metod, zorganizowane w interfejsy (podobne do klas), które są gromadzone w pakietach. Składnia HIDL wygląda znajomo dla programistów C++ i Java, ale z innym zestawem słów kluczowych. HIDL używa również adnotacji w stylu Java.
Terminologia
W tej sekcji zastosowano następujące terminy związane z HIDL:
spojony | Wskazuje, że HIDL jest używany do zdalnych wywołań procedur między procesami, zaimplementowanych za pośrednictwem mechanizmu podobnego do Bindera. Zobacz także przejście . |
---|---|
wywołanie zwrotne, asynchroniczne | Interfejs obsługiwany przez użytkownika warstwy HAL, przekazywany do warstwy HAL (za pomocą metody HIDL) i wywoływany przez warstwę HAL w celu zwrócenia danych w dowolnym momencie. |
wywołanie zwrotne, synchroniczne | Zwraca dane z implementacji metody HIDL serwera do klienta. Nieużywane w przypadku metod, które zwracają void lub pojedynczą wartość pierwotną. |
klient | Proces wywołujący metody określonego interfejsu. Proces platformy HAL lub Android może być klientem jednego interfejsu i serwerem innego. Zobacz także przejście . |
rozciąga się | Wskazuje interfejs, który dodaje metody i/lub typy do innego interfejsu. Interfejs może rozszerzać tylko jeden inny interfejs. Może być używany do przyrostu podrzędnej wersji w tej samej nazwie pakietu lub do nowego pakietu (np. rozszerzenia dostawcy) w celu zbudowania na starszym pakiecie. |
generuje | Wskazuje metodę interfejsu, która zwraca wartości do klienta. Aby zwrócić jedną wartość inną niż prymitywna lub więcej niż jedną wartość, generowana jest synchroniczna funkcja wywołania zwrotnego. |
interfejs | Zbiór metod i typów. Przetłumaczone na klasę w C++ lub Javie. Wszystkie metody w interfejsie są wywoływane w tym samym kierunku: proces klienta wywołuje metody zaimplementowane przez proces serwera. |
jednokierunkowa | W przypadku zastosowania do metody HIDL wskazuje, że metoda nie zwraca żadnych wartości i nie blokuje. |
pakiet | Zbiór interfejsów i typów danych współdzielących wersję. |
Przejść przez | Tryb dlopen , w którym serwerem jest biblioteka współdzielona, otwierana przez klienta. W trybie tranzytowym klient i serwer to ten sam proces, ale oddzielne bazy kodu. Używany tylko do przeniesienia starszych baz kodu do modelu HIDL. Zobacz także zbindowane . |
serwer | Proces implementujący metody interfejsu. Zobacz także przejście . |
transport | Infrastruktura HIDL, która przenosi dane między serwerem a klientem. |
wersja | Wersja pakietu. Składa się z dwóch liczb całkowitych, major i minor. Drobne przyrosty wersji mogą dodawać (ale nie zmieniać) typy i metody. |
Projekt HIDL
Celem HIDL jest to, aby framework Androida mógł zostać zastąpiony bez konieczności przebudowywania warstw HAL. Warstwy HAL będą budowane przez dostawców lub twórców SOC i umieszczane na partycji /vendor
na urządzeniu, umożliwiając zastąpienie platformy Android we własnej partycji przez OTA bez ponownej kompilacji warstw HAL.
Projekt HIDL równoważy następujące kwestie:
- Interoperacyjność . Twórz niezawodne interoperacyjne interfejsy między procesami, które można kompilować z różnymi architekturami, łańcuchami narzędzi i konfiguracjami kompilacji. Interfejsy HIDL są wersjonowane i nie można ich zmienić po ich opublikowaniu.
- Wydajność . HIDL próbuje zminimalizować liczbę operacji kopiowania. Dane zdefiniowane w HIDL są dostarczane do kodu C++ w standardowych strukturach danych układu C++, których można używać bez rozpakowywania. HIDL udostępnia również interfejsy pamięci współdzielonej, a ponieważ wywołania RPC są z natury nieco powolne, HIDL obsługuje dwa sposoby przesyłania danych bez użycia wywołania RPC: pamięć współdzieloną i szybką kolejkę komunikatów (FMQ).
- Intuicyjny . HIDL pozwala uniknąć drażliwych problemów związanych z własnością pamięci, używając tylko
in
dla RPC (patrz Android Interface Definition Language (AIDL) ); wartości, których nie można wydajnie zwrócić z metod, są zwracane za pośrednictwem funkcji wywołania zwrotnego. Ani przekazywanie danych do HIDL w celu przesłania, ani odbieranie danych z HIDL nie zmienia własności danych — własność zawsze pozostaje w gestii funkcji wywołującej. Dane muszą być przechowywane tylko przez czas trwania wywołanej funkcji i mogą zostać zniszczone natychmiast po powrocie wywołanej funkcji.
Korzystanie z trybu przekazywania
Aby zaktualizować urządzenia z wcześniejszymi wersjami Androida do Androida O, można umieścić zarówno konwencjonalne (i starsze) warstwy HAL w nowym interfejsie HIDL, który obsługuje warstwę HAL w trybach binderized i tym samym procesie (przekazywanie). To opakowanie jest przezroczyste zarówno dla środowiska HAL, jak i systemu Android.
Tryb przekazywania jest dostępny tylko dla klientów i implementacji języka C++. Urządzenia z wcześniejszymi wersjami Androida nie mają warstw HAL napisanych w języku Java, więc warstwy Java HAL są z natury powiązane.
Przejściowe pliki nagłówkowe
Kiedy plik .hal
jest kompilowany, hidl-gen
tworzy dodatkowy plik nagłówkowy BsFoo.h
oprócz nagłówków używanych do komunikacji bindera; ten nagłówek definiuje funkcje, które mają zostać dlopen
. Ponieważ HAL-y przekazujące działają w tym samym procesie, w którym są wywoływane, w większości przypadków metody przekazujące są wywoływane przez bezpośrednie wywołanie funkcji (ten sam wątek). metody oneway
działają we własnym wątku, ponieważ nie mają czekać, aż warstwa HAL je przetworzy (oznacza to, że każda warstwa HAL, która używa metod oneway
w trybie przekazywania, musi być bezpieczna dla wątków).
Biorąc pod uwagę IFoo.hal
, BsFoo.h
zawija metody generowane przez oneway
, aby zapewnić dodatkowe funkcje (takie jak uruchamianie transakcji jednokierunkowych w innym wątku). Ten plik jest podobny do BpFoo.h
, jednak zamiast przekazywania połączeń IPC za pomocą bindera, żądane funkcje są wywoływane bezpośrednio. Przyszłe implementacje HAL mogą zapewniać wiele implementacji, takich jak FooFast HAL i FooAccurate HAL. W takich przypadkach tworzony byłby plik dla każdej dodatkowej implementacji (np. PTFooFast.cpp
i PTFooAccurate.cpp
).
Wiążące przekazywanie HAL
Implementacje HAL, które obsługują tryb przekazywania, można wiązać. Biorąc pod uwagę interfejs HAL abcd@MN::IFoo
, tworzone są dwa pakiety:
-
abcd@MN::IFoo-impl
. Zawiera implementację warstwy HAL i udostępnia funkcjęIFoo* HIDL_FETCH_IFoo(const char* name)
. Na starszych urządzeniach ten pakiet jest otwierany, a instancja implementacji jestHIDL_FETCH_IFoo
dlopen
Możesz wygenerować kod podstawowy za pomocąhidl-gen
i-Lc++-impl
i-Landroidbp-impl
. -
abcd@MN::IFoo-service
. Otwiera warstwę HAL przekazującą i rejestruje się jako usługa powiązana, umożliwiając użycie tej samej implementacji warstwy HAL zarówno jako tranzytowej, jak i powiązanej.
Biorąc pod uwagę typ IFoo
, możesz wywołać sp<IFoo> IFoo::getService(string name, bool getStub)
aby uzyskać dostęp do instancji IFoo
. Jeśli getStub
ma wartość true, getService
próbuje otworzyć warstwę HAL tylko w trybie przekazywania. Jeśli getStub
ma wartość false, getService
próbuje znaleźć powiązaną usługę; jeśli to się nie powiedzie, próbuje znaleźć usługę przekazywania. Parametr getStub
nigdy nie powinien być używany, z wyjątkiem defaultPassthroughServiceImplementation
. (Urządzenia uruchamiane z systemem Android O są urządzeniami w pełni powiązanymi, więc otwieranie usługi w trybie przekazywania jest niedozwolone).
gramatyki HIDL
Z założenia język HIDL jest podobny do C (ale nie korzysta z preprocesora C). Wszystkie znaki interpunkcyjne nieopisane poniżej (oprócz oczywistego użycia =
i |
) są częścią gramatyki.
Uwaga: Aby uzyskać szczegółowe informacje na temat stylu kodu HIDL, zobacz Przewodnik po stylach kodu .
-
/** */
oznacza komentarz do dokumentacji. Można je zastosować tylko do deklaracji typu, metody, pola i wartości wyliczenia. -
/* */
oznacza komentarz wielowierszowy. -
//
wskazuje komentarz do końca linii. Oprócz//
znaki nowej linii są takie same jak wszystkie inne spacje. - W poniższej przykładowej gramatyce tekst od
//
do końca wiersza nie jest częścią gramatyki, ale jest komentarzem do gramatyki. -
[empty]
oznacza, że termin może być pusty. -
?
następujący po dosłownym lub terminie oznacza, że jest opcjonalny. -
...
wskazuje sekwencję zawierającą zero lub więcej elementów z rozdzielającymi znakami interpunkcyjnymi, jak wskazano. W języku HIDL nie ma zmiennych argumentów. - Przecinki oddzielają elementy sekwencji.
- Średniki kończą każdy element, w tym ostatni element.
- WIELKIE LITERY to nieterminal.
-
italics
to rodzina tokenów, taka jakinteger
lubidentifier
(standardowe reguły analizy składni C). -
constexpr
jest wyrażeniem stałym w stylu C (takim jak1 + 1
i1L << 3
). -
import_name
to nazwa pakietu lub interfejsu kwalifikowana zgodnie z opisem w sekcji Wersjonowanie HIDL . -
words
pisane małymi literami są dosłownymi tokenami.
Przykład:
ROOT = PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... } // not for types.hal | PACKAGE IMPORTS ITEM ITEM... // only for types.hal; no method definitions ITEM = ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?; | safe_union identifier { UFIELD; UFIELD; ...}; | struct identifier { SFIELD; SFIELD; ...}; // Note - no forward declarations | union identifier { UFIELD; UFIELD; ...}; | enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar | typedef TYPE identifier; VERSION = integer.integer; PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION; PREAMBLE = interface identifier EXTENDS EXTENDS = <empty> | extends import_name // must be interface, not package GENERATES = generates (FIELD, FIELD ...) // allows the Binder interface to be used as a type // (similar to typedef'ing the final identifier) IMPORTS = [empty] | IMPORTS import import_name; TYPE = uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t | float | double | bool | string | identifier // must be defined as a typedef, struct, union, enum or import // including those defined later in the file | memory | pointer | vec<TYPE> | bitfield<TYPE> // TYPE is user-defined enum | fmq_sync<TYPE> | fmq_unsync<TYPE> | TYPE[SIZE] FIELD = TYPE identifier UFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...}; | struct identifier { FIELD; FIELD; ...}; | union identifier { FIELD; FIELD; ...}; | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SIZE = // Must be greater than zero constexpr ANNOTATIONS = [empty] | ANNOTATIONS ANNOTATION ANNOTATION = | @identifier | @identifier(VALUE) | @identifier(ANNO_ENTRY, ANNO_ENTRY ...) ANNO_ENTRY = identifier=VALUE VALUE = "any text including \" and other escapes" | constexpr | {VALUE, VALUE ...} // only in annotations ENUM_ENTRY = identifier | identifier = constexpr