W Androidzie 11 można korzystać z AIDL w przypadku list HAL na Androidzie. Dzięki temu wdrożenia części Androida bez HIDL. Przenoszenie kont HAL do AIDL wyłącznie w miarę możliwości (jeśli w przypadku HAL nadrzędne są używane HIDL, należy stosować HIDL).
Listy HAL korzystające z AIDL do komunikacji między komponentami platformy, takimi jak
system.img
oraz komponenty sprzętowe, takie jak vendor.img
, muszą używać
Stabilna wersja AIDL. Jednak do komunikacji w obrębie partycji, np. z jednego
HAL, nie ma ograniczeń w korzystaniu z mechanizmu IPC.
Motywacja
Technologia AIDL jest stosowana dłużej niż HIDL i jest używana w wielu innych miejscach, między komponentami platformy Androida i aplikacjami. Teraz, gdy AIDL ma stabilność obsługiwane jest wdrożenie całego stosu w jednym środowisku wykonawczym IPC. AIDL ma również lepszy system obsługi wersji niż HIDL.
- Korzystanie z jednego języka IPC oznacza, że mamy tylko jedną rzecz do nauki, debugowania w celu optymalizacji i bezpieczeństwa.
- AIDL obsługuje obsługę wersji w miejscu dla właścicieli interfejsu:
- Właściciele mogą dodawać metody na końcu interfejsów lub w polach pakietów. Oznacza to, że zmiana wersji kodu na przestrzeni lat i roku życia jest łatwiejsza w skali rocznej jest mniejszy (rodzaje można zmienić lokalnie, Potrzeba dodatkowych bibliotek dla każdej wersji interfejsu).
- Interfejsy rozszerzeń można dołączać w czasie działania, a nie w typie nie ma więc potrzeby, by używać nowszych rozszerzeń różnych wersji interfejsów.
- Istniejącego interfejsu AIDL można używać bezpośrednio, jeśli jego właściciel zdecyduje się na ją ustabilizować. Wcześniej cała kopia interfejsu musiała być uzależniona od utworzony w HIDL.
Kompilacja na podstawie środowiska wykonawczego AIDL
AIDL ma 3 różne backendy: Java, NDK i CPP. Aby korzystać ze stabilnej wersji AIDL, musisz:
zawsze używaj kopii systemowej biblioteki libbinder w domenie system/lib*/libbinder.so
i Talk
w dniu /dev/binder
. W przypadku kodu na obrazie dostawcy oznacza to, że atrybut libbinder
(z VNDK): ta biblioteka ma niestabilny interfejs API C++ i
lub niestabilne wewnętrzne. Zamiast tego natywny kod dostawcy musi używać backendu NDK
AIDL, link do: libbinder_ndk
(wspierany przez system libbinder.so
),
i link do bibliotek NDK utworzonych przez wpisy aidl_interface
. Dla:
dokładne nazwy modułów,
reguł nazewnictwa modułów.
Pisanie interfejsu AIDL HAL
Aby można było używać interfejsu AIDL między systemem a dostawcą, interfejs musi dwie zmiany:
- Do każdej definicji typu należy dodać adnotację
@VintfStability
. - Deklaracja
aidl_interface
musi zawieraćstability: "vintf",
.
Tylko właściciel interfejsu może wprowadzić te zmiany.
Przy wprowadzaniu tych zmian interfejs musi być
Plik manifestu VINTF. Przetestuj to (i powiązane z nim informacje)
takich jak weryfikacja, czy zwolnione interfejsy zostały zablokowane) za pomocą
Test VTS vts_treble_vintf_vendor_test
. Za pomocą @VintfStability
interfejsu bez tych wymagań, wywołując
AIBinder_forceDowngradeToLocalStability
w backendzie NDK,
android::Stability::forceDowngradeToLocalStability
w backendzie C++,
lub android.os.Binder#forceDowngradeToSystemStability
w backendzie Java
na obiekt wiązań przed wysłaniem go do innego procesu. Przechodzenie na niższą wersję usługi
stabilność dostawcy nie jest obsługiwana w Javie, ponieważ wszystkie aplikacje działają w systemie
i dodaje kontekst.
Ponadto, aby zapewnić maksymalną możliwość przenoszenia kodu oraz uniknąć potencjalnych problemów, takich jak: jako zbędnych bibliotek dodatkowych, wyłącz backend CPP.
Zwróć uwagę, że użycie backends
w poniższym przykładzie kodu jest poprawne, ponieważ
to 3 backendy (Java, NDK i CPP). Poniższy kod pokazuje, jak wybrać
w backendzie CPP, aby go wyłączyć.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Znajdź interfejsy AIDL HAL
Interfejsy AOSP Stable AIDL dla HAL znajdują się w tych samych katalogach podstawowych co
Interfejsy HIDL w aidl
folderach.
- sprzęt/interfejsy
- platformy/sprzęt/interfejsy
- system/sprzęt/interfejsy
Interfejsy rozszerzeń należy umieścić w innych obszarach hardware/interfaces
podkatalogi w vendor
lub hardware
.
Interfejsy rozszerzeń
Każda wersja Androida ma zestaw oficjalnych interfejsów AOSP. Gdy Android chcą dodać funkcje do tych interfejsów, nie powinni zmieniać ponieważ oznacza to, że środowisko wykonawcze w Androidzie jest niezgodne ze środowiskiem wykonawczym AOSP na Androidzie. W przypadku urządzeń GMS: te interfejsy zapewniają też nieprzerwane działanie obrazu GSI.
Rozszerzenia mogą się rejestrować na 2 sposoby:
- w czasie działania aplikacji znajdziesz w artykule o dołączonych rozszerzeniach.
- samodzielny, zarejestrowany na całym świecie i w systemie VINTF.
Rozszerzenie jest jednak rejestrowane, jeśli jest związane z konkretnym dostawcą (czyli nie jest częścią komponenty AOSP) używają interfejsu, nie ma możliwości scalenia . Jednak w przypadku modyfikacji podrzędnych komponentów AOSP mogą spowodować konflikty podczas scalania. Zalecamy stosowanie tych strategii:
- dodatki do interfejsu można przesłać do AOSP w kolejnej wersji
- które zapewniają większą elastyczność bez konfliktów można przesłać na serwer w następnej wersji
Rozszerzenie parcelables: ParcelableHolder
ParcelableHolder
to typ elementu Parcelable
, który może zawierać inny element Parcelable
.
Głównym przypadkiem użycia funkcji ParcelableHolder
jest możliwość rozszerzenia Parcelable
.
Może to być na przykład obraz, który osoby implementujące urządzenia mają mieć możliwość rozszerzenia
Zdefiniowany przez AOSP Parcelable
, AospDefinedParcelable
, aby uwzględnić dodatkową wartość
funkcje zabezpieczeń.
Wcześniej bez ParcelableHolder
narzędzia implementujące na urządzeniach nie mogły modyfikować
stabilnego interfejsu AIDL zdefiniowanego przez AOSP, ponieważ dodanie kolejnych byłoby błędem
pola:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Jak widać w poprzednim kodzie, ta praktyka jest uszkodzona, ponieważ pola dodany przez narzędzie do implementacji na urządzeniu może mieć konflikt, gdy komponent Parcelable jest które zostały poprawione w kolejnych wersjach Androida.
Za pomocą ParcelableHolder
właściciel nieruchomości może zdefiniować rozszerzenie
punkt w Parcelable
.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Następnie osoby wdrażające na urządzeniach mogą zdefiniować własne Parcelable
.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Na koniec nowe Parcelable
można dołączyć do oryginalnego dokumentu Parcelable
za pomocą
w polu ParcelableHolder
.
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
Nazwy instancji serwera AIDL HAL
Zgodnie z konwencją usługi AIDL HAL mają nazwę instancji w formacie
$package.$type/$instance
Na przykład wystąpienie HAL wibracji to
zarejestrowana jako android.hardware.vibrator.IVibrator/default
.
Zapisywanie serwera HAL AIDL
@VintfStability
serwery AIDL muszą być zadeklarowane w pliku manifestu VINTF, w przypadku
przykład w następujący sposób:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
W przeciwnym razie powinni zarejestrować usługę AIDL w zwykły sposób. Podczas uruchamiania VTS testy powinny być dostępne wszystkie zadeklarowane wartości HAL AIDL.
Zapisywanie klienta AIDL
Klienci AIDL muszą zadeklarować się w tabeli zgodności, na przykład podobny do tego:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
Konwertowanie istniejącej usługi HAL z HIDL na AIDL
Za pomocą narzędzia hidl2aidl
konwertuj interfejs HIDL na AIDL.
Funkcje usługi hidl2aidl
:
- Utwórz pliki typu
.aidl
na podstawie plików typu.hal
w danym pakiecie - Utwórz reguły kompilacji dla nowo utworzonego pakietu AIDL ze wszystkimi backendami włączono
- Tworzenie metod translacji w backendach Java, CPP i NDK na potrzeby tłumaczenia od typów HIDL po AIDL
- Tworzenie reguł kompilacji dla bibliotek translacji z wymaganymi zależnościami
- Utwórz asercje statyczne, aby zapewnić, że moduły wyliczające HIDL i AIDL mają te same wartości w backendach CPP i NDK
Aby przekonwertować pakiet plików .hal na pliki .aidl, wykonaj te czynności:
Skompiluj narzędzie w obszarze
system/tools/hidl/hidl2aidl
.Utworzenie tego narzędzia na podstawie najnowszego źródła zapewnia najpełniejsze i uzyskiwanie dodatkowych informacji. Aby konwertować interfejsy na starszych wersjach, możesz użyć najnowszej wersji i gałęzi, które pojawiły się w poprzednich wersjach.
m hidl2aidl
Uruchom narzędzie z katalogiem wyjściowym, a następnie pakietem, który ma być dokonali konwersji.
Opcjonalnie możesz użyć argumentu
-l
, aby dodać zawartość nowego pliku licencji u góry wszystkich wygenerowanych plików. Użyj właściwej licencji i daty.hidl2aidl -o <output directory> -l <file with license> <package>
Na przykład:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Przeczytaj wygenerowane pliki i rozwiąż wszystkie problemy z konwersją.
conversion.log
zawiera nierozwiązane problemy do rozwiązania.- Wygenerowane pliki
.aidl
mogą zawierać ostrzeżenia i sugestie, które mogą które wymagają działań. Zaczynają się one od://
. - Skorzystaj z okazji, by wyczyścić pakiet i go ulepszyć.
- Zapoznaj się z dokumentem
@JavaDerive
adnotacja do funkcji, które mogą być potrzebne, takich jaktoString
lubequals
.
Twórz tylko te cele, których potrzebujesz.
- Wyłącz backendy, które nie będą używane. Wybieraj backend NDK zamiast CPP , patrz wybór środowiska wykonawczego.
- Usuń biblioteki translacji i wygenerowany przez nie kod, który nie będzie używany.
Zobacz Główne różnice w AIDL i HIDL.
- Korzystanie z wbudowanych funkcji
Status
i wyjątków AIDL zazwyczaj poprawia i wyeliminowanie konieczności stosowania innego typu stanu zależnego od interfejsu. - Argumenty interfejsu AIDL w metodach nie mają domyślnie wartości
@nullable
, np. byli w HIDL.
- Korzystanie z wbudowanych funkcji
SEPolicy dla klientów HAL AIDL
Typ usługi AIDL, który jest widoczny dla kodu dostawcy, musi mieć parametr
hal_service_type
. W przeciwnym razie konfiguracja sepolicy jest taka sama
tak jak w przypadku każdej innej usługi AIDL (choć w przypadku HAL istnieją specjalne atrybuty). Tutaj
to przykładowa definicja kontekstu usługi HAL:
type hal_foo_service, service_manager_type, hal_service_type;
W przypadku większości usług zdefiniowanych przez platformę kontekst usługi z prawidłowym
typ jest już dodany (na przykład android.hardware.foo.IFoo/default
spowoduje
już oznaczone jako hal_foo_service
). Jeśli jednak klient korzystający ze platformy obsługuje
wielu nazw instancji, należy dodać dodatkowe nazwy instancji w
plików service_contexts
przeznaczonych na konkretne urządzenia.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Atrybuty HAL należy dodać podczas tworzenia nowego typu HAL. Konkretna HAL
może być powiązany z wieloma typami usług (z których każdy może
(co już omówiliśmy), W przypadku HAL foo
hal_attribute(foo)
To makro definiuje atrybuty hal_foo_client
i
hal_foo_server
W przypadku danej domeny atrybuty hal_client_domain
i
Makra hal_server_domain
łączą domenę z danym atrybutem HAL. Dla:
Na przykład: serwer systemu będący klientem tej listy HAL odpowiada zasadom
hal_client_domain(system_server, hal_foo)
Serwer HAL zawiera podobnie
hal_server_domain(my_hal_domain, hal_foo)
Zwykle dla danej HAL
tworzymy również domenę taką jak hal_foo_default
tylko w celach referencyjnych lub
przykładowych HAL. Niektóre urządzenia używają jednak tych domen na potrzeby własnych serwerów.
Rozróżnienie między domenami dla kilku serwerów ma znaczenie tylko wtedy, gdy
wiele serwerów, które wyświetlają ten sam interfejs i wymagają różnych uprawnień;
w ich implementacjach. We wszystkich tych makrach element hal_foo
obiektem sepolicy. Ten token jest używany przez te makra do odwoływania się
grupę atrybutów powiązanych z parą serwerów klienta.
Jednak do tej pory nie powiązaliśmy żadnych usług hal_foo_service
i hal_foo
(para atrybutów z hal_attribute(foo)
). Powiązany jest atrybut HAL
z usługami AIDL HAL wykorzystującymi makro hal_attribute_service
(w przypadku HAL HIDL
makro hal_attribute_hwservice
). Przykład:
hal_attribute_service(hal_foo, hal_foo_service)
Oznacza to, że
Procesy hal_foo_client
mogą otrzymywać dane HAL i hal_foo_server
mogą rejestrować HAL. Egzekwowanie tych reguł rejestracji jest
zostało wykonane przez menedżera kontekstu (servicemanager
). Uwaga: nazwy usług mogą
nie zawsze odpowiadają atrybutom HAL. Na przykład możemy zobaczyć,
hal_attribute_service(hal_foo, hal_foo2_service)
Zasadniczo jednak
co oznacza, że usługi są zawsze używane razem, warto rozważyć usunięcie
hal_foo2_service
i hal_foo_service
w całej naszej usłudze
kontekstach. Większość kont HAL, które mają wiele wartości hal_attribute_service
, wynika z tego,
nazwa oryginalnego atrybutu HAL jest zbyt ogólna i nie można jej zmienić.
Po połączeniu wszystkich tych danych przykład HAL wygląda tak:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
Interfejsy podłączonych rozszerzeń
Rozszerzenie można dołączyć do dowolnego interfejsu Binder, niezależnie od tego, czy jest to interfejs najwyższego poziomu interfejsu rejestrowanego bezpośrednio przez menedżera usługi lub jest to interfejs podrzędny. Gdy pobierasz rozszerzenie, musisz sprawdzić, czy jego typ to: nie jest oczekiwany. Rozszerzenia można skonfigurować tylko podczas procesu udostępniającego segregator.
Załączonych rozszerzeń należy używać zawsze, gdy rozszerzenie modyfikuje funkcji istniejącej listy HAL. Gdy potrzebna jest zupełnie nowa funkcja, tego mechanizmu nie trzeba używać, a interfejs rozszerzenia można zarejestrowany bezpośrednio u menedżera usługi. Interfejsy podłączonych rozszerzeń mają sens, gdy są dołączone do podinterfejsów, ponieważ mogą być głęboką lub wieloinstancyjną. Użycie rozszerzenia globalnego w celu powielania hierarchia interfejsu Binder innej usługi wymagałaby księgowości, aby zapewnić funkcjonalność równoważną rozszerzeniom bezpośrednio dołączanym.
Aby skonfigurować rozszerzenie w narzędziu do segregatora, użyj tych interfejsów API:
- W backendzie NDK:
AIBinder_setExtension
- W backendzie Javy:
android.os.Binder.setExtension
- W backendzie CPP:
android::Binder::setExtension
- W backendzie Rust:
binder::Binder::set_extension
Aby uzyskać rozszerzenie do separatora, użyj tych interfejsów API:
- W backendzie NDK:
AIBinder_getExtension
- W backendzie Javy:
android.os.IBinder.getExtension
- W backendzie CPP:
android::IBinder::getExtension
- W backendzie Rust:
binder::Binder::get_extension
Więcej informacji o tych interfejsach API znajdziesz w dokumentacji
getExtension
w odpowiednim backendzie. Przykład użycia
rozszerzenia można znaleźć w
sprzęt/interfejsy/testy/rozszerzenia/wibratory.
Główne różnice w AIDL i HIDL
Podczas korzystania z interfejsów AIDL HAL lub interfejsów AIDL HAL zwróć uwagę na różnice w porównaniu z zapisywaniem wartości HAL HIDL.
- Składnia języka AIDL jest bliższa Javie. Składnia HIDL jest podobna do języka C++.
- Wszystkie interfejsy AIDL mają wbudowane stany błędu. Zamiast tworzyć niestandardowe
typów stanu, tworzyć stałe stany w plikach interfejsu i wykorzystywać
EX_SERVICE_SPECIFIC
w backendach CPP/NDK iServiceSpecificException
w backendzie Javy. Patrz: błąd Obsługa. - AIDL nie uruchamia automatycznie pul wątków po wysłaniu obiektów powiązań. Należy je uruchamiać ręcznie (zobacz wątek ).
- AIDL nie przerywa działania w przypadku niezaznaczonych błędów transportu (HIDL
Return
przerywa działanie włączone niezaznaczonych błędów). - AIDL może zadeklarować tylko 1 typ na plik.
- Oprócz danych wyjściowych argumentów AIDL można określić jako wejścia/wyjścia/wyjścia (nie ma „synchronicznych wywołań zwrotnych”).
- AIDL używa typu podstawowego fd zamiast uchwytu.
- HIDL używa wersji głównych w przypadku niezgodnych zmian i wersji podrzędnych
zgodne zmiany. W AIDL wprowadzane są zmiany o zgodności wstecznej.
AIDL nie ma wyraźnego koncepcji wersji głównych. jest to
zawarte w nazwach pakietów. Na przykład AIDL może użyć nazwy pakietu
bluetooth2
- AIDL domyślnie nie dziedziczy priorytetu w czasie rzeczywistym.
setInheritRt
W celu włączenia dziedziczenia priorytetów w czasie rzeczywistym należy używać funkcji w danym powiązaniu.