HIDL-Datendeklarationen generieren C++-Datenstrukturen mit Standardlayout. Diese Strukturen können überall platziert werden, wo es sinnvoll ist (auf dem Stack, im Datei- oder globalen Gültigkeitsbereich oder auf dem Heap) und auf dieselbe Weise zusammengesetzt werden. Der Clientcode ruft HIDL-Proxycode auf, der Konstantenreferenzen und primitive Typen übergibt, während die Details der Serialization im Stub- und Proxycode ausgeblendet werden.
Hinweis:Es ist zu keinem Zeitpunkt erforderlich, von Entwicklern geschriebenen Code zu verwenden, um Datenstrukturen explizit zu serialisieren oder zu deserialisieren.
In der folgenden Tabelle werden HIDL-Primitive den C++-Datentypen zugeordnet:
HIDL-Typ | C++-Typ | Header/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 genauer beschrieben.
enum
Ein Enum in HIDL wird zu einem Enum in C++. Beispiel:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
… wird zu:
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 Enum-Liste mit ::android::hardware::hidl_enum_range
durchlaufen werden. Dieser Bereich umfasst jeden Enumerator in der Reihenfolge, in der er im HIDL-Quellcode erscheint, beginnend mit dem übergeordneten Enum bis hin zum letzten untergeordneten Element. In diesem Code wird beispielsweise in dieser Reihenfolge über WRITE
, READ
, NONE
und COMPARE
iteriert. Bei 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 Rückwärtsiteratoren und kann in constexpr
-Kontexten verwendet werden. Wenn ein Wert in einer Aufzählung mehrmals vorkommt, wird er auch im Bereich mehrmals berücksichtigt.
bitfield<T>
bitfield<T>
(T
ist ein benutzerdefiniertes Enum) wird zum zugrunde liegenden Typ dieses Enums in C++. Im obigen Beispiel wird bitfield<Mode>
zu uint8_t
.
vec<T>
Die hidl_vec<T>
-Klassenvorlage ist Teil von libhidlbase
und kann verwendet werden, um einen Vektor beliebigen HIDL-Typs mit beliebiger Größe zu übergeben. Der vergleichbare Container mit fester Größe ist hidl_array
. Ein hidl_vec<T>
kann auch mit der Funktion hidl_vec::setToExternal()
initialisiert werden, um auf einen externen Datenpuffer vom Typ T
zu verweisen.
Neben der korrekten Ausgabe/Einfügung des Structs in den generierten C++-Header generiert die Verwendung von vec<T>
einige praktische Funktionen zum Umwandeln von/nach std::vector
- und T
-Pointern. Wenn vec<T>
als Parameter verwendet wird, wird die Funktion, in der er verwendet wird, überladen (zwei Prototypen werden generiert), um sowohl die HIDL-Struktur als auch einen std::vector<T>
-Typ für diesen Parameter zu akzeptieren und weiterzugeben.
Array
Konstante Arrays in hidl werden in libhidlbase
durch die Klasse hidl_array
dargestellt. Ein hidl_array<T, S1, S2, …,
SN>
steht für ein n-dimensionales Array mit fester GrößeT[S1][S2]…[SN]
.
String
Die Klasse hidl_string
(Teil von libhidlbase
) kann zum Übergeben von Strings über HIDL-Schnittstellen verwendet werden und ist in /system/libhidl/base/include/hidl/HidlSupport.h
definiert. Der erste Speicherort in der Klasse ist ein Verweis auf den Zeichenpuffer.
hidl_string
kann mithilfe von operator=
, impliziten Umwandlungen und der Funktion .c_str()
von und zu std::string and char*
(String im C-Format) konvertieren.
HIDL-String-Structs haben die entsprechenden Kopierkonstruktoren und Zuweisungsoperatoren für:
- Laden Sie den HIDL-String aus einem
std::string
- oder C-String. - Erstellen Sie eine neue
std::string
aus einem HIDL-String.
Außerdem haben HIDL-Strings Konvertierungskonstruktoren, sodass C-Strings (char *
) und C++-Strings (std::string
) in Methoden verwendet werden können, die einen HIDL-String annehmen.
struct
Ein struct
in HIDL kann nur Datentypen fester Größe und keine Funktionen enthalten. HIDL-Struct-Definitionen werden direkt struct
s mit Standardlayout in C++ zugeordnet, sodass struct
s ein einheitliches Speicherlayout haben. Ein STRUCT kann HIDL-Typen wie handle
, string
und vec<T>
enthalten, die auf separate Puffer mit variabler Länge verweisen.
Alias
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 Abrufen des zugewiesenen Arbeitsspeichers innerhalb eines Prozesses verwendet werden. Andernfalls können fehlerhafte Handles zu einem fehlerhaften Speicherzugriff oder zur Beschädigung des Arbeitsspeichers führen.
Der Typ handle
wird in C++ durch die Struktur hidl_handle
dargestellt, die ein einfacher Wrapper um einen Verweis auf ein const native_handle_t
-Objekt ist. Dieser Typ ist schon lange in Android 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 die Inhaberschaft des native_handle_t
-Zeigers, den es umschließt. Er dient lediglich dazu, einen Verweis auf eine native_handle_t
sicher zu speichern, damit er sowohl in 32- als auch in 64-Bit-Prozessen verwendet werden kann.
Szenarien, in denen hidl_handle
die zugehörigen Dateien-Beschreibungen besitzt:
- Nach einem Aufruf der Methode
setTo(native_handle_t* handle, bool shouldOwn)
, bei dem 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 zu/von native_handle_t*
-Objekten. Der Typ handle
wird in HIDL hauptsächlich zum Übergeben von Dateideskriptoren über HIDL-Schnittstellen verwendet. Ein einzelner Dateideskriptor wird daher durch einen native_handle_t
ohne int
s und eine einzelne fd
dargestellt. Wenn sich Client und Server in einem anderen Prozess befinden, kümmert sich die RPC-Implementierung automatisch um den Dateideskriptor, damit beide Prozesse auf dieselbe Datei zugreifen können.
Ein Dateideskriptor, der in einer hidl_handle
von einem Prozess empfangen wird, ist in diesem Prozess zwar gültig, aber er bleibt nicht über die empfangende Funktion hinaus bestehen. Er wird geschlossen, wenn die Funktion zurückkehrt. Ein Prozess, der dauerhaften Zugriff auf den Dateideskriptor behalten möchte, muss die enthaltenen Dateideskriptoren dup()
oder das gesamte hidl_handle
-Objekt kopieren.
Speicher
Der HIDL-Typ memory
wird der hidl_memory
-Klasse in libhidlbase
zugeordnet, die nicht zugeordneten gemeinsam genutzten Speicher darstellt. Dieses Objekt muss zwischen Prozessen übergeben werden, um den Speicher in HIDL freizugeben. So verwenden Sie den gemeinsamen Arbeitsspeicher:
- Rufen Sie eine Instanz von
IAllocator
ab (derzeit ist nur die Instanz „ashmem“ verfügbar) und verwenden Sie sie, um gemeinsamen Arbeitsspeicher zuzuweisen. IAllocator::allocate()
gibt einhidl_memory
-Objekt zurück, das über HIDL RPC übergeben und mit dermapMemory
-Funktion vonlibhidlmemory
einem Prozess zugeordnet werden kann.mapMemory
gibt eine Referenz auf einsp<IMemory>
-Objekt zurück, mit dem auf den Arbeitsspeicher zugegriffen werden kann. (IMemory
undIAllocator
sind inandroid.hidl.memory@1.0
definiert.)
Eine Instanz von IAllocator
kann zum Zuweisen von Arbeitsspeicher 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 Arbeitsspeicher müssen über ein IMemory
-Objekt erfolgen, entweder auf der Seite, auf der mem
erstellt wurde, 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();
interface
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. Außerdem 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 annehmen, wandeln Rohzeiger in starke Zeiger um, was zu einem nicht intuitiven Verhalten führt (der Zeiger kann unerwartet gelöscht werden). Speichern Sie HIDL-Schnittstellen immer als sp<>
, um Probleme zu vermeiden.