Datentypen

In diesem Abschnitt werden HIDL-Datentypen beschrieben. Implementierungsdetails: HIDL C++ (für C++ Implementierungen) oder HIDL Java (für Java-Implementierungen).

Zu den Gemeinsamkeiten mit C++ gehören:

  • structs verwendet die C++-Syntax. unions unterstützen die C++-Syntax ist standardmäßig aktiviert. Beide müssen benannt werden: anonyme Structs und Unions werden nicht unterstützt.
  • Typedefs sind in HIDL zulässig (wie in C++).
  • Kommentare im C++-Stil sind zulässig und werden in die generierte Headerdatei kopiert.

Zu den Ähnlichkeiten mit Java gehören:

  • Für jede Datei definiert HIDL einen Namespace im Java-Stil, der mit android.hardware. Der generierte C++-Namespace ist ::android::hardware::…
  • Alle Definitionen der Datei sind in einem Java-Stil enthalten. interface-Wrapper.
  • HIDL-Array-Deklarationen folgen dem Java-Stil, nicht dem C++-Stil. Beispiel:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • Kommentare ähneln dem Javadoc-Format.

Datendarstellung

Ein struct oder union bestehend aus Standard-Layout (eine Teilmenge der Anforderung an „einfache alte Datentypen“) hat einen konsistenten Arbeitsspeicher Layout im generierten C++-Code, erzwungen mit expliziten Ausrichtungsattributen auf struct und union Mitglieder.

Einfache HIDL-Typen sowie enum und bitfield -Typen (die immer aus primitiven Typen abgeleitet werden) werden den standardmäßigen C++-Typen zugeordnet. wie z. B. std::uint32_t von cstdint.

Da Java keine vorzeichenlosen Typen unterstützt, werden unsignierte HIDL-Typen den entsprechenden signierten Java-Typ. Structs sind Java-Klassen zugeordnet. arrays sind Java-Arrays zugeordnet. unions werden derzeit nicht unterstützt in Java. Strings werden intern als UTF8 gespeichert. Da Java die werden nur UTF16-Strings und Stringwerte, die an eine oder von einer Java-Implementierung gesendet werden, übersetzt und sind möglicherweise bei einer erneuten Übersetzung nicht identisch, da die Zeichensätze nicht immer reibungslos kartografieren.

In C++ über IPC empfangene Daten sind mit const gekennzeichnet und befinden sich in schreibgeschützten Speicher, der nur für die Dauer des Funktionsaufrufs bestehen bleibt. Daten in Java empfangene IPC-Daten bereits in Java-Objekte kopiert wurden. ohne zusätzliches Kopieren aufbewahrt (und geändert werden).

Anmerkungen

Anmerkungen im Java-Stil können zu Typdeklarationen hinzugefügt werden. Anmerkungen sind vom Vendor Test Suite (VTS) Backend des HIDL-Compilers geparst, aber keine der wie geparste Anmerkungen vom HIDL-Compiler tatsächlich verstanden werden. Stattdessen geparste VTS-Annotationen vom VTS Compiler (VTSC).

Für Anmerkungen wird die Java-Syntax verwendet: @annotation oder @annotation(value) oder @annotation(id=value, id=value…) Dabei kann value entweder ein konstanter Ausdruck, ein String oder eine Liste von Werten sein in {} genau wie in Java. Mehrere Anmerkungen mit demselben Namen an dasselbe Objekt angehängt werden können.

Weiterleitende Deklarationen

In HIDL sind Strukturen möglicherweise nicht vorwärts deklariert. selbstreferenzielle Datentypen nicht möglich (z. B. können Sie keine verknüpfte Liste oder ein Baum im HIDL). In den meisten HALs (vor Android 8.x) werden Forward-Deklarationen, die durch eine Neuanordnung der Datenstruktur entfernt werden können Deklarationen.

Diese Einschränkung ermöglicht das Kopieren von Datenstrukturen nach Wert mit einer einfachen Deepcopy, anstatt Zeigerwerte nachzuverfolgen, die mehrere Male auftreten können in einer selbstreferenziellen Datenstruktur. Werden dieselben Daten zweimal übergeben, z. B. mit zwei Methodenparametern oder vec<T>s, die auf werden zwei separate Kopien erstellt und geliefert.

Verschachtelte Deklarationen

HIDL unterstützt verschachtelte Deklarationen auf beliebig vielen Ebenen (mit einem Ausnahme (siehe unten). Beispiel:

interface IFoo {
    uint32_t[3][4][5][6] multidimArray;

    vec<vec<vec<int8_t>>> multidimVector;

    vec<bool[4]> arrayVec;

    struct foo {
        struct bar {
            uint32_t val;
        };
        bar b;
    }
    struct baz {
        foo f;
        foo.bar fb; // HIDL uses dots to access nested type names
    }
    …

Die Ausnahme ist, dass Schnittstellentypen nur in vec<T> und nur eine Ebene tiefer (keine vec<vec<IFoo>>).

Rohzeigersyntax

Die HIDL-Sprache verwendet weder * noch unterstützt sie volle Flexibilität bei C/C++-Rohzeigern. Weitere Informationen dazu, wie HIDL Zeiger und Arrays/Vektoren, siehe vec<T> Vorlage.

Schnittstellen

Das Keyword interface kann in zweierlei Hinsicht verwendet werden.

  • Die Definition einer Schnittstelle wird in einer .hal-Datei geöffnet.
  • Sie kann als besonderer Typ in Struktur-/Union-Feldern, Methodenparametern und Rückgaben. Es wird als allgemeine Benutzeroberfläche und Synonym für android.hidl.base@1.0::IBase

IServiceManager hat beispielsweise die folgende Methode:

get(string fqName, string name) generates (interface service);

Die Methode verspricht, eine Schnittstelle anhand des Namens zu suchen. Es ist auch ist identisch mit der Ersetzung der Schnittstelle durch android.hidl.base@1.0::IBase.

Schnittstellen können nur auf zwei Arten übergeben werden: als Parameter der obersten Ebene oder als Mitglieder von vec<IMyInterface>. Sie dürfen nicht Mitglied von verschachtelte vecs, Structs, Arrays oder Unions.

MQDescriptorSync und MQDescriptorUnsync

Die Typen MQDescriptorSync und MQDescriptorUnsync Synchronisierte oder nicht synchronisierte FMQ-Deskriptoren (Fast Message Queue) übergeben über eine HIDL-Schnittstelle. Weitere Informationen finden Sie unter HIDL C++ (FMQs sind nicht in Java unterstützt).

Speichertyp

Mit dem Typ memory wird ein nicht zugeordneter gemeinsamer Speicher in HIDL Es wird nur in C++ unterstützt. Ein Wert dieses Typs kann im empfangendes Ende zum Initialisieren eines IMemory-Objekts, wobei der Speicher zugeordnet wird und sie nutzungsfreundlich zu machen. Weitere Informationen finden Sie unter HIDL C++:

Warnung:Strukturierte Daten wurden freigegeben Speicher MUSS ein Typ sein, dessen Format sich während der Lebensdauer des Schnittstellenversion übergeben, die den memory übergibt. Andernfalls können HALs schwerwiegenden Kompatibilitätsproblemen.

Zeigertyp

Der Typ pointer ist nur für den internen Gebrauch mit HIDL vorgesehen.

Bitfield<T> Typvorlage

bitfield<T>, wobei T ein user-defined enum legt nahe, dass der Wert ein bitweises ODER des In T definierte Enum-Werte Im generierten Code bitfield<T> wird als zugrunde liegender Typ von T angezeigt. Für Beispiel:

enum Flag : uint8_t {
    HAS_FOO = 1 << 0,
    HAS_BAR = 1 << 1,
    HAS_BAZ = 1 << 2
};
typedef bitfield<Flag> Flags;
setFlags(Flags flags) generates (bool success);

Der Compiler verarbeitet die Typ-Flags wie uint8_t.

Vorteile (u)int8_t/(u)int16_t/(u)int32_t/(u)int64_t? Mit bitfield werden dem Leser zusätzliche HAL-Informationen bereitgestellt. der jetzt weiß, dass setFlags einen bitweisen ODER-Wert von Flag annimmt (d.h. weiß, dass der Aufruf von setFlags mit 16 ungültig ist. Ohne bitfield, werden diese Informationen nur in Form von Dokumentationen übermittelt. In VTS kann tatsächlich prüfen, ob der Wert von Flags ein bitweises ODER von Flag ist.

Ziehpunkte des einfachen Typs

WARNUNG:Adressen jeder Art (auch physische) Geräteadressen) dürfen niemals Teil eines nativen Alias sein. Bestanden Informationen zwischen Prozessen sind gefährlich und anfällig für Angriffe. Alle zwischen Prozessen übergebenen Werte müssen validiert werden, bevor sie für Folgendes verwendet werden: den zugewiesenen Speicher in einem Prozess zu finden. Andernfalls können schlechte Aliasse zu oder eine Beschädigung des Arbeitsspeichers.

Die HIDL-Semantik basiert auf Kopieren pro Wert, was impliziert, dass Parameter kopiert werden. Alle großen Datenmengen, die zwischen Prozessen geteilt werden müssen (z. B. ein Synchronisierungszaun) werden durch die Übergabe von Dateideskriptoren verarbeitet, die auf in persistente Objekte: ashmem für gemeinsamen Arbeitsspeicher, tatsächliche Dateien oder was sich hinter einer Dateibeschreibung verbirgt. Binder-Treiber dupliziert die Dateibeschreibung im anderen Prozess.

natives_Handle_t

Android unterstützt native_handle_t, ein allgemeines Handle-Konzept definiert in libcutils.

typedef struct native_handle
{
  int version;        /* sizeof(native_handle_t) */
  int numFds;         /* number of file-descriptors at &data[0] */
  int numInts;        /* number of ints at &data[numFds] */
  int data[0];        /* numFds + numInts ints */
} native_handle_t;

Ein natives Handle ist eine Sammlung von Ganzzahlen und Dateideskriptoren, die übergeben wird. nach Wert sortiert. Ein einzelner Dateideskriptor kann in einem nativen Handle mit keine Ganzzahlen und einen einzelnen Dateideskriptor enthalten. Aliasse mit nativen Aliassen übergeben Die Kapselung mit dem Primitivtyp handle sorgt dafür, dass native Aliasse sind direkt in HIDL enthalten.

Da native_handle_t eine variable Größe hat, kann sie nicht aufgenommen werden in einer Struktur hinzufügen. Ein Handle-Feld generiert einen Zeiger auf ein separat native_handle_t zugewiesen.

In früheren Android-Versionen wurden native Aliasse mit demselben und Funktionen, die in libcutils In Android 8.0 und höher werden diese Funktionen nun in den android::hardware::hidl-Namespace wurde in das NDK verschoben. HIDL automatisch generierten Code serialisiert und deserialisiert diese Funktionen, ohne von Nutzenden geschriebenen Code zu verwenden.

Handle und Dateideskriptor-Eigentümerschaft

Wenn Sie eine HIDL-Schnittstellenmethode aufrufen, hidl_handle-Objekt (entweder der obersten Ebene oder als Teil eines zusammengesetzten Typs) Die Eigentumsrechte für die darin enthaltenen Dateideskriptoren lauten wie folgt:

  • Der Aufrufer übergibt ein hidl_handle-Objekt als behält die Eigentümerschaft der Dateideskriptoren im native_handle_t wird umschlossen; Der Aufrufer muss diese Datei schließen wenn sie fertig sind.
  • Der Prozess, der eine hidl_handle zurückgibt durch Übergeben an eine _cb-Funktion behält die Inhaberschaft des Dateideskriptoren, die in der native_handle_t enthalten sind, umschlossen vom Objekt; Der Prozess muss diese Dateideskriptoren nach der Bearbeitung schließen.
  • Ein Verkehrsmittel, das ein hidl_handle erhält, hat Eigentumsrechte der Dateideskriptoren innerhalb der native_handle_t vom -Objekt umschlossen; kann der Empfänger diese Dateideskriptoren unverändert verwenden. den Transaktions-Callback, muss aber das native Handle klonen, um die Datei zu verwenden -Deskriptoren, die über den Callback hinausgehen. Das Transportunternehmen ruft automatisch an close() für die Dateideskriptoren, wenn die Transaktion abgeschlossen ist.

HIDL unterstützt keine Handles in Java (da Java keine Handles auf alle).

Arrays mit großen Größen

Bei Arrays mit einer bestimmten Größe in HIDL-Strukturen können die Elemente einen beliebigen kann Folgendes enthalten:

struct foo {
uint32_t[3] x; // array is contained in foo
};

Strings

Strings werden in C++ und Java unterschiedlich angezeigt, aber der zugrunde liegende Transport Speichertyp ist eine C++-Struktur. Weitere Informationen finden Sie unter HIDL C++ Datentypen oder HIDL-Java-Datentypen.

Hinweis: Die Übergabe eines Strings an oder von Java durch eine HIDL-Schnittstelle (einschließlich Java zu Java) verursacht Zeichensatzkonvertierungen die die ursprüngliche Codierung möglicherweise nicht beibehält.

vec<T> Typvorlage

Die Vorlage vec<T> steht für einen Puffer mit variabler Größe mit Instanzen von T.

T kann einer der folgenden Werte sein:

  • Einfache Typen (z.B. uint32_t)
  • Strings
  • Benutzerdefinierte Enums
  • Benutzerdefinierte Strukturen
  • Benutzeroberflächen oder das Keyword interface (vec<IFoo>, vec<interface> wird unterstützt nur als Parameter der obersten Ebene)
  • Aliasse
  • Bitfield<U>
  • vec<U>, wobei U in dieser Liste enthalten ist, mit Ausnahme der Schnittstelle (z.B. vec<vec<IFoo>> wird nicht unterstützt)
  • U[] (großes Array von U), wobei U in der Liste außer der Schnittstelle steht

Benutzerdefinierte Typen

In diesem Abschnitt werden benutzerdefinierte Typen beschrieben.

Aufzählung

HIDL unterstützt keine anonymen Aufzählungen. Andernfalls ähneln sich die Enums in HIDL an C++11:

enum name : type { enumerator , enumerator = constexpr , …  }

Ein Basis-enum wird in Bezug auf einen der Ganzzahltypen in HIDL definiert. Falls nein Der Wert wird für den ersten Zähler einer Aufzählung basierend auf einer Ganzzahl angegeben -Typ handelt, ist der Wert standardmäßig 0. Wenn für einen späteren Zähler kein Wert angegeben ist, wird standardmäßig der vorherige Wert plus eins verwendet. Beispiel:

// RED == 0
// BLUE == 4 (GREEN + 1)
enum Color : uint32_t { RED, GREEN = 3, BLUE }

Eine Enum kann auch von einer zuvor definierten enum übernommen werden. Wenn kein Wert der für den ersten Zähler einer untergeordneten Aufzählung angegeben ist (in diesem Fall FullSpectrumColor, wird standardmäßig der Wert des letzten Zähler der übergeordneten Aufzählung plus eins. Beispiel:

// ULTRAVIOLET == 5 (Color:BLUE + 1)
enum FullSpectrumColor : Color { ULTRAVIOLET }

Warnung:Die Enum-Übernahme funktioniert rückwärts. von den meisten anderen Vererbungsarten. Ein untergeordneter enum-Wert kann nicht als übergeordneter enum-Wert. Das liegt daran, dass eine untergeordnete Aufzählung mehr Werte als die Parent. Ein übergeordneter enum-Wert kann jedoch bedenkenlos als untergeordneter enum-Wert verwendet werden. da untergeordnete enum-Werte per Definition eine Obermenge von übergeordneten enum-Werten sind. Behalten Sie das beim Entwerfen von Schnittstellen im Hinterkopf, da es bedeutet, dass Typen, die sich auf Übergeordnete Aufzählungen dürfen nicht auf untergeordnete Aufzählungen in späteren Iterationen Ihres .

Werte von Enums werden mit der Doppelpunktsyntax bezeichnet (nicht mit der Punktsyntax als verschachtelte Typen). Die Syntax lautet Type:VALUE_NAME. Keine Angabe erforderlich Typ, wenn auf den Wert im selben Enum-Typ oder in untergeordneten Typen verwiesen wird. Beispiel:

enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 };
enum Color : Grayscale { RED = WHITE + 1 };
enum Unrelated : uint32_t { FOO = Color:RED + 1 };

Ab Android 10 haben Enums eine len-Attribut, das in konstanten Ausdrücken verwendet werden kann. MyEnum::len ist die Gesamtzahl der Einträge in dieser Aufzählung. Dies unterscheidet sich von der Gesamtzahl der Werte, die kleiner sein kann, Werte dupliziert werden.

Struktur

HIDL unterstützt keine anonymen Strukturen. Andernfalls sind Strukturen in HIDL sehr ähnlich wie C.

HIDL unterstützt keine Datenstrukturen mit variabler Länge, die vollständig in Struktur. Dazu gehört auch das Array mit unbegrenzter Länge, das manchmal als das letzte Feld einer Struktur in C/C++ (manchmal mit einer [0]). HIDL vec<T> repräsentiert die dynamische Größe Arrays mit den in einem separaten Zwischenspeicher gespeicherten Daten; dass solche Instanzen durch eine Instanz von vec<T> in der struct.

Entsprechend kann string in einem struct enthalten sein. (Verknüpfte Zwischenspeicher sind separat). In der generierten C++-Instanz des HIDL werden durch einen Zeiger zum eigentlichen nativen Alias Instanzen des zugrunde liegenden Datentyps eine variable Länge.

Union

HIDL unterstützt keine anonymen Unions. Andernfalls ähneln Unions denen in C.

Unions dürfen keine Korrekturtypen enthalten (z. B. Verweise, Dateideskriptoren, Binder). -Objekten). Sie benötigen keine speziellen Felder oder zugehörigen Typen und einfach mit memcpy() oder einem gleichwertigen Element kopiert. Eine Union kann nicht direkt Alles enthalten (oder unter Verwendung anderer Datenstrukturen enthalten), was festgelegt werden muss Binder-Offsets (d. h. Handle- oder Binder-Interface-Referenzen) Beispiel:

union UnionType {
uint32_t a;
//  vec<uint32_t> r;  // Error: can't contain a vec<T>
uint8_t b;1
};
fun8(UnionType info); // Legal

Unions können auch innerhalb von Strukturen deklariert werden. Beispiel:

struct MyStruct {
    union MyUnion {
      uint32_t a;
      uint8_t b;
    }; // declares type but not member

    union MyUnion2 {
      uint32_t a;
      uint8_t b;
    } data; // declares type but not member
  }