AIDL für HALs

Mit Android 11 wird die Möglichkeit eingeführt, AIDL für HALs in Android zu verwenden. So können Sie Android kann auch ohne HIDL implementiert werden. HALs auf AIDL umstellen ausschließlich, wo möglich (wenn vorgelagerte HALs HIDL verwenden, muss HIDL verwendet werden).

HALs, die AIDL für die Kommunikation zwischen Framework-Komponenten wie denen in system.img und Hardwarekomponenten wie die in vendor.img müssen Stabile AIDL. Um jedoch innerhalb einer Partition zu kommunizieren, z. B. von einem HAL zu einem anderen HAL führt, gibt es keine Einschränkungen für den IPC-Mechanismus.

Ziel

AIDL gibt es schon länger als HIDL und wird auch an vielen anderen Stellen verwendet, zwischen Android-Framework-Komponenten oder in Apps. Da AIDL jetzt stabil ist, unterstützen, ist es möglich, einen gesamten Stack mit einer einzigen IPC-Laufzeit zu implementieren. AIDL hat auch ein besseres Versionsverwaltungssystem als HIDL.

  • Die Verwendung einer IPC-Sprache braucht nur eine Sache, die Sie lernen müssen: optimieren und sichern.
  • AIDL unterstützt die direkte Versionsverwaltung für die Inhaber einer Schnittstelle: <ph type="x-smartling-placeholder">
      </ph>
    • Inhaber können Methoden am Ende von Schnittstellen oder Feldern zu Parcelables hinzufügen. Das bedeutet, dass es im Laufe der Jahre einfacher ist, Code zu versionieren, im Vergleich zum Vorjahr geringer sind (Typen können direkt geändert werden und es gibt keine zusätzliche Bibliotheken für jede Schnittstellenversion benötigt.)
    • Erweiterungsschnittstellen können zur Laufzeit hinzugefügt werden, nicht im Typ Daher ist es nicht notwendig, ein Rebase von Downstream-Erweiterungen auf neuere Versionen von Schnittstellen.
  • Eine vorhandene AIDL-Schnittstelle kann direkt verwendet werden, wenn der Inhaber stabilisieren Sie es. Bisher musste eine vollständige Kopie der Benutzeroberfläche die in HIDL erstellt wurden.

Build für die AIDL-Laufzeit

AIDL hat drei verschiedene Back-Ends: Java, NDK und CPP. Zur Verwendung der stabilen AIDL müssen Sie Verwenden Sie immer die Systemkopie von libbinder unter system/lib*/libbinder.so und sprechen Sie am /dev/binder. Für Code für das Anbieterbild bedeutet dies, dass libbinder (vom VNDK) kann nicht verwendet werden: Diese Bibliothek enthält eine instabile C++ API und instabilen internen Strukturen. Stattdessen muss der Code des nativen Anbieters das NDK-Backend von AIDL, Verknüpfung mit libbinder_ndk (unterstützt vom System libbinder.so), und eine Verknüpfung mit den von aidl_interface-Einträgen erstellten NDK-Bibliotheken erstellen. Für die genauen Modulnamen, Benennungsregeln für Module.

AIDL HAL-Schnittstelle schreiben

Damit eine AIDL-Schnittstelle zwischen System und Anbieter verwendet werden kann, muss sie zwei Änderungen:

  • Jede Typdefinition muss mit @VintfStability annotiert werden.
  • Die Deklaration zu aidl_interface muss stability: "vintf", enthalten.

Nur der Inhaber einer Benutzeroberfläche kann diese Änderungen vornehmen.

Wenn Sie diese Änderungen vornehmen, muss sich die Oberfläche im VINTF-Manifest konfiguriert ist. Testen Sie diese (und ähnliche) wie z. B. das Einfrieren freigegebener Schnittstellen mithilfe des VTS-Test vts_treble_vintf_vendor_test. Du kannst einen @VintfStability verwenden ohne diese Anforderungen zu nutzen, indem Sie entweder AIBinder_forceDowngradeToLocalStability im NDK-Back-End android::Stability::forceDowngradeToLocalStability im C++ Back-End, oder android.os.Binder#forceDowngradeToSystemStability im Java-Back-End an einem Binderobjekt an, bevor es an einen anderen Prozess gesendet wird. Downgrade eines Dienstes ausführen Anbieterstabilität wird in Java nicht unterstützt, da alle Anwendungen in einem System Kontext.

Darüber hinaus sorgen Sie für maximale Codeportabilität und vermeiden potenzielle Probleme wie als unnötige zusätzliche Bibliotheken, deaktivieren Sie das CPP-Back-End.

Beachten Sie, dass die Verwendung von backends im Codebeispiel unten korrekt ist, da sind drei Back-Ends (Java, NDK und CPP). Der folgende Code zeigt, wie die CPP-Back-End, um es zu deaktivieren.

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

AIDL HAL-Schnittstellen finden

AOSP Stable AIDL-Schnittstellen für HALs befinden sich in denselben Basisverzeichnissen wie HIDL-Schnittstellen in aidl Ordnern.

  • Hardware/Schnittstellen
  • Frameworks/Hardware/Schnittstellen
  • System/Hardware/Schnittstellen

Erweiterungsoberflächen in anderen hardware/interfaces Unterverzeichnisse in vendor oder hardware.

Erweiterungsoberflächen

Android hat mit jedem Release eine Reihe von offiziellen AOSP-Oberflächen. Wenn Android möchten unsere Partner Funktionen zu diesen Oberflächen hinzufügen, da dies bedeutet, dass ihre Android-Laufzeit nicht mit der Android-Laufzeit von AOSP kompatibel. Bei GMD-Geräten sollten Durch diese Oberflächen wird auch sichergestellt, dass das GSI-Image weiterhin funktioniert.

Erweiterungen können auf zwei verschiedene Arten registriert werden:

Eine Erweiterung wird jedoch registriert, wenn sie anbieterspezifisch, d. h. nicht Teil des vorgelagerte AOSP-Komponenten die Schnittstelle nutzen, ist eine Zusammenführung Konflikt. Wenn jedoch nachgelagerte Änderungen an vorgelagerten AOSP-Komponenten auftreten, können Zusammenführungskonflikte auftreten, und die folgenden Strategien werden empfohlen:

  • Die Ergänzungen der Benutzeroberfläche können im nächsten Release an AOSP übertragen werden.
  • Ergänzungen der Benutzeroberfläche, die mehr Flexibilität ohne Zusammenführungskonflikte kann in der nächsten Version vorgeschaltet werden

Erweiterungsparcelables: ParcelableHolder

ParcelableHolder ist eine Parcelable, die ein weiteres Parcelable enthalten kann. Der Hauptanwendungsfall von ParcelableHolder besteht darin, eine Parcelable erweiterbar zu machen. Beispiel: Bild, das Geräteimplementierungen für die Erweiterung eines AOSP-definierte Parcelable, AospDefinedParcelable, um ihren Mehrwert einzubeziehen Funktionen.

Bisher ohne ParcelableHolder konnten Geräte-Implementierer die Änderungen nicht ändern eine AOSP-definierte stabile AIDL-Schnittstelle, da es ein Fehler wäre, weitere Felder:

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

Wie im vorherigen Code zu sehen ist, funktioniert diese Vorgehensweise nicht, da die Felder die vom Geräte-Implementierer hinzugefügt wurden, kann zu Konflikten führen, wenn das Paket die in den nächsten Android-Versionen überarbeitet wurden.

Mit ParcelableHolder kann der Eigentümer eines Pakets eine Erweiterung definieren Punkt in einem Parcelable.

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

Dann können die Geräteimplementierungen ihre eigene Parcelable für ihre .

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

Schließlich kann die neue Parcelable mit folgendem Befehl an die ursprüngliche Parcelable angehängt werden: Das Feld ParcelableHolder.


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

AIDL HAL-Serverinstanznamen

Konventionsgemäß haben AIDL HAL-Dienste einen Instanznamen im folgenden Format: $package.$type/$instance Eine Instanz des Vibrations HAL ist beispielsweise registriert als android.hardware.vibrator.IVibrator/default.

AIDL HAL-Server schreiben

@VintfStability AIDL-Server müssen im VINTF-Manifest deklariert sein, für Beispiel wie folgt:

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

Andernfalls sollte der AIDL-Dienst normal registriert werden. Bei Ausführung von VTS Tests durchgeführt werden, sollten alle deklarierten AIDL HALs verfügbar sein.

AIDL-Client schreiben

AIDL-Clients müssen sich selbst in der Kompatibilitätsmatrix deklarieren, z. B.: wie hier:

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

Vorhandene HAL von HIDL in AIDL konvertieren

Verwenden Sie das hidl2aidl-Tool, um eine HIDL-Schnittstelle in AIDL zu konvertieren.

hidl2aidl-Funktionen:

  • .aidl-Dateien basierend auf den .hal-Dateien für das angegebene Paket erstellen
  • Build-Regeln für das neu erstellte AIDL-Paket mit allen Back-Ends erstellen aktiviert
  • Erstellen von Übersetzungsmethoden in den Java-, CPP- und NDK-Back-Ends für die Übersetzung von den HIDL-Typen bis zu den AIDL-Typen
  • Build-Regeln für Übersetzungsbibliotheken mit erforderlichen Abhängigkeiten erstellen
  • Erstellen Sie statische Assertions, um sicherzustellen, dass HIDL- und AIDL-Enumeratoren die dieselben Werte in den CPP- und NDK-Back-Ends

Führen Sie die folgenden Schritte aus, um ein Paket mit .hal-Dateien in AIDL-Dateien zu konvertieren:

  1. Erstellen Sie das Tool in system/tools/hidl/hidl2aidl.

    Die Erstellung dieses Tools aus der neuesten Quelle bietet die umfassendste Nutzererfahrung. Sie können die neueste Version verwenden, um Schnittstellen auf ältere Versionen zu konvertieren. aus früheren Releases.

    m hidl2aidl
    
  2. Führen Sie das Tool mit einem Ausgabeverzeichnis, gefolgt vom gewünschten Paket aus. Conversion durchgeführt haben.

    Optional können Sie mit dem Argument -l den Inhalt einer neuen Lizenzdatei hinzufügen. am Anfang aller generierten Dateien. Achten Sie darauf, die richtige Lizenz und das richtige Datum zu verwenden.

    hidl2aidl -o <output directory> -l <file with license> <package>
    

    Beispiel:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
    
  3. Lesen Sie sich die generierten Dateien durch und beheben Sie eventuelle Probleme mit der Konvertierung.

    • conversion.log enthält alle unbehandelten Probleme, die zuerst behoben werden müssen.
    • Die generierten .aidl-Dateien enthalten möglicherweise Warnungen und Vorschläge, die möglicherweise Maßnahme erforderlich. Diese Kommentare beginnen mit //.
    • Nutzen Sie die Gelegenheit, bereinigen und verbessern Sie das Paket.
    • Prüfen Sie die @JavaDerive. für Funktionen, die möglicherweise erforderlich sind, wie toString oder equals
  4. Erstellen Sie nur die Ziele, die Sie benötigen.

    • Deaktivieren Sie Back-Ends, die nicht verwendet werden. NDK-Backend gegenüber CPP bevorzugen finden Sie unter Laufzeit auswählen.
    • Entfernen Sie Übersetzungsbibliotheken und jeglichen generierten Code, der nicht verwendet wird.
  5. Siehe Wichtige Unterschiede zu AIDL/HIDL.

    • Die Verwendung der integrierten Status von AIDL und Ausnahmen verbessern in der Regel die und überflüssige andere schnittstellenspezifische Statustypen.
    • AIDL-Schnittstellenargumente in Methoden sind standardmäßig nicht @nullable, wie sie im HIDL.

SEPolicy für AIDL HALs

Ein für den Anbietercode sichtbarer AIDL-Diensttyp muss die Attribut „hal_service_type“. Andernfalls ist die sepolicy-Konfiguration die gleiche wie bei jedem anderen AIDL-Dienst (obwohl es spezielle Attribute für HALs gibt). Hier ist eine Beispieldefinition eines HAL-Dienstkontexts:

    type hal_foo_service, service_manager_type, hal_service_type;

Für die meisten von der Plattform definierten Dienste ist ein Dienstkontext mit dem korrekten Typ wurde bereits hinzugefügt. android.hardware.foo.IFoo/default würde zum Beispiel bereits als hal_foo_service gekennzeichnet sein). Wenn ein Framework-Client jedoch mehrere Instanznamen, müssen zusätzliche Instanznamen hinzugefügt werden. gerätespezifische service_contexts-Dateien.

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

HAL-Attribute müssen hinzugefügt werden, wenn ein neuer HAL-Typ erstellt wird. Ein bestimmter HAL kann mehreren Dienstleistungstypen zugeordnet sein (jeweils wie wir gerade besprochen haben. Für einen HAL, foo, haben wir hal_attribute(foo). Mit diesem Makro werden die Attribute hal_foo_client und hal_foo_server. Für eine bestimmte Domain werden die hal_client_domain und hal_server_domain-Makros verknüpfen eine Domain mit einem bestimmten HAL-Attribut. Für Beispiel: Der Systemserver, der ein Client dieses HAL ist, entspricht der Richtlinie hal_client_domain(system_server, hal_foo) Ein HAL-Server umfasst ähnlich hal_server_domain(my_hal_domain, hal_foo) Normalerweise wird für einen bestimmten HAL erstellen wir auch eine Domain wie hal_foo_default als Referenz oder Beispiel-HALs. Einige Geräte verwenden diese Domains jedoch für ihre eigenen Server. Die Unterscheidung zwischen Domains für mehrere Server ist nur wichtig, wenn Auf mehreren Servern, die dieselbe Schnittstelle bedienen und unterschiedliche Berechtigungen benötigen Implementierung festgelegt. In all diesen Makros ist hal_foo nicht wirklich ein sepolicy-Objekt. Stattdessen wird dieses Token von diesen Makros verwendet, um auf Die Gruppe von Attributen, die einem Client-Server-Paar zugeordnet sind.

Bisher haben wir hal_foo_service und hal_foo jedoch noch nicht verknüpft. (Attributpaar aus hal_attribute(foo)). Ein HAL-Attribut wird mit AIDL HAL-Diensten unter Verwendung des Makros hal_attribute_service (HIDL HALs verwenden hal_attribute_hwservice-Makro einfügen. Beispiel: hal_attribute_service(hal_foo, hal_foo_service) Das bedeutet, dass hal_foo_client-Prozesse können den HAL abrufen und hal_foo_server Prozesse können den HAL registrieren. Die Durchsetzung dieser Registrierungsregeln vom Kontextmanager (servicemanager) ausgeführt. Beachten Sie, dass Dienstnamen nicht immer HAL-Attributen entsprechen. Zum Beispiel könnten wir hal_attribute_service(hal_foo, hal_foo2_service) Generell gilt jedoch, bedeutet dies, dass die Dienste immer zusammen genutzt werden. Wir könnten überlegen, hal_foo2_service und die Nutzung von hal_foo_service für alle unsere Dienste Kontexte. Die meisten HALs, die mehrere hal_attribute_service festlegen, sind aus folgenden Gründen: Der ursprüngliche HAL-Attributname ist nicht allgemein genug und kann nicht geändert werden.

Zusammenfassend sieht ein HAL-Beispiel wie folgt aus:

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

Angehängte Erweiterungsoberflächen

Eine Erweiterung kann an jede Binder-Oberfläche angehängt werden, unabhängig davon, ob es sich um eine oberste Ebene handelt direkt beim Dienstmanager registriert oder eine Subschnittstelle. Wenn Sie eine Erweiterung erhalten, müssen Sie bestätigen, dass der Erweiterungstyp zu erwarten war. Erweiterungen können nur über den Prozess festgelegt werden, in dem ein Binder bereitgestellt wird.

Angehängte Erweiterungen sollten immer dann verwendet werden, wenn eine Erweiterung die eines vorhandenen HAL. Wenn völlig neue Funktionen benötigt werden, Dieser Mechanismus muss nicht verwendet werden und eine Erweiterungsoberfläche die direkt beim Dienstmanager registriert sind. Angehängte Erweiterungsoberflächen ist am sinnvollsten, wenn sie an Subschnittstellen angehängt sind. Hierarchien können tief oder mehrere Instanzen sein. Globale Erweiterung zum Spiegeln verwenden würde die Hierarchie der Binder-Schnittstelle eines anderen Dienstes Buchführung, um eine gleichwertige Funktionalität wie direkt angehängte Erweiterungen zur Verfügung zu stellen.

Verwenden Sie die folgenden APIs, um eine Erweiterung für Binder festzulegen:

  • Im NDK-Back-End: AIBinder_setExtension
  • Im Java-Back-End: android.os.Binder.setExtension
  • Im CPP-Back-End: android::Binder::setExtension
  • Im Rust-Back-End: binder::Binder::set_extension

Verwenden Sie die folgenden APIs, um eine Erweiterung für einen Binder zu erhalten:

  • Im NDK-Back-End: AIBinder_getExtension
  • Im Java-Back-End: android.os.IBinder.getExtension
  • Im CPP-Back-End: android::IBinder::getExtension
  • Im Rust-Back-End: binder::Binder::get_extension

Weitere Informationen zu diesen APIs finden Sie in der Dokumentation der getExtension im entsprechenden Back-End. Ein Beispiel für die Verwendung finden Sie unter Hardware/Schnittstellen/Tests/Erweiterungen/Vibration

Wesentliche AIDL- und HIDL-Unterschiede

Beachten Sie bei der Verwendung von AIDL HALs oder AIDL HAL-Schnittstellen die Unterschiede im Vergleich zum Schreiben von HIDL HALs.

  • Die Syntax der AIDL-Sprache kommt der Java-Syntax näher. Die HIDL-Syntax ähnelt der C++-Syntax.
  • Alle AIDL-Schnittstellen haben integrierte Fehlerstatus. Anstelle von benutzerdefinierten erstellen Sie konstante Status-intts in Benutzeroberflächendateien und verwenden EX_SERVICE_SPECIFIC in den CPP/NDK-Back-Ends und ServiceSpecificException im Java-Back-End. Siehe Fehler Handhabung.
  • AIDL startet Threadpools nicht automatisch, wenn Binderobjekte gesendet werden. Sie müssen manuell gestartet werden (siehe Thread Verwaltung).
  • AIDL bricht bei ungeprüften Transportfehlern nicht ab (HIDL Return bricht ab am ungeprüften Fehlern).
  • AIDL kann nur einen Typ pro Datei deklarieren.
  • AIDL-Argumente können zusätzlich zur Ausgabe als Ein-/Ausgang/In-Out angegeben werden -Parameter (es gibt keine "synchronen Callbacks").
  • AIDL verwendet „fd“ anstelle von „Handle“ als primitiven Typ.
  • HIDL verwendet Hauptversionen für inkompatible Änderungen und Nebenversionen für kompatible Änderungen vornehmen. In AIDL werden abwärtskompatible Änderungen direkt vorgenommen. AIDL hat kein explizites Konzept von Hauptversionen. Stattdessen ist dies in Paketnamen integriert werden. AIDL könnte beispielsweise den Paketnamen bluetooth2
  • AIDL übernimmt nicht standardmäßig die Echtzeitpriorität. Das setInheritRt muss pro Binder verwendet werden, um die Prioritätsübernahme in Echtzeit zu ermöglichen.