Types de données

Cette section décrit les types de données HIDL. Pour en savoir plus sur l'implémentation, consultez HIDL C++ (pour C++ implémentations) ou HIDL Java (pour les implémentations Java).

Les similitudes avec C++ sont les suivantes:

  • structs utilise la syntaxe C++. unions est compatible avec la syntaxe C++ par défaut. Les deux doivent être nommés. les structs et les unions anonymes ne sont pas pris en charge.
  • Les typesdefs sont autorisés en HIDL (comme en C++).
  • Les commentaires de style C++ sont autorisés et sont copiés dans le fichier d'en-tête généré.

Les similitudes avec Java sont les suivantes:

  • Pour chaque fichier, HIDL définit un espace de noms de style Java qui doit commencer par android.hardware. L'espace de noms C++ généré est ::android::hardware::…
  • Toutes les définitions du fichier sont contenues dans un fichier Wrapper interface.
  • Les déclarations de tableau HIDL suivent le style Java, et non le style C++. Exemple:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • Les commentaires sont similaires au format Javadoc.

Représentation des données

Un élément struct ou union composé des éléments suivants : Mise en page standard (un sous-ensemble des exigences des types de données standards) mise en page dans le code C++ généré, appliquée avec des attributs d'alignement explicites struct et union membres.

Types HIDL primitifs, ainsi que enum et bitfield (qui dérivent toujours de types primitifs), mappés aux types C++ standards comme std::uint32_t, cstdint

Java n'étant pas compatible avec les types non signés, les types HIDL non signés sont mappés sur le type Java signé correspondant. Les objets Structs correspondent aux classes Java. Les tableaux sont mappés à des tableaux Java. Les unions ne sont pas acceptées actuellement en Java. Les chaînes sont stockées en interne au format UTF8. Java prend en charge uniquement des chaînes UTF16, les valeurs de chaîne envoyées vers ou depuis une implémentation Java sont traduits et peuvent être différents lors d'une nouvelle traduction, car les jeux de caractères cartographiée de manière fluide.

Les données reçues via IPC en C++ sont marquées const et sont dans en lecture seule qui ne persiste que pendant la durée de l'appel de fonction. Données reçu via IPC en Java a déjà été copié dans des objets Java, il peut donc être conservées sans copie supplémentaire (et peuvent être modifiées).

Annotations

Des annotations de style Java peuvent être ajoutées aux déclarations de type. Les annotations sont analysés par le backend de la suite de tests fournisseurs (VTS) du compilateur HIDL, mais aucune des ces annotations analysées sont réellement compréhensibles par le compilateur HIDL. À la place, les annotations VTS analysées sont gérées par le compilateur VTS (VTSC).

Les annotations utilisent la syntaxe Java: @annotation ou @annotation(value) ou @annotation(id=value, id=value…) où "valeur" peut être une expression constante, une chaîne ou une liste de valeurs dans {}, tout comme en Java. Plusieurs annotations du même nom peuvent être associés au même élément.

Transférer les déclarations

Avec HIDL, les structs peuvent ne pas être déclarés avant, ce qui rend les données définies par l'utilisateur les types de données auto-références impossibles (par exemple, vous ne pouvez pas décrire une liste liée ou un arbre dans HIDL). La plupart des HAL existants (antérieures à Android 8.x) utilisent de manière limitée les déclarations avant, qui peuvent être supprimées en réorganisant la structure des données .

Cette restriction permet de copier des structures de données par valeur à l'aide d'un simple au lieu de garder la trace des valeurs du pointeur pouvant apparaître plusieurs fois fois dans une structure de données auto-référence. Si les mêmes données sont transmises deux fois, par exemple avec deux paramètres de méthode, ou vec<T>, qui pointent vers les mêmes données, deux copies distinctes sont faites et livrées.

Déclarations imbriquées

HIDL accepte les déclarations imbriquées à autant de niveaux que vous le souhaitez (avec un l'exception indiquée ci-dessous). Exemple :

interface IFoo {
    uint32_t[3][4][5][6] multidimArray;

    vec<vec<vec<int8_t>>> multidimVector;

    vec<bool[4]> arrayVec;

    struct foo {
        struct bar {
            uint32_t val;
        };
        bar b;
    }
    struct baz {
        foo f;
        foo.bar fb; // HIDL uses dots to access nested type names
    }
    …

La seule exception est que les types d'interface ne peuvent être intégrés vec<T> et un seul niveau de profondeur (pas vec<vec<IFoo>>).

Syntaxe du pointeur brut

Le langage HIDL n'utilise pas * et n'est pas compatible avec le toute la flexibilité des pointeurs bruts C/C++. Pour plus de détails sur l’encapsulation de HIDL les pointeurs et les tableaux/vecteurs, voir vec<T> modèle.

Interfaces

Le mot clé interface a deux utilisations.

  • Elle ouvre la définition d'une interface dans un fichier .hal.
  • Il peut être utilisé en tant que type spécial dans les champs struct/union, les paramètres de méthode, et des retours. Il est considéré comme une interface générale et un synonyme de android.hidl.base@1.0::IBase

Par exemple, IServiceManager possède la méthode suivante:

get(string fqName, string name) generates (interface service);

La méthode promet de rechercher une interface par son nom. Il est également identique au remplacement de l'interface par android.hidl.base@1.0::IBase.

Les interfaces ne peuvent être transmises que de deux manières: en tant que paramètres de niveau supérieur ou en tant que membres d'un vec<IMyInterface>. Ils ne peuvent pas être membres les vecteurs, structs, tableaux ou unions imbriqués.

MQDescriptorSync et MQDescriptorUnsync

Types MQDescriptorSync et MQDescriptorUnsync transmettre des descripteurs FMQ synchronisés ou non synchronisés via une interface HIDL. Pour en savoir plus, consultez HIDL C++ (les FMQ ne sont pas compatibles avec Java).

type de mémoire

Le type memory est utilisé pour représenter la mémoire partagée non mappée dans HIDL. Il n'est compatible qu'avec C++. Une valeur de ce type peut être utilisée côté récepteur pour initialiser un objet IMemory, mappant la mémoire. et de le rendre utilisable. Pour en savoir plus, consultez HIDL C++

Avertissement:Données structurées placées dans des fichiers partagés "mémoire" DOIT être un type dont le format ne change jamais pendant la durée de vie du version d'interface en transmettant memory. Sinon, les HAL risquent de problèmes de compatibilité fatals.

type de pointeur

Le type pointer est réservé à un usage interne HIDL.

champ de bits<T> modèle de type

bitfield<T>, où T est énumération définie par l'utilisateur suggère que la valeur est un opérateur OU au niveau du bit de la Valeurs d'énumération définies dans T. Dans le code généré, bitfield<T> apparaît comme le type sous-jacent de T. Exemple :

enum Flag : uint8_t {
    HAS_FOO = 1 << 0,
    HAS_BAR = 1 << 1,
    HAS_BAZ = 1 << 2
};
typedef bitfield<Flag> Flags;
setFlags(Flags flags) generates (bool success);

Le compilateur gère les options de type de la même manière que uint8_t.

Pourquoi ne pas utiliser (u)int8_t/(u)int16_t/(u)int32_t/(u)int64_t? L'utilisation de bitfield fournit des informations HAL supplémentaires au lecteur, qui sait maintenant que setFlags accepte une valeur OR (OU) bit à bit de cet élément (c'est-à-dire sait que l'appel de setFlags avec 16 n'est pas valide). Sans bitfield, ces informations ne sont transmises que via la documentation. Dans En outre, le VTS peut vérifier si la valeur des indicateurs est un OR au niveau du bit de l'indicateur.

Poignées de type primitif

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 dans un processus. Sinon, des poignées de mauvaise qualité un accès à la mémoire ou une corruption de la mémoire.

La sémantique HIDL est une copie par valeur, ce qui implique que les paramètres sont copiés. Toute grande quantité de données ou données qui doivent être partagées entre les processus (par exemple, une barrière de synchronisation), sont gérés en transmettant les descripteurs de fichier aux objets persistants: ashmem pour la mémoire partagée, des fichiers réels ou tout autre élément pouvant se cacher derrière un descripteur de fichier. Le lecteur de liaison duplique le descripteur de fichier dans l'autre processus.

poignée_native

Android est compatible avec native_handle_t, un concept d'identifiant général défini dans libcutils.

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;

Un handle natif est un ensemble d'ents et de descripteurs de fichier transmis en fonction de leur valeur. Un seul descripteur de fichier peut être stocké dans un handle natif avec sans entier et un seul descripteur de fichier. Transmettre des identifiants à l'aide d'identifiants natifs encapsulé avec le type primitif handle garantit que le trafic natif les poignées sont directement inclus dans HIDL.

Comme native_handle_t a une taille variable, il ne peut pas être inclus directement dans un struct. Un champ de handle génère un pointeur vers un élément a alloué native_handle_t.

Dans les versions antérieures d'Android, les identifiants natifs étaient créés à l'aide de la même fonctions présentes dans libcutils Dans Android 8.0 et versions ultérieures, ces fonctions sont désormais copiées dans le Espace de noms android::hardware::hidl ou déplacé vers le NDK. HIDL le code généré automatiquement sérialise et désérialise ces fonctions automatiquement, sans impliquer le code écrit par l'utilisateur.

Gérer la propriété des descripteurs de fichiers

Lorsque vous appelez une méthode d'interface HIDL qui transmet (ou renvoie) un Un objet hidl_handle (niveau supérieur ou faisant partie d'un type composé) la propriété des descripteurs de fichier qu'il contient est la suivante:

  • L'appelant transmettant un objet hidl_handle en tant que conserve la propriété des descripteurs de fichier contenus dans native_handle_t il encapsule. l'appelant doit fermer ce fichier quand vous en avez fini avec eux.
  • Le processus renvoyant un hidl_handle (en le transmettant dans une fonction _cb) conserve la propriété du descripteurs de fichier contenus dans le native_handle_t encapsulés par objet ; le processus doit fermer ces descripteurs de fichier une fois qu'il en a terminé avec eux.
  • Un transport qui reçoit une hidl_handle possède propriété des descripteurs de fichier dans native_handle_t encapsulés par l'objet. le récepteur peut utiliser ces descripteurs de fichier tels quels le rappel de transaction, mais vous devez cloner le handle natif pour utiliser le fichier au-delà du rappel. Le transport appelle automatiquement close() pour les descripteurs de fichier lorsque la transaction est terminée.

HIDL ne prend pas en charge les poignées en Java (car Java n'est pas compatible avec les handle au niveau toutes).

Tableaux dimensionnés

Pour les tableaux dimensionnés dans les structs HIDL, leurs éléments peuvent être de n'importe quel type Peut contenir:

struct foo {
uint32_t[3] x; // array is contained in foo
};

Strings

Les chaînes apparaissent différemment en C++ et en Java, mais le transport sous-jacent est une structure C++. Pour en savoir plus, consultez Types de données C++ HIDL ou Types de données Java HIDL.

Remarque:La transmission d'une chaîne vers ou depuis Java via un L'interface HIDL (y compris Java vers Java) génère des conversions de jeux de caractères. qui peut ne pas conserver l'encodage d'origine.

vec<T> modèle de type

Le modèle vec<T> représente un tampon de taille variable contenant des instances de T.

T peut être:

  • Types primitifs (par exemple, uint32_t)
  • Strings
  • Énumérations définies par l'utilisateur
  • Structures définies par l'utilisateur
  • Interfaces, ou mot clé interface (vec<IFoo> et vec<interface> sont acceptés) uniquement en tant que paramètre de niveau supérieur)
  • Identifiants
  • champ de bits<U>
  • vec<U>, où U figure dans cette liste, sauf pour l'interface (par exemple, vec<vec<IFoo>> n'est pas accepté)
  • U[] (tableau dimensionné de U), où U figure dans cette liste, à l'exception de l'interface

Types définis par l'utilisateur

Cette section décrit les types définis par l'utilisateur.

Énumération

HIDL n'est pas compatible avec les énumérations anonymes. Sinon, les énumérations dans HIDL sont similaires vers C++11:

enum name : type { enumerator , enumerator = constexpr , …  }

Une énumération de base est définie en fonction de l'un des types d'entiers dans HIDL. Si non valeur est spécifiée pour le premier énumérateur d'une énumération basée sur un entier la valeur par défaut est 0. Si aucune valeur n'est spécifiée pour un énumérateur ultérieur, la valeur par défaut est la valeur précédente, plus un. Exemple :

// RED == 0
// BLUE == 4 (GREEN + 1)
enum Color : uint32_t { RED, GREEN = 3, BLUE }

Une énumération peut également hériter d'une énumération déjà définie. Si aucune valeur n'est spécifié pour le premier énumérateur d'une énumération enfant (dans ce cas, FullSpectrumColor), la valeur par défaut du dernier énumérateur de l'énumération parente plus un. Exemple :

// ULTRAVIOLET == 5 (Color:BLUE + 1)
enum FullSpectrumColor : Color { ULTRAVIOLET }

Avertissement:L'héritage des énumérations fonctionne dans l'ordre inverse. de la plupart des autres types d'héritage. Vous ne pouvez pas utiliser de valeur d'énumération enfant valeur d'énumération parent. En effet, une énumération enfant inclut plus de valeurs que parent. Cependant, une valeur d'énumération parente peut être utilisée en toute sécurité comme valeur d'énumération enfant. car les valeurs d'énumération enfants sont, par définition, un sur-ensemble de valeurs d'énumération parentes. Gardez cela à l'esprit lors de la conception d'interfaces, car cela signifie que les types faisant référence à les énumérations parentes ne peuvent pas faire référence aux énumérations enfants dans les itérations ultérieures de votre de commande.

Les valeurs des énumérations sont appelées par la syntaxe deux-points (et non par la syntaxe à point, types imbriqués). La syntaxe est Type:VALUE_NAME. Pas besoin de préciser type si la valeur est référencée dans le même type d'énumération ou les mêmes types enfants. Exemple :

enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 };
enum Color : Grayscale { RED = WHITE + 1 };
enum Unrelated : uint32_t { FOO = Color:RED + 1 };

À partir d'Android 10, les énumérations ont un Attribut len pouvant être utilisé dans des expressions constantes. MyEnum::len est le nombre total d'entrées dans cette énumération. Ce nombre est différent du nombre total de valeurs, qui peut être inférieur lorsque sont en double.

Structure

HIDL n'est pas compatible avec les structs anonymes. Sinon, les structs en HIDL sont très semblable à C.

HIDL n'est pas compatible avec les structures de données de longueur variable contenues entièrement dans un struct. Cela inclut le tableau de longueur indéfinie qui est parfois utilisé comme le dernier champ d'un struct en C/C++ (parfois vu avec une taille de [0]). HIDL vec<T> représente la taille dynamique des tableaux avec les données stockées dans un tampon distinct ; ces instances sont représentées par une instance de vec<T> dans struct.

De même, string peut être contenu dans un struct. (les tampons associés sont distincts). Dans le C++ généré, les instances du HIDL type de handle sont représentés via un pointeur vers le handle natif réel en tant que les instances du type de données sous-jacent ont une longueur variable.

Union

HIDL n'est pas compatible avec les unions anonymes. Sinon, les unions sont semblables à C.

Les unions ne peuvent pas contenir de types de correction (tels que des pointeurs, des descripteurs de fichier, des liaisons des objets). Ils n'ont pas besoin de champs spéciaux ni de types associés et sont est simplement copié à l'aide de memcpy() ou équivalent. Il se peut qu'une union ne soit pas contiennent (ou contiennent à l'aide d'autres structures de données) tout ce qui nécessite de définir Décalages de liaison (c'est-à-dire les références de poignée ou d'interface de liaison). Exemple :

union UnionType {
uint32_t a;
//  vec<uint32_t> r;  // Error: can't contain a vec<T>
uint8_t b;1
};
fun8(UnionType info); // Legal

Les unions peuvent également être déclarées à l'intérieur des objets STRUCT. Exemple :

struct MyStruct {
    union MyUnion {
      uint32_t a;
      uint8_t b;
    }; // declares type but not member

    union MyUnion2 {
      uint32_t a;
      uint8_t b;
    } data; // declares type but not member
  }