Stabilna wersja AIDL

Android 10 obsługuje stabilny interfejs Androida Język definiowania (AIDL) – nowy sposób śledzenia programu aplikacji interfejs (API) i interfejs binarny aplikacji (ABI) dostarczane przez AIDL; i interfejsów. Stabilna wersja AIDL działa dokładnie tak samo jak AIDL, ale system kompilacji śledzi zgodność interfejsu. Istnieją też ograniczenia dotyczące czynności, które można wykonać:

  • Interfejsy są zdefiniowane w systemie kompilacji za pomocą aidl_interfaces.
  • Interfejsy mogą zawierać tylko uporządkowane dane. Interfejs Parcelables reprezentujący preferowane typy są tworzone automatycznie na podstawie ich definicji AIDL i są automatycznie usuwane i usuwane z sieci.
  • Interfejsy mogą być zadeklarowane jako stabilne (zgodne wstecznie). Gdy to interfejs API jest śledzony i umieszczany w pliku obok identyfikatora AIDL. za pomocą prostego interfejsu online.

Uporządkowane a stabilne AIDL

Uporządkowane AIDL odnosi się do typów zdefiniowanych wyłącznie w AIDL. Na przykład plik deklaracja parcelable (prywatna działka niestandardowa) nie ma struktury AIDL. Parcelables z ich polami zdefiniowanymi w AIDL są nazywane elementami strukturalnymi.

Stabilna AIDL wymaga uporządkowanych danych AIDL, tak aby system kompilacji i kompilator może sprawdzić, czy zmiany wprowadzone w pakiecie są zgodne wstecznie. Jednak nie wszystkie uporządkowane interfejsy są stabilne. Aby wszystko było stabilne, interfejs może używać wyłącznie uporządkowanych typów oraz musi zawierać następujące funkcje obsługi wersji. Interfejs jest niestabilny, jeśli główna kompilacja do jego skompilowania lub ustawienia 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 dla typu AIDL Foo zdefiniowanego w pakiecie com.acme powinna mieć wartość <base_path>/com/acme/Foo.aidl, gdzie <base_path> może być dowolnym katalogiem powiązane z katalogiem, w którym znajduje się Android.bp. W poprzednim przykładzie <base_path>srcs/aidl.
  • local_include_dir: ścieżka, od której zaczyna się nazwa pakietu. it odpowiada <base_path> wyjaśnionemu powyżej.
  • imports: lista modułów aidl_interface używanych w tym module. Jeśli jedno z Twoich Interfejsy AIDL używają interfejsu lub pakietu Parcelable aidl_interface, wpisz tutaj jej nazwę. Może to być sama nazwa, aby powołać się na lub nazwa z sufiksem wersji (np. -V1), by się odwołać konkretnej wersji. Określanie wersji jest obsługiwane od Androida 12
  • versions: poprzednie wersje interfejsu, które są konto zablokowane w okresie api_dir, począwszy od Androida 11, versions są zablokowane w przypadku licencji aidl_api/name. Jeśli nie ma zablokowanych wersji interfejsu, tego parametru nie należy określać i nie będzie przeprowadzana weryfikacja zgodności. W przypadku Androida to pole zostało zastąpione polem versions_with_info 13 lub więcej.
  • versions_with_info: lista krotek, z których każda zawiera nazwę zablokowana wersja i lista importów wersji innego interfejsu aidl_interface które zostały zaimportowane z tej wersji interfejsu aidl_interface. Definicja wersji V IFACE interfejsu AIDL znajduje się pod adresem aidl_api/IFACE/V To pole wprowadziliśmy w Androidzie 13, i nie należy go modyfikować bezpośrednio w Android.bp. To pole jest dodane lub zaktualizowane przez wywołanie metody *-update-api lub *-freeze-api. Oprócz tego pola versions są automatycznie przenoszone do versions_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 zasada stability nie jest skonfigurowana, kompilacja sprawdza, czy interfejs jest zgodny wstecznie, chyba że Podano unstable. Gdy to ustawienie jest nieskonfigurowane, odpowiada interfejsowi stabilności w tym kontekście kompilacji (a więc zarówno wszystkie elementy systemu, na przykład rzeczy w elemencie system.img i powiązane partycje lub we wszystkich dostawcach na przykład elementy w tabeli vendor.img i powiązane partycje). Jeśli stability 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. Zaczyna się za Android 14 jest domyślnie ustawiony na true w przypadku aplikacji cpp oraz java backendów.
  • host_supported: opcjonalna flaga, która po ustawieniu na true powoduje, że z wygenerowanymi bibliotekami dostępnymi dla środowiska hosta.
  • unstable: opcjonalna flaga służąca do oznaczenia, że ten interfejs nie zezwala muszą być stabilne. Gdy zasada ma wartość true, system kompilacji nie tworzy zrzut API dla interfejsu ani nie wymaga jego aktualizacji.
  • frozen: opcjonalna flaga informująca o tym, że ustawienie true oznacza, że interfejs Nie wprowadziliśmy żadnych zmian od poprzedniej wersji interfejsu. Dzięki temu więcej kontroli w czasie kompilacji. Ustawienie wartości false oznacza, że interfejs jest włączony. i ma nowe zmiany, dlatego uruchomienie polecenia foo-freeze-api generuje nowej wersji i automatycznie zmień wartość na true. Wprowadzona w grze Android 14.
  • backend.<type>.enabled: te flagi przełączają każdy z backendów kompilator AIDL generuje kod. Cztery backendy obsługiwane: Java, C++, NDK i Rust. Backendy Java, C++ i NDK są włączone domyślnie. Jeśli którykolwiek z tych 3 backendów nie jest potrzebny, wyłączono jawnie. System Rust jest domyślnie wyłączony do czasu Androida 15.
  • backend.<type>.apex_available: lista nazw APEX wygenerowanych przez dla których dostępna jest biblioteka stub.
  • backend.[cpp|java].gen_log: opcjonalna flaga określająca, czy wygenerować dodatkowy kod do gromadzenia informacji o transakcji.
  • backend.[cpp|java].vndk.enabled: opcjonalna flaga do tworzenia tego interfejsu. stanowią część VNDK. Wartość domyślna to false.
  • backend.[cpp|ndk].additional_shared_libraries: wprowadzono w Androida 14, ta flaga dodaje zależności do bibliotek natywnych. Ta flaga jest przydatna w przypadku ndk_header i cpp_header.
  • backend.java.sdk_version: opcjonalna flaga do określania wersji. pakietu SDK, na podstawie którego została utworzona biblioteka Javy. Wartość domyślna to "system_current" To ustawienie nie powinno być ustawione, gdy backend.java.platform_apis jest true.
  • backend.java.platform_apis: opcjonalna flaga, która powinna być ustawiona na true, gdy wygenerowane biblioteki muszą korzystać z interfejsu API platformy. a nie SDK.

Dla każdej kombinacji wersji i włączonych backendów dostępny jest po utworzeniu biblioteki. Jak odwołać się do konkretnej wersji biblioteki namiotowej dla konkretnego backendu, zapoznaj się z regułami nazewnictwa modułów.

Zapisywanie plików AIDL

Interfejsy w stabilnej wersji AIDL są podobne do interfejsów tradycyjnych, z wyjątkiem tego, że nie mogą używać obiektów nieuporządkowanych (ponieważ Nie są one stabilne. zobacz Uporządkowany a stabilny AIDL). Podstawową różnicą w stabilnej wersji AIDL jest to, Parcelables są zdefiniowane. Wcześniej pola parcelable były zadeklarowane. cale stabilnej (a więc uporządkowanej) AIDL, pola i zmienne Parcelables są w sposób jednoznaczny.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

Wartości domyślne są obsługiwane (ale nie wymagane) dla tych atrybutów: boolean, char, float, double, byte, int, long i String. Na Androidzie 12, wartości domyślne dla wyliczeń definiowanych przez użytkownika są również obsługiwane. 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ą zainicjowane na 0, nawet jeśli zero wyliczającego.

Korzystanie z bibliotek z stubami

Po dodaniu atrakcji do modułu jako zależności możesz umieścić je w swoich plikach. Oto przykłady bibliotek pomocniczych w system kompilacji (Android.mk może być też używany do określania 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 celu w systemie kompilacji. które mogą służyć do zarządzania interfejsem API w module. Po utworzeniu foo-freeze-api dodaje nową definicję interfejsu API w sekcji api_dir lub aidl_api/name w zależności od wersji Androida oraz dodaje plik .hash, które reprezentują nowo zablokowaną wersję za pomocą prostego interfejsu online. foo-freeze-api aktualizuje też właściwość versions_with_info. aby odzwierciedlić wersję dodatkową, a imports dla wersji. Zasadniczo Pole imports w tabeli versions_with_info zostało skopiowane z pola imports. Ale najnowsza wersja stabilna jest określona w imports w versions_with_info dla które nie ma jednoznacznej wersji. Po określeniu właściwości versions_with_info uruchamia się system kompilacji. testy zgodności zablokowanych wersji oraz między wierzchołkiem drzewa (ToT) oraz najnowszą zamrożoną wersję.

Musisz też zarządzać definicją interfejsu API wersji ToT. Za każdym razem, gdy interfejs API jest zaktualizowany, uruchom 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 numery seryjne)
  • elementy na końcu pakietu (wymaga domyślnego dodania do każdego obiektu) ).
  • 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 istnieje ryzyko kolizji ze zmianami wprowadzonymi przez właściciela).

Aby sprawdzić, czy wszystkie interfejsy są zablokowane w wersji produkcyjnej, możesz kompilować następujący zestaw zmiennych środowiskowych:

  • AIDL_FROZEN_REL=true m ... – kompilacja wymaga wszystkich stabilnych interfejsów AIDL zablokowane, które nie mają określonego pola owner:.
  • AIDL_FROZEN_OWNERS="aosp test" – kompilacja wymaga wszystkich stabilnych interfejsów AIDL do zablokowania z polem owner: określonym jako „aosp” czy „test”.

Stabilność importu

Aktualizowanie wersji importów zablokowanych wersji interfejsu jest kompatybilności wstecznej w warstwie stabilnej AIDL. Jednak ich aktualizacja wymaga aktualizowanie wszystkich serwerów i klientów korzystających z poprzedniej wersji interfejsu, a niektóre aplikacje mogą być zdezorientowane, gdy mieszamy różne wersje danego typu. Zasadniczo w przypadku typowych lub przeznaczonych tylko typów pakietów jest to bezpieczne, ponieważ kod musi już zapisane w celu obsługi nieznanych typów z transakcji IPC.

Na platformie Android kod android.hardware.graphics.common jest największym przykład uaktualnienia wersji.

Używaj interfejsów z różnymi wersjami interfejsu

Metody interfejsu

W trakcie działania przy próbie wywołania nowych metod na starym serwerze nowe klienty otrzymują może to być błąd lub wyjątek, w zależności od backendu.

  • Backend cpp otrzymuje ::android::UNKNOWN_TRANSACTION.
  • Backend ndk otrzymuje STATUS_UNKNOWN_TRANSACTION.
  • Backend java otrzymuje android.os.RemoteException z komunikatem o treści Interfejs API nie został zaimplementowany.

Aby dowiedzieć się, jak sobie z tym poradzić, zobacz wersje zapytań przy użyciu ustawień 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 dla nowych są wypełniane automatycznie. Oznacza to, że wartości domyślne muszą być określony dla wszystkich nowych pól w pakiecie.

Klienci nie powinni oczekiwać, że serwery będą używać nowych pól, jeśli nie znają serwer implementuje wersję, która ma zdefiniowane pole (patrz wersje zapytań).

Wyliczenia i stałe

Klienty i serwery powinny odrzucać lub ignorować nierozpoznane stałych wartości i wyliczaczy, ponieważ więcej można dodać przyszłości. Na przykład serwer nie powinien przerywać działania po otrzymaniu żądania jest to coś, o czym nie wie. Serwer powinien albo zignorować parametr wyliczacza, ani zwrócić elementu, tak 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 o tym polu. Implementacja nigdy nie wykryje połączenia z nowe pole. Niepowodzenie jest ignorowane, jeśli transakcja w jedną stronę, w przeciwnym razie błąd to BAD_VALUE(dla języka C++ lub NDK backend) lub IllegalArgumentException(w przypadku backendu Javy). Błąd: odbierany, jeśli klient wysyła wartość sumaryczną ustawioną na nowe pole na stare lub gdy jest to stary klient odbierający połączenie z nowego serwera.

Zarządzanie wieloma wersjami

Przestrzeń nazw tagu łączącego na Androidzie może mieć tylko 1 wersję określonej aidl w interfejsie, by uniknąć sytuacji, w których wygenerowane typy aidl mają wiele definicji. C++ ma regułę jednej definicji, która wymaga tylko jednej definicji każdego symbolu.

Kompilacja Androida powoduje błąd, gdy moduł zależy od różnych wartości. wersji tej samej biblioteki aidl_interface. Moduł może zależeć od bezpośrednio lub pośrednio przez zależności zależności. Błędy te przedstawiają wykres zależności z modułu kolidujących wersji biblioteki aidl_interface. Wszystkie trzeba zaktualizować zależności, aby uwzględnić tę samą (zwykle najnowszą) tych bibliotek.

Jeżeli biblioteka interfejsu jest wykorzystywana przez wiele różnych modułów, pomocna może być aby utworzyć cc_defaults, java_defaults i rust_defaults dla dowolnej grupy bibliotek i procesów, które muszą korzystać z tej samej wersji. Wprowadzając nowej wersji interfejsu. Można aktualizować te wartości domyślne, a wszystkie moduły są aktualizowane razem, dzięki czemu nie korzystają z różnych wersji interfejsu.

cc_defaults {
  name: "my.aidl.my-process-group-ndk-shared",
  shared_libs: ["my.aidl-V3-ndk"],
  ...
}

cc_library {
  name: "foo",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

cc_binary {
  name: "bar",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

Gdy moduły aidl_interface zaimportują inne moduły aidl_interface, powstaną dodatkowe zależności, które wymagają jednoczesnego użycia konkretnych wersji. Ten może być trudna do zarządzania w przypadku powszechnych aidl_interface zaimportowane do kilku używanych modułów aidl_interface w ramach tych samych procesów.

aidl_interfaces_defaults może posłużyć do zachowania jednej definicji najnowsze wersje zależności obiektu „aidl_interface”, które można zaktualizować w w jednym miejscu. Są one używane przez wszystkie moduły aidl_interface, które chcą zaimportować taki wspólny interfejs.

aidl_interface_defaults {
  name: "android.popular.common-latest-defaults",
  imports: ["android.popular.common-V3"],
  ...
}

aidl_interface {
  name: "android.foo",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

aidl_interface {
  name: "android.bar",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

Programowanie oparte na flagach

Na urządzeniach w wersji produkcyjnej nie można używać interfejsów w wersji rozwojowej (niezablokowanej), ponieważ nie ma gwarancji zgodności wstecznej.

AIDL obsługuje kreacje zastępcze czasu działania dla tych niezablokowanych bibliotek interfejsu w kolejności , aby kod był pisany w najnowszej niezablokowanej wersji i nadal był używany na urządzeniach z włączoną wersją. Zachowanie zgodności wstecznej klientów jest podobne do i w przypadku kreacji zastępczych implementacje też muszą być takich zachowań. Zobacz Używanie interfejsów z obsługą różnych wersji.

Flaga kompilacji AIDL

Flaga tego działania to RELEASE_AIDL_USE_UNFROZEN zdefiniowane w: build/release/build_flags.bzl. true oznacza niezablokowaną wersję interfejs jest używany w czasie działania, a false oznacza biblioteki bibliotek wszystkie niezablokowane wersje zachowują się tak samo jak ostatnio zablokowane. Możesz zmienić flagę na true w przypadku programistyczny, ale musi przywrócić go do wersji false przed opublikowaniem. Zwykle Programowanie odbywa się przy użyciu konfiguracji, która ma flagę ustawioną na true.

Macierz zgodności i pliki manifestu

Obiekty interfejsu dostawcy (obiekty VINTF) – definicję które wersje powinny być poprawne, a jakie wersje po obu stronach za pomocą interfejsu dostawcy.

Większość urządzeń innych niż mątwy jest kierowana na najnowszą tablicę zgodności dopiero po zamrożeniu interfejsów, więc nie ma różnicy w AIDL biblioteki na podstawie RELEASE_AIDL_USE_UNFROZEN.

Macierze

Interfejsy należące do partnera są dodawane do interfejsów, które są związane z konkretnym urządzeniem lub usługą. lub macierzy zgodności, na które urządzenie jest kierowane w trakcie programowania. Kiedy więc do macierzy zgodności jest dodawana nowa, niezablokowana wersja interfejsu, poprzednio zablokowane wersje muszą być dostępne przez RELEASE_AIDL_USE_UNFROZEN=false Możesz sobie z tym poradzić, stosując różne pliki macierzy zgodności dla różnych RELEASE_AIDL_USE_UNFROZEN konfiguracji lub zezwolenie na obie wersje w jednym pliku macierzy zgodności która jest używana 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 false

Pliki manifestu

W Androidzie 15 wprowadziliśmy zmianę w libvintf: modyfikować pliki manifestu w czasie kompilacji na podstawie wartości atrybutu RELEASE_AIDL_USE_UNFROZEN

Pliki manifestu i fragmenty pliku manifestu określają wersję interfejsu implementowana przez usługę. Jeśli korzystasz z najnowszej niezablokowanej wersji interfejsu, Musisz zaktualizować plik manifestu, aby odzwierciedlał tę nową wersję. Kiedy RELEASE_AIDL_USE_UNFROZEN=false wpisy w pliku manifestu są dostosowywane według: libvintf, aby odzwierciedlić zmianę w wygenerowanej bibliotece AIDL. Wersja została zmodyfikowana z niezablokowanej wersji (N) na ostatnio zamrożoną wersję N - 1. Dzięki temu użytkownicy nie muszą zarządzać wieloma pliki manifestu lub fragmenty plików manifestu dla poszczególnych usług.

Zmiany klienta HAL

Kod klienta HAL musi być zgodny wstecznie z każdym wcześniej obsługiwanym zablokowanym kodem klienta wersji. Gdy RELEASE_AIDL_USE_UNFROZEN ma wartość false, usługi są zawsze sprawdzane jak ostatnia zamrożona wersja lub wcześniej (na przykład wywołanie nowego niezablokowanego zwraca wartość UNKNOWN_TRANSACTION lub nowe pola parcelable mają swoje wartości wartości domyślne). Klienty platformy Android muszą działać wstecz zgodne z dodatkowymi poprzednimi wersjami. Jest to jednak nowy szczegół klientów dostawców i klientów korzystających z 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 wymaga, aby implementacje HAL były zgodne wstecznie z ostatnimi zablokowana wersja do działania, gdy RELEASE_AIDL_USE_UNFROZEN ma wartość false. Uwzględnienie zgodności wstecznej w implementacjach i kodzie urządzenia to nowość. ćwiczenia. Zobacz Używanie obsługi wersji .

Uwagi na temat zgodności wstecznej są zasadniczo takie same w przypadku dla klientów i serwerów, kodu platformy i kodu dostawcy. subtelne różnice, o których należy pamiętać, zaimplementowanie dwóch wersji, które korzystają z tego samego kodu źródłowego (obecnej, niezablokowanej wersji).

Przykład: interfejs ma 3 zablokowane wersje. Interfejs został zaktualizowany o nowej metody. Klient i usługa zostaną zaktualizowane tak, aby korzystały z nowej wersji 4. bibliotece. Ponieważ biblioteka V4 opiera się na niezablokowanej wersji biblioteki działa jak ostatnia zablokowana wersja (wersja 3), Metoda 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 używają tego zamrożoną wersję, a kod obsługujący zgodność wsteczną może zostać usunięty.

Gdy wywołujesz metody z wywołaniami zwrotnymi, musisz płynnie obsługiwać Zwracana jest wartość UNKNOWN_TRANSACTION. Klienci mogą wdrażać 2 różne na podstawie konfiguracji wersji. Nie możesz więc przy założeniu, że klient wysyła najnowszą wersję, a nowe metody mogą zwrócić to osiągnąć. Jest to podobne do tego, jak stabilne są stabilne klienty AIDL w tle zgodność z serwerami opisanymi w artykule Używanie obsługi wersji .

// 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();
  }
}

Nowe pola w istniejących typach (parcelable, enum, union) mogą nie istnieje lub nie zawierają swoich wartości domyślnych, gdy RELEASE_AIDL_USE_UNFROZEN jest Pola false i wartości nowych pól, które próbuje wysłać usługa, są usuwane jest dla nas nie do przyjęcia.

Nie można wysyłać nowych typów dodanych do tej niezablokowanej wersji lub odebrany jej za pomocą interfejsu.

Implementacja nigdy nie żąda nowych metod od żadnego klienta, Obecny stan „RELEASE_AIDL_USE_UNFROZEN”: false.

Pamiętaj, aby używać nowych modułów wyliczających tylko w wersji, w której zostały wprowadzone, a nie w poprzedniej wersji.

Zwykle do sprawdzania wersji pilota służy foo->getInterfaceVersion() przez interfejs API. Jednak dzięki obsłudze wersji opartej na flagach implementacji dwóch różnych wersji, warto więc pobrać wersję do bieżącego interfejsu. Aby to zrobić, pobierz wersję interfejsu bieżący obiekt, na przykład this->getInterfaceVersion() lub inny dla metody my_ver. Patrz: Wysyłanie zapytań o wersję interfejsu pilota .

Nowe stabilne interfejsy VINTF

Po dodaniu nowego pakietu interfejsu AIDL nie ma ostatniej zablokowanej wersji, więc nie ma żadnego działania, do którego można wrócić, gdy RELEASE_AIDL_USE_UNFROZEN to false Nie używaj tych interfejsów. Gdy RELEASE_AIDL_USE_UNFROZEN to false, menedżer usług nie zezwala usłudze na zarejestrowanie interfejsu i klienci ich nie znajdą.

Możesz dodawać usługi warunkowo, bazując na wartości atrybutu Flaga 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żna dodać jej na urządzeniu warunkowo możesz sprawdzić, czy usługa jest zadeklarowana za pomocą argumentu IServiceManager::isDeclared() Jeśli jest zadeklarowana, ale nie udało się jej zarejestrować, przerwać 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 zasad VINTF dostosowujemy zgodność platformy macierz (FCM) target-level i PRODUCT_SHIPPING_API_LEVEL mątwy więc odzwierciedlają one urządzenia udostępniane w przyszłym roku. Dostosujemy target-level i PRODUCT_SHIPPING_API_LEVEL, aby upewnić się, że zostały poprawnie wprowadzić na rynek urządzenie, które zostało przetestowane i spełnia nowe wymagania na przyszły rok wersji.

Gdy RELEASE_AIDL_USE_UNFROZEN ma wartość true, mątwy jest służy do tworzenia przyszłych wersji Androida. Jest kierowana na Androida w przyszłym roku na poziomie FCM wydania i w ustawieniu PRODUCT_SHIPPING_API_LEVEL, co wymaga, aby spełniała wymagania dotyczące oprogramowania dostawcy (VSR) kolejnej wersji.

Gdy RELEASE_AIDL_USE_UNFROZEN to false, mątwy ma poprzedni target-level i PRODUCT_SHIPPING_API_LEVEL, aby uwzględnić urządzenie, na którym wydano produkt. W Androidzie 14 i starszych wersjach w różnych gałęziach Git, które nie przyjmują zmiany w FCM. target-level, poziom interfejsu API dostawy lub dowolny inny kod kierowany na następny wersji.

Reguły nazewnictwa modułów

W Androidzie 11 dla każdej kombinacji wersji Gdy backendy są włączone, moduł biblioteki skróconej jest tworzony automatycznie. Aby polecić do określonego modułu biblioteki skróconej w celu połączenia, nie używaj nazwy modułu aidl_interface ale nazwa modułu biblioteki skróconej, która jest ifacename-version-backend, gdzie

  • ifacename: nazwa modułu aidl_interface
  • version należy do jednego z
    • Vversion-number dla zablokowanych wersji
    • Vlatest-frozen-version-number + 1 dla wersja na wierzchu drzewa (jeszcze do zamrożenia)
  • backend należy do jednego z
    • java dla backendu Javy,
    • cpp dla backendu C++,
    • ndk lub ndk_platform dla backendu NDK. Pierwsza dotyczy aplikacji, a drugie – do korzystania z platformy do czasu wprowadzenia Androida 13. W Android 13 i nowsze korzystają tylko z ndk.
    • rust dla backendu Rust.

Załóżmy, że istnieje moduł o nazwie foo, a jego najnowsza wersja to 2, i obsługuje zarówno NDK, jak i C++. W takim przypadku AIDL generuje te moduły:

  • Na podstawie wersji 1
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • Na podstawie 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 odwołuje się do najnowszej stabilnej wersji wersja zmieni się na foo-V2-backend
  • foo-unstable-backend, który odwołuje się do Warunków korzystania z usługi wersja zmieni się na 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 wersji unstable, albo moduł bez wersji dla stabilnego interfejsu AIDL. Od Androida 12 nazwa modułu jest generowana na podstawie stabilny interfejs AIDL zawsze zawiera jego wersję.

Nowe metody interfejsu meta

Android 10 dodaje kilka metod metainterfejsu stabilnej wersji AIDL.

Wyślij zapytanie do wersji interfejsu obiektu zdalnego

Klienty mogą wysyłać zapytania o wersję i hasz interfejsu wskazywany przez obiekt zdalny implementuje i porównuje zwrócone wartości z wartościami podanymi w interfejsie używanych przez klienta.

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 obsługiwać zmienne getInterfaceVersion() i getInterfaceHash() w następujący sposób (super zamiast IFoo jest używany, aby uniknąć przy kopiowaniu i wklejaniu błędów. Adnotacja @SuppressWarnings("static") może może być konieczne wyłączenie ostrzeżeń, zależnie od konfiguracji javac):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

Wynika to z faktu, że wygenerowane zajęcia (IFoo, IFoo.Stub itp.) są udostępniane między klientem a serwerem (na przykład klasy mogą znajdować się w rozruchu classpath). Podczas udostępniania zajęć serwer jest też powiązany z najnowszą wersję zajęć, nawet jeśli zostały utworzone przy użyciu starszej wersji; wersji interfejsu. Jeśli ten metainterfejs jest zaimplementowany w klasa, zawsze zwraca najnowszą wersję. Jeśli jednak zastosujesz metodę jak powyżej, numer wersji interfejsu jest osadzony w kodzie serwera (ponieważ IFoo.VERSION to static final int, który jest wbudowany, gdy się odwołuje) więc ta metoda może zwrócić dokładnie tę wersję serwera,

Radzenie sobie ze starszymi interfejsami

Możliwe, że klient został zaktualizowany do nowszej wersji 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 wartości domyślnej jest wywoływane tylko wtedy, gdy metoda nie jest zaimplementowana na pilocie (ponieważ utworzono go przy użyciu starszej wersji interfejsu). Od są ustawiane globalnie, więc nie należy ich używać kontekstach.

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 AIDL za pomocą prostego interfejsu online. Metody, które mają zagwarantowane wdrożenie na zdalnym serwerze (ponieważ masz pewność, że pilot został stworzony, gdy metody były w AIDL) nie trzeba zastępować w domyślnej wartości impl. zajęcia.

Przekonwertuj istniejące AIDL na uporządkowane lub stabilne AIDL

Jeśli masz już interfejs AIDL i kod, który z niego korzysta, skorzystaj z następującego konwersji interfejsu do stabilnego interfejsu AIDL.

  1. Określ wszystkie zależności interfejsu. Dla każdego pakietu należy sprawdzić, czy pakiet jest zdefiniowany w stabilnej wersji AIDL. Jeśli nie zdefiniowano, należy przekonwertować pakiet.

  2. Przekonwertuj wszystkie pakiety w interfejsie na pakiety stabilne ( same pliki interfejsu mogą pozostać niezmienione). Zrób to do oraz bezpośrednio w plikach AIDL. Zajęcia zarządzania muszą i zapisywać je od nowa, by używać ich nowych typów. Można to zrobić przed utworzeniem aidl_interface pakiet (poniżej).

  3. Utwórz pakiet aidl_interface (w sposób opisany powyżej), który zawiera 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.