HAL-Schnittstelle erstellen

Sie müssen HIDL verwenden, um alle Build-Flags zu beschreiben, die zum bedingten Kompilieren des Frameworks verwendet werden. Relevante Build-Flags müssen gruppiert und in einer einzigen .hal-Datei enthalten sein. Die Verwendung von HIDL zum Angeben von Konfigurationselementen bietet die folgenden Vorteile:

  • Mit Versionsangabe (zum Hinzufügen neuer Konfigurationselemente müssen Anbieter/OEMs das HAL explizit erweitern)
  • Gut dokumentiert
  • Zugriffssteuerung mit SELinux
  • Plausibilitätsprüfung von Konfigurationselementen über die Vendor Test Suite (Bereichsprüfung, Prüfung der gegenseitigen Abhängigkeit zwischen Elementen usw.)
  • Automatisch generierte APIs in C++ und Java

Build-Flags des Frameworks ermitteln

Identifizieren Sie zuerst die Build-Konfigurationen, die zum bedingten Kompilieren des Frameworks verwendet werden, und entfernen Sie dann veraltete Konfigurationen, um die Menge zu verkleinern. Für surfaceflinger wird beispielsweise die folgende Gruppe von Build-Flags identifiziert:

  • TARGET_USES_HWC2
  • TARGET_BOARD_PLATFORM
  • TARGET_DISABLE_TRIPLE_BUFFERING
  • TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS
  • NUM_FRAMEBUFFER_SURFACE_BUFFERS
  • TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK
  • VSYNC_EVENT_PHASE_OFFSET_NS
  • SF_VSYNC_EVENT_PHASE_OFFSET_NS
  • PRESENT_TIME_OFFSET_FROM_VSYNC_NS
  • MAX_VIRTUAL_DISPLAY_DIMENSION

HAL-Schnittstelle erstellen

Auf Build-Konfigurationen für ein Subsystem wird über eine HAL-Schnittstelle zugegriffen, während Schnittstellen zum Bereitstellen von Konfigurationswerten im HAL-Paket android.hardware.configstore (derzeit Version 1.0) gruppiert sind. So erstellen Sie beispielsweise eine HAL-Schnittstellendatei für surfaceflinger in hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

package android.hardware.configstore@1.0;

interface ISurfaceFlingerConfigs {
    // TO-BE-FILLED-BELOW
};

Führen Sie nach dem Erstellen der Datei .hal den Befehl hardware/interfaces/update-makefiles.sh aus, um die neue Datei .hal den Dateien Android.bp und Android.mk hinzuzufügen.

Funktionen für Build-Flags hinzufügen

Fügen Sie für jedes Build-Flag eine neue Funktion in die Schnittstelle ein. Beispiel für hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

interface ISurfaceFlingerConfigs {
    disableTripleBuffering() generates(OptionalBool ret);
    forceHwcForVirtualDisplays() generates(OptionalBool ret);
    enum NumBuffers: uint8_t {
        USE_DEFAULT = 0,
        TWO = 2,
        THREE = 3,
    };
    numFramebufferSurfaceBuffers() generates(NumBuffers ret);
    runWithoutSyncFramework() generates(OptionalBool ret);
    vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret);
    presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret);
    maxVirtualDisplayDimension() generates(OptionalInt32 ret);
};

Beim Hinzufügen einer Funktion:

  • Namen kurz fassen. Vermeiden Sie es, Makefile-Variablennamen in Funktionsnamen zu konvertieren, und denken Sie daran, dass die Präfixe TARGET_ und BOARD_ nicht mehr erforderlich sind.
  • Kommentare hinzufügen: Helfen Sie Entwicklern, den Zweck des Konfigurationselements, die Auswirkungen auf das Framework-Verhalten, gültige Werte und andere relevante Informationen zu verstehen.

Funktionsrückgabetypen können Optional[Bool|String|Int32|UInt32|Int64|UInt64] sein. Typen werden in types.hal im selben Verzeichnis definiert und umschließen primitive Werte mit einem Feld, das angibt, ob der Wert vom HAL angegeben wird. Andernfalls wird der Standardwert verwendet.

struct OptionalString {
    bool specified;
    string value;
};

Definieren Sie gegebenenfalls das Enum, das den Typ des Konfigurationselements am besten repräsentiert, und verwenden Sie dieses Enum als Rückgabetyp. Im obigen Beispiel wird das Enum NumBuffers definiert, um die Anzahl der gültigen Werte zu begrenzen. Fügen Sie beim Definieren solcher benutzerdefinierten Datentypen ein Feld oder einen Enum-Wert (z. B. USE_DEFAULT) hinzu, um anzugeben, ob der Wert vom HAL angegeben wird.

Es ist nicht zwingend erforderlich, dass ein einzelnes Build-Flag zu einer einzelnen Funktion in HIDL wird. Modulinhaber können alternativ eng verwandte Build-Flags in einem Struct zusammenfassen und eine Funktion haben, die dieses Struct zurückgibt. Dadurch kann die Anzahl der Funktionsaufrufe reduziert werden.

Eine Option zum Aggregieren von zwei Build-Flags in einer einzelnen Struktur in hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal ist beispielsweise:

 interface ISurfaceFlingerConfigs {
    // other functions here
    struct SyncConfigs {
        OptionalInt64 vsyncEventPhaseoffsetNs;
        OptionalInt64 presentTimeoffsetFromSyncNs;
    };
    getSyncConfigs() generates (SyncConfigs ret);
    // other functions here
};

Alternativen zu einer einzelnen HAL-Funktion

Als Alternative zur Verwendung einer einzelnen HAL-Funktion für alle Build-Flags bietet die HAL-Schnittstelle auch einfache Funktionen wie getBoolean(string key) und getInteger(string key). Die tatsächlichen key=value-Paare werden in separaten Dateien gespeichert und der HAL-Dienst stellt Werte durch Lesen/Parsen dieser Dateien bereit.

Dieser Ansatz ist zwar einfach zu definieren, bietet aber nicht die Vorteile von HIDL (erzwungene Versionsverwaltung, einfache Dokumentation, Zugriffssteuerung) und wird daher nicht empfohlen.

Einzelne und mehrere Schnittstellen

Für die Gestaltung der HAL-Schnittstelle für Konfigurationselemente gibt es zwei Möglichkeiten:

  • Eine einzige Oberfläche für alle Konfigurationselemente
  • Mehrere Schnittstellen, die jeweils eine Reihe zugehöriger Konfigurationselemente abdecken

Eine einzelne Schnittstelle ist einfacher, kann aber schwer zu verwalten sein, wenn der einzelnen Datei immer mehr Konfigurationselemente hinzugefügt werden. Außerdem ist die Zugriffssteuerung nicht detailliert. Ein Prozess, dem Zugriff auf die Schnittstelle gewährt wird, kann alle Konfigurationselemente lesen. Der Zugriff auf eine Teilmenge von Konfigurationselementen kann nicht gewährt werden. Wenn der Zugriff nicht gewährt wird, können Konfigurationselemente nicht gelesen werden.

Aus diesen Gründen verwendet Android mehrere Schnittstellen mit einer einzelnen HAL-Schnittstelle für eine Gruppe verwandter Konfigurationselemente. Beispiel: ISurfaceflingerConfigs für surfaceflinger-bezogene Konfigurationselemente und IBluetoothConfigs für Bluetooth-bezogene Konfigurationselemente.