Guide de style de code

Le style de code HIDL ressemble au code C++ dans le framework Android, avec des retraits à 4 espaces et des noms de fichiers à casse mixte. Les déclarations de packages, les importations et les docstrings sont similaires à celles 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 détails 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 de nommage

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 dénomination des fichiers

La structure des répertoires doit apparaître comme suit :

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (facultatif, peut comporter plusieurs niveaux)
        • VERSION
          • Android.mk
          • I INTERFACE_1 .hal
          • I INTERFACE_2 .hal
          • I INTERFACE_N .hal
          • types.hal (facultatif)

Où:

  • ROOT-DIRECTORY est :
    • 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 minuscule décrivant le sous-système (par exemple nfc ). Si plusieurs mots sont nécessaires, utilisez SUBMODULE imbriqué. Il peut y avoir plusieurs niveaux d'imbrication.
  • VERSION doit être exactement la même version (major.minor) que celle décrite dans Versions .
  • I INTERFACE_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) suivant (appelé PACKAGE-NAME ) :

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

Où:

  • PACKAGE est le package qui correspond au ROOT-DIRECTORY . En particulier, PACKAGE c'est :
    • android.hardware pour les packages HIDL de base (mappage avec 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 vers vendor/ VENDOR /interfaces ).
  • MODULE [. SUBMODULE [. SUBMODULE […]]]@ VERSION sont exactement les mêmes noms de dossier dans la structure décrite dans Structure du répertoire .
  • Les noms de packages doivent être en minuscules. S'ils comportent plus d'un mot, les mots doivent être utilisés comme sous-modules ou écrits dans snake_case .
  • Aucun espace n'est autorisé.

Le FQN est toujours utilisé dans les déclarations de packages.

Versions

Les versions doivent avoir le format suivant :

MAJOR.MINOR

Les versions MAJOR et MINOR doivent être un seul entier. HIDL utilise des règles de gestion de versions sémantiques .

Importations

Une importation a l'un des trois formats suivants :

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

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

Noms pleinement qualifiés (FQN)

Utilisez des noms complets pour une importation de type définie par l'utilisateur uniquement 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 comme INfcClientCallback . Sinon, utilisez uniquement le nom complet.

Regrouper et trier 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 en retrait. Regroupez les importations dans l’ordre suivant :

  1. Autres packages android.hardware (utilisez des noms complets).
  2. Autre vendor. VENDOR Packages vendor. VENDOR (utilisez des noms complets).
    • Chaque vendeur doit être un groupe.
    • Classez les fournisseurs par ordre alphabétique.
  3. Importations depuis d'autres interfaces dans le même package (utilisez des noms simples).

Utilisez une ligne vide entre les groupes. À l’intérieur de 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 un I , suivi d'un nom UpperCamelCase / PascalCase . Une interface avec le nom 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 I NAME doit être au format I NAME .hal ).

Les 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 de champs de structure/union

Pour les noms de champs struct/union, utilisez lowerCamelCase . Exemple:

struct FooReply {
    vec<uint8_t> replyData;
}

Tapez les noms

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

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

Valeurs d'énumération

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 enum est explicitement déclaré après les deux points. Comme il ne dépend pas du compilateur, l’utilisation du type enum réel est plus claire.

Pour les noms complets pour les valeurs d'énumération, deux points sont utilisés entre le nom du type d'énumération et le nom de la valeur d'énumération :

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

Il ne doit pas y avoir d'espaces à l'intérieur d'un nom complet. Utilisez un nom complet uniquement 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 prenne en charge // 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. Celles-ci 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 avec /** sur une ligne séparée. Utilisez * au début de chaque ligne. Terminez le commentaire par */ sur une ligne séparée, en alignant les astérisques. Exemple :
    /**
     * My multi-line
     * comment
     */
    
  • L'avis de licence et les journaux de modifications doivent commencer une nouvelle ligne avec /* (un seul astérisque), utiliser * au début de chaque ligne et placer */ seul sur la dernière ligne (les astérisques doivent s'aligner). Exemple :
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */
    

Commentaires du fichier

Démarrez chaque fichier avec l'avis de licence approprié. Pour les HAL principaux, il doit s'agir de la licence AOSP Apache dans development/docs/copyright-templates/c.txt . N'oubliez pas de mettre à jour l'année et d'utiliser les commentaires multilignes de style /* */ comme expliqué ci-dessus.

Vous pouvez éventuellement placer une ligne vide après l'avis de licence, suivie d'informations sur le journal des modifications/versions. Utilisez les commentaires multilignes de style /* */ comme expliqué ci-dessus, placez la ligne vide après le journal des modifications, puis suivez la déclaration du package.

Commentaires à faire

Les TODO doivent inclure la chaîne TODO en majuscules suivie de 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 exister dans les interfaces publiées.

Commentaires sur l'interface/fonction (docstrings)

Utilisez /** */ pour les docstrings multilignes et sur une seule ligne. N'utilisez pas // pour les docstrings.

Les docstrings pour les interfaces doivent décrire les mécanismes généraux de l'interface, la justification de la conception, l'objectif, etc. Les docstrings pour les fonctions doivent être spécifiques à la fonction (la documentation au niveau du package se trouve dans un fichier README dans le 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 @param s et @return s pour chaque paramètre/valeur de retour :

  • @param doit être ajouté pour chaque paramètre. Il doit être suivi du nom du paramètre puis de la docstring.
  • @return doit être ajouté pour chaque valeur de retour. Il doit être suivi du nom de la valeur de retour 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);

Mise en page

Les règles générales de formatage incluent :

  • Longueur de la ligne . Chaque ligne de texte doit comporter au maximum 100 colonnes.
  • Espaces blancs . Pas d'espace de fin sur les lignes ; les lignes vides ne doivent pas contenir d'espaces.
  • Espaces contre tabulations . Utilisez uniquement des espaces.
  • Taille du retrait . Utilisez 4 espaces pour les blocs et 8 espaces pour les retours à la ligne
  • Renforcement . À l'exception des valeurs d'annotation , une accolade ouvrante va 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 du paquet

La déclaration du package doit figurer en haut du fichier après l'avis de licence, doit occuper toute la ligne et ne doit pas être en retrait. Les packages sont déclarés en utilisant le format suivant (pour le formatage des noms, voir Noms des packages ) :

package PACKAGE-NAME;

Exemple:

package android.hardware.nfc@1.0;

Déclarations de fonctions

Le nom de la fonction, les paramètres, les valeurs generates et les valeurs de retour doivent être sur la même ligne s'ils correspondent. 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 de retour dans le même niveau de retrait et faites la distinction generate pour aider le lecteur à voir rapidement les paramètres et les valeurs de retour. 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);
}

Détails supplémentaires:

  • Une parenthèse ouverte 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 ouverte.
  • Pas d'espace entre les parenthèses et les paramètres sauf lorsqu'il y a des sauts de ligne entre eux.
  • Si generates est sur la même ligne que la parenthèse fermante précédente, utilisez un espace précédent. Si generates est sur la même ligne que la parenthèse ouverte suivante, faites suivre d'un espace.
  • Alignez tous les paramètres et les valeurs de retour (si possible).
  • L'indentation par défaut est de 4 espaces.
  • Les paramètres enveloppés sont alignés sur les premiers paramètres de la ligne précédente, sinon ils ont un retrait de 8 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 peuvent pas tenir sur la même ligne, indentez de 8 espaces. Exemple:

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

Si l'ensemble du tableau de valeurs ne peut pas tenir sur la même ligne, placez des sauts de ligne après les accolades ouvertes { et après chaque virgule à l'intérieur du tableau. Placez une parenthèse fermante immédiatement après la dernière valeur. Ne mettez pas les 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 ouvertes et 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 enum :

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

Déclarations structurelles

Utilisez les règles suivantes pour les déclarations de structure :

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

Déclarations de tableaux

Ne mettez pas d'espaces entre les éléments suivants :

  • Type d'élément et crochet ouvert.
  • Crochet ouvert et taille du tableau.
  • Taille du tableau et crochet fermé.
  • Fermez le crochet et ouvrez le crochet 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

Ne mettez pas d'espaces entre les éléments suivants :

  • vec et équerre ouverte.
  • Support angulaire ouvert et type d'élément ( Exception : le type d'élément est également un vec ).
  • Type d'élément et crochet angulaire fermé ( 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;