Types de données

Les déclarations de données HIDL génèrent des structures de données de mise en page standard C++. Ces les structures peuvent être placées n’importe où qui vous semble naturel (sur la pile, dans un fichier ou un champ d'application global ou sur le tas de mémoire) et peuvent être composés de la même manière. Client le code appelle le code proxy HIDL en transmettant des références const et des types primitifs, tandis que le bouchon et le code de proxy masquent les détails de la sérialisation.

Remarque:Le code n'est jamais écrit par le développeur. nécessaires pour sérialiser ou désérialiser explicitement les structures de données.

Le tableau ci-dessous met en correspondance les primitives HIDL et 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 les types de données plus en détail.

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. que d'utiliser ::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 de l'énumération parent jusqu'au dernier enfant. Par exemple, ce code itère sur WRITE, READ, NONE et COMPARE dans cet ordre. Donnée 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 les contextes constexpr. Si une valeur apparaît dans une énumération plusieurs fois, 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 permet de transmettre un vecteur de n'importe quel type HIDL avec une taille arbitraire. Le conteneur de taille fixe comparable 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/d'insérer le struct de manière appropriée dans le en-tête C++, l'utilisation de vec<T> est pratique Fonctions de traduction vers/depuis std::vector et T simple ces pointeurs. Si vec<T> est utilisé en tant que paramètre, la fonction son utilisation est surchargée (deux prototypes sont générés) pour accepter et transmettez à la fois le struct HIDL et un type std::vector<T> pour cette .

tableau

Les tableaux constants dans Hidl sont représentés par la classe hidl_array dans libhidlbase. Un élément hidl_array<T, S1, S2, …, SN> représente un tableau de taille fixe à N dimensions T[S1][S2]…[SN]

string

La classe hidl_string (partie de libhidlbase) peut être utilisé pour transmettre des chaînes via des interfaces HIDL et est défini dans /system/libhidl/base/include/hidl/HidlSupport.h Le premier espace de stockage "location" dans la classe pointe vers son tampon de caractères.

hidl_string sait comment effectuer une conversion vers et depuis std::string and char* (chaîne de style C) avec operator=, conversions implicites et fonction .c_str(). Les structures de chaîne HIDL ont les constructeurs de copie appropriés et l'affectation pour:

  • Chargez la chaîne HIDL à partir d'une chaîne std::string ou C.
  • Créez un std::string à partir d'une chaîne HIDL.

De plus, les chaînes HIDL ont des constructeurs de conversion, de sorte que les chaînes C (char *) et des chaînes C++ (std::string) peuvent être utilisées sur qui acceptent une chaîne HIDL.

structure

Un struct en HIDL ne peut contenir que des types de données de taille fixe et aucun fonctions. Les définitions de structure HIDL sont mappées directement à la mise en page standard les struct en C++, ce qui garantit que les struct ont un une mise en page cohérente de la mémoire. Un struct peut inclure des types HIDL, y compris handle, string et vec<T>, qui pour séparer des tampons de longueur variable.

poignée

AVERTISSEMENT:Toute adresse (même physique, quelle qu'elle soit) adresses de l'appareil) ne doit jamais faire partie d'un identifiant natif. Transmission à les 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 au cours d'un processus. Sinon, des poignées de mauvaise qualité peuvent un accès à la mémoire ou une corruption de la mémoire.

Le type handle est représenté par l'élément hidl_handle en C++, qui est un simple wrapper autour d'un pointeur vers Objet const native_handle_t (présent sur Android depuis 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 n'est pas propriétaire du pointeur native_handle_t qu'il encapsule. Il sert simplement à stocker un pointeur vers un élément native_handle_t de sorte qu'il puisse être utilisé dans les processus 32 et 64 bits.

Scénarios dans lesquels hidl_handle est propriétaire du fichier inclus descripteurs incluent:

  • Après un appel à la méthode setTo(native_handle_t* handle, bool shouldOwn) avec le paramètre shouldOwn défini sur true
  • Lorsque l'objet hidl_handle est créé par construction d'une copie à partir d'un autre objet hidl_handle
  • Lorsque l'objet hidl_handle est copié à partir d'un autre hidl_handle objet

hidl_handle fournit des conversions implicites et explicites vers/depuis des objets native_handle_t* . La principale fonction Le type handle en HIDL est de transmettre les descripteurs de fichier via HIDL de commande. Un seul descripteur de fichier est donc représenté par un native_handle_t sans ints et un seul fd Si le client et le serveur vivent dans un processus différent, la méthode RPC s'occupe automatiquement du descripteur de fichier pour garantir les deux processus peuvent fonctionner sur le même fichier.

Même si un descripteur de fichier reçu dans un hidl_handle par un processus est valide dans ce processus, il ne persiste pas au-delà de la réception (elle est fermée lorsque la fonction est renvoyée). Un processus qui veut conserver un accès persistant au descripteur de fichier doit dup() descripteurs de fichier inclus, ou copiez 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. À utiliser la mémoire partagée:

  1. Récupérez une instance de IAllocator (actuellement, seule l'instance "ashmem" est disponible) et utilisez-la pour allouer de la mémoire partagée.
  2. IAllocator::allocate() renvoie un hidl_memory. objet pouvant être transmis via HIDL RPC et être mappé dans un processus à l'aide de la fonction mapMemory de libhidlmemory.
  3. mapMemory renvoie une référence à un Objet sp<IMemory> permettant d'accéder à la mémoire. (IMemory et IAllocator sont définis dans android.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 apportées à la mémoire doivent être effectuées via un 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é en tant que sucre syntaxique pour le type android.hidl.base@1.0::IBase ; En outre, l'interface actuelle et toutes les interfaces importées sont définies en tant que type.

Les variables qui contiennent des interfaces doivent constituer des pointeurs forts: sp<IName> Fonctions HIDL qui acceptent des paramètres d'interface convertir 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 HIDL interfaces en tant que sp<>.