HIDL w C++

Android 8 zmienia architekturę systemu operacyjnego Android, definiując interfejsy między platformę Androida niezależnie od urządzenia, a także kod związany z określonym urządzeniem i dostawcą. Android ma już wiele takich interfejsów w postaci interfejsów HAL, zdefiniowane jako nagłówki C w komponencie hardware/libhardware. HIDL zastępuje te ustawienia Interfejsy HAL ze stabilnymi, wersjonowanymi interfejsami, które mogą być zarówno interfejsów HIDL po stronie serwera w języku C++ (opisane poniżej) lub Java.

Strony w tej sekcji opisują implementacje interfejsów HIDL w języku C++, w tym szczegóły o plikach wygenerowanych automatycznie na podstawie HIDL .hal przez kompilatora hidl-gen, sposób ich spakowania oraz jak zintegrować te pliki z kodem C++, który z nich korzysta.

Implementacje klient-serwer

Interfejsy HIDL mają implementacje klienta i serwera:

  • Klient interfejsu HIDL to kod, który korzysta z metody za pomocą metod jego wywoływania.
  • Serwer to implementacja interfejsu HIDL, odbiera połączenia od klientów i zwraca wyniki (w razie potrzeby).

Podczas przechodzenia z libhardware HAL na HAL HIDL staje się serwerem, a proces wywołujący wartość HAL zmienia się w do klienta. Implementacje domyślne mogą obsługiwać zarówno przekazywanie, jak i powiązania HAL, które mogą się z czasem zmieniać:

Rysunek 1. Postęp w rozwoju starszych wersji HAL.

Tworzenie klienta HAL

Zacznij od dodania bibliotek HAL do pliku Makefile:

  • Marka: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Utwór: shared_libs: [ …, android.hardware.nfc@1.0 ]

Następnie dołącz pliki nagłówka HAL:

#include <android/hardware/nfc/1.0/IFoo.h>
…
// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

Tworzenie serwera HAL

Aby utworzyć implementację HAL, musisz mieć pliki .hal reprezentujące Twoją HAL i wygenerowane dla niej przez kod -Lmakefile lub -Landroidbp w aplikacji hidl-gen (./hardware/interfaces/update-makefiles.sh robi to w przypadku zasobów wewnętrznych plików HAL). Podczas przenoszenia kont HAL z libhardware, możesz z łatwością wykonać wiele tych czynności, korzystając z c2hal.

Aby utworzyć pliki niezbędne do implementacji HAL:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

Aby HAL działał w trybie przekazywania, musisz mieć funkcja HIDL_FETCH_IModuleName korzystająca z funkcji /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so gdzie OPTIONAL_IDENTIFIER to ciąg znaków identyfikujący przekazywanie implementacji. Wymagania trybu przekazywania są automatycznie spełnione przez tych poleceń, które również tworzą android.hardware.nfc@1.0-impl ale można użyć dowolnego rozszerzenia. Przykład: android.hardware.nfc@1.0-impl-foo używa -foo do: się wyróżniać.

Jeśli HAL jest wersją podrzędną lub rozszerzeniem innej HAL, do nazwania tego pliku binarnego należy używać podstawowej struktury HAL. Przykład: Implementacje „android.hardware.graphics.mapper@2.1” powinny być w pliku binarnym o nazwie android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER) Zwykle wartość OPTIONAL_IDENTIFIER w tym miejscu zawiera rzeczywistą wartość HAL wersji. Klienty wersji 2.0 mogą pobrać plik binarny w taki sposób, a klienci 2.1 mogą ją skonfigurować.

Następnie wypełnij wycinki z funkcjami i skonfiguruj demona. Przykład kod demona (obsługujący przekazywanie):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation połączenia dlopen() dla podanej biblioteki -impl i udostępnia ją jako w powiązanej usłudze. Przykładowy kod demona (na potrzeby czystej usługi powiązanej):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool never exceeds
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

Ten demon zwykle mieszka w regionie $PACKAGE + "-service-suffix" (dla np. android.hardware.nfc@1.0-service), ale może być gdziekolwiek. Polityka separacyjna konkretnej klasa HAL to atrybut hal_<module> (na przykład hal_nfc) Ten atrybut należy zastosować do demona, który uruchamia konkretną HAL (jeśli ten sam proces obsługuje wiele HAL, wiele atrybutów ).