Android 10 unterstützt die stabile Android Interface Definition Language (AIDL), eine neue Möglichkeit, die über AIDL-Schnittstellen bereitgestellte API und die Binärschnittstelle (Application binary Interface, ABI) im Blick zu behalten. Die stabile AIDL unterscheidet sich in folgenden wichtigen Punkten von AIDL:
- Schnittstellen werden im Build-System mit
aidl_interfaces
definiert. - Schnittstellen können nur strukturierte Daten enthalten. Parcelables, die die bevorzugten Typen darstellen, werden automatisch anhand ihrer AIDL-Definition erstellt und automatisch marschiert und unmarshalliert.
- Schnittstellen können als stabil (abwärtskompatibel) deklariert werden. Die API wird dann in einer Datei neben der AIDL-Schnittstelle verfolgt und versioniert.
Strukturierte vs. stabile AIDL
Strukturierte AIDL bezieht sich auf Typen, die ausschließlich in AIDL definiert sind. Beispielsweise ist eine Deklaration für Parcelable (ein benutzerdefiniertes Paket) kein strukturierter AIDL. Parcelables, deren Felder in AIDL definiert sind, werden als strukturierte Parcelables bezeichnet.
Stabile AIDL erfordert eine strukturierte AIDL, damit das Build-System und der Compiler verstehen können, ob Änderungen an Paketen abwärtskompatibel sind.
Allerdings sind nicht alle strukturierten Oberflächen stabil. Damit eine Schnittstelle stabil ist, darf sie nur strukturierte Typen sowie die folgenden Versionsverwaltungsfunktionen verwenden. Umgekehrt ist eine Schnittstelle nicht stabil, wenn das Core-Build-System zu ihrer Erstellung verwendet wird oder wenn unstable:true
festgelegt ist.
AIDL-Schnittstelle definieren
Eine Definition von aidl_interface
sieht so aus:
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
: Der Name des AIDL-Schnittstellenmoduls, das eine AIDL-Schnittstelle eindeutig identifiziert.srcs
: Die Liste der AIDL-Quelldateien, aus denen die Schnittstelle besteht. Der Pfad für einen AIDL-TypFoo
, der in einemcom.acme
-Paket definiert ist, sollte sich unter<base_path>/com/acme/Foo.aidl
befinden. Dabei kann<base_path>
ein beliebiges Verzeichnis sein, das sich auf das Verzeichnis vonAndroid.bp
bezieht. Im vorherigen Beispiel ist<base_path>
der Wertsrcs/aidl
.local_include_dir
: Pfad, von dem aus der Paketname beginnt. Sie entspricht dem oben erläuterten<base_path>
-Objekt.imports
: Eine Liste vonaidl_interface
-Modulen, die verwendet werden. Wenn eine Ihrer AIDL-Schnittstellen eine Schnittstelle oder ein Paket aus einem anderenaidl_interface
verwendet, geben Sie hier den Namen ein. Dies kann der Name an sich sein, um auf die neueste Version zu verweisen, oder der Name mit dem Versionssuffix (z. B.-V1
), um auf eine bestimmte Version zu verweisen. Die Angabe einer Version wird seit Android 12 unterstütztversions
: Die früheren Versionen der Benutzeroberfläche, die unterapi_dir
fixiert sind, werden ab Android 11 dieversions
unteraidl_api/name
fixiert. Wenn keine eingefrorenen Versionen einer Schnittstelle vorhanden sind, sollten Sie dies nicht angeben und es werden keine Kompatibilitätsprüfungen durchgeführt. Dieses Feld wurde für Android 13 und höher durchversions_with_info
ersetzt.versions_with_info
: Liste von Tupeln, die jeweils den Namen einer eingefrorenen Version und eine Liste mit Versionsimporten anderer aidl_interface-Module enthalten, die diese Version von aidl_interface importiert hat. Die Definition von Version V der AIDL-Schnittstelle IFACE finden Sie unteraidl_api/IFACE/V
. Dieses Feld wurde mit Android 13 eingeführt und soll nicht direkt inAndroid.bp
geändert werden. Das Feld wird durch Aufrufen von*-update-api
oder*-freeze-api
hinzugefügt oder aktualisiert. Außerdem werdenversions
-Felder automatisch zuversions_with_info
migriert, wenn ein Nutzer*-update-api
oder*-freeze-api
aufruft.stability
: Das optionale Flag für das Stabilitätversprechen dieser Schnittstelle. Dies unterstützt nur"vintf"
. Wennstability
nicht festgelegt ist, prüft das Build-System, ob die Schnittstelle abwärtskompatibel ist, sofern nichtunstable
angegeben ist. Wenn die Richtlinie nicht konfiguriert ist, entspricht dies einer Schnittstelle mit Stabilität innerhalb dieses Kompilierungskontexts (also entweder alle Systemelemente, z. B. Elemente insystem.img
und zugehörigen Partitionen oder alle Anbieterinhalte, z. B.vendor.img
und zugehörige Partitionen). Wennstability
auf"vintf"
gesetzt ist, entspricht dies einem Stabilitätsversprechen: Die Schnittstelle muss stabil gehalten werden, solange sie verwendet wird.gen_trace
: Das optionale Flag zum Aktivieren oder Deaktivieren des Tracings. Ab Android 14 ist die Standardeinstellungtrue
für die Back-Endscpp
undjava
.host_supported
: Das optionale Flag, das bei Einstellung auftrue
die generierten Bibliotheken für die Hostumgebung zur Verfügung stellt.unstable
: Das optionale Flag, das angibt, dass diese Schnittstelle nicht stabil sein muss. Wenn dieser Wert auftrue
gesetzt ist, erstellt das Build-System weder den API-Dump für die Schnittstelle noch muss er aktualisiert werden.frozen
: Das optionale Flag, das bei Festlegung auftrue
bedeutet, dass sich seit der vorherigen Version der Benutzeroberfläche keine Änderungen an der Schnittstelle ergeben. Dies ermöglicht mehr Prüfungen während der Build-Erstellung. Wennfalse
festgelegt ist, befindet sich die Schnittstelle noch in der Entwicklungsphase und enthält neue Änderungen. Wennfoo-freeze-api
ausgeführt wird, wird eine neue Version generiert und der Wert automatisch intrue
geändert. Wurde mit Android 14 eingeführt.backend.<type>.enabled
: Diese Flags schalten die einzelnen Back-Ends ein, für die der AIDL-Compiler Code generiert. Es werden vier Back-Ends unterstützt: Java, C++, NDK und Rust. Java-, C++- und NDK-Back-Ends sind standardmäßig aktiviert. Wenn eines dieser drei Back-Ends nicht benötigt wird, muss es explizit deaktiviert werden. Rust ist bis Android 15 standardmäßig deaktiviert (AOSP experimentell).backend.<type>.apex_available
: Die Liste der APEX-Namen, für die die generierte Stub-Bibliothek verfügbar ist.backend.[cpp|java].gen_log
: Das optionale Flag, das steuert, ob zusätzlicher Code zum Erfassen von Informationen zur Transaktion generiert werden soll.backend.[cpp|java].vndk.enabled
ist das optionale Flag, mit dem diese Schnittstelle Teil des VNDK wird. Der Standardwert istfalse
.backend.[cpp|ndk].additional_shared_libraries
: Dieses Flag wurde in Android 14 eingeführt und fügt den nativen Bibliotheken Abhängigkeiten hinzu. Dieses Flag ist fürndk_header
undcpp_header
nützlich.backend.java.sdk_version
: Das optionale Flag zum Angeben der SDK-Version, für die die Java-Stub-Bibliothek erstellt wird. Der Standardwert ist"system_current"
. Sollte nicht festgelegt werden, wennbackend.java.platform_apis
den Werttrue
hat.backend.java.platform_apis
: Das optionale Flag, das auftrue
gesetzt werden sollte, wenn die generierten Bibliotheken nicht mit dem SDK, sondern mit der Plattform-API erstellt werden müssen.
Für jede Kombination aus Versionen und aktivierten Back-Ends wird eine Stub-Bibliothek erstellt. Wie Sie auf die spezifische Version der Stub-Bibliothek für ein bestimmtes Back-End verweisen, können Sie unter Benennungsregeln für Module nachlesen.
AIDL-Dateien schreiben
Schnittstellen in stabiler AIDL ähneln herkömmlichen Schnittstellen, mit der Ausnahme, dass sie keine unstrukturierten Pakete verwenden dürfen (da diese instabil sind! Siehe Strukturierter und stabiler AIDL). Der Hauptunterschied in der stabilen AIDL besteht darin, wie Pakete definiert werden. Bisher wurden Parcelables Forward-Deklarationen erstellt. In stabilen (und daher strukturierten) AIDL werden Parcelable-Felder und Variablen explizit definiert.
// in a file like 'some/package/Thing.aidl'
package some.package;
parcelable SubThing {
String a = "foo";
int b;
}
Ein Standardwert wird für boolean
, char
, float
, double
, byte
, int
, long
und String
unterstützt (aber nicht erforderlich). In Android 12 werden auch Standardeinstellungen für benutzerdefinierte Aufzählungen unterstützt. Wenn kein Standardwert angegeben ist, wird ein 0-ähnlicher oder leerer Wert verwendet.
Aufzählungen ohne Standardwert werden mit 0 initialisiert, auch wenn kein Null-Enumerator vorhanden ist.
Stub-Bibliotheken verwenden
Nachdem Sie Stub-Bibliotheken als Abhängigkeit zu Ihrem Modul hinzugefügt haben, können Sie sie in Ihre Dateien einfügen. Im Folgenden finden Sie Beispiele für Stub-Bibliotheken im Build-System (Android.mk
kann auch für Legacy-Moduldefinitionen verwendet werden):
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"],
...
}
Beispiel in C++:
#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
// use just like traditional AIDL
Beispiel in Java:
import some.package.IFoo;
import some.package.Thing;
...
// use just like traditional AIDL
Beispiel in Rust:
use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
// use just like traditional AIDL
Versionsverwaltung von Schnittstellen
Durch die Deklaration eines Moduls mit dem Namen foo wird im Build-System auch ein Ziel erstellt, mit dem Sie die API des Moduls verwalten können. Nach der Erstellung fügt foo-Free-api je nach Android-Version eine neue API-Definition unter api_dir
oder aidl_api/name
hinzu. Außerdem wird eine .hash
-Datei hinzugefügt, die beide die neu eingefrorene Version der Benutzeroberfläche darstellt. Außerdem aktualisiert foo-stay-api das Attribut versions_with_info
, um die zusätzliche Version und imports
für die Version widerzuspiegeln. Grundsätzlich wird imports
in versions_with_info
aus dem Feld imports
kopiert. Die neueste stabile Version wird jedoch in imports
in versions_with_info
für den Import angegeben. Sie hat keine explizite Version.
Nachdem das Attribut versions_with_info
angegeben wurde, führt das Build-System Kompatibilitätsprüfungen zwischen fixierten Versionen sowie zwischen Top of Tree (ToT) und der neuesten eingefrorenen Version aus.
Außerdem musst du die API-Definition der ToT-Version verwalten. Bei jeder API-Aktualisierung führen Sie foo-update-api aus, um aidl_api/name/current
zu aktualisieren, das die API-Definition der ToT-Version enthält.
Um die Stabilität einer Oberfläche zu wahren, können Inhaber neue Elemente hinzufügen:
- Methoden am Ende einer Schnittstelle (oder Methoden mit explizit definierten neuen Seriennummern)
- Elemente am Ende eines Pakets (erfordert das Hinzufügen eines Standardwerts für jedes Element)
- Konstantenwerte
- In Android 11 können Zähler
- In Android 12 sind Felder am Ende einer Union (Union)
Andere Aktionen sind nicht zulässig und niemand sonst kann die Schnittstelle ändern. Andernfalls besteht die Gefahr von Konflikten mit Änderungen, die ein Inhaber vornimmt.
Wenn Sie testen möchten, ob alle Schnittstellen für den Release eingefroren sind, können Sie einen Build mit den folgenden Umgebungsvariablen erstellen:
AIDL_FROZEN_REL=true m ...
: Für den Build müssen alle stabilen AIDL-Schnittstellen eingefroren sein, für die keinowner:
-Feld angegeben ist.AIDL_FROZEN_OWNERS="aosp test"
: Für den Build müssen alle stabilen AIDL-Schnittstellen mit dem Feldowner:
als „aosp“ oder „test“ eingefroren sein.
Stabilität von Importen
Das Aktualisieren der Versionen von Importen für eingefrorene Versionen einer Schnittstelle ist auf der stabilen AIDL-Ebene abwärtskompatibel. Zum Aktualisieren müssen jedoch alle Server und Clients aktualisiert werden, die eine frühere Version der Benutzeroberfläche verwenden. Außerdem können einige Anwendungen verwirrt werden, wenn verschiedene Versionen von Typen gemischt werden. Im Allgemeinen ist dies bei nur Typen oder gängigen Paketen sicher, da Code bereits geschrieben werden muss, um unbekannte Typen aus IPC-Transaktionen zu verarbeiten.
Im Android-Plattformcode ist android.hardware.graphics.common
das beste Beispiel für diese Art von Versionsupgrade.
Versionierte Schnittstellen verwenden
Schnittstellenmethoden
Wenn Sie während der Laufzeit versuchen, neue Methoden auf einem alten Server aufzurufen, erhalten neue Clients je nach Back-End entweder einen Fehler oder eine Ausnahme.
cpp
-Back-End erhält::android::UNKNOWN_TRANSACTION
.ndk
-Back-End erhältSTATUS_UNKNOWN_TRANSACTION
.- Das
java
-Back-End erhältandroid.os.RemoteException
mit der Meldung, dass die API nicht implementiert ist.
Entsprechende Strategien finden Sie unter Versionen abfragen und Standardeinstellungen verwenden.
Parcelables
Wenn zu Paketen neue Felder hinzugefügt werden, werden sie von alten Clients und Servern verworfen. Wenn neue Clients und Server alte Pakete erhalten, werden die Standardwerte für die neuen Felder automatisch ausgefüllt. Dies bedeutet, dass für alle neuen Felder in einem Paket Standardeinstellungen angegeben werden müssen.
Clients sollten nicht erwarten, dass Server die neuen Felder verwenden, es sei denn, sie wissen, dass der Server die Version implementiert, für die das Feld definiert ist (siehe Versionen abfragen).
Enums und Konstanten
Ebenso sollten Clients und Server nicht erkannte konstante Werte und Zähler entweder ablehnen oder ignorieren, da in Zukunft möglicherweise weitere hinzugefügt werden können. Ein Server sollte beispielsweise nicht abgeschaltet werden, wenn er einen Zähler empfängt, von dem er nichts weiß. Der Server sollte den Zähler entweder ignorieren oder etwas zurückgeben, das dem Client signalisiert, dass dies in dieser Implementierung nicht unterstützt wird.
Gewerkschaften
Der Versuch, eine Union mit einem neuen Feld zu senden, schlägt fehl, wenn der Empfänger alt ist und das Feld nicht kennt. Bei der Implementierung wird die Union mit dem neuen Feld nie angezeigt. Der Fehler wird ignoriert, wenn es sich um eine Einwegtransaktion handelt. Andernfalls lautet der Fehler BAD_VALUE
(für das C++- oder NDK-Back-End) oder IllegalArgumentException
(für das Java-Back-End). Dieser Fehler wird zurückgegeben, wenn der Client eine Union-Einstellung an einen alten Server an das neue Feld sendet oder wenn es sich um einen alten Client handelt, der die Union von einem neuen Server empfängt.
Flag-basierte Entwicklung
In der Entwicklung befindliche (nicht eingefrorene) Schnittstellen können auf Release-Geräten nicht verwendet werden, da ihre Abwärtskompatibilität nicht garantiert ist.
AIDL unterstützt das Laufzeit-Fallback für diese nicht eingefrorenen Schnittstellenbibliotheken, damit Code für die neueste nicht eingefrorene Version geschrieben und weiterhin auf Release-Geräten verwendet werden kann. Das abwärtskompatible Verhalten von Clients ähnelt dem vorhandenen Verhalten und mit dem Fallback müssen die Implementierungen diesem Verhalten ebenfalls folgen. Weitere Informationen finden Sie unter Versionierte Schnittstellen verwenden.
AIDL-Build-Flag
Das Flag, das dieses Verhalten steuert, ist RELEASE_AIDL_USE_UNFROZEN
in build/release/build_flags.bzl
definiert. true
bedeutet, dass die nicht eingefrorene Version der Schnittstelle zur Laufzeit verwendet wird, und false
bedeutet, dass sich die Bibliotheken der nicht fixierten Versionen wie ihre letzte eingefrorene Version verhalten.
Sie können das Flag für die lokale Entwicklung mit true
überschreiben, müssen es aber vor der Veröffentlichung auf false
zurücksetzen. In der Regel erfolgt die Entwicklung mit einer Konfiguration, bei der das Flag auf true
gesetzt ist.
Kompatibilitätsmatrix und Manifeste
Anbieterschnittstellenobjekte (VINTF-Objekte) definieren, welche Versionen zu erwarten sind und welche Versionen auf beiden Seiten der Anbieterschnittstelle zur Verfügung stehen.
Die meisten Nicht-Cuttlefish-Geräte zielen erst auf die neueste Kompatibilitätsmatrix ab, nachdem Schnittstellen eingefroren wurden. Daher gibt es keinen Unterschied in den AIDL-Bibliotheken basierend auf RELEASE_AIDL_USE_UNFROZEN
.
Matrizen
Partnereigene Schnittstellen werden geräte- oder produktspezifischen Kompatibilitätsmatrixen hinzugefügt, auf die das Gerät während der Entwicklung ausgerichtet ist. Wenn also eine neue, nicht eingefrorene Version einer Schnittstelle zu einer Kompatibilitätsmatrix hinzugefügt wird, müssen die vorherigen fixierten Versionen für RELEASE_AIDL_USE_UNFROZEN=false
beibehalten werden. Dazu können Sie unterschiedliche Kompatibilitätsmatrixdateien für verschiedene RELEASE_AIDL_USE_UNFROZEN
-Konfigurationen verwenden oder beide Versionen in einer einzigen Kompatibilitätsmatrixdatei zulassen, die in allen Konfigurationen verwendet wird.
Wenn Sie beispielsweise eine nicht fixierte Version 4 hinzufügen, verwenden Sie <version>3-4</version>
.
Wenn Version 4 eingefroren ist, können Sie Version 3 aus der Kompatibilitätsmatrix entfernen, da die eingefrorene Version 4 verwendet wird, wenn RELEASE_AIDL_USE_UNFROZEN
den Wert false
hat.
Manifeste
In Android 15 (AOSP experimentell) wird eine Änderung in libvintf
eingeführt, um die Manifestdateien zum Zeitpunkt der Erstellung basierend auf dem Wert von RELEASE_AIDL_USE_UNFROZEN
zu ändern.
Die Manifeste und Manifestfragmente geben an, welche Version einer Schnittstelle ein Dienst implementiert. Wenn Sie die neueste nicht eingefrorene Version einer Oberfläche verwenden, muss das Manifest entsprechend aktualisiert werden. Wenn RELEASE_AIDL_USE_UNFROZEN=false
, werden die Manifesteinträge von libvintf
angepasst, um die Änderung an der generierten AIDL-Bibliothek widerzuspiegeln. Die Version wird von der nicht eingefrorenen Version N
in die letzte eingefrorene Version N - 1
geändert. Daher müssen Nutzer nicht mehrere Manifeste oder Manifestfragmente für jeden ihrer Dienste verwalten.
Änderungen am HAL-Client
Der HAL-Clientcode muss mit jeder zuvor unterstützten eingefrorenen Version abwärtskompatibel sein. Wenn RELEASE_AIDL_USE_UNFROZEN
auf false
gesetzt ist, sehen die Dienste immer wie die zuletzt oder eine frühere Version aus. So wird beispielsweise beim Aufrufen neuer nicht eingefrorener Methoden UNKNOWN_TRANSACTION
zurückgegeben oder neue parcelable
-Felder haben ihre Standardwerte. Android-Framework-Clients müssen mit weiteren vorherigen Versionen abwärtskompatibel sein. Dies ist jedoch ein neues Detail für Anbieterclients und Kunden von partnereigenen Benutzeroberflächen.
Änderungen an der HAL-Implementierung
Der größte Unterschied bei der HAL-Entwicklung mit Flag-basierter Entwicklung besteht darin, dass HAL-Implementierungen abwärtskompatibel mit der letzten eingefrorenen Version funktionieren, wenn RELEASE_AIDL_USE_UNFROZEN
den Wert false
hat.
Die Berücksichtigung der Abwärtskompatibilität bei Implementierungen und Gerätecode ist eine neue Übung. Weitere Informationen finden Sie unter Versionierte Schnittstellen verwenden.
Die Überlegungen zur Abwärtskompatibilität gelten im Allgemeinen für die Clients und Server sowie für den Framework-Code und Anbietercode gleich. Es gibt jedoch kleine Unterschiede, die Sie beachten müssen, da Sie jetzt effektiv zwei Versionen implementieren, die denselben Quellcode verwenden (die aktuelle, nicht eingefrorene Version).
Beispiel: Eine Schnittstelle hat drei eingefrorene Versionen. Die Benutzeroberfläche wird mit einer neuen Methode aktualisiert. Der Client und der Dienst werden beide auf die Verwendung der neuen Bibliothek Version 4 aktualisiert. Da die V4-Bibliothek auf einer nicht eingefrorenen Version der Schnittstelle basiert, verhält sie sich wie die letzte eingefrorene Version, Version 3, wenn RELEASE_AIDL_USE_UNFROZEN
auf false
gesetzt ist, und verhindert die Verwendung der neuen Methode.
Wenn die Schnittstelle eingefroren ist, verwenden alle Werte von RELEASE_AIDL_USE_UNFROZEN
diese eingefrorene Version und der Code, der die Abwärtskompatibilität steuert, kann entfernt werden.
Beim Aufrufen von Methoden für Callbacks muss der Fall korrekt verarbeitet werden, wenn UNKNOWN_TRANSACTION
zurückgegeben wird. Clients implementieren möglicherweise zwei verschiedene Versionen eines Callbacks, die auf der Releasekonfiguration basieren. Daher können Sie nicht davon ausgehen, dass der Client die neueste Version sendet. Neue Methoden können dies zurückgeben. Dies ähnelt der Aufrechterhaltung der Abwärtskompatibilität mit Servern, die von stabilen AIDL-Clients aufrechterhalten wird, wie unter Versionierte Schnittstellen verwenden beschrieben.
// 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();
}
}
Neue Felder in vorhandenen Typen (parcelable
, enum
, union
) sind möglicherweise nicht vorhanden oder enthalten ihre Standardwerte, wenn RELEASE_AIDL_USE_UNFROZEN
gleich false
ist und die Werte neuer Felder, die ein Dienst zu senden versucht, beim Verlassen des Prozesses gelöscht werden.
Neue Typen, die in dieser nicht eingefrorenen Version hinzugefügt werden, können nicht über die Schnittstelle gesendet oder empfangen werden.
Die Implementierung erhält niemals einen Aufruf für neue Methoden von Clients, wenn RELEASE_AIDL_USE_UNFROZEN
den Wert false
hat.
Achten Sie darauf, neue Zähler nur mit der Version zu verwenden, in der sie eingeführt wurden, und nicht mit der vorherigen Version.
Normalerweise sehen Sie mit foo->getInterfaceVersion()
, welche Version von der Remoteschnittstelle verwendet wird. Mit der Unterstützung für die Flag-basierte Versionsverwaltung implementieren Sie jedoch zwei verschiedene Versionen. Möglicherweise möchten Sie die Version der aktuellen Benutzeroberfläche abrufen. Rufen Sie dazu die Schnittstellenversion des aktuellen Objekts ab, z. B. this->getInterfaceVersion()
oder die anderen Methoden für my_ver
. Weitere Informationen finden Sie unter Schnittstellenversion des Remote-Objekts abfragen.
Neue stabile VINTF-Oberflächen
Wenn ein neues AIDL-Schnittstellenpaket hinzugefügt wird, gibt es keine letzte eingefrorene Version. Es gibt also kein Verhalten, auf das zurückgegriffen werden kann, wenn RELEASE_AIDL_USE_UNFROZEN
den Wert false
hat. Verwenden Sie diese Oberflächen nicht. Wenn RELEASE_AIDL_USE_UNFROZEN
auf false
gesetzt ist, lässt Service Manager nicht zu, dass der Dienst die Schnittstelle registriert und die Clients sie nicht finden.
Sie können die Dienste basierend auf dem Wert des Flags RELEASE_AIDL_USE_UNFROZEN
im Geräte-Makefile bedingt hinzufügen:
ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
android.hardware.health.storage-service
endif
Wenn der Dienst Teil eines größeren Prozesses ist und Sie ihn dem Gerät bedingt nicht hinzufügen können, können Sie prüfen, ob der Dienst mit IServiceManager::isDeclared()
deklariert wurde. Wenn er deklariert ist und nicht registriert werden konnte, brechen Sie den Prozess ab. Wenn es nicht deklariert ist, schlägt die Registrierung wahrscheinlich fehl.
Sepien als Entwicklungstool
Jedes Jahr, nachdem die VINTF eingefroren ist, passen wir die Framework-Kompatibilitätsmatrix (FCM) target-level
und den PRODUCT_SHIPPING_API_LEVEL
von Cuttlefish an, sodass sie Geräte widerspiegeln, die mit dem Release im nächsten Jahr eingeführt werden. Wir passen target-level
und PRODUCT_SHIPPING_API_LEVEL
an, um sicherzustellen, dass ein Startgerät getestet wurde und die neuen Anforderungen für den Release im nächsten Jahr erfüllt.
Wenn RELEASE_AIDL_USE_UNFROZEN
den Wert true
hat, wird Cuttlefish für die Entwicklung zukünftiger Android-Releases verwendet. Sie ist auf das FCM-Level des nächsten Android-Release und auf PRODUCT_SHIPPING_API_LEVEL
ausgerichtet, wobei die Anforderungen an die anbieterseitige Software des nächsten Releases erfüllt werden müssen.
Wenn RELEASE_AIDL_USE_UNFROZEN
den Wert false
hat, hat Cuttlefish die vorherige target-level
und die vorherige PRODUCT_SHIPPING_API_LEVEL
, um ein Releasegerät widerzuspiegeln.
In Android 14 und niedriger wird diese Differenzierung für verschiedene Git-Zweige erreicht, die die Änderung auf FCM-target-level
, Versand-API-Level oder anderen Code, der auf das nächste Release ausgerichtet ist, nicht übernehmen.
Regeln für die Benennung von Modulen
In Android 11 wird für jede Kombination der aktivierten Versionen und Back-Ends automatisch ein Stub-Bibliotheksmodul erstellt. Wenn Sie für die Verknüpfung auf ein bestimmtes Stub-Bibliotheksmodul verweisen möchten, verwenden Sie nicht den Namen des aidl_interface
-Moduls, sondern den Namen des Stub-Bibliotheksmoduls (ifacename-version-backend), wobei
ifacename
: Name des Modulsaidl_interface
version
ist entwederVversion-number
für die eingefrorenen VersionenVlatest-frozen-version-number + 1
für die Tip-of-Tree-Version (noch nicht eingefroren)
backend
ist entwederjava
für das Java-Back-Endcpp
für das C++-Back-Endndk
oderndk_platform
für das NDK-Back-End. Ersteres ist für Apps und Letzteres für die Plattformnutzung bis Android 13. Verwenden Sie unter Android 13 und höher nurndk
.rust
für Rust-Back-End.
Angenommen, es gibt ein Modul namens foo mit der neuesten Version 2, das sowohl NDK als auch C++ unterstützt. In diesem Fall generiert AIDL diese Module:
- Basierend auf Version 1
foo-V1-(java|cpp|ndk|ndk_platform|rust)
- Basierend auf Version 2 (der neuesten stabilen Version)
foo-V2-(java|cpp|ndk|ndk_platform|rust)
- Basierend auf der Version der ToT-Funktion
foo-V3-(java|cpp|ndk|ndk_platform|rust)
Im Vergleich zu Android 11:
foo-backend
, die auf die neueste stabile Version verweist, wird zufoo-V2-backend
.foo-unstable-backend
, der auf die Version mit den Nutzungsbedingungen verweist, wird zufoo-V3-backend
.
Die Namen der Ausgabedateien sind immer mit den Modulnamen identisch.
- Basierend auf Version 1:
foo-V1-(cpp|ndk|ndk_platform|rust).so
- Basierend auf Version 2:
foo-V2-(cpp|ndk|ndk_platform|rust).so
- Basierend auf Version der Nutzungsbedingungen:
foo-V3-(cpp|ndk|ndk_platform|rust).so
Der AIDL-Compiler erstellt weder ein unstable
-Versionsmodul noch ein nicht versioniertes Modul für eine stabile AIDL-Schnittstelle.
Ab Android 12 enthält der von einer stabilen AIDL-Schnittstelle generierten Modulname immer die Version.
Neue Metaschnittstellenmethoden
Android 10 fügt mehrere Metaschnittstellenmethoden für die stabile AIDL hinzu.
Schnittstellenversion des Remote-Objekts abfragen
Clients können die Version und den Hash der vom Remoteobjekt implementierten Schnittstelle abfragen und die zurückgegebenen Werte mit den Werten der vom Client verwendeten Schnittstelle vergleichen.
Beispiel mit dem Back-End 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();
Beispiel mit dem Back-End ndk
(und dem 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);
Beispiel mit dem Back-End 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();
Für die Sprache Java MÜSSEN auf der Remote-Seite getInterfaceVersion()
und getInterfaceHash()
wie im Folgenden dargestellt implementiert werden (super
wird anstelle von IFoo
verwendet, um Fehler beim Kopieren und Einfügen zu vermeiden. Die Annotation @SuppressWarnings("static")
kann je nach javac
-Konfiguration erforderlich sein, um Warnungen zu deaktivieren:
class MyFoo extends IFoo.Stub {
@Override
public final int getInterfaceVersion() { return super.VERSION; }
@Override
public final String getInterfaceHash() { return super.HASH; }
}
Das liegt daran, dass die generierten Klassen (IFoo
, IFoo.Stub
usw.) vom Client und dem Server gemeinsam genutzt werden. Die Klassen können sich beispielsweise im Bootklassenpfad befinden. Wenn Klassen freigegeben werden, ist der Server auch mit der neuesten Version der Klassen verknüpft, auch wenn dieser möglicherweise mit einer älteren Version der Benutzeroberfläche erstellt wurde. Wenn diese Metaschnittstelle in der gemeinsam genutzten Klasse implementiert ist, wird immer die neueste Version zurückgegeben. Bei der Implementierung der Methode wie oben wird jedoch die Versionsnummer der Schnittstelle in den Code des Servers eingebettet, da IFoo.VERSION
eine static final int
ist, die beim Verweis eingefügt wird. Dadurch kann die Methode genau die Version zurückgeben, mit der der Server erstellt wurde.
Umgang mit älteren Benutzeroberflächen
Es ist möglich, dass ein Client mit der neueren Version einer AIDL-Schnittstelle aktualisiert wird, aber der Server die alte AIDL-Schnittstelle verwendet. In solchen Fällen wird beim Aufrufen einer Methode auf einer alten Schnittstelle UNKNOWN_TRANSACTION
zurückgegeben.
Mit der stabilen AIDL haben Clients mehr Kontrolle. Clientseitig können Sie eine Standardimplementierung für eine AIDL-Schnittstelle festlegen. Eine Methode in der Standardimplementierung wird nur aufgerufen, wenn die Methode nicht auf der Remoteseite implementiert wurde, weil sie mit einer älteren Version der Schnittstelle erstellt wurde. Da die Standardeinstellungen global festgelegt sind, sollten sie nicht aus potenziell freigegebenen Kontexten verwendet werden.
Beispiel in C++ unter Android 13 und höher:
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
Beispiel in Java:
IFoo.Stub.setDefaultImpl(new IFoo.Default() {
@Override
public xxx anAddedMethod(...) throws RemoteException {
// do something default
}
}); // once per an interface in a process
foo.anAddedMethod(...);
Sie müssen nicht die Standardimplementierung aller Methoden in einer AIDL-Schnittstelle bereitstellen. Methoden, die garantiert auf der Remoteseite implementiert werden (da Sie sicher sind, dass das Remote erstellt wurde, wenn die Methoden in der Beschreibung der AIDL-Schnittstelle aufgeführt sind) müssen nicht in der Standardklasse impl
überschrieben werden.
Vorhandene AIDL in strukturierte oder stabile AIDL konvertieren
Wenn Sie bereits eine AIDL-Schnittstelle und Code haben, der sie verwendet, konvertieren Sie die folgenden Schritte, um die Schnittstelle in eine stabile AIDL-Schnittstelle zu konvertieren.
Identifizieren Sie alle Abhängigkeiten Ihrer Schnittstelle. Ermitteln Sie für jedes Paket, von dem die Schnittstelle abhängt, ob das Paket in stabilem AIDL definiert ist. Wenn nicht definiert, muss das Paket konvertiert werden.
Konvertieren Sie alle Parcelables in der Schnittstelle in stabile Parcelables (die Schnittstellendateien selbst können unverändert bleiben). Dazu geben Sie die Struktur direkt in AIDL-Dateien aus. Verwaltungsklassen müssen umgeschrieben werden, um diese neuen Typen zu verwenden. Dies kann vor dem Erstellen eines
aidl_interface
-Pakets erfolgen (siehe unten).Erstellen Sie wie oben beschrieben ein
aidl_interface
-Paket, das den Namen des Moduls, seine Abhängigkeiten und alle weiteren benötigten Informationen enthält. Damit sie stabil und nicht nur strukturiert ist, muss sie auch versioniert werden. Weitere Informationen finden Sie unter Schnittstellen versionieren.