Gestion des versions

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

HIDL exige que chaque interface écrite en HIDL soit versionnée. Une fois qu'une interface HAL est publiée, elle est gelée et toute autre modification doit être apportée à une nouvelle version de cette interface. Si une interface publiée donnée ne peut pas être modifiée, elle peut être étendue par une autre interface.

Structure du code HIDL

Le code HIDL est organisé en types, interfaces et packages définis par l'utilisateur :

  • Types définis par l'utilisateur (UDT) . HIDL donne accès à un ensemble de types de données primitifs qui peuvent être utilisés pour composer des types plus complexes via des structures, des unions et des énumérations . Les UDT sont passés aux méthodes des interfaces et peuvent être définis au niveau d'un package (commun à toutes les interfaces) ou localement à une interface.
  • Interfaces . En tant que bloc de construction de base de HIDL, une interface se compose d'UDT et de déclarations de méthode. Les interfaces peuvent également hériter d'une autre interface.
  • Forfaits . Organise les interfaces HIDL associées et les types de données sur lesquels elles opèrent. Un package est identifié par un nom et une version et comprend les éléments suivants :
    • Fichier de définition de type de données appelé types.hal .
    • Zéro ou plusieurs interfaces, chacune dans son propre fichier .hal .

Le fichier de définition de type de données types.hal contient uniquement des UDT (tous les UDT au niveau du package sont conservés dans un seul fichier). Les représentations dans la langue cible sont disponibles pour toutes les interfaces du package.

Philosophie de gestion des versions

Un package HIDL (tel que android.hardware.nfc ), après avoir été publié pour une version donnée (telle que 1.0 ), est immuable ; il ne peut pas être modifié. Les modifications des interfaces dans le package ou toute modification de ses UDT ne peuvent avoir lieu que dans un autre package.

Dans HIDL, la gestion des versions s'applique au niveau du package, pas au niveau de l'interface, et toutes les interfaces et UDT d'un package partagent la même version. Les versions de package suivent la version sémantique sans les composants de niveau de correctif et de métadonnées de construction. Dans un package donné, un changement de version mineur implique que la nouvelle version du package est rétrocompatible avec l'ancien package et un changement de version majeure implique que la nouvelle version du package n'est pas rétrocompatible avec l'ancien package.

Conceptuellement, un package peut être lié à un autre package de plusieurs manières :

  • Pas du tout .
  • Extensibilité rétrocompatible au niveau du package . Cela se produit pour les nouvelles uprevs de version mineure (prochaine révision incrémentée) d'un paquet ; le nouveau package a le même nom et la même version majeure que l'ancien package, mais une version mineure supérieure. Fonctionnellement, le nouveau package est un sur-ensemble de l'ancien package, ce qui signifie :
    • Les interfaces de niveau supérieur du package parent sont présentes dans le nouveau package, bien que les interfaces puissent avoir de nouvelles méthodes, de nouveaux UDT locaux d'interface (l'extension au niveau de l'interface décrite ci-dessous) et de nouveaux UDT dans types.hal .
    • De nouvelles interfaces peuvent également être ajoutées au nouveau package.
    • Tous les types de données du package parent sont présents dans le nouveau package et peuvent être gérés par les méthodes (éventuellement réimplémentées) de l'ancien package.
    • De nouveaux types de données peuvent également être ajoutés pour être utilisés soit par de nouvelles méthodes d'interfaces existantes mises à jour, soit par de nouvelles interfaces.
  • Extensibilité rétrocompatible au niveau de l'interface . Le nouveau package peut également étendre le package d'origine en se composant d'interfaces logiquement séparées qui fournissent simplement des fonctionnalités supplémentaires, et non celles de base. A cet effet, les éléments suivants peuvent être souhaitables :
    • Les interfaces du nouveau package doivent recourir aux types de données de l'ancien package.
    • Les interfaces du nouveau package peuvent étendre les interfaces d'un ou plusieurs anciens packages.
  • Prolongez l'incompatibilité descendante d'origine . Il s'agit d'une version majeure du package et il n'est pas nécessaire qu'il y ait de corrélation entre les deux. Dans la mesure du possible, il peut être exprimé avec une combinaison de types de l'ancienne version du package et l'héritage d'un sous-ensemble d'interfaces de l'ancien package.

Interfaces structurantes

Pour une interface bien structurée, l'ajout de nouveaux types de fonctionnalités qui ne font pas partie de la conception d'origine devrait nécessiter une modification de l'interface HIDL. Inversement, si vous pouvez ou prévoyez d'apporter une modification des deux côtés de l'interface qui introduit de nouvelles fonctionnalités sans modifier l'interface elle-même, l'interface n'est pas structurée.

Treble prend en charge les composants fournisseur et système compilés séparément dans lesquels le vendor.img sur un périphérique et le system.img peuvent être compilés séparément. Toutes les interactions entre vendor.img et system.img doivent être explicitement et complètement définies afin qu'elles puissent continuer à fonctionner pendant de nombreuses années. Cela inclut de nombreuses surfaces API, mais une surface majeure est le mécanisme IPC que HIDL utilise pour la communication interprocessus sur la frontière system.img / vendor.img .

Conditions

Toutes les données transmises via HIDL doivent être explicitement définies. Pour garantir qu'une implémentation et un client peuvent continuer à travailler ensemble même lorsqu'ils sont compilés séparément ou développés indépendamment, les données doivent respecter les exigences suivantes :

  • Peut être décrit directement en HIDL (en utilisant des structs enums, etc.) avec des noms sémantiques et une signification.
  • Peut être décrit par une norme publique telle que ISO/IEC 7816.
  • Peut être décrit par une norme matérielle ou une disposition physique du matériel.
  • Peut être des données opaques (telles que des clés publiques, des identifiants, etc.) si nécessaire.

Si des données opaques sont utilisées, elles doivent être lues uniquement par un côté de l'interface HIDL. Par exemple, si le code de vendor.img donne à un composant sur le system.img un message de chaîne ou des données vec<uint8_t> , ces données ne peuvent pas être analysées par le system.img lui-même ; il ne peut être renvoyé qu'à vendor.img pour interprétation. Lors du passage d'une valeur de vendor.img au code du fournisseur sur system.img ou à un autre périphérique, le format des données et la façon dont elles doivent être interprétées doivent être décrits avec précision et font toujours partie de l'interface .

Des lignes directrices

Vous devriez être capable d'écrire une implémentation ou un client d'un HAL en utilisant uniquement les fichiers .hal (c'est-à-dire que vous ne devriez pas avoir besoin de regarder la source Android ou les normes publiques). Nous vous recommandons de spécifier le comportement exact requis. Des déclarations telles que "une implémentation peut faire A ou B" encouragent les implémentations à s'entremêler avec les clients avec lesquels elles sont développées.

Disposition du code HIDL

HIDL comprend des packages de base et de fournisseur.

Les interfaces HIDL principales sont celles spécifiées par Google. Les packages auxquels ils appartiennent commencent par android.hardware. et sont nommés par sous-système, potentiellement avec des niveaux de dénomination imbriqués. Par exemple, le package NFC est nommé android.hardware.nfc et le package caméra est android.hardware.camera . En général, un package de base porte le nom android.hardware. [ name1 ].[ name2 ]…. Les packages HIDL ont une version en plus de leur nom. Par exemple, le package android.hardware.camera peut être en version 3.4 ; c'est important, car la version d'un paquet affecte son emplacement dans l'arborescence des sources.

Tous les packages de base sont placés sous hardware/interfaces/ dans le système de construction. Le paquet android.hardware. [ name1 ].[ name2 ]… à la version $m.$n se trouve sous hardware/interfaces/name1/name2//$m.$n/ ; Le package android.hardware.camera version 3.4 se trouve dans le répertoire hardware/interfaces/camera/3.4/. Un mappage codé en dur existe entre le préfixe de package android.hardware. et le chemin hardware/interfaces/ .

Les packages non essentiels (fournisseur) sont ceux produits par le fournisseur SoC ou ODM. Le préfixe des packages non essentiels est vendor.$(VENDOR).hardware.$(VENDOR) fait référence à un fournisseur SoC ou OEM/ODM. Cela correspond au chemin vendor/$(VENDOR)/interfaces dans l'arborescence (ce mappage est également codé en dur).

Noms complets des types définis par l'utilisateur

Dans HIDL, chaque UDT a un nom complet composé du nom de l'UDT, du nom du package dans lequel l'UDT est défini et de la version du package. Le nom complet est utilisé uniquement lorsque des instances du type sont déclarées et non lorsque le type lui-même est défini. Par exemple, supposons que le package android.hardware.nfc, version 1.0 définit une structure nommée NfcData . Sur le site de la déclaration (que ce soit dans types.hal ou dans la déclaration d'une interface), la déclaration indique simplement :

struct NfcData {
    vec<uint8_t> data;
};

Lors de la déclaration d'une instance de ce type (que ce soit dans une structure de données ou en tant que paramètre de méthode), utilisez le nom de type complet :

android.hardware.nfc@1.0::NfcData

La syntaxe générale est PACKAGE @ VERSION :: UDT , où :

  • PACKAGE est le nom séparé par des points d'un package HIDL (par exemple, android.hardware.nfc ).
  • VERSION est le format de version major.minor séparé par des points du package (par exemple, 1.0 ).
  • UDT est le nom séparé par des points d'un UDT HIDL. Étant donné que HIDL prend en charge les UDT imbriqués et que les interfaces HIDL peuvent contenir des UDT (un type de déclaration imbriquée), des points sont utilisés pour accéder aux noms.

Par exemple, si la déclaration imbriquée suivante a été définie dans le fichier de types communs du package android.hardware.example version 1.0 :

// types.hal
package android.hardware.example@1.0;
struct Foo {
    struct Bar {
        // …
    };
    Bar cheers;
};

Le nom complet de Bar est android.hardware.example@1.0::Foo.Bar . Si, en plus d'être dans le package ci-dessus, la déclaration imbriquée se trouvait dans une interface appelée IQuux :

// IQuux.hal
package android.hardware.example@1.0;
interface IQuux {
    struct Foo {
        struct Bar {
            // …
        };
        Bar cheers;
    };
    doSomething(Foo f) generates (Foo.Bar fb);
};

Le nom complet de Bar est android.hardware.example@1.0::IQuux.Foo.Bar .

Dans les deux cas, Bar ne peut être appelé Bar que dans le cadre de la déclaration de Foo . Au niveau du package ou de l'interface, vous devez vous référer à Bar via Foo : Foo.Bar , comme dans la déclaration de la méthode doSomething ci-dessus. Alternativement, vous pouvez déclarer la méthode de manière plus détaillée comme suit :

// IQuux.hal
doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);

Valeurs d'énumération complètes

Si un UDT est un type enum, chaque valeur du type enum a un nom complet qui commence par le nom complet du type enum, suivi de deux-points, puis suivi du nom de la valeur enum. Par exemple, supposons que le package android.hardware.nfc, version 1.0 définit un type d'énumération NfcStatus :

enum NfcStatus {
    STATUS_OK,
    STATUS_FAILED
};

Lorsque vous faites référence à STATUS_OK , le nom complet est :

android.hardware.nfc@1.0::NfcStatus:STATUS_OK

La syntaxe générale est PACKAGE @ VERSION :: UDT : VALUE , où :

  • PACKAGE @ VERSION :: UDT est exactement le même nom qualifié complet pour le type enum.
  • VALUE est le nom de la valeur.

Règles d'auto-inférence

Un nom UDT complet n'a pas besoin d'être spécifié. Un nom UDT peut omettre en toute sécurité les éléments suivants :

  • Le package, par exemple @1.0::IFoo.Type
  • Le package et la version, par exemple IFoo.Type

HIDL tente de compléter le nom en utilisant des règles d'auto-interférence (un numéro de règle inférieur signifie une priorité plus élevée).

Règle 1

Si aucun package ni aucune version ne sont fournis, une recherche de nom local est tentée. Exemple:

interface Nfc {
    typedef string NfcErrorMessage;
    send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m);
};

NfcErrorMessage est recherché localement et le typedef au-dessus est trouvé. NfcData est également recherché localement, mais comme il n'est pas défini localement, les règles 2 et 3 sont utilisées. @1.0::NfcStatus fournit une version, donc la règle 1 ne s'applique pas.

Règle 2

Si la règle 1 échoue et qu'un composant du nom complet est manquant (package, version ou package et version), le composant est rempli automatiquement avec les informations du package actuel. Le compilateur HIDL recherche ensuite dans le fichier actuel (et toutes les importations) pour trouver le nom complet rempli automatiquement. En utilisant l'exemple ci-dessus, supposons que la déclaration de ExtendedNfcData a été faite dans le même package ( android.hardware.nfc ) à la même version ( 1.0 ) que NfcData , comme suit :

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

Le compilateur HIDL remplit le nom du package et le nom de la version du package actuel pour produire le nom UDT complet android.hardware.nfc@1.0::NfcData . Comme le nom existe dans le package courant (en supposant qu'il soit correctement importé), il est utilisé pour la déclaration.

Un nom dans le package actuel est importé uniquement si l'une des conditions suivantes est vraie :

  • Il est importé explicitement avec une instruction d' import .
  • Il est défini dans types.hal dans le package actuel

Le même processus est suivi si NfcData a été qualifié uniquement par le numéro de version :

struct ExtendedNfcData {
    // autofill the current package name (android.hardware.nfc)
    @1.0::NfcData base;
    // … additional members
};

Règle 3

Si la règle 2 ne parvient pas à produire une correspondance (l'UDT n'est pas défini dans le package actuel), le compilateur HIDL recherche une correspondance dans tous les packages importés. En utilisant l'exemple ci-dessus, supposons que ExtendedNfcData est déclaré dans la version 1.1 du package android.hardware.nfc , 1.1 importe 1.0 comme il se doit (voir Extensions au niveau du package ) et la définition spécifie uniquement le nom UDT :

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

Le compilateur recherche tout UDT nommé NfcData et en trouve un dans android.hardware.nfc à la version 1.0 , ce qui donne un UDT complet de android.hardware.nfc@1.0::NfcData . Si plusieurs correspondances sont trouvées pour un UDT partiellement qualifié donné, le compilateur HIDL renvoie une erreur.

Exemple

En utilisant la règle 2, un type importé défini dans le package actuel est préféré à un type importé d'un autre package :

// hardware/interfaces/foo/1.0/types.hal
package android.hardware.foo@1.0;
struct S {};

// hardware/interfaces/foo/1.0/IFooCallback.hal
package android.hardware.foo@1.0;
interface IFooCallback {};

// hardware/interfaces/bar/1.0/types.hal
package android.hardware.bar@1.0;
typedef string S;

// hardware/interfaces/bar/1.0/IFooCallback.hal
package android.hardware.bar@1.0;
interface IFooCallback {};

// hardware/interfaces/bar/1.0/IBar.hal
package android.hardware.bar@1.0;
import android.hardware.foo@1.0;
interface IBar {
    baz1(S s); // android.hardware.bar@1.0::S
    baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback
};
  • S est interpolé comme android.hardware.bar@1.0::S , et se trouve dans bar/1.0/types.hal (car types.hal est automatiquement importé).
  • IFooCallback est interpolé en tant que android.hardware.bar@1.0::IFooCallback en utilisant la règle 2, mais il est introuvable car bar/1.0/IFooCallback.hal n'est pas importé automatiquement (comme types.hal ). Ainsi, la règle 3 le résout en android.hardware.foo@1.0::IFooCallback à la place, qui est importé via import android.hardware.foo@1.0; ).

types.hal

Chaque package HIDL contient un fichier types.hal contenant des UDT partagés entre toutes les interfaces participant à ce package. Les types HIDL sont toujours publics ; qu'un UDT soit déclaré dans types.hal ou dans une déclaration d'interface, ces types sont accessibles en dehors de la portée où ils sont définis. types.hal n'est pas destiné à décrire l'API publique d'un package, mais plutôt à héberger les UDT utilisés par toutes les interfaces du package. En raison de la nature de HIDL, tous les UDT font partie de l'interface.

types.hal se compose d'UDT et d'instructions import . Étant donné que types.hal est mis à la disposition de chaque interface du package (il s'agit d'une importation implicite), ces instructions import sont par définition au niveau du package. Les UDT dans types.hal peuvent également incorporer des UDT et des interfaces ainsi importées.

Par exemple, pour un IFoo.hal :

package android.hardware.foo@1.0;
// whole package import
import android.hardware.bar@1.0;
// types only import
import android.hardware.baz@1.0::types;
// partial imports
import android.hardware.qux@1.0::IQux.Quux;
// partial imports
import android.hardware.quuz@1.0::Quuz;

Sont importés :

  • android.hidl.base@1.0::IBase (implicitement)
  • android.hardware.foo@1.0::types (implicitement)
  • Tout dans android.hardware.bar@1.0 (y compris toutes les interfaces et ses types.hal )
  • types.hal de android.hardware.baz@1.0::types (les interfaces dans android.hardware.baz@1.0 ne sont pas importées)
  • IQux.hal et types.hal de android.hardware.qux@1.0
  • Quuz de android.hardware.quuz@1.0 (en supposant que Quuz est défini dans types.hal , l'intégralité du fichier types.hal est analysé, mais les types autres que Quuz ne sont pas importés).

Gestion des versions au niveau de l'interface

Chaque interface d'un package réside dans son propre fichier. Le package auquel appartient l'interface est déclaré en haut de l'interface à l'aide de l'instruction package . Après la déclaration du package, zéro ou plusieurs importations au niveau de l'interface (partiel ou package entier) peuvent être répertoriées. Par exemple:

package android.hardware.nfc@1.0;

Dans HIDL, les interfaces peuvent hériter d'autres interfaces à l'aide du mot clé extends . Pour qu'une interface puisse étendre une autre interface, elle doit y avoir accès via une instruction d' import . Le nom de l'interface en cours d'extension (l'interface de base) suit les règles de qualification du nom de type expliquées ci-dessus. Une interface ne peut hériter que d'une seule interface ; HIDL ne prend pas en charge l'héritage multiple.

Les exemples de version uprev ci-dessous utilisent le package suivant :

// types.hal
package android.hardware.example@1.0
struct Foo {
    struct Bar {
        vec<uint32_t> val;
    };
};

// IQuux.hal
package android.hardware.example@1.0
interface IQuux {
    fromFooToBar(Foo f) generates (Foo.Bar b);
}

Règles supérieures

Pour définir un package package@major.minor , A ou tout B doit être vrai :

Règle A "Est une version mineure de début": Toutes les versions mineures précédentes, package@major.0 , package@major.1 , …, package@major.(minor-1) ne doivent pas être définies.
OU
Règle B

Tout ce qui suit est vrai :

  1. "La version mineure précédente est valide": package@major.(minor-1) doit être défini et suivre la même règle A (aucun de package@major.0 à package@major.(minor-2) n'est défini) ou la règle B (si c'est un uprev de @major.(minor-2) );

    ET

  2. "Hériter d'au moins une interface avec le même nom" : il existe une interface package@major.minor::IFoo qui étend package@major.(minor-1)::IFoo (si le package précédent a une interface) ;

    ET

  3. "Pas d'interface héritée avec un nom différent": Il ne doit pas exister package@major.minor::IBar qui étend package@major.(minor-1)::IBaz , où IBar et IBaz sont deux noms différents. S'il existe une interface portant le même nom, package@major.minor::IBar doit étendre package@major.(minor-k)::IBar sorte qu'il n'existe aucune IBar avec un k plus petit.

A cause de la règle A :

  • Le package peut commencer par n'importe quel numéro de version mineure (par exemple, android.hardware.biometrics.fingerprint commence à @2.1 .)
  • L'exigence " android.hardware.foo@1.0 n'est pas défini" signifie que le répertoire hardware/interfaces/foo/1.0 ne devrait même pas exister.

Cependant, la règle A n'affecte pas un package avec le même nom de package mais une version majeure différente (par exemple, android.hardware.camera.device a à la fois @1.0 et @3.2 définis ; @3.2 n'a pas besoin d'interagir avec @1.0 .) Par conséquent, @3.2::IExtFoo peut étendre @1.0::IFoo .

Si le nom du package est différent, package@major.minor::IBar peut s'étendre à partir d'une interface avec un nom différent (par exemple, android.hardware.bar@1.0::IBar peut étendre android.hardware.baz@2.2::IBaz ). Si une interface ne déclare pas explicitement un super type avec le mot-clé extend , elle étendra android.hidl.base@1.0::IBase (sauf IBase lui-même).

B.2 et B.3 doivent être suivis en même temps. Par exemple, même si android.hardware.foo@1.1::IFoo étend android.hardware.foo@1.0::IFoo pour passer la règle B.2, si android.hardware.foo@1.1::IExtBar étend android.hardware.foo@1.0::IBar , ce n'est toujours pas un uprev valide.

Amélioration des interfaces

Pour uprev android.hardware.example@1.0 (défini ci-dessus) à @1.1 :

// types.hal
package android.hardware.example@1.1;
import android.hardware.example@1.0;

// IQuux.hal
package android.hardware.example@1.1
interface IQuux extends @1.0::IQuux {
    fromBarToFoo(Foo.Bar b) generates (Foo f);
}

Il s'agit d'une import au niveau du package de la version 1.0 de android.hardware.example dans types.hal . Bien qu'aucun nouvel UDT ne soit ajouté dans la version 1.1 du package, les références aux UDT de la version 1.0 sont toujours nécessaires, d'où l'importation au niveau du package dans types.hal . (Le même effet aurait pu être obtenu avec une importation au niveau de l'interface dans IQuux.hal .)

Dans extends @1.0::IQuux dans la déclaration de IQuux , nous avons spécifié la version d' IQuux qui est héritée (la désambiguïsation est requise car IQuux est utilisé pour déclarer une interface et hériter d'une interface). Comme les déclarations sont simplement des noms qui héritent de tous les attributs de package et de version sur le site de la déclaration, la désambiguïsation doit être dans le nom de l'interface de base ; nous aurions également pu utiliser l'UDT pleinement qualifié, mais cela aurait été redondant.

La nouvelle interface IQuux ne re-déclare pas la méthode fromFooToBar() elle hérite de @1.0::IQuux ; il répertorie simplement la nouvelle méthode qu'il ajoute fromBarToFoo() . Dans HIDL, les méthodes héritées ne peuvent pas être déclarées à nouveau dans les interfaces enfants, de sorte que l'interface IQuux ne peut pas déclarer explicitement la méthode fromFooToBar() .

Conventions précédentes

Parfois, les noms d'interface doivent renommer l'interface d'extension. Nous recommandons que les extensions, les structures et les unions enum aient le même nom que ce qu'elles étendent, à moins qu'elles ne soient suffisamment différentes pour justifier un nouveau nom. Exemples:

// in parent hal file
enum Brightness : uint32_t { NONE, WHITE };

// in child hal file extending the existing set with additional similar values
enum Brightness : @1.0::Brightness { AUTOMATIC };

// extending the existing set with values that require a new, more descriptive name:
enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };

Si une méthode peut avoir un nouveau nom sémantique (par exemple fooWithLocation ), alors c'est préférable. Sinon, il doit être nommé de la même manière que ce qu'il étend. Par exemple, la méthode foo_1_1 dans @1.1::IFoo peut remplacer la fonctionnalité de la méthode foo dans @1.0::IFoo s'il n'y a pas de meilleur nom alternatif.

Gestion des versions au niveau du package

La gestion des versions HIDL se produit au niveau du package ; après la publication d'un package, il est immuable (son ensemble d'interfaces et d'UDT ne peut pas être modifié). Les packages peuvent être liés les uns aux autres de plusieurs manières, qui sont toutes exprimables via une combinaison d'héritage au niveau de l'interface et de construction d'UDT par composition.

Cependant, un type de relation est strictement défini et doit être appliqué : Héritage rétrocompatible au niveau du package . Dans ce scénario, le package parent est le package hérité et le package enfant est celui qui étend le parent. Les règles d'héritage rétrocompatibles au niveau du package sont les suivantes :

  1. Toutes les interfaces de niveau supérieur du package parent sont héritées des interfaces du package enfant.
  2. De nouvelles interfaces peuvent également être ajoutées au nouveau package (aucune restriction concernant les relations avec d'autres interfaces dans d'autres packages).
  3. De nouveaux types de données peuvent également être ajoutés pour être utilisés soit par de nouvelles méthodes d'interfaces existantes mises à jour, soit par de nouvelles interfaces.

Ces règles peuvent être implémentées à l'aide de l'héritage au niveau de l'interface HIDL et de la composition UDT, mais nécessitent des connaissances au niveau méta pour savoir que ces relations constituent une extension de package rétrocompatible. Cette connaissance est déduite comme suit :

Si un paquet répond à cette exigence, hidl-gen applique les règles de rétrocompatibilité.