Guide de style du code

Le style de code HIDL ressemble au code C++ du framework Android, avec des retraits de quatre espaces et des noms de fichiers en casse mixte. Les déclarations de package, les importations et les docstrings sont semblables à ceux de Java, avec de légères modifications.

Les exemples suivants pour IFoo.hal et types.hal illustrent les styles de code HIDL et fournissent des liens rapides vers des informations détaillées sur chaque style (IFooClientCallback.hal, IBar.hal et IBaz.hal ont été omis).

hardware/interfaces/foo/1.0/IFoo.hal
/*
 * (License Notice)
 */

package android.hardware.foo@1.0;

import android.hardware.bar@1.0::IBar;

import IBaz;
import IFooClientCallback;

/**
 * IFoo is an interface that*/
interface IFoo {

    /**
     * This is a multiline docstring.
     *
     * @return result 0 if successful, nonzero otherwise.
     */
     foo() generates (FooStatus result);

    /**
     * Restart controller by power cycle.
     *
     * @param bar callback interface that* @return result 0 if successful, nonzero otherwise.
     */
    powerCycle(IBar bar) generates (FooStatus result);

    /** Single line docstring. */
    baz();


    /**
     * The bar function.
     *
     * @param clientCallback callback after function is called
     * @param baz related baz object
     * @param data input data blob
     */
    bar(IFooClientCallback clientCallback,
        IBaz baz,
        FooData data);

};
hardware/interfaces/foo/1.0/types.hal
/*
 * (License Notice)
 */

package android.hardware.foo@1.0;

/** Replied status. */
enum Status : int32_t {
    OK,
    /* invalid arguments */
    ERR_ARG,
    /* note, no transport related errors */
    ERR_UNKNOWN = -1,
};

struct ArgData {
    int32_t[20]  someArray;
    vec<uint8_t> data;
};

Conventions d'attribution de noms

Les noms de fonctions, de variables et de fichiers doivent être descriptifs. Évitez les abréviations excessives. Traitez les acronymes comme des mots (par exemple, utilisez INfc au lieu de INFC).

Structure des répertoires et nommage des fichiers

La structure de répertoire doit se présenter comme suit :

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (facultatif, peut comporter plusieurs niveaux)
        • VERSION
          • Android.mk
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal (facultatif)

Où :

  • ROOT-DIRECTORY :
    • hardware/interfaces pour les packages HIDL de base.
    • vendor/VENDOR/interfaces pour les packages de fournisseurs, où VENDOR fait référence à un fournisseur de SoC ou à un OEM/ODM.
  • MODULE doit être un mot en minuscules décrivant le sous-système (par exemple, nfc). Si plusieurs mots sont nécessaires, utilisez SUBMODULE imbriqués. Il peut y avoir plusieurs niveaux d'imbrication.
  • VERSION doit être exactement la même version (majeure.mineure) que celle décrite dans Versions.
  • IINTERFACE_X doit être le nom de l'interface avec UpperCamelCase/PascalCase (par exemple, INfc), comme décrit dans Noms d'interface.

Exemple :

  • hardware/interfaces
    • nfc
      • 1.0
        • Android.mk
        • INfc.hal
        • INfcClientCallback.hal
        • types.hal

Remarque : Tous les fichiers doivent disposer d'autorisations non exécutables (dans Git).

Noms des packages

Les noms de packages doivent utiliser le format de nom complet (FQN, Fully Qualified Name) suivant (appelé PACKAGE-NAME) :

PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[]]]@VERSION

Où :

  • PACKAGE est le package qui correspond à ROOT-DIRECTORY. En particulier,PACKAGE est :
    • android.hardware pour les packages HIDL de base (correspondant à hardware/interfaces).
    • vendor.VENDOR.hardware pour les packages de fournisseurs, où VENDOR fait référence à un fournisseur de SoC ou à un OEM/ODM (mappage sur vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION sont exactement les mêmes noms de dossiers que ceux de la structure décrite dans Structure des répertoires.
  • Les noms de package doivent être en minuscules. S'ils sont composés de plusieurs mots, ils doivent être utilisés comme sous-modules ou écrits en snake_case.
  • Les espaces ne sont pas autorisés.

Le nom complet est toujours utilisé dans les déclarations de package.

Versions

Les versions doivent respecter le format suivant :

MAJOR.MINOR

Les versions MAJOR et MINOR doivent être des nombres entiers. HIDL utilise des règles de gestion sémantique des versions.

Importations

Une importation peut avoir l'un des trois formats suivants :

  • Importations de packages entiers : import PACKAGE-NAME;
  • Importations partielles : import PACKAGE-NAME::UDT; (ou import UDT; si le type importé se trouve dans le même package)
  • Importations de types uniquement : import PACKAGE-NAME::types;

PACKAGE-NAME suit le format décrit dans Noms de packages. Le types.hal du package actuel (s'il existe) est automatiquement importé (ne l'importez pas explicitement).

Noms complets

N'utilisez les noms complets pour l'importation d'un type défini par l'utilisateur que lorsque cela est nécessaire. Omettez PACKAGE-NAME si le type d'importation se trouve dans le même package. Un FQN ne doit pas contenir d'espaces. Exemple de nom complet :

android.hardware.nfc@1.0::INfcClientCallback

Dans un autre fichier sous android.hardware.nfc@1.0, faites référence à l'interface ci-dessus en tant que INfcClientCallback. Sinon, utilisez uniquement le nom complet.

Regrouper et ordonner les importations

Utilisez une ligne vide après la déclaration du package (avant les importations). Chaque importation doit occuper une seule ligne et ne doit pas être mise en retrait. Regroupez les importations dans l'ordre suivant :

  1. Autres packages android.hardware (utilisez les noms complets).
  2. Autres packages vendor.VENDOR (utilisez des noms complets).
    • Chaque fournisseur doit être un groupe.
    • Triez les fournisseurs par ordre alphabétique.
  3. Importations à partir d'autres interfaces du même package (utiliser des noms simples).

Laissez une ligne vide entre les groupes. Dans chaque groupe, triez les importations par ordre alphabétique. Exemple :

import android.hardware.nfc@1.0::INfc;
import android.hardware.nfc@1.0::INfcClientCallback;

/* Importing the whole module. */
import vendor.barvendor.bar@3.1;

import vendor.foovendor.foo@2.2::IFooBar;
import vendor.foovendor.foo@2.2::IFooFoo;

import IBar;
import IFoo;

Noms d'interface

Les noms d'interface doivent commencer par I, suivi d'un nom UpperCamelCase/PascalCase. Une interface nommée IFoo doit être définie dans le fichier IFoo.hal. Ce fichier ne peut contenir que des définitions pour l'interface IFoo (l'interface INAME doit se trouver dans INAME.hal).

Fonctions

Pour les noms de fonctions, les arguments et les noms de variables de retour, utilisez lowerCamelCase. Exemple :

open(INfcClientCallback clientCallback) generates (int32_t retVal);
oneway pingAlive(IFooCallback cb);

Noms des champs struct et union

Pour les noms de champs structurés ou d'union, utilisez lowerCamelCase. Exemple :

struct FooReply {
    vec<uint8_t> replyData;
}

Noms des types

Les noms de types font référence aux définitions de struct ou d'union, aux définitions de types enum et aux typedef. Pour ces noms, utilisez UpperCamelCase/PascalCase. Exemples :

enum NfcStatus : int32_t {
    /*...*/
};
struct NfcData {
    /*...*/
};

Valeurs enum

Les valeurs d'énumération doivent être UPPER_CASE_WITH_UNDERSCORES. Lorsque vous transmettez des valeurs d'énumération en tant qu'arguments de fonction et que vous les renvoyez en tant que retours de fonction, utilisez le type d'énumération réel (et non le type entier sous-jacent). Exemple :

enum NfcStatus : int32_t {
    HAL_NFC_STATUS_OK               = 0,
    HAL_NFC_STATUS_FAILED           = 1,
    HAL_NFC_STATUS_ERR_TRANSPORT    = 2,
    HAL_NFC_STATUS_ERR_CMD_TIMEOUT  = 3,
    HAL_NFC_STATUS_REFUSED          = 4
};

Remarque : Le type sous-jacent d'un type d'énumération est explicitement déclaré après les deux-points. Comme il ne dépend pas du compilateur, il est plus clair d'utiliser le type d'énumération réel.

Pour les noms complets des valeurs enum, un deux-points est utilisé entre le nom du type enum et le nom de la valeur enum :

PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME

Il ne doit pas y avoir d'espaces dans un nom complet. N'utilisez un nom complet que lorsque cela est nécessaire et omettez les parties inutiles. Exemple :

android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK

Commentaires

Pour un commentaire sur une seule ligne, //, /* */ et /** */ conviennent.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Utilisez /* */ pour les commentaires. Bien que HIDL accepte // pour les commentaires, ils sont déconseillés, car ils n'apparaissent pas dans la sortie générée.
  • Utilisez /** */ pour la documentation générée. Elles ne peuvent être appliquées qu'aux déclarations de type, de méthode, de champ et de valeur d'énumération. Exemple :
    /** Replied status */
    enum TeleportStatus {
        /** Object entirely teleported. */
        OK              = 0,
        /** Methods return this if teleportation is not completed. */
        ERROR_TELEPORT  = 1,
        /**
         * Teleportation could not be completed due to an object
         * obstructing the path.
         */
        ERROR_OBJECT    = 2,
        ...
    }
  • Commencez les commentaires sur plusieurs lignes par /** sur une ligne distincte. Utilisez * au début de chaque ligne. Ajoutez */ sur une ligne distincte à la fin du commentaire, en alignant les astérisques. Exemple :
    /**
     * My multi-line
     * comment
     */
  • Les mentions de licence et les journaux des modifications doivent commencer sur une nouvelle ligne avec /* (un seul astérisque), utiliser * au début de chaque ligne et placer */ sur la dernière ligne seule (les astérisques doivent être alignés). Exemple :
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */

Commentaires sur les fichiers

Commencez chaque fichier par la mention de licence appropriée. Pour les HAL de base, il s'agit de la licence Apache AOSP dans development/docs/copyright-templates/c.txt. N'oubliez pas de mettre à jour l'année et d'utiliser des commentaires multilignes de style /* */ comme expliqué ci-dessus.

Vous pouvez éventuellement insérer une ligne vide après la mention de licence, suivie d'un journal des modifications/d'informations sur le versionnage. Utilisez des commentaires multilignes de style /* */ comme expliqué ci-dessus, placez la ligne vide après le journal des modifications, puis suivez avec la déclaration de package.

Commentaires TODO

Les tâches à faire doivent inclure la chaîne TODO en majuscules, suivie d'un deux-points. Exemple :

// TODO: remove this code before foo is checked in.

Les commentaires TODO ne sont autorisés que pendant le développement. Ils ne doivent pas figurer dans les interfaces publiées.

Commentaires sur l'interface et les fonctions (docstrings)

Utilisez /** */ pour les docstrings sur une ou plusieurs lignes. N'utilisez pas // pour les docstrings.

Les chaînes de documentation des interfaces doivent décrire les mécanismes généraux de l'interface, la logique de conception, l'objectif, etc. Les chaînes de documentation des fonctions doivent être spécifiques à la fonction (la documentation au niveau du package se trouve dans un fichier README du répertoire du package).

/**
 * IFooController is the controller for foos.
 */
interface IFooController {
    /**
     * Opens the controller.
     *
     * @return status HAL_FOO_OK if successful.
     */
    open() generates (FooStatus status);

    /** Close the controller. */
    close();
};

Vous devez ajouter des @param et des @return pour chaque paramètre/valeur renvoyée :

  • @param doit être ajouté pour chaque paramètre. Il doit être suivi du nom du paramètre, puis de la chaîne de documentation.
  • @return doit être ajouté pour chaque valeur renvoyée. Il doit être suivi du nom de la valeur renvoyée, puis de la docstring.

Exemple :

/**
 * Explain what foo does.
 *
 * @param arg1 explain what arg1 is
 * @param arg2 explain what arg2 is
 * @return ret1 explain what ret1 is
 * @return ret2 explain what ret2 is
 */
foo(T arg1, T arg2) generates (S ret1, S ret2);

Règles de mise en forme

Voici quelques règles de mise en forme générales :

  • Longueur de ligne : Chaque ligne de texte ne doit pas dépasser 100 colonnes.
  • Espaces blancs : Aucun espace blanc en fin de ligne. Les lignes vides ne doivent pas contenir d'espaces blancs.
  • Espaces vs tabulations N'utilisez que des espaces.
  • Taille du retrait. Utilisez 4 espaces pour les blocs et 8 espaces pour les retours à la ligne.
  • Brace : À l'exception des valeurs d'annotation, une accolade ouvrante se trouve sur la même ligne que le code précédent, mais une accolade fermante et le point-virgule suivant occupent toute la ligne. Exemple :
    interface INfc {
        close();
    };

Déclaration de package

La déclaration du package doit se trouver en haut du fichier après la mention de licence, doit occuper toute la ligne et ne doit pas être mise en retrait. Les packages sont déclarés au format suivant (pour le format des noms, consultez Noms de packages) :

package PACKAGE-NAME;

Exemple :

package android.hardware.nfc@1.0;

Déclarations de fonctions

Le nom de la fonction, les paramètres, generates et les valeurs de retour doivent figurer sur la même ligne s'ils tiennent. Exemple :

interface IFoo {
    /** ... */
    easyMethod(int32_t data) generates (int32_t result);
};

S'ils ne tiennent pas sur la même ligne, essayez de placer les paramètres et les valeurs renvoyées au même niveau d'indentation et de distinguer generate pour aider le lecteur à identifier rapidement les paramètres et les valeurs renvoyées. Exemple :

interface IFoo {
    suchALongMethodThatCannotFitInOneLine(int32_t theFirstVeryLongParameter,
                                          int32_t anotherVeryLongParameter);
    anEvenLongerMethodThatCannotFitInOneLine(int32_t theFirstLongParameter,
                                             int32_t anotherVeryLongParameter)
                                  generates (int32_t theFirstReturnValue,
                                             int32_t anotherReturnValue);
    superSuperSuperSuperSuperSuperSuperLongMethodThatYouWillHateToType(
            int32_t theFirstVeryLongParameter, // 8 spaces
            int32_t anotherVeryLongParameter
        ) generates (
            int32_t theFirstReturnValue,
            int32_t anotherReturnValue
        );
    /* method name is even shorter than 'generates' */
    foobar(AReallyReallyLongType aReallyReallyLongParameter,
           AReallyReallyLongType anotherReallyReallyLongParameter)
        generates (ASuperLongType aSuperLongReturnValue, // 4 spaces
                   ASuperLongType anotherSuperLongReturnValue);
}

Informations supplémentaires :

  • Une parenthèse ouvrante se trouve toujours sur la même ligne que le nom de la fonction.
  • Aucun espace entre le nom de la fonction et la parenthèse ouvrante.
  • Aucun espace entre les parenthèses et les paramètres, sauf lorsqu'il y a des retours à la ligne entre eux.
  • Si generates se trouve sur la même ligne que la parenthèse fermante précédente, utilisez un espace avant. Si generates se trouve sur la même ligne que la parenthèse ouvrante suivante, ajoutez un espace.
  • Alignez tous les paramètres et les valeurs renvoyées (si possible).
  • L'indentation par défaut est de quatre espaces.
  • Les paramètres encapsulés sont alignés sur les premiers paramètres de la ligne précédente. Sinon, ils sont indentés de huit espaces.

Annotations

Utilisez le format suivant pour les annotations :

@annotate(keyword = value, keyword = {value, value, value})

Triez les annotations par ordre alphabétique et utilisez des espaces autour des signes égal. Exemple :

@callflow(key = value)
@entry
@exit

Assurez-vous qu'une annotation occupe toute la ligne. Exemples :

/* Good */
@entry
@exit

/* Bad */
@entry @exit

Si les annotations ne tiennent pas sur la même ligne, mettez-les en retrait de huit espaces. Exemple :

@annotate(
        keyword = value,
        keyword = {
                value,
                value
        },
        keyword = value)

Si l'ensemble du tableau de valeurs ne tient pas sur la même ligne, insérez des sauts de ligne après les accolades ouvrantes { et après chaque virgule à l'intérieur du tableau. Placez la parenthèse fermante immédiatement après la dernière valeur. Ne mettez pas d'accolades s'il n'y a qu'une seule valeur.

Si l'ensemble du tableau de valeurs peut tenir sur la même ligne, n'utilisez pas d'espaces après les accolades ouvrantes ni avant les accolades fermantes, et utilisez un espace après chaque virgule. Exemples :

/* Good */
@callflow(key = {"val", "val"})

/* Bad */
@callflow(key = { "val","val" })

Il ne doit PAS y avoir de lignes vides entre les annotations et la déclaration de fonction. Exemples :

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Déclarations d'énumération

Utilisez les règles suivantes pour les déclarations d'énumération :

  • Si les déclarations d'énumération sont partagées avec un autre package, placez-les dans types.hal plutôt que de les intégrer dans une interface.
  • Utilisez un espace avant et après le deux-points, et un espace après le type sous-jacent avant l'accolade ouvrante.
  • La dernière valeur d'énumération ne comporte peut-être pas de virgule supplémentaire.

Déclarations de structure

Utilisez les règles suivantes pour les déclarations structurées :

  • Si les déclarations de struct sont partagées avec un autre package, placez-les dans types.hal plutôt que de les intégrer dans une interface.
  • Utilisez un espace après le nom du type struct avant l'accolade ouvrante.
  • Alignez les noms de champs (facultatif). Exemple :
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }

Déclarations de tableaux

N'insérez pas d'espaces entre les éléments suivants :

  • Type d'élément et crochet ouvrant.
  • Crochet ouvrant et taille du tableau.
  • Taille du tableau et crochet fermant.
  • Crochet fermant et crochet ouvrant suivant, s'il existe plusieurs dimensions.

Exemples :

/* Good */
int32_t[5] array;

/* Good */
int32_t[5][6] multiDimArray;

/* Bad */
int32_t [ 5 ] [ 6 ] array;

Vecteurs

N'insérez pas d'espaces entre les éléments suivants :

  • vec et un chevron ouvrant.
  • Chevron gauche et type d'élément (Exception : le type d'élément est également un vec).
  • Type d'élément et chevron fermant (exception : le type d'élément est également un vec).

Exemples :

/* Good */
vec<int32_t> array;

/* Good */
vec<vec<int32_t>> array;

/* Good */
vec< vec<int32_t> > array;

/* Bad */
vec < int32_t > array;

/* Bad */
vec < vec < int32_t > > array;