Stabile AIDL

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-Typ Foo, der in einem Paket com.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 sich Android.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 verwendeten aidl_interface-Module. Wenn eine Ihrer AIDL-Schnittstellen eine Schnittstelle oder ein Parcelable aus einer anderen aidl_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 unter api_dir eingefroren sind. Ab Android 11 sind die versions unter aidl_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 durch versions_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 befindet aidl_api/IFACE/V Dieses Feld wurde in Android 13 eingeführt und sollte nicht direkt in Android.bp geändert werden. Das Feld wird durch Aufrufen von *-update-api oder *-freeze-api hinzugefügt oder aktualisiert. Außerdem werden versions-Felder automatisch zu versions_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". Wenn stability nicht festgelegt ist, prüft das Buildsystem, ob die Benutzeroberfläche abwärtskompatibel ist, sofern unstable nicht angegeben ist. Wenn „unset“ festgelegt ist, entspricht dies einer stabilen Schnittstelle in diesem Kompilierungskontext (d. h. entweder alle Systemelemente, z. B. Elemente in system.img und zugehörige Partitionen, oder alle Anbieterelemente, z. B. Elemente in vendor.img und zugehörige Partitionen). Wenn stability 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 Standardwert true für die cpp- und java-Backends.
  • host_supported: Optionales Flag, das die generierten Bibliotheken für die Hostumgebung verfügbar macht, wenn es auf true gesetzt ist.
  • unstable: Das optionale Flag, das verwendet wird, um zu kennzeichnen, dass diese Schnittstelle nicht müssen stabil sein. Wenn dieser Wert auf true 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 auf true gesetzt ist, hat sich die Benutzeroberfläche seit der vorherigen Version nicht geändert. Dadurch sind mehr Buildzeitprüfungen möglich. Wenn der Wert auf false festgelegt ist, befindet sich die Benutzeroberfläche in der Entwicklungsphase und es gibt neue Änderungen. Wenn Sie foo-freeze-api ausführen, wird eine neue Version generiert und der Wert wird automatisch in true 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 ist false.
  • 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ür ndk_header und cpp_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, wenn backend.java.platform_apis ist true.
  • backend.java.platform_apis: Das optionale Flag, das auf true, 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 kein owner:-Feld angegeben ist.
  • AIDL_FROZEN_OWNERS="aosp test" – Für den Build müssen alle stabilen AIDL-Schnittstellen eingefroren sein und das Feld owner: 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ält STATUS_UNKNOWN_TRANSACTION.
  • Das java-Backend erhält android.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 Moduls aidl_interface
  • version ist entweder
    • Vversion-number für die eingefrorenen Versionen
    • Vlatest-frozen-version-number + 1 für die Version am Ende des Stammbaums (noch nicht eingefroren)
  • backend ist entweder
    • java für das Java-Back-End
    • cpp für das C++-Back-End,
    • ndk oder ndk_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 nur ndk.
    • 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 in foo-V2-backend geändert.
  • foo-unstable-backend, das sich auf die ToT-Version bezog, wird zu foo-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.

  1. 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.

  2. 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).

  3. 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.