HIDL-Datendeklarationen generieren Datenstrukturen im C++-Standardlayout. Diese Strukturen können an einer beliebigen Stelle platziert werden, die sich natürlich anfühlt (auf dem Stapel, im Datei- oder globalen Bereich oder auf dem Heap) und können auf die gleiche Weise zusammengesetzt werden. Der Clientcode ruft HIDL-Proxycode auf und übergibt konstante Referenzen und primitive Typen, während der Stub- und Proxycode die Details der Serialisierung verbirgt.
Hinweis: Zu keinem Zeitpunkt ist vom Entwickler geschriebener Code erforderlich, um Datenstrukturen explizit zu serialisieren oder zu deserialisieren.
Die folgende Tabelle ordnet HIDL-Grundelemente C++-Datentypen zu:
HIDL-Typ | C++-Typ | Kopfzeile/Bibliothek |
---|---|---|
enum | enum class | |
uint8_t..uint64_t | uint8_t..uint64_t | <stdint.h> |
int8_t..int64_t | int8_t..int64_t | <stdint.h> |
float | float | |
double | double | |
vec<T> | hidl_vec<T> | libhidlbase |
T[S1][S2]...[SN] | T[S1][S2]...[SN] | |
string | hidl_string | libhidlbase |
handle | hidl_handle | libhidlbase |
safe_union | (custom) struct | |
struct | struct | |
union | union | |
fmq_sync | MQDescriptorSync | libhidlbase |
fmq_unsync | MQDescriptorUnsync | libhidlbase |
In den folgenden Abschnitten werden Datentypen detaillierter beschrieben.
Aufzählung
Eine Aufzählung in HIDL wird zu einer Aufzählung in C++. Zum Beispiel:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
… wird:
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
Ab Android 10 kann eine Enumeration mit ::android::hardware::hidl_enum_range
iteriert werden. Dieser Bereich umfasst jeden Enumerator in der Reihenfolge, in der er im HIDL-Quellcode erscheint, beginnend mit der übergeordneten Enumeration bis zum letzten untergeordneten Enumerator. Dieser Code durchläuft beispielsweise WRITE
, READ
, NONE
und COMPARE
in dieser Reihenfolge. Gegebener SpecialMode
oben:
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
hidl_enum_range
implementiert auch Reverse-Iteratoren und kann in constexpr
Kontexten verwendet werden. Wenn ein Wert in einer Aufzählung mehrmals vorkommt, erscheint der Wert auch im Bereich mehrmals.
bitfield<T>
bitfield<T>
(wobei T
eine benutzerdefinierte Enumeration ist) wird zum zugrunde liegenden Typ dieser Enumeration in C++. Im obigen Beispiel wird bitfield<Mode>
zu uint8_t
.
vec<T>
Die Klassenvorlage hidl_vec<T>
ist Teil von libhidlbase
und kann verwendet werden, um einen Vektor eines beliebigen HIDL-Typs mit beliebiger Größe zu übergeben. Der vergleichbare Container mit fester Größe ist hidl_array
. Ein hidl_vec<T>
kann mithilfe der Funktion hidl_vec::setToExternal()
auch so initialisiert werden, dass er auf einen externen Datenpuffer vom Typ T
zeigt.
Zusätzlich zum korrekten Ausgeben/Einfügen der Struktur im generierten C++-Header generiert die Verwendung von vec<T>
einige praktische Funktionen zum Übersetzen in/von std::vector
und Bare- T
Zeigern. Wenn vec<T>
als Parameter verwendet wird, wird die Funktion, die es verwendet, überladen (es werden zwei Prototypen generiert), um sowohl die HIDL-Struktur als auch einen std::vector<T>
-Typ für diesen Parameter zu akzeptieren und zu übergeben.
Array
Konstante Arrays in hidl werden durch die Klasse hidl_array
in libhidlbase
dargestellt. Ein hidl_array<T, S1, S2, …, SN>
stellt ein N-dimensionales Array fester Größe T[S1][S2]…[SN]
dar.
Zeichenfolge
Die Klasse hidl_string
(Teil von libhidlbase
) kann zum Übergeben von Zeichenfolgen über HIDL-Schnittstellen verwendet werden und ist in /system/libhidl/base/include/hidl/HidlSupport.h
definiert. Der erste Speicherort in der Klasse ist ein Zeiger auf ihren Zeichenpuffer.
hidl_string
weiß, wie man mithilfe von operator=
, impliziten Umwandlungen und der Funktion .c_str()
in und aus std::string and char*
Zeichenfolge im C-Stil) konvertiert. HIDL-String-Strukturen verfügen über die entsprechenden Kopierkonstruktoren und Zuweisungsoperatoren, um:
- Laden Sie den HIDL-String aus einem
std::string
oder einem C-String. - Erstellen Sie einen neuen
std::string
aus einem HIDL-String.
Darüber hinaus verfügen HIDL-Strings über Konvertierungskonstruktoren, sodass C-Strings ( char *
) und C++-Strings ( std::string
) für Methoden verwendet werden können, die einen HIDL-String akzeptieren.
Struktur
Eine struct
in HIDL kann nur Datentypen fester Größe und keine Funktionen enthalten. HIDL-Strukturdefinitionen werden direkt auf Standardlayout- struct
in C++ abgebildet, wodurch sichergestellt wird, dass struct
ein konsistentes Speicherlayout haben. Eine Struktur kann HIDL-Typen enthalten, einschließlich handle
, string
und vec<T>
, die auf separate Puffer variabler Länge verweisen.
handhaben
WARNUNG: Adressen jeglicher Art (auch physische Geräteadressen) dürfen niemals Teil eines nativen Handles sein. Die Weitergabe dieser Informationen zwischen Prozessen ist gefährlich und macht sie anfällig für Angriffe. Alle zwischen Prozessen übergebenen Werte müssen validiert werden, bevor sie zum Nachschlagen des zugewiesenen Speichers innerhalb eines Prozesses verwendet werden. Andernfalls können fehlerhafte Handles zu fehlerhaftem Speicherzugriff oder Speicherbeschädigung führen.
Der handle
Typ wird in C++ durch die hidl_handle
Struktur dargestellt, die ein einfacher Wrapper um einen Zeiger auf ein const native_handle_t
Objekt ist (dies ist in Android schon seit langem vorhanden).
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;
Standardmäßig übernimmt hidl_handle
nicht den Besitz des native_handle_t
-Zeigers, den es umschließt. Es dient lediglich dazu, einen Zeiger auf einen native_handle_t
sicher zu speichern, sodass er sowohl in 32- als auch in 64-Bit-Prozessen verwendet werden kann.
Zu den Szenarios, in denen hidl_handle
Eigentümer der beigefügten Dateideskriptoren ist, gehören:
- Nach einem Aufruf der
setTo(native_handle_t* handle, bool shouldOwn)
-Methode, wobei der ParametershouldOwn
auftrue
gesetzt ist - Wenn das
hidl_handle
Objekt durch Kopierkonstruktion aus einem anderenhidl_handle
Objekt erstellt wird - Wenn das
hidl_handle
Objekt von einem anderenhidl_handle
Objekt kopiert wird
hidl_handle
bietet sowohl implizite als auch explizite Konvertierungen in/von native_handle_t*
-Objekten. Der Hauptzweck des handle
Typs in HIDL besteht darin, Dateideskriptoren über HIDL-Schnittstellen zu übergeben. Ein einzelner Dateideskriptor wird daher durch ein native_handle_t
ohne int
s und ein einzelnes fd
dargestellt. Wenn sich Client und Server in einem unterschiedlichen Prozess befinden, kümmert sich die RPC-Implementierung automatisch um den Dateideskriptor, um sicherzustellen, dass beide Prozesse mit derselben Datei arbeiten können.
Obwohl ein von einem Prozess in einem hidl_handle
empfangener Dateideskriptor in diesem Prozess gültig ist, bleibt er nicht über die empfangende Funktion hinaus bestehen (er wird geschlossen, sobald die Funktion zurückkehrt). Ein Prozess, der den dauerhaften Zugriff auf den Dateideskriptor beibehalten möchte, muss die eingeschlossenen Dateideskriptoren dup()
oder das gesamte hidl_handle
Objekt kopieren.
Erinnerung
Der HIDL- memory
ist der Klasse hidl_memory
in libhidlbase
zugeordnet, die nicht zugeordneten gemeinsam genutzten Speicher darstellt. Dies ist das Objekt, das zwischen Prozessen übergeben werden muss, um den Speicher in HIDL gemeinsam zu nutzen. So verwenden Sie den gemeinsamen Speicher:
- Besorgen Sie sich eine Instanz von
IAllocator
(derzeit ist nur die Instanz „ashmem“ verfügbar) und verwenden Sie sie, um gemeinsam genutzten Speicher zuzuweisen. -
IAllocator::allocate()
gibt einhidl_memory
Objekt zurück, das über HIDL RPC übergeben und mithilfe dermapMemory
Funktion vonlibhidlmemory
einem Prozess zugeordnet werden kann. -
mapMemory
gibt einen Verweis auf einsp<IMemory>
-Objekt zurück, das für den Zugriff auf den Speicher verwendet werden kann. (IMemory
undIAllocator
sind inandroid.hidl.memory@1.0
definiert.)
Eine Instanz von IAllocator
kann zum Zuweisen von Speicher verwendet werden:
#include <android/hidl/allocator/1.0/IAllocator.h> #include <android/hidl/memory/1.0/IMemory.h> #include <hidlmemory/mapping.h> using ::android::hidl::allocator::V1_0::IAllocator; using ::android::hidl::memory::V1_0::IMemory; using ::android::hardware::hidl_memory; .... sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem"); ashmemAllocator->allocate(2048, [&](bool success, const hidl_memory& mem) { if (!success) { /* error */ } // now you can use the hidl_memory object 'mem' or pass it around }));
Tatsächliche Änderungen am Speicher müssen über ein IMemory
Objekt erfolgen, entweder auf der Seite, die mem
erstellt hat, oder auf der Seite, die es über HIDL RPC empfängt.
// Same includes as above sp<IMemory> memory = mapMemory(mem); void* data = memory->getPointer(); memory->update(); // update memory however you wish after calling update and before calling commit data[0] = 42; memory->commit(); // … memory->update(); // the same memory can be updated multiple times // … memory->commit();
Schnittstelle
Schnittstellen können als Objekte übergeben werden. Das Wort interface kann als syntaktischer Zucker für den Typ android.hidl.base@1.0::IBase
verwendet werden; Darüber hinaus werden die aktuelle Schnittstelle und alle importierten Schnittstellen als Typ definiert.
Variablen, die Schnittstellen enthalten, sollten starke Zeiger sein: sp<IName>
. HIDL-Funktionen, die Schnittstellenparameter übernehmen, konvertieren Rohzeiger in starke Zeiger, was zu nicht intuitivem Verhalten führt (der Zeiger kann unerwartet gelöscht werden). Um Probleme zu vermeiden, speichern Sie HIDL-Schnittstellen immer als sp<>
.