HIDL

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Język definicji interfejsu HAL lub HIDL (wymawiane "ukryj-l") to język opisu interfejsu (IDL) określający interfejs między warstwą HAL a jej użytkownikami. Pozwala na określenie typów i wywołań metod, zebranych w interfejsy i pakiety. Mówiąc szerzej, HIDL to system do komunikacji między bazami kodu, które mogą być kompilowane niezależnie. Od Androida 10 HIDL jest przestarzały, a Android jest migrowany, aby używać AIDL wszędzie.

HIDL jest przeznaczony do użytku w komunikacji między procesami (IPC). Komunikacja między procesami jest określana jako Binderized . W przypadku bibliotek, które muszą być połączone z procesem, dostępny jest również tryb tranzytowy (nieobsługiwany w Javie).

HIDL określa struktury danych i sygnatury metod, zorganizowane w interfejsy (podobnie jak klasa), które są gromadzone w pakietach. Składnia HIDL będzie wyglądać znajomo programistom C++ i Java, choć z innym zestawem słów kluczowych. HIDL wykorzystuje również adnotacje w stylu Java.

Projekt HIDL

Celem HIDL jest umożliwienie zastąpienia struktury bez konieczności przebudowywania warstw HAL. Warstwy HAL będą budowane przez dostawców lub twórców SOC i umieszczane w partycji /vendor na urządzeniu, umożliwiając zastąpienie struktury we własnej partycji OTA bez ponownej kompilacji warstw HAL.

Projekt HIDL równoważy następujące problemy:

  • Interoperacyjność . Twórz niezawodnie 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 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 zapewnia również interfejsy pamięci współdzielonej, a ponieważ RPC są z natury nieco powolne, HIDL obsługuje dwa sposoby przesyłania danych bez użycia wywołania RPC: pamięć współdzielona i szybka kolejka wiadomości (FMQ).
  • Intuicyjny . HIDL pozwala uniknąć drażliwych problemów związanych z własnością pamięci, używając tylko in RPC (zobacz język definicji interfejsu Androida (AIDL) ); wartości, których nie można skutecznie zwrócić z metod, są zwracane za pośrednictwem funkcji wywołania zwrotnego. Ani przekazywanie danych do HIDL w celu przesłania, ani odbierania danych z HIDL nie zmienia własności danych — własność zawsze pozostaje z funkcją wywołującą. Dane muszą być utrwalane 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 systemu Android do systemu Android O, można opakować zarówno konwencjonalne (i starsze) warstwy HAL w nowy interfejs HIDL, który obsługuje warstwy HAL w trybie powiązania i tego samego procesu (przekazywanie). To zawijanie jest przezroczyste zarówno dla warstwy HAL, jak i platformy Android.

Tryb przekazywania jest dostępny tylko dla klientów i implementacji języka C++. Urządzenia z wcześniejszymi wersjami systemu Android nie mają warstw HAL napisanych w języku Java, więc warstwy HAL języka Java są z natury zbindowane.

Gdy plik .hal jest kompilowany, hidl-gen tworzy dodatkowy plik nagłówka BsFoo.h oprócz nagłówków używanych do komunikacji z binderami; ten nagłówek definiuje funkcje, które mają być dlopen ed. Ponieważ przekazujące warstwy HAL 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). oneway metody działają we własnym wątku, ponieważ nie są przeznaczone do oczekiwania na ich przetworzenie przez warstwę HAL (oznacza to, że każda warstwa HAL, która używa metod oneway w trybie tranzytowym, musi być bezpieczna wątkowo).

Biorąc pod uwagę IFoo.hal , BsFoo.h otacza metody generowane przez oneway , aby zapewnić dodatkowe funkcje (takie jak wykonywanie transakcji jednokierunkowych w innym wątku). Ten plik jest podobny do BpFoo.h , jednak zamiast przekazywania wywołań IPC za pomocą bindera, żądane funkcje są wywoływane bezpośrednio. Przyszłe implementacje warstw 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 przepuszczające warstwy HAL

Możesz powiązać implementacje warstwy HAL, które obsługują tryb tranzytowy. Mając 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 dlopen ed, a implementacja jest tworzona przy użyciu HIDL_FETCH_IFoo . Możesz wygenerować kod podstawowy za pomocą hidl-gen i -Lc++-impl i -Landroidbp-impl .
  • abcd@MN::IFoo-service . Otwiera przekazującą warstwę HAL i rejestruje się jako usługa zbindowana, umożliwiając używanie tej samej implementacji warstwy HAL zarówno jako przekazującej, jak i zbinderyzowanej.

Biorąc pod uwagę typ IFoo , możesz wywołać sp<IFoo> IFoo::getService(string name, bool getStub) aby uzyskać dostęp do wystąpienia IFoo . Jeśli getStub ma wartość true, getService próbuje otworzyć warstwę HAL tylko w trybie tranzytowym. 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. Parametru getStub nigdy nie należy używać z wyjątkiem defaultPassthroughServiceImplementation . (Urządzenia uruchamiane z Androidem O są urządzeniami w pełni powiązanymi, więc otwieranie usługi w trybie przekazywania jest niedozwolone).

Gramatyka HIDL

Z założenia język HIDL jest podobny do C (ale nie używa preprocesora C). Wszystkie znaki interpunkcyjne nie opisane poniżej (poza oczywistym użyciem = 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 na końcu linii. Poza // znaki nowej linii są takie same jak inne białe znaki.
  • W przykładowej gramatyce poniżej 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ące po literze lub terminie oznacza, że ​​jest to opcjonalne.
  • ... wskazuje sekwencję zawierającą zero lub więcej elementów z oddzielającą interpunkcją, jak wskazano. W HIDL nie ma argumentów wariadycznych.
  • Przecinki oddzielają elementy sekwencji.
  • Średniki kończą każdy element, w tym ostatni element.
  • WIELKIE LITERY to nieterminal.
  • italics to rodzina tokenów, taka jak integer lub identifier (standardowe reguły analizowania języka C).
  • constexpr to stałe wyrażenie w stylu C (takie jak 1 + 1 i 1L << 3 ).
  • import_name to nazwa pakietu lub interfejsu kwalifikowana zgodnie z opisem w HIDL Versioning .
  • words pisane małymi literami to tokeny dosłowne.

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

Terminologia

W tej sekcji używane są następujące terminy związane z HIDL:

spoiwo Wskazuje, że HIDL jest używany do zdalnych wywołań procedur między procesami, zaimplementowanych przez mechanizm podobny do Bindera. Zobacz także przechodzenie .
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żywany w przypadku metod, które zwracają void lub pojedynczą wartość pierwotną.
klient Proces, który wywołuje metody określonego interfejsu. Proces HAL lub framework może być klientem jednego interfejsu i serwerem innego. Zobacz także przechodzenie .
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 zwiększenia mniejszej wersji w tej samej nazwie pakietu lub do nowego pakietu (np. rozszerzenia dostawcy) w celu zbudowania starszego pakietu.
generuje Wskazuje metodę interfejsu, która zwraca wartości do klienta. Aby zwrócić jedną wartość niepierwotną lub więcej niż jedną, generowana jest synchroniczna funkcja wywołania zwrotnego.
berło 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 implementowane 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 udostępniających wersję.
Przejść przez Tryb dlopen , w którym serwer jest biblioteką współdzieloną, dodaną przez klienta. W trybie tranzytowym klient i serwer to ten sam proces, ale oddzielne bazy kodu. Używany tylko do przenoszenia starszych baz kodu do modelu HIDL. Zobacz także Zbindowane .
serwer Proces implementujący metody interfejsu. Zobacz także przechodzenie .
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. Przyrosty wersji drugorzędnych mogą dodawać (ale nie zmieniać) typy i metody.