HIDL C++

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

Android O zmienia architekturę systemu operacyjnego Android w celu zdefiniowania przejrzystych interfejsów między platformą Android niezależną od urządzenia a kodem specyficznym dla urządzenia i dostawcy. Android już definiuje wiele takich interfejsów w postaci interfejsów HAL, zdefiniowanych jako nagłówki C w hardware/libhardware . HIDL zastępuje te interfejsy HAL stabilnymi, wersjonowanymi interfejsami, które mogą być interfejsami HIDL po stronie klienta i serwera w języku C++ (opisanym poniżej) lub Java .

Strony w tej sekcji opisują implementacje C++ interfejsów HIDL, w tym szczegóły dotyczące plików automatycznie generowanych z plików .hal .hal przez kompilator hidl-gen , sposobu pakowania tych plików oraz sposobu integracji tych plików z kodem C++, który używa ich.

Implementacje klienckie i serwerowe

Interfejsy HIDL mają implementacje klienckie i serwerowe:

  • Klient interfejsu HIDL to kod, który korzysta z interfejsu, wywołując na nim metody.
  • Serwer to implementacja interfejsu HIDL, która odbiera wywołania od klientów i zwraca wyniki (jeśli to konieczne).

Przy przejściu z warstw HAL libhardware na warstwy HAL HIDL implementacja warstwy HAL staje się serwerem, a proces wywołujący warstwę HAL staje się klientem. Domyślne implementacje mogą obsługiwać zarówno przekazujące, jak i zbindowane warstwy HAL i mogą się zmieniać w czasie:

Rysunek 1. Postęp rozwoju starszych warstw HAL.

Tworzenie klienta HAL

Zacznij od włączenia bibliotek HAL do pliku makefile:

  • Zrób: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Wkrótce: shared_libs: [ …, android.hardware.nfc@1.0 ]

Następnie dołącz pliki nagłówkowe 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 , które reprezentują Twój HAL i wygenerowałeś już pliki makefile dla HAL za pomocą -Lmakefile lub -Landroidbp na hidl-gen ( ./hardware/interfaces/update-makefiles.sh robi to dla wewnętrzne pliki HAL i jest dobrym odniesieniem). Przesyłając warstwy HAL z libhardware , możesz łatwo wykonać wiele z tych prac za pomocą c2hal.

Aby utworzyć pliki niezbędne do wdrożenia Twojej warstwy 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 warstwa HAL działała w trybie przekazującym, musisz mieć funkcję HIDL_FETCH_IMmoduleName znajdującą się w /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl( OPTIONAL_IDENTIFIER ).so gdzie OPTIONAL_IDENTIFIER to ciąg znaków identyfikujący implementację tranzytu. Wymogi trybu przekazywania są automatycznie spełniane przez powyższe polecenia, które również tworzą cel android.hardware.nfc@1.0-impl , ale można użyć dowolnego rozszerzenia. Na przykład android.hardware.nfc@1.0-impl-foo używa -foo do odróżnienia się.

Jeśli warstwa HAL jest podrzędną wersją lub rozszerzeniem innej warstwy HAL, do nazwania tego pliku binarnego należy użyć podstawowej warstwy HAL. Na przykład implementacje android.hardware.graphics.mapper@2.1 powinny nadal znajdować się w pliku binarnym o nazwie android.hardware.graphics.mapper@2.0-impl( OPTIONAL_IDENTIFIER ) . Zwykle OPTIONAL_IDENTIFIER zawiera rzeczywistą wersję HAL. Nazywając plik binarny w ten sposób, klienci 2.0 mogą go bezpośrednio pobierać, a klienci 2.1 mogą przesyłać implementację.

Następnie wypełnij skróty funkcjonalnością i skonfiguruj demona. Przykładowy kod demona (obsługujący przekazywanie):

#include <hidl/LegacySupport.h>

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

defaultPassthroughServiceImplementation wykona dlopen() dostarczoną bibliotekę -impl i udostępni ją jako usługę powiązaną. Przykładowy kod demona (dla usługi czysto binderyzowanej):

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 will never exceed
    // 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 znajduje się w $PACKAGE + "-service-suffix" (na przykład android.hardware.nfc@1.0-service ), ale może być wszędzie. Sepolityką dla określonej klasy HAL jest atrybut hal_<module> (na przykład hal_nfc) . Atrybut ten należy zastosować do demona, który uruchamia konkretną warstwę HAL (jeśli ten sam proces obsługuje wiele warstw HAL, można do niego zastosować wiele atrybutów).