Les déclarations de données HIDL génèrent des structures de données de présentation standard C++. Ces structures peuvent être placées n'importe où cela semble naturel (sur la pile, dans un fichier ou à l'échelle globale, ou sur le tas) et peuvent être composées de la même manière. Le code client appelle le code proxy HIDL en passant des références const et des types primitifs, tandis que le code stub et 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 les structures de données.
Le tableau ci-dessous mappe les primitives HIDL aux 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 les types de données plus en détail.
énumération
Une énumération en 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 chaque énumérateur dans l'ordre dans lequel il apparaît dans le code source HIDL, en commençant par l'énumération parent jusqu'au dernier enfant. Par exemple, ce code parcourt WRITE
, READ
, NONE
et COMPARE
dans cet ordre. Étant donné 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 inverses et peut être utilisé dans des contextes constexpr
. Si une valeur apparaît plusieurs fois dans une énumération, la valeur apparaît plusieurs fois dans la plage.
champ de bits<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 comparable de taille fixe est hidl_array
. Un hidl_vec<T>
peut également être initialisé pour pointer vers un tampon de données externe de type T
, en utilisant la fonction hidl_vec::setToExternal()
.
En plus d'émettre/insérer la structure 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 à traduire vers/depuis std::vector
et les pointeurs T
nus. Si le vec<T>
est utilisé comme paramètre, la fonction qui l'utilise sera surchargée (deux prototypes seront 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 constants dans hidl sont représentés par la classe hidl_array
dans libhidlbase
. Un hidl_array<T, S1, S2, …, SN>
représente un tableau de taille fixe à N dimensions T[S1][S2]…[SN]
.
chaîne
La classe hidl_string
(qui fait partie de libhidlbase
) peut être utilisée pour transmettre des chaînes sur les 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 comment convertir vers et depuis std::string and char*
(chaîne de style C) en utilisant operator=
, les conversions implicites et la fonction .c_str()
. Les structures de chaîne HIDL ont les constructeurs de copie et les opérateurs d'affectation appropriés pour :
- Chargez la chaîne HIDL à partir d'un
std::string
ou d'une chaîne C. - Créez un nouveau
std::string
à partir d'une chaîne HIDL.
De plus, les chaînes HIDL ont des constructeurs de conversion, donc les chaînes C ( char *
) et les chaînes C++ ( std::string
) peuvent être utilisées sur les méthodes qui acceptent une chaîne HIDL.
structurer
Une struct
en HIDL ne peut contenir que des types de données de taille fixe et aucune fonction. Les définitions de structure HIDL correspondent directement aux struct
de présentation standard en C++, garantissant que les struct
ont une disposition de mémoire cohérente. Une structure peut inclure des types HIDL, notamment handle
, string
et vec<T>
, qui pointent vers des tampons séparés de longueur variable.
poignée
AVERTISSEMENT : les adresses de toute nature (même les adresses de périphériques physiques) ne doivent jamais faire partie d'un handle natif. La transmission de ces informations entre processus est dangereuse 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 au sein d'un processus. Sinon, de mauvais handles peuvent entraîner un mauvais accès à 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
(cela 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 enveloppe. Il existe simplement pour stocker en toute sécurité un pointeur vers un native_handle_t
de sorte qu'il puisse être utilisé dans les processus 32 et 64 bits.
Les scénarios dans lesquels hidl_handle
possède ses descripteurs de fichiers inclus incluent :
- Suite à 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 copie de construction à 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/depuis les objets native_handle_t*
. L'utilisation principale du type handle
dans HIDL est de transmettre des descripteurs de fichiers via les 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 vivent dans un processus différent, l'implémentation RPC prendra automatiquement en charge le descripteur de fichier pour garantir 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 persistera pas au-delà de la fonction de réception (il sera fermé une fois la fonction renvoyée). Un processus qui souhaite conserver un accès persistant au descripteur de fichier doit dup()
les descripteurs de fichiers inclus ou copier l'intégralité de l'objet hidl_handle
.
mémoire
Le type memory
HIDL correspond à la classe hidl_memory
dans libhidlbase
, qui représente la mémoire partagée non mappée. C'est l'objet qui doit être transmis entre les processus pour partager la mémoire en 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 peut être utilisé pour 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 la 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 toutes les interfaces importées seront définies comme un type.
Les variables qui contiennent des interfaces doivent être des pointeurs forts : sp<IName>
. Les fonctions HIDL qui prennent des paramètres d'interface convertiront les pointeurs bruts en pointeurs forts, provoquant 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<>
.