Gestion des versions

HIDL nécessite que chaque interface écrite en HIDL soit versionnée. Une fois qu'une interface HAL est publiée, elle est gelée et toute modification ultérieure doit être apportée à une nouvelle version de cette interface. Même 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 transmis 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 qu'élément 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.
  • Paquets . Organise les interfaces HIDL associées et les types de données sur lesquels elles fonctionnent. 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 du 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, et non au niveau de l'interface, et toutes les interfaces et UDT d'un package partagent la même version. Les versions de package suivent le versionnement sémantique sans les composants de niveau de correctif et de métadonnées de construction. Dans un package donné, une modification de version mineure implique que la nouvelle version du package est rétrocompatible avec l'ancien package et une modification 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 mises à niveau de version mineure (prochaine révision incrémentée) d'un package ; 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 améliorées, soit par de nouvelles interfaces.
  • Extensibilité rétrocompatible au niveau de l'interface . Le nouveau package peut également étendre le package d'origine en étant constitué d'interfaces logiquement distinctes qui fournissent simplement des fonctionnalités supplémentaires, et non la fonctionnalité principale. À cette fin, 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.
  • Étendre l'incompatibilité ascendante d'origine . Il s’agit d’une version majeure du package et il n’est pas nécessaire qu’il y ait une corrélation entre les deux. Dans la mesure où il existe, 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.

Structuration des interfaces

Pour une interface bien structurée, l'ajout de nouveaux types de fonctionnalités qui ne font pas partie de la conception originale devrait nécessiter une modification de l'interface HIDL. À l’inverse, si vous pouvez ou prévoyez apporter une modification des deux côtés de l’interface introduisant de nouvelles fonctionnalités sans modifier l’interface elle-même, alors 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 appareil et le system.img peuvent être compilés séparément. Toutes les interactions entre vendor.img et system.img doivent être explicitement et minutieusement 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 limite system.img / vendor.img .

Exigences

Toutes les données transmises via HIDL doivent être explicitement définies. Pour garantir qu'une implémentation et que le client puissent 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 énumérations de structures, 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.
  • Il peut s'agir de 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 vendor.img donne à un composant du 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 être interprété. Lors de la transmission d'une valeur de vendor.img au code du fournisseur sur system.img ou vers un autre appareil, le format des données et la manière 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 consulter 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'intégrer aux clients avec lesquels elles sont développées.

Disposition du code HIDL

HIDL comprend des packages de base et de fournisseur.

Les interfaces Core HIDL 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 s'appelle 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 package affecte son emplacement dans l'arborescence des sources.

Tous les packages de base sont placés sous hardware/interfaces/ dans le système de build. Le package android.hardware. [ name1 ].[ name2 ]… à la version $m.$n est 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 du 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 de SoC ou à un OEM/ODM. Cela correspond au chemin vendor/$(VENDOR)/interfaces dans l'arborescence (ce mappage est également codé en dur).

Noms de type complets définis par l'utilisateur

Dans HIDL, chaque UDT possède 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 les 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éfinisse 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;
};

Lorsque vous déclarez 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 :

// 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, la version 1.0 définit un type d'énumération NfcStatus :

enum NfcStatus {
    STATUS_OK,
    STATUS_FAILED
};

Lorsqu'on fait 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 complet pour le type enum.
  • VALUE est le nom de la valeur.

Règles d'auto-inférence

Il n'est pas nécessaire de spécifier un nom UDT complet. Un nom UDT peut omettre en toute sécurité les éléments suivants :

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

HIDL tente de compléter le nom à l'aide de 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 version n'est fourni, 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 automatiquement rempli avec les informations du package actuel. Le compilateur HIDL recherche ensuite dans le fichier actuel (et toutes les importations) le nom complet rempli automatiquement. En utilisant l'exemple ci-dessus, supposons que la déclaration de ExtendedNfcData a été effectuée dans le même package ( android.hardware.nfc ) avec 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 actuel (en supposant qu'il soit importé correctement), 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 import .
  • Il est défini dans types.hal dans le package actuel

Le même processus est suivi si NfcData était qualifié par le seul 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 que 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 pleinement qualifié 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 favorisé par rapport à 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é sous la forme android.hardware.bar@1.0::S et se trouve dans bar/1.0/types.hal (car types.hal est automatiquement importé).
  • IFooCallback est interpolé comme 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 l'est 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 ; peu importe si un UDT est 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 à 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 les UDT et les 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;

Les éléments suivants 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 soit 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 . Suite à la déclaration du package, zéro ou plusieurs importations au niveau de l'interface (package partiel ou complet) peuvent être répertoriées. Par exemple:

package android.hardware.nfc@1.0;

En HIDL, les interfaces peuvent hériter d’autres interfaces à l’aide du mot-clé extends . Pour qu’une interface étende une autre interface, elle doit y avoir accès via une instruction 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 Uprév

Pour définir un package package@major.minor , A ou tous les B doivent être vrais :

Règle A "Est une version mineure de démarrage" : 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 même règle B (s'il s'agit d'une uprev de @major.(minor-2) );

    ET

  2. "Hériter d'au moins une interface du 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. "Aucune 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 du même nom, package@major.minor::IBar doit étendre package@major.(minor-k)::IBar de telle sorte qu'aucun IBar n'existe avec un k plus petit.

En raison de la règle A :

  • Le package peut commencer par n'importe quel numéro de version mineur (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 portant 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 .

À condition que le nom du package soit 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 transmettre la règle B.2, si android.hardware.foo@1.1::IExtBar étend android.hardware.foo@1.0::IBar , ce n'est toujours pas une version supérieure valide.

Interfaces améliorées

Pour faire passer 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 dans 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 (une homonymie 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() dont elle hérite de @1.0::IQuux ; il répertorie simplement la nouvelle méthode qu'il ajoute fromBarToFoo() . En HIDL, les méthodes héritées ne peuvent pas être déclarées à nouveau dans les interfaces enfants, donc l'interface IQuux ne peut pas déclarer explicitement la méthode fromFooToBar() .

Conventions Uprév

Parfois, les noms d’interface doivent renommer l’interface d’extension. Nous recommandons que les extensions, structures et unions d'énumération portent 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 cela est préféré. Sinon, il devrait ê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 ; une fois qu'un package est publié, 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 peuvent toutes être exprimées 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é : l'héritage rétrocompatible au niveau du package . Dans ce scénario, le package parent est le package dont on hérite et le package enfant est celui qui étend le parent. Les règles d'héritage rétrocompatible 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 sur 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 améliorées, 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 se déduit de la façon suivante :

Si un package répond à cette exigence, hidl-gen applique les règles de compatibilité ascendante.