Android O zmienia architekturę systemu operacyjnego Android w celu zdefiniowania przejrzystych interfejsów pomiędzy niezależną od urządzenia platformą Android 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órymi mogą być interfejsy HIDL po stronie klienta i serwera w C++ (opisane poniżej) lub Java .
Strony w tej sekcji opisują implementacje interfejsów HIDL w C++, w tym szczegóły dotyczące plików automatycznie generowanych z plików .hal
HIDL przez kompilator hidl-gen
, sposób pakowania tych plików i sposób integrowania tych plików z kodem C++, który ich używa.
Wdrożenia klient-serwer
Interfejsy HIDL mają implementacje klienta i serwera:
- Klient interfejsu HIDL to kod korzystający 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).
Podczas przechodzenia z warstw HAL libhardware
do warstw 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 warstwy HAL przekazywane, jak i powiązane, i mogą zmieniać się w czasie:
Rysunek 1. Postęp rozwoju starszych warstw HAL.
Tworzenie klienta HAL
Zacznij od dołączenia bibliotek HAL do pliku makefile:
- Marka:
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
reprezentujące twoją warstwę HAL i wygenerować już pliki makefile dla swojej warstwy 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). Podczas przesyłania przez warstwy HAL z libhardware
wiele z tych prac można łatwo wykonać za pomocą c2hal.
Aby utworzyć pliki niezbędne do wdrożenia 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 przekazowym, funkcja HIDL_FETCH_IModuleName musi znajdować się w /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl( OPTIONAL_IDENTIFIER ).so
gdzie OPTIONAL_IDENTIFIER jest ciągiem znaków identyfikującym implementację przekazywania. Powyższe polecenia automatycznie spełniają wymagania trybu przekazywania, które również tworzą obiekt docelowy 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
, aby się wyróżnić.
Jeśli warstwa HAL jest wersją pomocniczą lub rozszerzeniem innej warstwy HAL, do nadania nazwy temu plikowi binarnemu 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 tutaj będzie zawierał rzeczywistą wersję HAL. Nazywając plik binarny w ten sposób, klienci 2.0 mogą go pobrać bezpośrednio, a klienci 2.1 mogą przesłać implementację.
Następnie wypełnij kody pośredniczące 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
wywoła dlopen()
dostarczoną bibliotekę -impl
i udostępni ją jako usługę powiązaną. Przykładowy kod demona (dla czystej usługi spakowanej):
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 znajdować się gdziekolwiek. Sepolicy dla określonej klasy HAL to atrybut hal_<module>
(na przykład hal_nfc)
. Ten atrybut musi zostać zastosowany do demona uruchamiającego określoną warstwę HAL (jeśli ten sam proces obsługuje wiele warstw HAL, można do niego zastosować wiele atrybutów).