AIDL dla licencji HAL

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:

  1. 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
    
  2. 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
    
  3. 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 jak toString lub equals.
  4. 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.
  5. 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.

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 i ServiceSpecificException 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.