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 docstrings 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 delle interfacce.

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 di 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 enumerati, viene utilizzata una virgola tra il nome del tipo di enumerazione e il nome del valore di enumerazione:

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 e poi 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 e 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 delle annotazioni, 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 inserisci 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 quanto segue:

  • Tipo di elemento e parentesi tonda 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 esistono 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 quanto segue:

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