Stabile AIDL

Android 10 unterstützt die stabile Android-Oberfläche Definition Language (AIDL), eine neue Möglichkeit, den Überblick über das Anwendungsprogramm zu behalten Interface (API) und Binärschnittstelle (Application binary Interface, ABI) von AIDL Schnittstellen. Stabile AIDL funktioniert genauso wie AIDL, aber das Build-System verfolgt die Kompatibilität der Benutzeroberfläche. Außerdem gibt es Einschränkungen im Hinblick darauf, was Sie tun können:

  • Schnittstellen werden im Build-System mit aidl_interfaces definiert.
  • Schnittstellen können nur strukturierte Daten enthalten. Parcelables, die für die bevorzugte Typen automatisch anhand ihrer AIDL-Definition und automatisch marschiert und unmarshalliert.
  • 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 ein AIDL-Schnittstelle.
  • srcs: Die Liste der AIDL-Quelldateien, aus denen die Schnittstelle besteht. Der Pfad Für einen in einem Paket definierten AIDL-Typ Foo muss sich com.acme hier befinden: <base_path>/com/acme/Foo.aidl, wobei <base_path> ein beliebiges Verzeichnis sein kann zu dem Verzeichnis, in dem sich Android.bp befindet. Im vorherigen Beispiel <base_path> ist srcs/aidl.
  • local_include_dir: Pfad, von dem aus der Paketname beginnt. Es entspricht dem oben erläuterten <base_path>.
  • imports: Eine Liste von aidl_interface-Modulen, die verwendet werden. Wenn eines Ihrer AIDL-Schnittstellen verwenden eine Schnittstelle oder ein Paket von einer anderen aidl_interface, gib hier den Namen ein. 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 früheren Versionen der Benutzeroberfläche, die unter api_dir eingefroren, ab Android 11 Die versions sind unter aidl_api/name fixiert. 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 mit Android 13 eingeführt. und nicht direkt in Android.bp geändert werden soll. Das Feld ist durch Aufrufen von *-update-api oder *-freeze-api hinzugefügt oder aktualisiert wurde. 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 das Stabilitätversprechen dieser Schnittstelle. Dies unterstützt nur "vintf". Wenn stability nicht festgelegt ist, wird der Build System prüft, ob die Schnittstelle abwärtskompatibel ist, es sei denn, unstable ist angegeben. Wenn die Festlegung nicht festgelegt ist, entspricht dies einer Schnittstelle mit Stabilität innerhalb dieses Kompilierungskontexts (also entweder alle Systemelemente, z. B. Dinge in system.img und die zugehörigen Partitionen oder alle Anbieter Dinge, z. B. Dinge in vendor.img und zugehörige Partitionen). Wenn stability ist auf "vintf" gesetzt, was einem Stabilitätsversprechen entspricht: muss die Schnittstelle während ihrer Verwendung stabil gehalten werden.
  • gen_trace: Das optionale Flag zum Aktivieren oder Deaktivieren des Tracings. Beginnt in Android 14 ist die Standardeinstellung true für cpp und java Back-Ends.
  • host_supported: Das optionale Flag, das bei Einstellung auf true den Bibliotheken generiert, die für die Hostumgebung zur Verfügung stehen.
  • 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: Das optionale Flag, das bei Einstellung auf true bedeutet, dass die Schnittstelle keine Änderungen seit der vorherigen Version der Benutzeroberfläche. Dadurch können Sie mehr Überprüfungen während der Build-Erstellung. Wenn false festgelegt ist, befindet sich die Schnittstelle in und neue Änderungen enthält, sodass das Ausführen von foo-freeze-api ein neue Version hinzu und ändern Sie den Wert automatisch in true. Vorgestellt in Android 14
  • 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 aktiviert ist standardmäßig aktiviert. Wenn eines dieser drei Back-Ends nicht benötigt wird, explizit deaktiviert werden. Rust ist bis Android standardmäßig deaktiviert 15.
  • 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 zur Transaktion generieren.
  • backend.[cpp|java].vndk.enabled: Das optionale Flag zum Erstellen dieser Schnittstelle. Teil des VNDK. Der Standardwert ist false.
  • backend.[cpp|ndk].additional_shared_libraries: Vorgestellt in Unter Android 14 fügt dieses Flag Abhängigkeiten zum native Bibliotheken. 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. Die Standardeinstellung 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 der Versionen und aktivierten Back-Ends wird ein Stub Bibliothek erstellt wird. So verweisen Sie auf die spezifische Version der Stub-Bibliothek für ein bestimmtes Back-End finden Sie unter Benennungsregeln für Module.

AIDL-Dateien schreiben

Schnittstellen in der stabilen AIDL ähneln herkömmlichen Schnittstellen, wobei die Sie dürfen keine unstrukturierten Parcelables verwenden, diese sind nicht stabil! Siehe Strukturiert im Vergleich zu stabil 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 sind 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, 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

Versionsverwaltung von Schnittstellen

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. Nach dem Erstellen: foo-Free-api fügt unter api_dir eine neue API-Definition hinzu oder aidl_api/name, je nach Android-Version und fügt eine .hash-Datei hinzu, die beide die neu fixierte Version des . Mit foo-Free-api wird auch die Eigenschaft versions_with_info aktualisiert. 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 die neueste stabile Version ist in imports in versions_with_info für die für die es keine explizite Version gibt. Nachdem das Attribut versions_with_info angegeben wurde, wird das Build-System ausgeführt. Kompatibilitätsprüfungen zwischen eingefrorenen Versionen und zwischen Top of Tree (ToT) und die neueste eingefrorene Version.

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 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 Attribut) -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 eine Schnittstelle ändern. (Andernfalls riskieren sie einen Konflikt mit Änderungen, die der Inhaber vornimmt.)

Um zu testen, ob alle Schnittstellen für die Veröffentlichung eingefroren sind, können Sie einen Build mit dem folgende Umgebungsvariablen festgelegt:

  • AIDL_FROZEN_REL=true m ... – für den Build sind alle stabilen AIDL-Schnittstellen erforderlich, um für die kein owner:-Feld angegeben ist.
  • AIDL_FROZEN_OWNERS="aosp test" – für den Build sind alle stabilen AIDL-Schnittstellen erforderlich wird mit dem owner:-Feld als "aosp" fixiert oder „Test“.

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

Zur Laufzeit erhalten neue Clients, wenn Sie versuchen, neue Methoden auf einem alten Server aufzurufen, je nach Back-End entweder einen Fehler oder eine Ausnahme.

  • cpp-Back-End erhält ::android::UNKNOWN_TRANSACTION.
  • ndk-Back-End 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 erwarten, dass Server die neuen Felder verwenden, es sei denn, sie kennen der Server die Version implementiert, für die das Feld definiert ist (siehe Abfragen von Versionen) ausführen.

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 z. B. nicht abgeschaltet werden, wenn er eine den es nicht kennt. Der Server sollte das Feld oder etwas zurückgeben, damit der Client weiß, dass dies nicht dieser Implementierung.

Gewerkschaften

Der Versuch, eine Union mit einem neuen Feld zu senden, schlägt fehl, wenn der Empfänger alt und auf diesem Gebiet nicht weiß. Bei der Implementierung wird die Vereinigung mit in das neue Feld ein. Der Fehler wird ignoriert, wenn es sich um One-Way-Transaktion Andernfalls ist der Fehler BAD_VALUE(für die C++- oder NDK- Back-End) oder IllegalArgumentException(für das Java-Back-End). Der Fehler lautet empfangen, wenn der Client einen Union-Set-Wert für das neue Feld an ein altes oder wenn es sich um einen alten Client handelt, der 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 enthalten dieser Bibliotheken.

Wenn die Interface-Bibliothek von vielen verschiedenen Modulen verwendet wird, um cc_defaults, java_defaults und rust_defaults für eine beliebige Gruppe von Bibliotheken und Prozesse, die dieselbe Version verwenden müssen. Bei der Einführung eines neue Version der Benutzeroberfläche. Diese Standardeinstellungen können aktualisiert werden und alle Module werden gemeinsam aktualisiert, sodass keine unterschiedlichen Versionen der Benutzeroberfläche.

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, wird dadurch Abhängigkeiten, die die gemeinsame Verwendung bestimmter Versionen erfordern. 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"],
  ...
}

Flag-basierte 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 das Laufzeit-Fallback für diese nicht eingefrorenen Schnittstellenbibliotheken der Reihe nach damit Code für die neueste nicht eingefrorene Version geschrieben und weiterhin verwendet wird. auf Release-Geräten. Das abwärtskompatible Verhalten von Clients ähnelt und mit dem Fallback müssen die Implementierungen für diese Verhaltensweisen. Weitere Informationen finden Sie unter Versionierte Schnittstellen verwenden

AIDL-Build-Flag

Das Flag, das dieses Verhalten steuert, ist RELEASE_AIDL_USE_UNFROZEN definiert in build/release/build_flags.bzl. true steht für die nicht eingefrorene Version von Die Schnittstelle wird zur Laufzeit verwendet und false steht für die Bibliotheken des Nicht eingefrorene Versionen verhalten sich wie ihre letzte eingefrorene Version. Sie können das Flag mit true überschreiben für lokale Entwicklung, muss aber vor der Veröffentlichung auf false zurückgesetzt werden. 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 Sepia sind, richten sich nach der neuesten Kompatibilitätsmatrix nachdem Schnittstellen eingefroren sind, gibt es also keinen Unterschied in der AIDL Bibliotheken basierend auf RELEASE_AIDL_USE_UNFROZEN.

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 ein einer Kompatibilitätsmatrix eine neue, nicht eingefrorene Version müssen die vorherigen eingefrorenen Versionen RELEASE_AIDL_USE_UNFROZEN=false 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 fixierte Version 4 hinzufügen, verwenden Sie <version>3-4</version>.

Wenn Version 4 eingefroren ist, kannst du Version 3 aus der Kompatibilitätsmatrix entfernen da die eingefrorene Version 4 verwendet wird, wenn RELEASE_AIDL_USE_UNFROZEN false.

Manifeste

In Android 15 wird eine Änderung der libvintf eingeführt für Manifestdateien zum Zeitpunkt der Erstellung basierend auf dem Wert RELEASE_AIDL_USE_UNFROZEN.

Die Manifeste und Manifestfragmente geben an, welche Version einer Schnittstelle die ein Dienst implementiert. Wenn Sie die neueste nicht eingefrorene Version einer Schnittstelle verwenden, muss das Manifest entsprechend aktualisiert werden. Wann? RELEASE_AIDL_USE_UNFROZEN=false werden die Manifesteinträge angepasst durch libvintf, um die Änderung an der generierten AIDL-Bibliothek widerzuspiegeln. Die Version wird von der nicht eingefrorenen Version N in der letzten eingefrorenen Version N - 1. So müssen Nutzer nicht mehrere Manifeste oder Manifest-Fragmente für jeden ihrer Dienste.

Änderungen am HAL-Client

HAL-Client-Code muss mit jedem zuvor unterstützten eingefrorenen Gerät abwärtskompatibel sein. Version. 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 rückwärts mit früheren Versionen kompatibel, aber das ist ein neues Detail von Anbietern und von partnereigenen Benutzeroberflächen.

Änderungen an 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 für die sowie für Framework-Code und Anbietercode. kleine Unterschiede, die Sie kennen müssen, da Sie jetzt Implementierung von zwei Versionen, die den gleichen Quellcode verwenden (die aktuelle, nicht eingefrorene Version).

Beispiel: Eine Schnittstelle hat drei eingefrorene Versionen. Die Benutzeroberfläche wird mit einem neue Methode. Der Client und der Dienst wurden auf die neue Version 4 aktualisiert. Bibliothek. Da die V4-Bibliothek auf einer nicht eingefrorenen Version des verhält es sich wie die letzte eingefrorene Version, Version 3, RELEASE_AIDL_USE_UNFROZEN ist false und verhindert die Verwendung der neuen Methode.

Wenn die Schnittstelle fixiert ist, verwenden alle Werte von RELEASE_AIDL_USE_UNFROZEN diese Abfrage die eingefrorene Version und der Code für die Abwärtskompatibilität entfernt werden.

Wenn Sie Methoden für Rückrufe aufrufen, müssen Sie den Fall UNKNOWN_TRANSACTION wird zurückgegeben. 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) nicht vorhanden sind oder ihre Standardwerte enthalten, wenn RELEASE_AIDL_USE_UNFROZEN gleich false und die Werte neuer Felder, die ein Dienst zu senden versucht, werden entfernt den Weg aus dem Prozess zu beseitigen.

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 niemals einen Aufruf für neue Methoden von Clients, wenn RELEASE_AIDL_USE_UNFROZEN ist false.

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. Rufen Sie dazu die Schnittstellenversion der aktuelles Objekt, z. B. this->getInterfaceVersion() oder das andere Methoden für my_ver. Siehe Oberflächenversion der Fernbedienung abfragen Objekt .

Neue stabile VINTF-Oberflächen

Wenn ein neues AIDL-Schnittstellenpaket hinzugefügt wird, gibt es keine letzte eingefrorene Version. Es gibt kein Verhalten für einen Fallback, wenn RELEASE_AIDL_USE_UNFROZEN false. 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, sodass er dem Gerät nicht hinzugefügt werden kann können Sie prüfen, ob der Dienst mit IServiceManager::isDeclared() 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 ist, passen wir die Framework-Kompatibilität an Matrix (FCM) target-level und PRODUCT_SHIPPING_API_LEVEL von Sepien also für Geräte, die nächstes Jahr 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. Es ist auf Android-Smartphones des FCM-Levels und PRODUCT_SHIPPING_API_LEVEL des Release, sodass es die Vendor Software requirements (VSR) des nächsten Release.

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

In Android 11 wird für jede Kombination aus Version und aktiviert sind, wird automatisch ein Stub-Bibliotheksmodul erstellt. Empfehlen mit einem bestimmten Stub-Bibliotheksmodul verknüpfen, verwenden Sie nicht den Namen der aidl_interface-Modul, aber der Name des Stub-Bibliotheksmoduls, ifacename, version, backend, wobei

  • ifacename: Name des Moduls aidl_interface
  • version ist entweder <ph type="x-smartling-placeholder">
      </ph>
    • Vversion-number für die eingefrorenen Versionen
    • Vlatest-frozen-version-number + 1 für den Tip-of-Tree-Version (noch nicht gefroren)
  • backend ist entweder <ph type="x-smartling-placeholder">
      </ph>
    • 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 gilt für Apps und der Letzteres für die Plattformnutzung bis Android 13. In 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 <ph type="x-smartling-placeholder">
      </ph>
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • Basierend auf Version 2 (der neuesten stabilen Version) <ph type="x-smartling-placeholder">
      </ph>
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • Basierend auf der Version der Nutzungsbedingungen <ph type="x-smartling-placeholder">
      </ph>
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

Im Vergleich zu Android 11:

  • foo-backend (siehe neueste stabile Version) Version zu foo-V2-backend
  • foo-unstable-backend (bezeichnet die Nutzungsbedingungen) Version zu foo-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 oder 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 das Remoteobjekt implementiert die zurückgegebenen Werte und vergleicht sie mit den Werten der -Schnittstelle. die der Client nutzt.

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 Remoteseite getInterfaceVersion() und getInterfaceHash() wie folgt (super wird anstelle von IFoo verwendet, um beim Kopieren und Einfügen. Die Anmerkung @SuppressWarnings("static") kann werden zum Deaktivieren von Warnungen benötigt (je nach javac-Konfiguration):

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 dieses Meta-Interface im gemeinsam verwendeten gibt es immer die neueste Version zurück. 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 Das Aufrufen einer Methode auf einer alten Schnittstelle gibt UNKNOWN_TRANSACTION zurück.

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. Seit sind die Standardeinstellungen global und sollten nicht für potenziell freigegebene Kontexte.

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 bereitstellen . Methoden, die garantiert auf der Remoteseite implementiert werden da Sie sicher sind, dass die Remote-Version erstellt wurde, als die Methoden in der AIDL-Schnittstellenbeschreibung) müssen im Standard-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. Für jedes Paket: ob das Paket in der stabilen AIDL definiert ist. Wenn nicht definiert ist, muss das Paket konvertiert werden.

  2. Konvertieren Sie alle Parcelables in Ihrer Schnittstelle in stabile Parcelables (das Benutzeroberflächendateien selbst nicht verändert werden können). Vorgehensweise und drückt ihre Struktur direkt in AIDL-Dateien aus. Verwaltungsklassen müssen um diese neuen Typen zu verwenden. Das können Sie tun, bevor Sie ein aidl_interface-Paket (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.