Les déclarations de données HIDL génèrent des structures de données de mise en page standard C++. Ces structures peuvent être placées n'importe où (sur la pile, au niveau du fichier ou de la portée globale, ou sur la pile) et peuvent être composées de la même manière. Le code client appelle le code de proxy HIDL en transmettant des références constantes et des types primitifs, tandis que le code de bouchon et de proxy masque les détails de la sérialisation.
Remarque:À aucun moment, le code écrit par le développeur n'est requis pour sérialiser ou désérialiser explicitement des structures de données.
Le tableau ci-dessous met en correspondance les primitives HIDL avec les types de données C++:
Type HIDL | Type C++ | En-tête/Bibliothèque |
---|---|---|
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 |
Les sections ci-dessous décrivent plus en détail les types de données.
enum
Une énumération dans HIDL devient une énumération en C++. Par exemple:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
… devient:
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
À partir d'Android 10, une énumération peut être itérée à l'aide de ::android::hardware::hidl_enum_range
. Cette plage inclut tous les énumérateurs dans l'ordre dans lequel ils apparaissent dans le code source HIDL, en commençant par l'énumération parente jusqu'au dernier enfant. Par exemple, ce code itère sur WRITE
, READ
, NONE
et COMPARE
dans cet ordre. Compte tenu de SpecialMode
ci-dessus:
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
hidl_enum_range
implémente également des itérateurs inversés et peut être utilisé dans des contextes constexpr
. Si une valeur apparaît plusieurs fois dans une énumération, elle apparaît plusieurs fois dans la plage.
bitfield<T>
bitfield<T>
(où T
est une énumération définie par l'utilisateur) devient le type sous-jacent de cette énumération en C++. Dans l'exemple ci-dessus, bitfield<Mode>
devient uint8_t
.
vec<T>
Le modèle de classe hidl_vec<T>
fait partie de libhidlbase
et peut être utilisé pour transmettre un vecteur de n'importe quel type HIDL avec une taille arbitraire. Le conteneur de taille fixe comparable est hidl_array
. Un hidl_vec<T>
peut également être initialisé pour pointer vers un tampon de données externe de type T
, à l'aide de la fonction hidl_vec::setToExternal()
.
En plus d'émettre/d'insérer la struct de manière appropriée dans l'en-tête C++ généré, l'utilisation de vec<T>
génère des fonctions pratiques pour traduire depuis/vers std::vector
et les pointeurs T
nus. Si vec<T>
est utilisé comme paramètre, la fonction qui l'utilise est surchargée (deux prototypes sont générés) pour accepter et transmettre à la fois la structure HIDL et un type std::vector<T>
pour ce paramètre.
tableau
Les tableaux de constantes dans hidl sont représentés par la classe hidl_array
dans libhidlbase
. Un hidl_array<T, S1, S2, …,
SN>
représente un tableau T[S1][S2]…[SN]
à taille fixe à N dimensions.
string
La classe hidl_string
(qui fait partie de libhidlbase
) peut être utilisée pour transmettre des chaînes via des interfaces HIDL et est définie dans /system/libhidl/base/include/hidl/HidlSupport.h
. Le premier emplacement de stockage de la classe est un pointeur vers son tampon de caractères.
hidl_string
sait convertir vers et depuis std::string and char*
(chaîne de style C) à l'aide de operator=
, de castings implicites et de la fonction .c_str()
.
Les structures de chaîne HIDL disposent des constructeurs de copie et des opérateurs d'affectation appropriés pour:
- Chargez la chaîne HIDL à partir d'une chaîne
std::string
ou C. - Créez une
std::string
à partir d'une chaîne HIDL.
De plus, les chaînes HIDL disposent de constructeurs de conversion afin que les chaînes C (char *
) et C++ (std::string
) puissent être utilisées sur les méthodes qui acceptent une chaîne HIDL.
struct
Un struct
dans HIDL ne peut contenir que des types de données de taille fixe et aucune fonction. Les définitions de struct HIDL sont mappées directement sur les struct
de mise en page standard en C++, ce qui garantit que les struct
ont une mise en page de mémoire cohérente. Une struct peut inclure des types HIDL, y compris handle
, string
et vec<T>
, qui pointent vers des tampons de longueur variable distincts.
poignée
AVERTISSEMENT:Les adresses de quelque nature que ce soit (même les adresses d'appareils physiques) ne doivent jamais faire partie d'un handle natif. Transmettre ces informations entre les processus est dangereux et les rend vulnérables aux attaques. Toutes les valeurs transmises entre les processus doivent être validées avant d'être utilisées pour rechercher la mémoire allouée dans un processus. Sinon, de mauvaises poignées peuvent entraîner un accès incorrect à la mémoire ou une corruption de la mémoire.
Le type handle
est représenté par la structure hidl_handle
en C++, qui est un simple wrapper autour d'un pointeur vers un objet const native_handle_t
(il est présent dans Android depuis longtemps).
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;
Par défaut, hidl_handle
ne s'approprie pas le pointeur native_handle_t
qu'il encapsule. Il existe simplement pour stocker de manière sécurisée un pointeur vers un native_handle_t
afin qu'il puisse être utilisé à la fois dans les processus 32 bits et 64 bits.
Voici des exemples de scénarios dans lesquels hidl_handle
possède ses descripteurs de fichiers inclus:
- Après un appel à la méthode
setTo(native_handle_t* handle, bool shouldOwn)
avec le paramètreshouldOwn
défini surtrue
- Lorsque l'objet
hidl_handle
est créé par construction par copie à partir d'un autre objethidl_handle
- Lorsque l'objet
hidl_handle
est attribué par copie à partir d'un autre objethidl_handle
hidl_handle
fournit des conversions implicites et explicites vers/à partir d'objets native_handle_t*
. L'utilisation principale du type handle
dans HIDL consiste à transmettre des descripteurs de fichiers via des interfaces HIDL. Un seul descripteur de fichier est donc représenté par un native_handle_t
sans int
et un seul fd
. Si le client et le serveur résident dans un processus différent, l'implémentation du RPC s'occupe automatiquement du descripteur de fichier pour s'assurer que les deux processus peuvent fonctionner sur le même fichier.
Bien qu'un descripteur de fichier reçu dans un hidl_handle
par un processus soit valide dans ce processus, il ne persiste pas au-delà de la fonction réceptrice (il est fermé lorsque la fonction renvoie). Un processus qui souhaite conserver un accès persistant au descripteur de fichier doit dup()
les descripteurs de fichiers inclus ou copier l'objet hidl_handle
entier.
mémoire
Le type memory
HIDL est mappé sur la classe hidl_memory
dans libhidlbase
, qui représente la mémoire partagée non mappée. Il s'agit de l'objet qui doit être transmis entre les processus pour partager la mémoire dans HIDL. Pour utiliser la mémoire partagée:
- Obtenez une instance de
IAllocator
(actuellement, seule l'instance "ashmem" est disponible) et utilisez-la pour allouer de la mémoire partagée. IAllocator::allocate()
renvoie un objethidl_memory
qui peut être transmis via HIDL RPC et mappé dans un processus à l'aide de la fonctionmapMemory
delibhidlmemory
.mapMemory
renvoie une référence à un objetsp<IMemory>
qui permet d'accéder à la mémoire. (IMemory
etIAllocator
sont définis dansandroid.hidl.memory@1.0
.)
Une instance de IAllocator
peut être utilisée pour allouer de la mémoire:
#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 }));
Les modifications réelles de la mémoire doivent être effectuées via un objet IMemory
, soit du côté qui a créé mem
, soit du côté qui le reçoit via HIDL RPC.
// 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
Les interfaces peuvent être transmises en tant qu'objets. Le mot interface peut être utilisé comme sucre syntaxique pour le type android.hidl.base@1.0::IBase
. De plus, l'interface actuelle et les interfaces importées sont définies comme un type.
Les variables qui contiennent des interfaces doivent être des pointeurs forts : sp<IName>
. Les fonctions HIDL qui acceptent des paramètres d'interface convertissent les pointeurs bruts en pointeurs forts, ce qui entraîne un comportement non intuitif (le pointeur peut être effacé de manière inattendue). Pour éviter les problèmes, stockez toujours les interfaces HIDL en tant que sp<>
.