Android 10 obsługuje stabilny język definiowania interfejsu Androida (AIDL), czyli nowy sposób śledzenia interfejsu aplikacji (API) i interfejsu binarnego aplikacji (ABI) udostępnianego przez interfejsy AIDL. Główne różnice między stabilną wersją AIDL a AIDL to:
- Interfejsy są zdefiniowane w systemie kompilacji za pomocą
aidl_interfaces
. - Interfejsy mogą zawierać tylko uporządkowane dane. Interfejsy Parcelable reprezentujące preferowane typy są tworzone automatycznie na podstawie ich definicji AIDL, a także są automatycznie modyfikowane i usuwane.
- Interfejsy mogą być zadeklarowane jako stabilne (zgodne wstecznie). W takim przypadku ich interfejs API jest śledzony i umieszczany w wersji w pliku obok interfejsu AIDL.
Uporządkowane a stabilne AIDL
Uporządkowane AIDL odnosi się do typów zdefiniowanych wyłącznie w AIDL. Na przykład deklaracja własna (niestandardowa) nie ma struktury AIDL. Materiały Parcelable z polami zdefiniowanymi w AIDL są nazywane elementami strukturalnymi.
Stabilna wersja AIDL wymaga uporządkowanych danych AIDL, tak aby system kompilacji i kompilator mógł rozpoznać, czy zmiany wprowadzone w plikach parcelable są zgodne wstecznie.
Jednak nie wszystkie uporządkowane interfejsy są stabilne. Aby interfejs był stabilny, interfejs może używać tylko typów uporządkowanych i otrzymywać wymienione poniżej funkcje obsługi wersji. Interfejs nie jest też stabilny, jeśli do jego utworzenia używany jest podstawowy system kompilacji lub jeśli skonfigurowano funkcję unstable:true
.
Definiowanie interfejsu AIDL
Definicja słowa aidl_interface
wygląda tak:
aidl_interface {
name: "my-aidl",
srcs: ["srcs/aidl/**/*.aidl"],
local_include_dir: "srcs/aidl",
imports: ["other-aidl"],
versions_with_info: [
{
version: "1",
imports: ["other-aidl-V1"],
},
{
version: "2",
imports: ["other-aidl-V3"],
}
],
stability: "vintf",
backend: {
java: {
enabled: true,
platform_apis: true,
},
cpp: {
enabled: true,
},
ndk: {
enabled: true,
},
rust: {
enabled: true,
},
},
}
name
: nazwa modułu interfejsu AIDL, który jednoznacznie identyfikuje interfejs AIDL.srcs
: lista plików źródłowych AIDL, które składają się na interfejs. Ścieżka typu AIDLFoo
zdefiniowanego w pakieciecom.acme
powinna mieć wartość<base_path>/com/acme/Foo.aidl
, gdzie<base_path>
może być dowolnym katalogiem powiązanym z katalogiem, w którym znajduje sięAndroid.bp
. W poprzednim przykładzie<base_path>
tosrcs/aidl
.local_include_dir
: ścieżka, od której zaczyna się nazwa pakietu. Odpowiada to<base_path>
wyjaśnionemu powyżej.imports
: lista modułówaidl_interface
używanych w tym module. Jeśli jeden z interfejsów AIDL używa interfejsu lub pakietu SDK innegoaidl_interface
, wpisz tutaj jego nazwę. Może to być sama nazwa, która odnosi się do najnowszej wersji, lub nazwa z sufiksem wersji (np.-V1
) odnosząca się do konkretnej wersji. Określanie wersji jest obsługiwane od Androida 12versions
: poprzednie wersje interfejsu, które zostały zablokowane zgodnie z zasadąapi_dir
(począwszy od Androida 11),versions
są zablokowane w okresieaidl_api/name
. Jeśli nie ma zablokowanych wersji interfejsu, nie należy go określać i nie przeprowadzamy testów zgodności. W Androidzie 13 i nowszych to pole zostało zastąpione polemversions_with_info
.versions_with_info
: lista krotek, z których każda zawiera nazwę zablokowanej wersji i listę z importami wersji innych modułów aidl_interface zaimportowanej przez tę wersję interfejsu aidl_interface. Definicja wersji V interfejsu IFACE interfejsu AIDL znajduje się na stronieaidl_api/IFACE/V
. To pole zostało wprowadzone w Androidzie 13 i nie należy go bezpośrednio modyfikować wAndroid.bp
. Pole jest dodawane lub aktualizowane przez wywołanie*-update-api
lub*-freeze-api
. Poza tym polaversions
są automatycznie przenoszone doversions_with_info
, gdy użytkownik wywoła metodę*-update-api
lub*-freeze-api
.stability
: opcjonalna flaga obietnicy stabilności interfejsu. Ta funkcja obsługuje tylko"vintf"
. Jeśli zasadastability
nie jest skonfigurowana, system kompilacji sprawdza, czy interfejs jest zgodny wstecznie, chyba że określono parametrunstable
. Brak ustawienia odpowiada stabilności interfejsu w kontekście tej kompilacji (czyli wszystkim elementom systemu, na przykład wszystkim elementom wsystem.img
i powiązanych partycjach, lub wszystkim elementom dostawcy, takim jakvendor.img
i powiązanych partycji). Jeślistability
ma wartość"vintf"
, odpowiada to obietnicy stabilności: interfejs musi być stabilny, dopóki jest używany.gen_trace
: opcjonalna flaga do włączania i wyłączania śledzenia. Od Androida 14 ustawieniem domyślnym jesttrue
dla backendówcpp
ijava
.host_supported
: opcjonalna flaga, która w przypadku ustawienia na wartośćtrue
udostępnia wygenerowane biblioteki w środowisku hosta.unstable
: opcjonalna flaga służąca do oznaczania interfejsu, że nie musi być on stabilny. Gdy zasada ma wartośćtrue
, system kompilacji nie tworzy zrzutu API dla interfejsu i nie wymaga jego aktualizacji.frozen
: opcjonalna flaga wskazująca, że wartośćtrue
oznacza, że interfejs nie uległ zmianie od poprzedniej wersji. Pozwala to na większą liczbę kontroli w czasie kompilacji. Ustawieniefalse
oznacza, że interfejs jest w fazie opracowywania i wprowadzono w nim zmiany, w wyniku czego uruchomieniefoo-freeze-api
powoduje wygenerowanie nowej wersji i automatyczne zmianę wartości natrue
. Wprowadzone w Androidzie 14.backend.<type>.enabled
: te flagi przełączają każdy z backendów, dla których kompilator AIDL generuje kod. Obsługiwane są 4 backendy: Java, C++, NDK i Rust. Backendy Java, C++ i NDK są domyślnie włączone. Jeśli któryś z tych 3 backendów nie jest potrzebny, należy go wyłączyć bezpośrednio. Do Androida 15 (eksperyment AOSP) narzędzie Rust jest domyślnie wyłączone.backend.<type>.apex_available
: lista nazw APEX, dla których dostępna jest wygenerowana biblioteka krótkoterminowa.backend.[cpp|java].gen_log
: opcjonalna flaga określająca, czy należy wygenerować dodatkowy kod do gromadzenia informacji o transakcji.backend.[cpp|java].vndk.enabled
: opcjonalna flaga, która włącza ten interfejs do VNDK. Wartość domyślna tofalse
.backend.[cpp|ndk].additional_shared_libraries
: ta flaga wprowadzona w Androidzie 14 dodaje zależności do bibliotek natywnych. Ta flaga jest przydatna w przypadkundk_header
icpp_header
.backend.java.sdk_version
: opcjonalna flaga określająca wersję pakietu SDK, na podstawie której utworzona biblioteka Javywa. Wartość domyślna to"system_current"
. Tej wartości nie należy ustawiać, gdybackend.java.platform_apis
ma wartośćtrue
.backend.java.platform_apis
: opcjonalna flaga, która powinna być ustawiona natrue
, gdy wygenerowane biblioteki muszą używać interfejsu API platformy, a nie SDK.
Dla każdej kombinacji wersji i włączonych backendów tworzona jest biblioteka namiotowa. Aby dowiedzieć się, jak odwołać się do konkretnej wersji biblioteki skróconej w przypadku konkretnego backendu, przeczytaj artykuł Reguły nazewnictwa modułów.
Zapisywanie plików AIDL
Interfejsy w stabilnej wersji AIDL są podobne do tradycyjnych interfejsów z tą różnicą, że nie mogą używać pakietów pakietów (ponieważ nie są one stabilne – patrz Uporządkowane a stabilne AIDL). Podstawową różnicą w stabilnej wersji AIDL jest sposób definiowania obiektów parcelable. Wcześniej obiekty parcelable były deklarowane. W stabilnym (a tym samym ustrukturyzowanym) systemie AIDL pola i zmienne parcelable są bezpośrednio zdefiniowane.
// in a file like 'some/package/Thing.aidl'
package some.package;
parcelable SubThing {
String a = "foo";
int b;
}
Domyślne ustawienie jest obsługiwane (ale nie wymagane) dla: boolean
, char
, float
, double
, byte
, int
, long
i String
. W Androidzie 12 obsługiwane są również domyślne wyliczenia zdefiniowane przez użytkownika. Jeśli wartość domyślna nie jest określona, używana jest wartość podobna do 0 lub pusta.
Wyliczenia bez wartości domyślnej są inicjowane na 0, nawet jeśli nie ma żadnego elementu wyliczającego.
Korzystanie z bibliotek z stubami
Po dodaniu atrapy bibliotek jako zależności do modułu możesz uwzględnić je w swoich plikach. Oto przykłady bibliotek wersji pośredniej w systemie kompilacji (Android.mk
może być też używany do starszych definicji modułów):
cc_... {
name: ...,
shared_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
// can also be shared_libs if your preference is to load a library and share
// it among multiple users or if you only need access to constants
static_libs: ["my-module-name-java"],
...
}
# or
rust_... {
name: ...,
rustlibs: ["my-module-name-rust"],
...
}
Przykład w C++:
#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
// use just like traditional AIDL
Przykład w Javie:
import some.package.IFoo;
import some.package.Thing;
...
// use just like traditional AIDL
Przykład w języku Rust:
use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
// use just like traditional AIDL
Interfejsy obsługi wersji
Zadeklarowanie modułu o nazwie foo powoduje też utworzenie w systemie kompilacji elementu docelowego umożliwiającego zarządzanie jego interfejsem API. Po utworzeniu foo-freeze-api do api_dir
lub aidl_api/name
dodaje nową definicję interfejsu API (w zależności od wersji Androida) oraz plik .hash
reprezentujący nową zamrożoną wersję interfejsu. foo-freeze-api aktualizuje też właściwość versions_with_info
, aby uwzględnić dodatkową wersję, oraz imports
dla wersji. Ogólnie rzecz biorąc, pole imports
w polu versions_with_info
jest kopiowane z pola imports
. Najnowsza stabilna wersja jest jednak podana w polu imports
w pliku versions_with_info
na potrzeby importu, który nie ma jednoznacznej wersji.
Po określeniu właściwości versions_with_info
system kompilacji uruchamia testy zgodności między zablokowanymi wersjami oraz między „wierzchołkiem drzewa” (ToT) a ostatnią zamrożoną wersją.
Musisz też zarządzać definicją interfejsu API wersji ToT. Po każdej aktualizacji interfejsu API uruchom polecenie foo-update-api, aby zaktualizować aidl_api/name/current
, który zawiera definicję interfejsu API wersji ToT.
Aby utrzymać stabilność interfejsu, właściciele mogą dodawać nowe:
- Metody umieszczone na końcu interfejsu (lub metody ze jawnie zdefiniowanymi nowymi numerami seryjnymi)
- Elementy na końcu przesyłki (do każdego elementu wymagane jest domyślne dodanie danych)
- Wartości stałe
- W Androidzie 11 liczniki
- W Androidzie 12 pola do końca sumy
Żadne inne działania nie są dozwolone ani nikt inny nie może modyfikować interfejsu (w przeciwnym razie ryzykuje to konflikt ze zmianami wprowadzonymi przez właściciela).
Aby sprawdzić, czy wszystkie interfejsy w wersji są zablokowane w wersji, możesz utworzyć kompilację, korzystając z tego zestawu zmiennych środowiskowych:
AIDL_FROZEN_REL=true m ...
– kompilacja wymaga zablokowania wszystkich stabilnych interfejsów AIDL, które nie mają określonego polaowner:
.AIDL_FROZEN_OWNERS="aosp test"
– kompilacja wymaga, aby wszystkie stabilne interfejsy AIDL zostały zablokowane z polemowner:
określonym jako „aosp” lub „test”.
Stabilność importu
Aktualizowanie wersji importów w przypadku zablokowanych wersji interfejsu jest zgodne wstecznie w warstwie stabilnej AIDL. Jednak ich aktualizowanie wymaga aktualizacji wszystkich serwerów i klientów korzystających z poprzedniej wersji interfejsu, co może powodować dezorientację niektórych aplikacji, gdy łączysz różne wersje danego typu. Ogólnie w przypadku typowych lub przeznaczonych tylko typów pakietów jest to bezpieczne, ponieważ trzeba już wcześniej napisać kod do obsługi nieznanych typów transakcji IPC.
Największym przykładem tego typu
uaktualnienia wersji jest kod android.hardware.graphics.common
na Androidzie.
Używaj interfejsów z różnymi wersjami interfejsu
Metody interfejsu
Podczas próby wywołania nowych metod na starym serwerze w czasie działania nowe klienty otrzymują błąd lub wyjątek (w zależności od backendu).
- Backend
cpp
otrzymuje::android::UNKNOWN_TRANSACTION
. - Backend
ndk
otrzymujeSTATUS_UNKNOWN_TRANSACTION
. - Backend
java
otrzymujeandroid.os.RemoteException
z komunikatem informującym, że interfejs API nie został zaimplementowany.
Aby dowiedzieć się, jak sobie z tym poradzić, przeczytaj artykuły o wersjach zapytań i używaniu wartości domyślnych.
Parcelables
Gdy do plików parcelable są dodawane nowe pola, stare klienty i serwery pomijają je. Gdy nowe klienty i serwery otrzymują stare pakiety parcels, domyślne wartości nowych pól są automatycznie wypełniane. Oznacza to, że wartości domyślne muszą być określone dla wszystkich nowych pól w pakiecie.
Klienty nie powinny oczekiwać, że serwery będą używać nowych pól, jeśli nie będą wiedzieć, że serwer implementuje wersję, która ma zdefiniowane pole (patrz wersje zapytań).
Wyliczenia i stałe
Podobnie klienty i serwery powinny odrzucać lub ignorować nierozpoznane wartości stałe i elementy wyliczające, ponieważ w przyszłości mogą zostać dodane kolejne. Na przykład serwer nie powinien przerywać działania, gdy otrzyma element wyliczający, o którym nie wie. Serwer powinien zignorować licznik albo zwrócić jakiś element, aby klient wiedział, że nie jest on obsługiwany w tej implementacji.
Związki
Próba wysłania sumy z nowym polem kończy się niepowodzeniem, jeśli odbiorca jest stary i nie wie nic o tym polu. Wdrożenie nigdy nie wykryje połączenia
z nowym polem. W przypadku transakcji jednokierunkowej błąd jest ignorowany. W przeciwnym razie błąd to BAD_VALUE
(w przypadku backendu C++ lub NDK) albo IllegalArgumentException
(w przypadku backendu Javy). Błąd zostanie odebrany, gdy klient wysyła wartość w nowym polu do starego serwera lub gdy jest to stary klient odbierający połączenie z nowego serwera.
Programowanie oparte na flagach
Na urządzeniach z wersją, które są w fazie rozwoju (niezablokowane), nie można używać, ponieważ nie mają one gwarancji zgodności wstecznej.
AIDL obsługuje zastępcze okresy działania dla tych niezablokowanych bibliotek interfejsu, aby umożliwić tworzenie kodu w najnowszej niezablokowanej wersji i nadal być używany na urządzeniach z wersją. Zachowanie zgodności wstecznej klientów jest podobne do dotychczasowego zachowania, ale w przypadku zasobów zastępczych implementacje również muszą być zgodne z tymi zachowaniami. Zapoznaj się z sekcją Używanie interfejsów z różnymi wersjami interfejsu.
Flaga kompilacji AIDL
Flaga kontrolująca to zachowanie jest RELEASE_AIDL_USE_UNFROZEN
zdefiniowana w build/release/build_flags.bzl
. true
oznacza, że w czasie działania używana jest niezablokowana wersja interfejsu, a false
oznacza, że biblioteki wszystkich niezablokowanych wersji zachowują się tak samo jak ostatnio zablokowane wersje.
Na potrzeby programowania lokalnego możesz zmienić flagę na true
, ale przed opublikowaniem wersji musisz przywrócić ją do wartości false
. Programowanie odbywa się zwykle z konfiguracją, która ma flagę ustawioną na true
.
Macierz zgodności i pliki manifestu
Obiekty interfejsu dostawcy (obiekty VINTF) określają, jakie wersje są oczekiwane i jakie wersje są udostępniane po obu stronach interfejsu dostawcy.
Większość urządzeń innych niż mątwy używa najnowszej macierzy zgodności dopiero po zamrożeniu interfejsów, więc nie ma różnic w bibliotekach AIDL opartych na RELEASE_AIDL_USE_UNFROZEN
.
Macierze
Interfejsy należące do partnerów są dodawane do macierzy zgodności określonych urządzeń lub usług, na które urządzenie jest kierowane w trakcie programowania. Gdy do macierzy zgodności zostanie dodana nowa, niezablokowana wersja interfejsu, poprzednie zablokowane wersje muszą pozostać w RELEASE_AIDL_USE_UNFROZEN=false
. Możesz to zrobić, używając różnych plików macierzy zgodności dla różnych konfiguracji RELEASE_AIDL_USE_UNFROZEN
lub umieszczając obie wersje w jednym pliku macierzy zgodności używanego we wszystkich konfiguracjach.
Na przykład podczas dodawania niezablokowanej wersji 4 użyj <version>3-4</version>
.
Gdy wersja 4 jest zablokowana, możesz usunąć wersję 3 z tablicy zgodności, ponieważ jest używana zablokowana wersja 4, gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość false
.
Pliki manifestu
W Androidzie 15 (AOSP w wersji eksperymentalnej) wprowadziliśmy zmianę w libvintf
, która umożliwia modyfikowanie plików manifestu w czasie kompilacji na podstawie wartości RELEASE_AIDL_USE_UNFROZEN
.
Pliki manifestu i fragmenty pliku manifestu określają, którą wersję interfejsu implementuje usługa. Jeśli używasz najnowszej nieodblokowanej wersji interfejsu, musisz zaktualizować plik manifestu, aby odzwierciedlał tę wersję. Gdy RELEASE_AIDL_USE_UNFROZEN=false
wpisy pliku manifestu są dostosowywane przez libvintf
, aby odzwierciedlały zmianę w wygenerowanej bibliotece AIDL. Wersja zostanie zmodyfikowana z niezablokowanej wersji (N
) do ostatniej zablokowanej wersji N - 1
. Dzięki temu użytkownicy nie muszą zarządzać wieloma plikami manifestu ani fragmentami manifestu dla każdej usługi.
Zmiany klienta HAL
Kod klienta HAL musi być zgodny wstecznie z każdą obsługiwaną wcześniej zamrożoną wersją. Gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość false
, usługi zawsze wyglądają jak ostatnio zablokowana wersja lub wcześniejsza (na przykład wywołanie nowych niezablokowanych metod zwraca UNKNOWN_TRANSACTION
lub nowe pola parcelable
mają wartości domyślne). Klienty Android Framework muszą być zgodne wstecz z dodatkowymi poprzednimi wersjami, ale jest to nowy szczegół dla dostawców i klientów interfejsów należących do partnerów.
Zmiany w implementacji HAL
Największą różnicą w programowaniu HAL w przypadku programowania opartego na flagach jest wymóg, aby implementacje HAL były zgodne wstecznie z ostatnią zamrożoną wersją, gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość false
.
Uwzględnienie zgodności wstecznej w implementacjach i kodzie urządzenia to nowy proces. Zobacz Używanie interfejsów z obsługą wersji.
Zasadniczo kwestie zgodności wstecznej są zasadniczo takie same w przypadku klientów i serwerów, jak i kodu platformy i kodu dostawcy, ale musisz pamiętać o niewielkich różnicach, ponieważ obecnie implementujesz 2 wersje korzystające z tego samego kodu źródłowego (obecną, niezablokowaną wersję).
Przykład: interfejs ma 3 zablokowane wersje. Interfejs zostanie zaktualizowany o nową metodę. Klient i usługa zostaną zaktualizowane tak, aby korzystały z nowej biblioteki wersji 4. Biblioteka V4 jest oparta na niezablokowanej wersji interfejsu, dlatego działa jak ostatnia zablokowana wersja (wersja 3), gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość false
, i uniemożliwia użycie nowej metody.
Gdy interfejs jest zablokowany, wszystkie wartości RELEASE_AIDL_USE_UNFROZEN
korzystają z tej zablokowanej wersji, a kod obsługujący zgodność wsteczną można usunąć.
Gdy wywołujesz metody z wywołaniami zwrotnymi, musisz płynnie obsługiwać przypadek, gdy zwracany jest parametr UNKNOWN_TRANSACTION
. Klienty mogą implementować 2 różne wersje wywołania zwrotnego zależnie od konfiguracji wersji, więc nie możesz zakładać, że klient wysłał najnowszą wersję, a nowe metody mogą ją zwracać. Przypomina to zachowanie zgodności wstecznej z serwerami stabilnych klientów AIDL z serwerami, o których mowa w artykule Używanie interfejsów z różnymi wersjami interfejsu.
// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
mMyCallback = cb;
// Get the version of the callback for later when we call methods on it
auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
return status;
}
// Example of using the callback later
void NotifyCallbackLater() {
// From the latest frozen version (V2)
mMyCallback->foo();
// Call this method from the unfrozen V3 only if the callback is at least V3
if (mMyCallbackVersion >= 3) {
mMyCallback->bar();
}
}
Gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość false
, nowe pola w istniejących typach (parcelable
, enum
, union
) mogą nie istnieć lub zawierać ich wartości domyślne, a wartości nowych pól, które usługa próbuje wysłać, są usuwane, gdy kończy się proces.
Nowych typów dodanych w tej nieodblokowanej wersji nie można wysyłać ani odbierać za pomocą interfejsu.
Gdy implementacja RELEASE_AIDL_USE_UNFROZEN
ma wartość false
, nie otrzymuje ona wywołania nowych metod od żadnego klienta.
Pamiętaj, aby używać nowych modułów wyliczających tylko w wersji, w której zostały wprowadzone, a nie poprzedniej.
Zwykle do sprawdzenia, której wersji używa interfejs zdalny, należy użyć polecenia foo->getInterfaceVersion()
. Jednak dzięki obsłudze wersji opartej na flagach implementujesz 2 różne wersje, więc warto pobrać wersję bieżącego interfejsu. Aby to zrobić, pobierz wersję interfejsu bieżącego obiektu, np. this->getInterfaceVersion()
, lub inne metody dla my_ver
. Więcej informacji znajdziesz w artykule Wysyłanie zapytań o wersję interfejsu obiektu zdalnego.
Nowe stabilne interfejsy VINTF
Po dodaniu nowego pakietu interfejsu AIDL nie ma żadnej ostatniej zablokowanej wersji, więc nie można wrócić do momentu, gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość false
. Nie używaj tych interfejsów. Gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość false
, menedżer usług nie zezwala usłudze na zarejestrowanie interfejsu, więc klienci nie mogą go znaleźć.
Możesz dodawać usługi warunkowo, opierając się na wartości flagi RELEASE_AIDL_USE_UNFROZEN
w pliku Makefile urządzenia:
ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
android.hardware.health.storage-service
endif
Jeśli usługa jest częścią większego procesu i nie możesz dodać jej na urządzeniu warunkowo, możesz sprawdzić, czy usługa jest zadeklarowana za pomocą IServiceManager::isDeclared()
. Jeśli jest zadeklarowany, ale nie udało się go zarejestrować,
przerwij ten proces. Jeśli nie jest zadeklarowana, prawdopodobnie nie uda się jej zarejestrować.
Mątwa jako narzędzie dla programistów
Co roku po zamrożeniu VINTF dostosowujemy macierz zgodności platformy (FCM) target-level
i PRODUCT_SHIPPING_API_LEVEL
Cuttlefish, aby uwzględniały urządzenia, które pojawią się w przyszłorocznej premierze. Korygujemy target-level
i PRODUCT_SHIPPING_API_LEVEL
, aby mieć pewność, że dostępne w przyszłorocznej wersji urządzenia są testowane i spełniają nowe wymagania.
Gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość true
, Cuttlefish jest używana do tworzenia przyszłych wersji Androida. Jest kierowana na poziom FCM i PRODUCT_SHIPPING_API_LEVEL
w przyszłorocznej wersji Androida, co wymaga spełnienia wymagań dotyczących oprogramowania dostawcy kolejnej wersji (VSR).
Gdy RELEASE_AIDL_USE_UNFROZEN
ma wartość false
, mątwy ma poprzedni target-level
i PRODUCT_SHIPPING_API_LEVEL
, który odzwierciedla urządzenie wersji.
W Androidzie 14 i starszych wersjach to rozróżnienie powstało przez różne gałęzie Git, które nie zauważyły zmiany w FCM target-level
, na poziomie interfejsu API dostawy ani żadnego innego kodu kierowanego do następnej wersji.
Reguły nazewnictwa modułów
W Androidzie 11 dla każdej kombinacji włączonych wersji i włączonych backendów automatycznie tworzony jest moduł uniwersalnej biblioteki. Aby odwołać się do konkretnego modułu biblioteki skróconej na potrzeby połączenia, nie używaj nazwy modułu aidl_interface
, tylko nazwy modułu biblioteki skróconej, czyli ifacename-version-backend, gdzie
ifacename
: nazwa modułuaidl_interface
version
należy do jednej z tych wartości:Vversion-number
dla zablokowanych wersjiVlatest-frozen-version-number + 1
dla wersji „na czubku drzewa” (jeszcze do zamrożenia)
backend
należy do jednej z tych wartości:java
dla backendu Javy,cpp
dla backendu C++,ndk
lubndk_platform
dla backendu NDK. Pierwszy z nich dotyczy aplikacji, a drugi – do Androida 13. Na Androidzie 13 i nowszych używaj tylkondk
.rust
dla backendu Rust.
Załóżmy, że istnieje moduł o nazwie foo, a jego najnowsza wersja to 2, który obsługuje zarówno NDK, jak i C++. W tym przypadku AIDL generuje te moduły:
- Na podstawie wersji 1:
foo-V1-(java|cpp|ndk|ndk_platform|rust)
- korzysta z wersji 2 (najnowszej stabilnej),
foo-V2-(java|cpp|ndk|ndk_platform|rust)
- Na podstawie wersji Warunków korzystania z usługi:
foo-V3-(java|cpp|ndk|ndk_platform|rust)
W porównaniu z Androidem 11:
foo-backend
, który do najnowszej wersji stabilnej odwołuje się dofoo-V2-backend
foo-unstable-backend
, który odwołuje się do wersji Warunków korzystania z usługi, stanie sięfoo-V3-backend
.
Nazwy plików wyjściowych są zawsze takie same jak nazwy modułów.
- Na podstawie wersji 1:
foo-V1-(cpp|ndk|ndk_platform|rust).so
- Na podstawie wersji 2:
foo-V2-(cpp|ndk|ndk_platform|rust).so
- Na podstawie wersji Warunków korzystania z usługi:
foo-V3-(cpp|ndk|ndk_platform|rust).so
Pamiętaj, że kompilator AIDL nie tworzy ani modułu w wersji unstable
, ani modułu bez wersji dla stabilnego interfejsu AIDL.
Od Androida 12 nazwa modułu wygenerowana przez stabilny interfejs AIDL zawsze zawiera jego wersję.
Nowe metody interfejsu meta
Android 10 dodaje kilka metod metainterfejsu dla stabilnej wersji AIDL.
Wyślij zapytanie do wersji interfejsu obiektu zdalnego
Klienty mogą wysyłać zapytania o wersję i hasz interfejsu implementowanego przez obiekt zdalny oraz porównać zwrócone wartości z wartościami interfejsu, z którego korzysta klient.
Przykład z backendem cpp
:
sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();
Przykład z backendem ndk
(i ndk_platform
):
IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);
Przykład z backendem java
:
IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
// the remote side is using an older interface
}
String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();
W przypadku języka Java strona zdalna MUSI implementować getInterfaceVersion()
i getInterfaceHash()
w następujący sposób (zamiast IFoo
należy użyć super
, aby uniknąć błędów podczas kopiowania i wklejania. W zależności od konfiguracji javac
może być potrzebna adnotacja @SuppressWarnings("static")
do wyłączenia ostrzeżeń:
class MyFoo extends IFoo.Stub {
@Override
public final int getInterfaceVersion() { return super.VERSION; }
@Override
public final String getInterfaceHash() { return super.HASH; }
}
Dzieje się tak, ponieważ wygenerowane klasy (IFoo
, IFoo.Stub
itp.) są współdzielone przez klienta i serwer (np. klasy mogą znajdować się w ścieżce klasy rozruchowej). Podczas udostępniania klas serwer jest również połączony z najnowszą wersją tych klas, mimo że mógł zostać utworzony przy użyciu starszej wersji interfejsu. Jeśli ten metainterfejs jest zaimplementowany w klasie współdzielonej, zawsze zwraca najnowszą wersję. Jednak po wdrożeniu metody opisanej powyżej numer wersji interfejsu jest osadzony w kodzie serwera (ponieważ IFoo.VERSION
to static final int
, który jest wstawiony w przypadku odwołania) i dzięki temu metoda może zwrócić dokładną wersję, z którą zbudowano serwer.
Radzenie sobie ze starszymi interfejsami
Może się zdarzyć, że klient zostanie zaktualizowany do nowszej wersji interfejsu AIDL, ale serwer używa starego interfejsu AIDL. W takich przypadkach wywołanie metody w starym interfejsie zwraca UNKNOWN_TRANSACTION
.
Stabilna wersja AIDL daje klientom większą kontrolę. Po stronie klienta możesz ustawić domyślną implementację interfejsu AIDL. Metoda w domyślnej implementacji jest wywoływana tylko wtedy, gdy nie jest zaimplementowana po stronie zdalnej (ponieważ została utworzona przy użyciu starszej wersji interfejsu). Ponieważ wartości domyślne są ustawiane globalnie, nie należy ich używać z kontekstów potencjalnie wspólnych.
Przykład w C++ na Androidzie 13 i nowszych:
class MyDefault : public IFooDefault {
Status anAddedMethod(...) {
// do something default
}
};
// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());
foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
// remote side is not implementing it
Przykład w Javie:
IFoo.Stub.setDefaultImpl(new IFoo.Default() {
@Override
public xxx anAddedMethod(...) throws RemoteException {
// do something default
}
}); // once per an interface in a process
foo.anAddedMethod(...);
Nie musisz podawać domyślnej implementacji wszystkich metod w interfejsie AIDL. Metody, które gwarantują, że zostaną wdrożone na serwerze zdalnym (ponieważ masz pewność, że pilot został utworzony, gdy znajdują się w opisie interfejsu AIDL), nie musisz zastępować go w domyślnej klasie impl
.
Przekonwertuj istniejące AIDL na uporządkowane lub stabilne AIDL
Jeśli masz interfejs AIDL i kod, który z niego korzysta, wykonaj poniższe czynności, aby przekonwertować interfejs na stabilny interfejs AIDL.
Określ wszystkie zależności interfejsu. Sprawdź, czy pakiet jest zdefiniowany w stabilnej wersji AIDL dla każdego pakietu, od którego zależy interfejs. Jeśli pakiet nie zostanie określony, musi zostać przekonwertowany.
Przekonwertuj wszystkie pakiety pakietów w interfejsie na pliki stabilne (sam pliki interfejsu mogą pozostać niezmienione). Można to zrobić przez wyrażenie ich struktury bezpośrednio w plikach AIDL. Aby z nich korzystać, trzeba zmodyfikować klasy zarządzania. Możesz to zrobić przed utworzeniem pakietu
aidl_interface
(poniżej).Utwórz pakiet
aidl_interface
(w sposób opisany powyżej) zawierający nazwę modułu, jego zależności i wszelkie inne potrzebne informacje. Aby był stabilizowany (a nie tylko uporządkowany), trzeba też zmienić jego wersję. Więcej informacji znajdziesz w artykule na temat interfejsów wersji.