HAL-Schnittstelle erstellen

Sie müssen alle Build-Flags, die für die bedingte Kompilierung des Frameworks verwendet werden, mit HIDL beschreiben. Relevante Build-Flags müssen gruppiert und in einer einzigen .hal-Datei enthalten sein. Die Verwendung von HIDL zur Angabe von Konfigurationselementen bietet folgende Vorteile:

  • Versioniert (um neue Konfigurationselemente hinzuzufügen, müssen Anbieter/OEMs die HAL explizit erweitern)
  • Gut dokumentiert
  • Zugriffssteuerung mit SELinux
  • Gültigkeitsprüfung für Konfigurationselemente über die Vendor Test Suite (Bereichsprüfung, Prüfung der Abhängigkeiten zwischen Elementen usw.)
  • Automatisch generierte APIs in C++ und Java

Build-Flags identifizieren, die vom Framework verwendet werden

Ermitteln Sie zuerst die Build-Konfigurationen, die zur bedingten Kompilierung des Frameworks verwendet werden, und verwerfen Sie dann veraltete Konfigurationen, um den Satz zu verkleinern. Für surfaceflinger werden beispielsweise die folgenden 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 Angeben von Konfigurationswerten im HAL-Paket android.hardware.configstore (aktuell 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 der Benutzeroberfläche für jede Build-Flag eine neue Funktion hinzu. Beispielsweise in 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:

  • Verwenden Sie kurze Namen. Konvertieren Sie keine Namen von Makefile-Variablen in Funktionsnamen und denken Sie daran, dass die Präfixe TARGET_ und BOARD_ nicht mehr erforderlich sind.
  • Kommentare hinzufügen Hilf den Entwicklern zu verstehen, welchen Zweck das Konfigurationselement hat, wie es das Framework-Verhalten ändert, welche Werte zulässig sind und welche anderen relevanten Informationen es gibt.

Rückgabetypen von Funktionen können Optional[Bool|String|Int32|UInt32|Int64|UInt64] sein. Typen werden im selben Verzeichnis in types.hal definiert und primitive Werte werden in einem Feld verpackt, das angibt, ob der Wert von der HAL angegeben ist. Andernfalls wird der Standardwert verwendet.

struct OptionalString {
    bool specified;
    string value;
};

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

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

Eine Option zum Aggregieren von zwei Build-Flags zu einer einzigen 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 einzigen HAL-Funktion für alle Build-Flags bietet die HAL-Oberfläche 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 bereit, indem er diese Dateien liest/parst.

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

Einzelne und mehrere Schnittstellen

Für das Design der HAL-Schnittstelle für Konfigurationselemente gibt es zwei Auswahlmöglichkeiten:

  • Eine Schnittstelle, die alle Konfigurationselemente abdeckt
  • Mehrere Oberflächen, von denen jede eine Reihe zugehöriger Konfigurationselemente abdeckt

Eine einzelne Schnittstelle ist einfacher, kann jedoch unhandlich werden, da der einzelnen Datei weitere Konfigurationselemente hinzugefügt werden. Außerdem ist die Zugriffssteuerung nicht detailliert. Ein Prozess, dem Zugriff auf die Benutzeroberfläche gewährt wurde, kann also alle Konfigurationselemente lesen. Der Zugriff auf einen Teil der Konfigurationselemente kann nicht gewährt werden. Alternativ können Konfigurationselemente nicht gelesen werden, wenn der Zugriff nicht gewährt wird.

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