Guida di stile per il codice

Lo stile di codice HIDL è simile al codice C++ nel framework Android, con rientri di 4 spazi e nomi di file con lettere maiuscole e minuscole. Le dichiarazioni del pacchetto, le importazioni e le stringhe di documenti sono simili a quelle in Java, con lievi modifiche.

I seguenti esempi per IFoo.hal e types.hal illustrano gli stili di codice HIDL e forniscono link rapidi ai dettagli su ciascun stile (IFooClientCallback.hal, IBar.hal e IBaz.hal sono stati omessi).

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;
};

Convenzioni di denominazione

I nomi delle funzioni, delle variabili e dei file devono essere descrittivi. Evita di usare troppe sigle. Tratta gli acronimi come parole (ad esempio, utilizza INfc invece di INFC).

Struttura delle directory e denominazione dei file

La struttura della directory dovrebbe essere la seguente:

  • ROOT-DIRECTORY
    • MODULE
      • SUBMODULE (facoltativo, può essere più di un livello)
        • VERSION
          • Android.mk
          • IINTERFACE_1.hal
          • IINTERFACE_2.hal
          • IINTERFACE_N.hal
          • types.hal (facoltativo)

Dove:

  • ROOT-DIRECTORY è:
    • hardware/interfaces per i pacchetti HIDL di base.
    • vendor/VENDOR/interfaces per i pacchetti del fornitore, dove VENDOR fa riferimento a un fornitore di SoC o a un OEM/ODM.
  • MODULE deve essere una parola in minuscolo che descriva il sottosistema (ad esempio nfc). Se è necessaria più di una parola, utilizza SUBMODULE nidificato. Può essere presente più di un livello di nidificazione.
  • VERSION deve essere esattamente la stessa versione (major.minor) descritta in Versioni.
  • IINTERFACE_X deve essere il nome dell'interfaccia conUpperCamelCase/PascalCase (ad esempio INfc) come descritto in Nomi interfaccia.

Esempio:

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

Nota: tutti i file devono avere autorizzazioni non eseguibili (in Git).

Nomi dei pacchetti

I nomi dei pacchetti devono utilizzare il seguente formato del nome completo (FQDN) (chiamato PACKAGE-NAME):

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

Dove:

  • PACKAGE è il pacchetto che mappa a ROOT-DIRECTORY. In particolare, PACKAGE è:
    • android.hardware per i pacchetti HIDL di base (mappatura a hardware/interfaces).
    • vendor.VENDOR.hardware per i pacchetti del fornitore, dove VENDOR si riferisce a un fornitore di SoC o a un OEM/ODM (mapping a vendor/VENDOR/interfaces).
  • MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION sono esattamente gli stessi nomi di cartella nella struttura descritta in Struttura della directory.
  • I nomi dei pacchetti devono essere in minuscolo. Se sono composte da più parole, queste devono essere utilizzate come sottomoduli o scritte in snake_case.
  • Non sono ammessi spazi.

Il nome completo viene sempre utilizzato nelle dichiarazioni del pacchetto.

Versioni

Le versioni devono avere il seguente formato:

MAJOR.MINOR

Sia la versione MAJOR che la versione MINOR devono essere un singolo numero intero. HIDL utilizza le regole di versionamento semantico.

Importazioni

Un'importazione ha uno dei seguenti tre formati:

  • Importazioni di pacchetti completi: import PACKAGE-NAME;
  • Importazioni parziali: import PACKAGE-NAME::UDT; (o, se il tipo importato appartiene allo stesso pacchetto,import UDT;
  • Importazioni solo di tipi: import PACKAGE-NAME::types;

PACKAGE-NAME segue il formato descritto in Nomi pacchetti. Il types.hal (se esistente) del pacchetto corrente viene importato automaticamente (non importarlo esplicitamente).

Nomi completi

Utilizza i nomi completi per un'importazione di tipo definito dall'utente solo se necessario. Ometti PACKAGE-NAME se il tipo di importazione si trova nello stesso package. Un DN non deve contenere spazi. Esempio di un nome completo:

android.hardware.nfc@1.0::INfcClientCallback

In un altro file in android.hardware.nfc@1.0, fai riferimento all'interfaccia sopra indicata come INfcClientCallback. In caso contrario, utilizza solo il nome completo.

Raggruppamento e ordinamento delle importazioni

Utilizza una riga vuota dopo la dichiarazione del pacchetto (prima delle importazioni). Ogni importazione deve occupare una singola riga e non deve essere rientrata. Raggruppa le importazioni nel seguente ordine:

  1. Altri pacchetti android.hardware (utilizza nomi completi).
  2. Altri pacchetti vendor.VENDOR (utilizza nomi completi).
    • Ogni fornitore deve essere un gruppo.
    • Ordina i fornitori in ordine alfabetico.
  3. Importazioni da altre interfacce nello stesso pacchetto (utilizza nomi semplici).

Inserisci una riga vuota tra i gruppi. All'interno di ogni gruppo, ordina le importazioni alfabeticamente. Esempio:

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;

Nomi delle interfacce

I nomi delle interfacce devono iniziare con I, seguito da un nome UpperCamelCase/PascalCase. Nel file IFoo.hal deve essere definita un'interfaccia con nome IFoo. Questo file può contenere definizioni solo per l'interfaccia IFoo (l'interfaccia INAME deve trovarsi in INAME.hal).

Funzioni

Per i nomi delle funzioni, degli argomenti e delle variabili di ritorno, utilizza lowerCamelCase. Esempio:

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

Nomi dei campi di struct e union

Per i nomi dei campi di struct o union, utilizza lowerCamelCase. Esempio:

struct FooReply {
    vec<uint8_t> replyData;
}

Nomi dei tipi

I nomi di tipo fanno riferimento a definizioni di struct o union, definizioni di tipo enum e typedef. Per questi nomi, utilizza UpperCamelCase/PascalCase. Esempi:

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

Valori enum

I valori enum devono essere UPPER_CASE_WITH_UNDERSCORES. Quando passi i valori dell'enum come argomenti della funzione e li restituisci come valori restituiti dalla funzione, utilizza il tipo di enum effettivo (non il tipo di numero intero sottostante). Esempio:

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
};

Nota:il tipo sottostante di un tipo enum è dichiarato esplicitamente dopo i due punti. Poiché non dipende dal compilatore, l'utilizzo del tipo di enum effettivo è più chiaro.

Per i nomi completi dei valori dell'enum, viene utilizzato un due punti tra il nome del tipo di enum e il nome del valore dell'enum:

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

All'interno di un nome completo non devono essere presenti spazi. Utilizza un nome completo solo se necessario e ometti le parti non necessarie. Esempio:

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

Commenti

Per un commento a riga singola, //, /* */ e /** */ vanno bene.

// This is a single line comment
/* This is also single line comment */
/** This is documentation comment */
  • Usa /* */ per i commenti. Sebbene HIDL supporti // per i commenti, questi non sono consigliati perché non vengono visualizzati nell'output generato.
  • Utilizza /** */ per la documentazione generata. Possono essere applicati solo alle dichiarazioni di tipo, metodo, campo e valore enum. Esempio:
    /** 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,
        ...
    }
  • Inizia i commenti su più righe con /** su una riga separata. Utilizza * all'inizio di ogni riga. Termina il commento con */ su una riga separata, allineando gli asterischi. Esempio:
    /**
     * My multi-line
     * comment
     */
  • L'informativa sulla licenza e i log delle modifiche devono iniziare una nuova riga con /* (un asterisco singolo), utilizzare * all'inizio di ogni riga e posizionare */ nell'ultima riga da solo (gli asterischi devono essere allineati). Esempio:
    /*
     * Copyright (C) 2017 The Android Open Source Project
     * ...
     */
    
    /*
     * Changelog:
     * ...
     */

Commenti dei file

Inizia ogni file con l'avviso relativo alla licenza appropriato. Per gli HAL principali, deve essere la licenza Apache AOSP in development/docs/copyright-templates/c.txt. Ricordati di aggiornare l'anno e di utilizzare i commenti multiriga di stile /* */ come spiegato sopra.

Se vuoi, puoi inserire una riga vuota dopo l'informativa sulla licenza, seguita da informazioni sul log delle modifiche/sul controllo delle versioni. Utilizza commenti su più righe di stile /* */ come spiegato sopra, inserisci la riga vuota dopo il changelog, quindi segui con la dichiarazione del pacchetto.

Commenti TODO

Le attività da svolgere devono includere la stringa TODO in maiuscolo seguita da un punto e virgola. Esempio:

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

I commenti TODO sono consentiti solo durante lo sviluppo; non devono essere presenti nelle interfacce pubblicate.

Commenti di interfaccia e funzione (docstring)

Utilizza /** */ per le docstring di una o più righe. Non utilizzare // per le docstring.

Le stringhe di documenti per le interfacce devono descrivere i meccanismi generali dell'interfaccia, la motivazione del design, lo scopo e così via. Le stringhe di documenti per le funzioni devono essere specifiche per la funzione (la documentazione a livello di pacchetto viene inserita in un file README nella directory del pacchetto).

/**
 * 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();
};

Devi aggiungere @param e @return per ogni valore del parametro/del ritorno:

  • @param deve essere aggiunto per ogni parametro. Deve essere seguito dal nome del parametro e dalla docstring.
  • @return deve essere aggiunto per ogni valore restituito. Deve essere seguito dal nome del valore restituito e dalla docstring.

Esempio:

/**
 * 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);

Regole di formattazione

Le regole di formattazione generali includono:

  • Lunghezza della riga. Ogni riga di testo deve avere una lunghezza massima di 100 colonne.
  • Spazi vuoti. Nessuno spazio finale nelle righe; le righe vuote non devono contenere spazi.
  • Spazi e tabulazioni. Utilizza solo spazi.
  • Dimensioni rientro. Utilizza 4 spazi per i blocchi e 8 spazi per i rientri di riga
  • Cinghie. Ad eccezione dei valori di annotazione, una parentesi graffa aperta si trova sulla stessa riga del codice precedente, ma una parentesi graffa chiusa e il punto e virgola successivo occupano l'intera riga. Esempio:
    interface INfc {
        close();
    };

Dichiarazione del pacco

La dichiarazione del pacchetto deve trovarsi nella parte superiore del file dopo l'informativa sulla licenza, deve occupare l'intera riga e non deve essere rientrata. I pacchetti vengono dichiarati utilizzando il seguente formato (per la formattazione dei nomi, consulta Nomi dei pacchetti):

package PACKAGE-NAME;

Esempio:

package android.hardware.nfc@1.0;

Dichiarazioni di funzione

Il nome della funzione, i parametri, generates e i valori restituiti devono essere sulla stessa riga, se possibile. Esempio:

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

Se non rientrano nella stessa riga, prova a inserire i parametri e i valori restituiti nello stesso livello di rientro e distingui generate per aiutare il lettore a vedere rapidamente i parametri e i valori restituiti. Esempio:

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);
}

Ulteriori dettagli:

  • Una parentesi aperta si trova sempre sulla stessa riga del nome della funzione.
  • Non devono esserci spazi tra il nome della funzione e la parentesi aperta.
  • Non devono esserci spazi tra le parentesi e i parametri tranne quando tra di loro sono presenti a capo.
  • Se generates si trova sulla stessa riga della parentesi chiusa precedente, utilizza uno spazio precedente. Se generates si trova sulla stessa riga della parentesi aperta successiva, segui con uno spazio.
  • Allinea tutti i parametri e i valori restituiti (se possibile).
  • Il rientro predefinito è di 4 spazi.
  • I parametri a capo sono allineati ai primi parametri della riga precedente, altrimenti hanno un rientro di 8 spazi.

Annotazioni

Utilizza il seguente formato per le annotazioni:

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

Ordina le annotazioni in ordine alfabetico e inserisci spazi intorno ai segni di uguale. Esempio:

@callflow(key = value)
@entry
@exit

Assicurati che un'annotazione occupi l'intera riga. Esempi:

/* Good */
@entry
@exit

/* Bad */
@entry @exit

Se le annotazioni non possono essere inserite nella stessa riga, inserisci un rientro di 8 spazi. Esempio:

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

Se l'intero array di valori non può essere inserito nella stessa riga, inserisci interruzioni di riga dopo le parentesi graffe aperte { e dopo ogni virgola all'interno dell'array. Inserisci la parentesi chiusa subito dopo l'ultimo valore. Non inserire le parentesi graffe se è presente un solo valore.

Se l'intero array di valori può essere inserito nella stessa riga, non utilizzare spazi dopo le parentesi graffe aperte e prima di quelle chiuse e utilizza uno spazio dopo ogni virgola. Esempi:

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

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

NON devono essere presenti righe vuote tra le annotazioni e la dichiarazione della funzione. Esempi:

/* Good */
@entry
foo();

/* Bad */
@entry

foo();

Dichiarazioni enum

Utilizza le seguenti regole per le dichiarazioni enum:

  • Se le dichiarazioni enum sono condivise con un altro pacchetto, inseriscile in types.hal anziché incorporarle in un'interfaccia.
  • Inserisci uno spazio prima e dopo i due punti e uno spazio dopo il tipo di base prima della parentesi graffa aperta.
  • L'ultimo valore enumerato potrebbe non avere una virgola aggiuntiva.

Dichiarazioni di struct

Utilizza le seguenti regole per le dichiarazioni struct:

  • Se le dichiarazioni delle strutture sono condivise con un altro pacchetto, inseriscile in types.hal anziché incorporarle in un'interfaccia.
  • Inserisci uno spazio dopo il nome del tipo di struttura prima della parentesi graffa aperta.
  • (Facoltativo) Allinea i nomi dei campi. Esempio:
    struct MyStruct {
        vec<uint8_t>   data;
        int32_t        someInt;
    }

Dichiarazioni di array

Non inserire spazi tra i seguenti elementi:

  • Tipo di elemento e parentesi graffe aperta.
  • Parentesi quadra aperta e dimensione dell'array.
  • Dimensioni dell'array e parentesi graffe chiusa.
  • Parentesi tonda di chiusura e parentesi tonda aperta successiva, se esistenti più dimensioni.

Esempi:

/* Good */
int32_t[5] array;

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

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

Vettori

Non inserire spazi tra i seguenti elementi:

  • vec e parentesi angolare aperta.
  • Parentesi angolare aperta e tipo di elemento (Eccezione: il tipo di elemento è anche un vec).
  • Tipo di elemento e parentesi graffe chiusa (Eccezione: il tipo di elemento è anche un vec).

Esempi:

/* 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;