Android 10 unterstützt die stabile Android Interface Definition Language (AIDL). Das ist eine neue Möglichkeit, die API (Application Program Interface) und die ABI (Application Binary Interface) zu verwalten, die von AIDL-Schnittstellen bereitgestellt werden. Stable AIDL funktioniert genau wie AIDL, aber das Build-System überwacht die Schnittstellenkompatibilität und es gibt Einschränkungen für Ihre Möglichkeiten:
- Schnittstellen werden im Build-System mit
aidl_interfaces
definiert. - Schnittstellen können nur strukturierte Daten enthalten. Parcelables, die die bevorzugten Typen darstellen, werden automatisch basierend auf ihrer AIDL-Definition erstellt und automatisch gemarshallt und unmarshallt.
- Schnittstellen können als stabil (abwärtskompatibel) deklariert werden. Wenn diese wird ihre API in einer Datei neben der AIDL nachverfolgt und versioniert. .
Strukturierte vs. stabile AIDL
Strukturierte AIDL bezieht sich auf Typen, die ausschließlich in AIDL definiert sind. Beispiel: Die Deklaration „parcelable“ (ein benutzerdefiniertes Paket) ist keine strukturierte AIDL. Parcelables Die in AIDL definierten Felder werden als strukturierte Parcelables bezeichnet.
Stabiler AIDL erfordert eine strukturierte AIDL, damit das Build-System und der Compiler erstellt werden können
können Sie nachvollziehen, ob Änderungen
an Paketen abwärtskompatibel sind.
Allerdings sind nicht alle strukturierten Oberflächen stabil. Um stabil zu bleiben,
Eine Schnittstelle darf nur strukturierte Typen sowie Folgendes enthalten:
Funktionen zur Versionsverwaltung. Umgekehrt ist eine Schnittstelle nicht stabil, wenn der Core
System 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 Benutzeroberfläche besteht. Der Pfad für einen AIDL-TypFoo
, der in einem Paketcom.acme
definiert ist, sollte sich unter<base_path>/com/acme/Foo.aidl
befinden. Dabei kann<base_path>
jedes Verzeichnis sein, das mit dem Verzeichnis, in dem sichAndroid.bp
befindet, in Beziehung steht. Im vorherigen Beispiel ist<base_path>
srcs/aidl
.local_include_dir
: Pfad, von dem aus der Paketname beginnt. Es entspricht dem oben erläuterten<base_path>
.imports
: Eine Liste der verwendetenaidl_interface
-Module. Wenn eine Ihrer AIDL-Schnittstellen eine Schnittstelle oder ein Parcelable aus einer anderenaidl_interface
verwendet, geben Sie hier den Namen an. Das kann der Name selbst, auf die neuesten Version oder der Name mit dem Versionssuffix (z. B.-V1
) für den Verweis einer bestimmten Version. Die Angabe einer Version wird seit Android 12 unterstützt.versions
: Die vorherigen Versionen der Benutzeroberfläche, die unterapi_dir
eingefroren sind. Ab Android 11 sind dieversions
unteraidl_api/name
eingefroren. Wenn es keine fixierten Versionen einer Schnittstelle gibt, Das sollte nicht angegeben werden und es gibt keine Kompatibilitätsprüfungen. Dieses Feld wurde durchversions_with_info
für Android ersetzt 13 und höher.versions_with_info
: Liste der Tupel, von denen jedes den Namen eines eingefrorene Version und eine Liste mit Versionsimporten anderer aidl_interface Module, die von dieser Version von aidl_interface importiert wurden. Die Definition der Version V einer AIDL-Schnittstelle, die sich unter IFACE befindetaidl_api/IFACE/V
Dieses Feld wurde in Android 13 eingeführt und sollte 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. Ein Nutzer ruft*-update-api
oder*-freeze-api
auf.stability
: Das optionale Flag für die Stabilitätsgarantie dieser Schnittstelle. Dies unterstützt nur"vintf"
. Wennstability
nicht festgelegt ist, prüft das Buildsystem, ob die Benutzeroberfläche abwärtskompatibel ist, sofernunstable
nicht angegeben ist. Wenn „unset“ festgelegt ist, entspricht dies einer stabilen Schnittstelle in diesem Kompilierungskontext (d. h. entweder alle Systemelemente, z. B. Elemente insystem.img
und zugehörige Partitionen, oder alle Anbieterelemente, z. B. Elemente invendor.img
und zugehörige Partitionen). Wennstability
auf"vintf"
festgelegt ist, entspricht dies einem Stabilitätsversprechen: Die Benutzeroberfläche muss so lange stabil bleiben, wie sie verwendet wird.gen_trace
: Optionales Flag, mit dem die Aufzeichnung aktiviert oder deaktiviert wird. Ab Android 14 ist der Standardwerttrue
für diecpp
- undjava
-Backends.host_supported
: Optionales Flag, das die generierten Bibliotheken für die Hostumgebung verfügbar macht, wenn es auftrue
gesetzt ist.unstable
: Das optionale Flag, das verwendet wird, um zu kennzeichnen, dass diese Schnittstelle nicht müssen stabil sein. Wenn dieser Wert auftrue
gesetzt ist, hat das Build-System weder erstellt den API-Dump für die Schnittstelle und muss nicht aktualisiert werden.frozen
: Optionales Flag. Wenn es auftrue
gesetzt ist, hat sich die Benutzeroberfläche seit der vorherigen Version nicht geändert. Dadurch sind mehr Buildzeitprüfungen möglich. Wenn der Wert auffalse
festgelegt ist, befindet sich die Benutzeroberfläche in der Entwicklungsphase und es gibt neue Änderungen. Wenn Siefoo-freeze-api
ausführen, wird eine neue Version generiert und der Wert wird automatisch intrue
geändert. In Android 14 eingeführt.backend.<type>.enabled
: Diese Flags aktivieren/deaktivieren die einzelnen Back-Ends, für den der AIDL-Compiler Code generiert. Vier Back-Ends werden unterstützt: Java, C++, NDK und Rust. Java-, C++- und NDK-Back-Ends sind standardmäßig aktiviert. Wenn eines dieser drei Backends nicht benötigt wird, muss es explizit deaktiviert werden. Rust ist bis Android 15 standardmäßig deaktiviert.backend.<type>.apex_available
: Die Liste der APEX-Namen, die generiert wurden -Stub-Bibliothek verfügbar ist.backend.[cpp|java].gen_log
: Das optionale Flag, das steuert, ob Zusätzlichen Code zum Sammeln von Informationen über die Transaktion generieren.backend.[cpp|java].vndk.enabled
: Das optionale Flag, um diese Schnittstelle zu einem Teil von VNDK zu machen. 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 Version des SDK, für das die Java-Stub-Bibliothek erstellt wurde. Der Standardwert ist"system_current"
Sollte nicht festgelegt werden, wennbackend.java.platform_apis
isttrue
.backend.java.platform_apis
: Das optionale Flag, das auftrue
, wenn die generierten Bibliotheken für die Plattform-API erstellt werden müssen anstatt auf das SDK.
Für jede Kombination aus Versionen und aktivierten Backends wird eine Stub-Bibliothek erstellt. Weitere Informationen dazu, wie Sie auf die spezifische Version der Stub-Bibliothek für ein bestimmtes Backend verweisen, finden Sie unter Regeln für die Modulbenennung.
AIDL-Dateien schreiben
Schnittstellen in stabiler AIDL ähneln herkömmlichen Schnittstellen, mit der Ausnahme, dass keine unstrukturierten Parcelables verwendet werden dürfen, da diese nicht stabil sind (siehe Strukturiertes und stabiles AIDL). Der Hauptunterschied bei der stabilen AIDL besteht darin, Parcelables definiert sind. Zuvor wurden Parcelables Forward deklariert; in stabile (und daher strukturierte) AIDL, Parcelables-Felder und Variablen explizit definiert ist.
// in a file like 'some/package/Thing.aidl'
package some.package;
parcelable SubThing {
String a = "foo";
int b;
}
Ein Standardwert wird unterstützt (aber nicht erforderlich) für boolean
, char
,
float
, double
, byte
, int
, long
und String
. In Android 12 werden auch Standardwerte 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, selbst wenn
Kein Zähler mit Null.
Stub-Bibliotheken verwenden
Nachdem Sie Stub-Bibliotheken als Abhängigkeit zu Ihrem Modul hinzugefügt haben,
die Sie in Ihre Dateien einfügen können. Hier sind Beispiele für Stub-Bibliotheken in der
Build-System erstellen (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
Versionierungsschnittstellen
Wird ein Modul mit dem Namen foo deklariert, wird auch ein Ziel im Build-System erstellt.
mit dem Sie die API des Moduls verwalten können. Beim Erstellen fügt foo-freeze-api je nach Android-Version eine neue API-Definition unter api_dir
oder aidl_api/name
und eine .hash
-Datei hinzu. Beide stellen die neu eingefrorene Version der Benutzeroberfläche dar. foo-freeze-api aktualisiert auch die Eigenschaft versions_with_info
, um die zusätzliche Version und imports
für die Version widerzuspiegeln. Grundsätzlich
„imports
“ im Feld „versions_with_info
“ wird aus dem Feld „imports
“ kopiert. Die neueste stabile Version ist jedoch in imports
in versions_with_info
für den Import angegeben, der keine explizite Version hat.
Nachdem die versions_with_info
-Eigenschaft angegeben wurde, führt das Buildsystem Kompatibilitätsüberprüfungen zwischen eingefrorenen Versionen und zwischen Top of Tree (ToT) und der neuesten eingefrorenen Version durch.
Außerdem musst du die API-Definition der ToT-Version verwalten. Wenn eine API
aktualisiert: Führen Sie zum Aktualisieren foo-update-api aus.
aidl_api/name/current
die die API-Definition der ToT-Version enthält.
Um die Stabilität einer Benutzeroberfläche zu erhalten, können Inhaber Folgendes hinzufügen:
- Methoden am Ende einer Schnittstelle (oder Methoden mit explizit definierten neuen Seriennummern)
- Elemente am Ende eines Parcelable (für jedes Element muss ein Standard hinzugefügt werden)
- Konstante Werte
- 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 eine Benutzeroberfläche ändern. Andernfalls besteht die Gefahr von Konflikten mit Änderungen, die ein Eigentümer vornimmt.
Wenn Sie prüfen möchten, ob alle Oberflächen für die Veröffentlichung eingefroren sind, können Sie mit den folgenden Umgebungsvariablen einen Build erstellen:
AIDL_FROZEN_REL=true m ...
– Für den Build müssen alle stabilen AIDL-Schnittstellen eingefroren werden, für die keinowner:
-Feld angegeben ist.AIDL_FROZEN_OWNERS="aosp test"
– Für den Build müssen alle stabilen AIDL-Schnittstellen eingefroren sein und das Feldowner:
muss als „aosp“ oder „test“ angegeben sein.
Stabilität von Importen
Das Aktualisieren der Importversionen für eingefrorene Versionen einer Schnittstelle auf der stabilen AIDL-Ebene. Die Aktualisierung erfordert jedoch, Aktualisierung aller Server und Clients, die eine frühere Version der Benutzeroberfläche verwenden und einige Apps können verwirrt sein, wenn sie verschiedene Versionen von Typen mischen. Im Allgemeinen ist dies bei nur Typen oder gängigen Paketen sicher, da der Code bereits geschrieben wurden, um unbekannte Typen aus IPC-Transaktionen zu verarbeiten.
Im Android-Plattformcode ist android.hardware.graphics.common
der größte
Beispiel für diese Art von Versionsupgrade.
Versionierte Schnittstellen verwenden
Schnittstellenmethoden
Wenn bei der Laufzeit neue Methoden auf einem alten Server aufgerufen werden, erhalten neue Clients je nach Backend entweder einen Fehler oder eine Ausnahme.
cpp
-Back-End erhält::android::UNKNOWN_TRANSACTION
.- Das
ndk
-Backend erhältSTATUS_UNKNOWN_TRANSACTION
. - Das
java
-Backend erhältandroid.os.RemoteException
mit einer Meldung, die besagt, dass Die API ist nicht implementiert.
Strategien zur Bewältigung dieses Problems finden Sie unter Abfragen von Versionen 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 neue werden automatisch ausgefüllt. Das bedeutet, dass Standardeinstellungen für alle neuen Felder in einem Paket angegeben.
Clients sollten nicht davon ausgehen, dass Server die neuen Felder verwenden, es sei denn, sie wissen, dass der Server die Version implementiert, in der das Feld definiert ist (siehe Versionen abfragen).
Enums und Konstanten
Ebenso sollten Clients und Server nicht erkannte Elemente entweder ablehnen oder ignorieren. je nach Bedarf konstante Werte und Zähler, da weitere addiert werden können. in die Zukunft zu führen. Ein Server sollte beispielsweise nicht abbrechen, wenn er einen Enumerator empfängt, den er nicht kennt. Der Server sollte den Enumerator entweder ignorieren oder etwas zurückgeben, damit der Client weiß, dass er 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. Die Implementierung sieht nie die Vereinigung mit dem neuen Feld. Bei einer Einwegtransaktion wird der Fehler ignoriert. Andernfalls ist der Fehler BAD_VALUE
(für das C++- oder NDK-Backend) oder IllegalArgumentException
(für das Java-Backend). Der Fehler wird ausgegeben, wenn der Client eine Union, die auf das neue Feld festgelegt ist, an einen alten Server sendet oder wenn ein alter Client die Union von einem neuen Server empfängt.
Mehrere Versionen verwalten
Ein Verknüpfungs-Namespace in Android kann nur 1 Version einer bestimmten aidl
haben
um Situationen zu vermeiden, in denen die generierten aidl
-Typen mehrere
Definitionen. In C++ gibt es eine Regel für eine einzige Definition, für die nur eine Definition erforderlich ist.
für jedes Symbol.
Der Android-Build gibt einen Fehler aus, wenn ein Modul auf verschiedenen
Versionen derselben aidl_interface
-Bibliothek. Das Modul kann je nach
direkt oder indirekt über Abhängigkeiten ihrer
Abhängigkeiten. Diese Fehler zeigen das Abhängigkeitsdiagramm vom fehlerhaften Modul bis
den in Konflikt stehenden Versionen der aidl_interface
-Bibliothek Alle Abhängigkeiten müssen aktualisiert werden, damit sie dieselbe (in der Regel die neueste) Version dieser Bibliotheken enthalten.
Wenn die Benutzeroberflächenbibliothek von vielen verschiedenen Modulen verwendet wird, kann es hilfreich sein, cc_defaults
, java_defaults
und rust_defaults
für jede Gruppe von Bibliotheken und Prozessen zu erstellen, die dieselbe Version verwenden müssen. Wenn eine neue Version der Benutzeroberfläche eingeführt wird, können diese Standardeinstellungen aktualisiert und alle Module, die sie verwenden, gemeinsam aktualisiert werden. So wird sichergestellt, dass nicht unterschiedliche Versionen der Benutzeroberfläche verwendet werden.
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"],
...
}
Wenn aidl_interface
-Module andere aidl_interface
-Module importieren, entstehen zusätzliche Abhängigkeiten, die erfordern, dass bestimmte Versionen zusammen verwendet werden. Dieses
kann es schwierig werden, mit der Situation umzugehen, wenn es gängige aidl_interface
Module, die in mehrere verwendete aidl_interface
-Module importiert werden
in denselben Prozessen.
Mit aidl_interfaces_defaults
kann eine Definition des
aktuelle Versionen der Abhängigkeiten für eine aidl_interface
, die in
an einem einzigen Ort. Sie werden von allen aidl_interface
-Modulen verwendet, die importiert werden sollen.
dieser gemeinsamen Schnittstelle.
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"],
...
}
Flaggenbasierte Entwicklung
In der Entwicklung befindliche (nicht eingefrorene) Schnittstellen können auf Release-Geräten nicht verwendet werden, weil dass sie nicht abwärtskompatibel sind.
AIDL unterstützt einen Laufzeit-Fallback für diese nicht eingefrorenen Schnittstellenbibliotheken, damit Code für die neueste nicht eingefrorene Version geschrieben und trotzdem auf Release-Geräten verwendet werden kann. Das rückwärtskompatible Verhalten von Clients ähnelt dem vorhandenen Verhalten. Bei der Umstellung müssen die Implementierungen auch diesen Verhaltensweisen folgen. Weitere Informationen finden Sie unter Versionierte Oberflächen verwenden.
AIDL-Build-Flag
Das Flag, das dieses Verhalten steuert, ist RELEASE_AIDL_USE_UNFROZEN
und in build/release/build_flags.bzl
definiert. true
steht für die nicht eingefrorene Version von
die Schnittstelle zur Laufzeit verwendet wird, und false
steht für die Bibliotheken des
Nicht eingefrorene Versionen verhalten sich wie ihre letzte eingefrorene Version.
Sie können das Flag für die lokale Entwicklung auf true
überschreiben, müssen es aber vor der Veröffentlichung auf false
zurücksetzen. Normalerweise
Die Entwicklung erfolgt mit einer Konfiguration, für die das Flag auf true
gesetzt ist.
Kompatibilitätsmatrix und Manifeste
Anbieterschnittstellenobjekte (VINTF-Objekte) definieren welche Versionen erwartet werden und welche Versionen auf beiden Seiten über die Benutzeroberfläche des Anbieters.
Die meisten Geräte, die nicht Cuttlefish sind, richten sich erst nach dem Einfrieren der Schnittstellen auf die neueste Kompatibilitätsmatrix aus. Daher gibt es keine Unterschiede bei den AIDL-Bibliotheken, die auf RELEASE_AIDL_USE_UNFROZEN
basieren.
Matrizen
Geräte- oder produktspezifische Benutzeroberflächen des Partners werden hinzugefügt.
Kompatibilitätsmatrix, auf die das Gerät bei der Entwicklung ausgerichtet ist. Wenn also einer Kompatibilitätsmatrix eine neue, nicht eingefrorene Version einer Benutzeroberfläche hinzugefügt wird, müssen die vorherigen eingefrorenen Versionen für RELEASE_AIDL_USE_UNFROZEN=false
erhalten bleiben. Sie können dies erreichen, indem Sie verschiedene
Kompatibilitätsmatrix-Dateien für verschiedene RELEASE_AIDL_USE_UNFROZEN
Konfigurationen oder beide Versionen in einer einzigen Kompatibilitätsmatrix-Datei zulassen
der in allen Konfigurationen verwendet wird.
Wenn Sie beispielsweise eine nicht eingefrorene 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
false
ist.
Manifeste
In Android 15 wurde eine Änderung an libvintf
eingeführt, um die Manifestdateien beim Build-Vorgang basierend auf dem Wert von RELEASE_AIDL_USE_UNFROZEN
zu ändern.
In den Manifesten und Manifestfragmenten wird angegeben, welche Version einer Benutzeroberfläche ein Dienst implementiert. Wenn Sie die neueste nicht eingefrorene Version einer Schnittstelle verwenden,
muss das Manifest entsprechend aktualisiert werden. Wenn RELEASE_AIDL_USE_UNFROZEN=false
, werden die Manifesteinträge um libvintf
angepasst, um die Änderung in der generierten AIDL-Bibliothek widerzuspiegeln. Die Version wird von der nicht eingefrorenen Version N
in die letzte eingefrorene Version N - 1
geändert. So müssen Nutzer nicht mehrere
Manifeste oder Manifest-Fragmente für jeden ihrer Dienste.
Änderungen am HAL-Client
Der HAL-Clientcode muss abwärtskompatibel mit jeder vorherigen unterstützten eingefrorenen Version sein. Wenn RELEASE_AIDL_USE_UNFROZEN
den Wert false
hat, suchen Dienste immer
z. B. die letzte eingefrorene Version oder eine frühere Version (z. B. Aufruf einer neuen, nicht fixierten Version).
gibt UNKNOWN_TRANSACTION
zurück oder neue parcelable
-Felder erhalten
Standardwerten). Android-Framework-Clients müssen mit zusätzlichen früheren Versionen abwärtskompatibel sein. Dies ist jedoch eine neue Anforderung für Anbieter-Clients und Clients von Partneroberflächen.
Änderungen bei der HAL-Implementierung
Der größte Unterschied in der HAL-Entwicklung mit Flag-basierter Entwicklung ist die
HAL-Implementierungen müssen abwärtskompatibel mit der
die eingefrorene Version funktioniert, wenn RELEASE_AIDL_USE_UNFROZEN
den Status false
hat.
Die Berücksichtigung der Abwärtskompatibilität bei Implementierungen und Gerätecode ist eine neue
Übung. Weitere Informationen finden Sie unter Versionierte Version verwenden
Benutzeroberflächen.
Die Überlegungen zur Abwärtskompatibilität sind im Allgemeinen für Clients und Server sowie für Framework-Code und Anbietercode gleich. Es gibt jedoch subtile 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. Sowohl der Client als auch der Dienst werden auf die neue Version 4 der Bibliothek aktualisiert. Da die V4-Bibliothek auf einer nicht eingefrorenen Version der Benutzeroberfläche basiert, verhält sie sich wie die letzte eingefrorene Version, Version 3, wenn RELEASE_AIDL_USE_UNFROZEN
= false
ist, und verhindert die Verwendung der neuen Methode.
Wenn die Schnittstelle fixiert ist, verwenden alle Werte von RELEASE_AIDL_USE_UNFROZEN
diese Abfrage
der eingefrorenen Version
und der Code für die Abwärtskompatibilität entfernt werden.
Wenn Sie Methoden in Callbacks aufrufen, müssen Sie den Fall, in dem UNKNOWN_TRANSACTION
zurückgegeben wird, ordnungsgemäß behandeln. Die Kundschaft könnte zwei verschiedene
Versionen eines Callbacks, die auf der Releasekonfiguration basieren,
dass der Client die neueste Version sendet, und neue Methoden
dies. Das ähnelt der Wartung von stabilen AIDL-Clients
wird im Artikel Versionierte Version verwenden
Benutzeroberflächen.
// 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
den Wert false
hat. Die Werte neuer Felder, die ein Dienst senden möchte, werden am Ende des Prozesses gelöscht.
Neue Typen, die in dieser nicht eingefrorenen Version hinzugefügt wurden, können nicht gesendet werden oder über die Benutzeroberfläche empfangen werden.
Die Implementierung erhält nie einen Aufruf für neue Methoden von Clients, wenn RELEASE_AIDL_USE_UNFROZEN
false
ist.
Achten Sie darauf, neue Zähler nur mit der Version zu verwenden, in der sie eingeführt wurden. und nicht die vorherige Version.
Normalerweise verwendest du foo->getInterfaceVersion()
, um zu sehen, welche Version der Fernbedienung
verwendet wird. Mit der Flag-basierten Versionsverwaltung
Implementierung von zwei verschiedenen Versionen. Sie sollten also die Version
der aktuellen Benutzeroberfläche. Dazu können Sie die Benutzeroberflächenversion des aktuellen Objekts abrufen, z. B. this->getInterfaceVersion()
oder die anderen Methoden für my_ver
. Weitere Informationen finden Sie unter Oberflächenversion des Remoteobjekts abfragen.
Neue stabile VINTF-Schnittstellen
Wenn ein neues AIDL-Interface-Paket hinzugefügt wird, gibt es keine letzte eingefrorene Version. Wenn RELEASE_AIDL_USE_UNFROZEN
also false
ist, kann nicht auf ein anderes Verhalten zurückgegriffen werden. Verwenden Sie diese Oberflächen nicht. Wenn RELEASE_AIDL_USE_UNFROZEN
gleich
false
, lässt Service Manager zu, dass der Dienst die Schnittstelle nicht registriert.
und die Kundschaft wird es nicht finden.
Sie können die Dienste basierend auf dem Wert des Felds
Das Flag RELEASE_AIDL_USE_UNFROZEN
im Geräte-Makefile:
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 nicht bedingt hinzufügen können, können Sie prüfen, ob der Dienst mit IServiceManager::isDeclared()
deklariert ist. Wenn es deklariert ist und nicht registriert werden konnte,
brechen Sie den Vorgang ab. Wenn es nicht deklariert ist, schlägt die Registrierung wahrscheinlich fehl.
Sepien als Entwicklungstool
Jedes Jahr, nachdem die VINTF eingefroren wurde, passen wir die Framework Compatibility Matrix (FCM) target-level
und die PRODUCT_SHIPPING_API_LEVEL
von Cuttlefish an, damit sie die Geräte widerspiegeln, die mit der Veröffentlichung des nächsten Jahres auf den Markt kommen. Wir passen die
target-level
und PRODUCT_SHIPPING_API_LEVEL
, um sicherzugehen, dass
Markteinführung eines Geräts, das getestet wurde und die neuen Anforderungen für
Veröffentlichung.
Wenn RELEASE_AIDL_USE_UNFROZEN
den Wert true
hat, ist der Sepia-Tintenfisch:
die für die Entwicklung zukünftiger Android-Versionen
verwendet werden. Sie ist auf die FCM-Ebene der Android-Version des nächsten Jahres und auf PRODUCT_SHIPPING_API_LEVEL
ausgerichtet. Daher muss sie die Softwareanforderungen des Anbieters (Vendor Software Requirements, VSR) der nächsten Version erfüllen.
Wenn RELEASE_AIDL_USE_UNFROZEN
den Wert false
hat, hat Tintenfisch den vorherigen Wert
target-level
und PRODUCT_SHIPPING_API_LEVEL
, um ein Releasegerät anzugeben.
In Android 14 und niedriger wäre diese Unterscheidung
mit verschiedenen Git-Zweigen erreicht, die die Änderung zu FCM nicht übernehmen
target-level
, Versand-API-Level oder einen anderen Code für die nächste
Veröffentlichung.
Regeln für die Benennung von Modulen
Unter Android 11 wird für jede Kombination der aktivierten Versionen und Backends 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, also 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 Version am Ende des Stammbaums (noch nicht eingefroren)
backend
ist entwederjava
für das Java-Back-Endcpp
für das C++-Back-End,ndk
oderndk_platform
für das NDK-Back-End. Ersteres ist für Apps und letzteres für die Plattformnutzung bis Android 13 vorgesehen. 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. und unterstützt sowohl NDK als auch C++. 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 Nutzungsbedingungen
foo-V3-(java|cpp|ndk|ndk_platform|rust)
Im Vergleich zu Android 11:
foo-backend
, das sich auf die neueste stabile Version bezog, wird infoo-V2-backend
geändert.foo-unstable-backend
, das sich auf die ToT-Version bezog, wird zufoo-V3-backend
.
Die Namen der Ausgabedateien stimmen immer mit den Namen der Module überein.
- 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 wird der aus einem
Die stabile AIDL-Schnittstelle enthält immer ihre Version.
Neue Metaschnittstellenmethoden
Android 10 fügt mehrere Meta-Schnittstellenmethoden für die und stabile AIDL.
Schnittstellenversion des Remote-Objekts abfragen
Clients können die Version und den Hash der Schnittstelle abfragen, die vom Remoteobjekt implementiert wird, und die zurückgegebenen Werte mit den Werten der vom Client verwendeten Schnittstelle vergleichen.
Beispiel mit dem cpp
-Backend:
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 ndk
- (und dem ndk_platform
-) Backend:
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 java
-Backend:
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();
Bei der Java-Sprache MÜSSEN getInterfaceVersion()
und getInterfaceHash()
auf der Remoteseite so implementiert werden. super
wird anstelle von IFoo
verwendet, um Fehler beim Kopieren und Einfügen zu vermeiden. Je nach javac
-Konfiguration ist die Anmerkung @SuppressWarnings("static")
möglicherweise erforderlich, 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.) freigegeben sind.
zwischen Client und Server (z. B. können sich die Klassen im Bootmodus
classpath). Wenn Klassen freigegeben werden, ist der Server auch mit dem
die neuesten Versionen der Klassen, auch wenn sie mit einer älteren Version erstellt wurden.
Version der Benutzeroberfläche. Wenn diese Meta-Benutzeroberfläche in der freigegebenen Klasse implementiert ist, wird immer die neueste Version zurückgegeben. Wenn Sie jedoch die Methode
Wie oben ist die Versionsnummer der Schnittstelle in den Code des Servers eingebettet.
(weil 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
Möglicherweise wird ein Client mit der neueren Version einer AIDL aktualisiert
aber der Server verwendet die alte AIDL-Schnittstelle. In solchen Fällen wird beim Aufrufen einer Methode über eine alte Schnittstelle UNKNOWN_TRANSACTION
zurückgegeben.
Mit der stabilen AIDL haben Clients mehr Kontrolle. Auf der Clientseite können Sie Eine Standardimplementierung für eine AIDL-Schnittstelle. Eine Methode in der Standardmethode -Implementierung wird nur aufgerufen, wenn die Methode nicht im Remote- da sie mit einer älteren Version der Benutzeroberfläche erstellt wurde. Da Standardeinstellungen global festgelegt werden, sollten sie nicht in 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 angeben. Methoden, die garantiert auf der Remote-Seite implementiert werden (weil Sie sicher sind, dass die Remote-Steuerung erstellt wurde, als die Methoden in der AIDL-Beschreibung der Schnittstelle enthalten waren), müssen in der Standardklasse impl
nicht überschrieben werden.
Vorhandene AIDL in strukturierte oder stabile AIDL konvertieren
Wenn Sie bereits eine AIDL-Schnittstelle und einen Code haben, der diese verwendet, verwenden Sie Folgendes: zum Konvertieren der Schnittstelle in eine stabile AIDL-Schnittstelle.
Identifizieren Sie alle Abhängigkeiten Ihrer Schnittstelle. Prüfen Sie für jedes Paket, von dem die Schnittstelle abhängt, ob das Paket in stabiler AIDL definiert ist. Wenn nicht definiert ist, muss das Paket konvertiert werden.
Konvertieren Sie alle Parcelable-Objekte in Ihrer Benutzeroberfläche in stabile Parcelable-Objekte. Die Benutzeroberflächedateien selbst können unverändert bleiben. Vorgehensweise und drückt ihre Struktur direkt in AIDL-Dateien aus. Verwaltungsklassen müssen neu geschrieben werden, damit diese neuen Typen verwendet werden können. Das ist auch möglich, bevor du ein
aidl_interface
-Paket erstellst (siehe unten).Erstellen Sie wie oben beschrieben ein
aidl_interface
-Paket, das die Name des Moduls, seine Abhängigkeiten und alle anderen Informationen, die Sie benötigen. Damit sie stabil und nicht nur strukturiert ist, muss sie auch versioniert werden. Weitere Informationen finden Sie unter Schnittstellen versionieren.