Lo stile del codice HIDL assomiglia al codice C++ nel framework Android, con rientri di 4 spazi e nomi di file composti da maiuscole e minuscole. Le dichiarazioni dei pacchetti, le importazioni e le docstring sono simili a quelle di Java, con lievi modifiche.
I seguenti esempi per IFoo.hal
types.hal
illustrano gli stili di codice HIDL e forniscono collegamenti rapidi ai dettagli su ciascuno 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, dei nomi delle variabili e dei file dovrebbero essere descrittivi; evitare eccessive abbreviazioni. Tratta gli acronimi come parole (ad esempio, usa INfc
invece di INFC
).
Struttura delle directory e denominazione dei file
La struttura della directory dovrebbe apparire come segue:
-
ROOT-DIRECTORY
-
MODULE
-
SUBMODULE
(opzionale, potrebbe essere più di un livello)-
VERSION
-
Android.mk
-
I INTERFACE_1 .hal
-
I INTERFACE_2 .hal
-
…
-
I INTERFACE_N .hal
-
types.hal
(facoltativo)
-
-
-
-
Dove:
-
ROOT-DIRECTORY
è:-
hardware/interfaces
per i pacchetti HIDL principali. -
vendor/ VENDOR /interfaces
per i pacchetti del fornitore, doveVENDOR
si riferisce a un fornitore SoC o a un OEM/ODM.
-
-
MODULE
dovrebbe essere una parola minuscola che descrive il sottosistema (ad esempionfc
). Se è necessaria più di una parola, utilizzareSUBMODULE
nidificato. Può esserci più di un livello di annidamento. -
VERSION
dovrebbe essere esattamente la stessa versione (major.minor) descritta in Versions . -
I INTERFACE_X
dovrebbe essere il nome dell'interfaccia conUpperCamelCase
/PascalCase
(ad esempioINfc
) 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 di nome completo (FQN) (denominato PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
Dove:
-
PACKAGE
è il pacchetto che mappa suROOT-DIRECTORY
. In particolarePACKAGE
è:-
android.hardware
per i pacchetti HIDL principali (mappatura suhardware/interfaces
). -
vendor. VENDOR .hardware
per pacchetti fornitore, doveVENDOR
si riferisce a un fornitore SoC o un OEM/ODM (mappatura avendor/ VENDOR /interfaces
).
-
-
MODULE [. SUBMODULE [. SUBMODULE […]]]@ VERSION
hanno esattamente gli stessi nomi di cartelle nella struttura descritta in Struttura delle directory . - I nomi dei pacchetti dovrebbero essere in minuscolo. Se sono lunghi più di una parola, le parole dovrebbero essere usate come sottomoduli o scritte in
snake_case
. - Non sono ammessi spazi.
L'FQN viene sempre utilizzato nelle dichiarazioni dei pacchi.
Versioni
Le versioni dovrebbero avere il seguente formato:
MAJOR.MINOR
Sia la versione MAJOR che quella MINOR dovrebbero essere un singolo numero intero. HIDL utilizza regole di controllo delle versioni semantiche .
Importazioni
Un'importazione ha uno dei tre formati seguenti:
- Importazioni di pacchetti interi:
import PACKAGE-NAME ;
- Importazioni parziali:
import PACKAGE-NAME :: UDT ;
(o, se il tipo importato è nello stesso pacchetto,import UDT ;
- Importazioni solo di tipi:
import PACKAGE-NAME ::types;
Il PACKAGE-NAME
segue il formato in Packagenames . Il types.hal
del pacchetto corrente (se esiste) viene importato automaticamente (non importarlo esplicitamente).
Nomi completi (FQN)
Utilizzare nomi completi per l'importazione di tipi definiti dall'utente solo quando necessario. Omettere PACKAGE-NAME
se il tipo di importazione è nello stesso pacchetto. Un FQN non deve contenere spazi. Esempio di nome completo:
android.hardware.nfc@1.0::INfcClientCallback
In un altro file in android.hardware.nfc@1.0
, fare riferimento all'interfaccia precedente come INfcClientCallback
. Altrimenti, utilizzare solo il nome completo.
Raggruppare e ordinare le importazioni
Utilizzare una riga vuota dopo la dichiarazione del pacco (prima delle importazioni). Ogni importazione dovrebbe occupare una singola riga e non dovrebbe essere rientrata. Raggruppare le importazioni nel seguente ordine:
- Altri pacchetti
android.hardware
(utilizzare nomi completi). - Altro
vendor. VENDOR
Pacchettivendor. VENDOR
(utilizzare nomi completi).- Ogni venditore dovrebbe essere un gruppo.
- Ordina i venditori in ordine alfabetico.
- Importazioni da altre interfacce nello stesso pacchetto (usa nomi semplici).
Utilizzare una riga vuota tra i gruppi. All'interno di ciascun gruppo, ordina le importazioni in ordine alfabetico. 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 una I
, seguita da un nome UpperCamelCase
/ PascalCase
. Un'interfaccia con nome IFoo
deve essere definita nel file IFoo.hal
. Questo file può contenere definizioni solo per l'interfaccia IFoo
(l'interfaccia I NAME
dovrebbe essere in I NAME .hal
).
Funzioni
Per i nomi delle funzioni, gli argomenti e i nomi delle variabili restituite, utilizzare lowerCamelCase
. Esempio:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Nomi dei campi di struttura/unione
Per i nomi dei campi struttura/unione, utilizzare lowerCamelCase
. Esempio:
struct FooReply { vec<uint8_t> replyData; }
Digita i nomi
I nomi dei tipi si riferiscono a definizioni di struttura/unione, definizioni di tipo enum e typedef
. Per questi nomi, utilizzare UpperCamelCase
/ PascalCase
. Esempi:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Valori di enumerazione
I valori enum dovrebbero essere UPPER_CASE_WITH_UNDERSCORES
. Quando si passano valori enum come argomenti di funzione e li si restituiscono come risultati di funzione, utilizzare il tipo enum effettivo (non il tipo 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 viene dichiarato esplicitamente dopo i due punti. Poiché non dipende dal compilatore, l'utilizzo del tipo enum effettivo è più chiaro.
Per i nomi completi per i valori enum, vengono utilizzati i due punti tra il nome del tipo enum e il nome del valore enum:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
Non devono essere presenti spazi all'interno di un nome completo. Utilizzare un nome completo solo quando necessario e omettere 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 */
- Utilizzare
/* */
per i commenti. Anche se HIDL supporta//
i commenti, questi sono sconsigliati perché non vengono visualizzati nell'output generato. - Utilizzare
/** */
per la documentazione generata. Questi 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. Utilizzare*
all'inizio di ogni riga. Termina il commento con*/
su una riga separata, allineando gli asterischi. Esempio:/** * My multi-line * comment */
- Gli avvisi di licenza e i log delle modifiche dovrebbero iniziare una nuova riga con
/*
(un singolo asterisco), utilizzare*
all'inizio di ogni riga e posizionare*/
sull'ultima riga da solo (gli asterischi dovrebbero allinearsi). Esempio:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
Archiviare i commenti
Inizia ogni file con l'avviso di licenza appropriato. Per gli HAL core, dovrebbe essere la licenza AOSP Apache in development/docs/copyright-templates/c.txt
. Ricordarsi di aggiornare l'anno e utilizzare lo stile /* */
per i commenti su più righe come spiegato sopra.
Facoltativamente è possibile inserire una riga vuota dopo l'avviso di licenza, seguita da informazioni sul registro delle modifiche/versione. Utilizzare i commenti su più righe in stile /* */
come spiegato sopra, posizionare la riga vuota dopo il registro delle modifiche, quindi seguire con la dichiarazione del pacchetto.
TODO commenti
I TODO dovrebbero includere la stringa TODO
tutta maiuscola seguita da due punti. Esempio:
// TODO: remove this code before foo is checked in.
I commenti TODO sono consentiti solo durante lo sviluppo; non devono esistere nelle interfacce pubblicate.
Commenti interfaccia/funzione (docstring)
Utilizzare /** */
per stringhe di documenti su più righe e su righe singole. Non utilizzare //
per le docstring.
Le docstring per le interfacce dovrebbero descrivere i meccanismi generali dell'interfaccia, la logica di progettazione, lo scopo, ecc. Le docstring per le funzioni dovrebbero essere specifiche per la funzione (la documentazione a livello di pacchetto va 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(); };
È necessario aggiungere @param
s e @return
s per ciascun parametro/valore restituito:
-
@param
deve essere aggiunto per ogni parametro. Dovrebbe essere seguito dal nome del parametro e poi dalla docstring. -
@return
deve essere aggiunto per ogni valore restituito. Dovrebbe essere seguito dal nome del valore restituito, quindi 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);
Formattazione
Le regole generali di formattazione includono:
- Lunghezza della linea . Ogni riga di testo deve essere lunga al massimo 100 colonne.
- Spazi bianchi . Nessuno spazio bianco finale sulle righe; le righe vuote non devono contenere spazi bianchi.
- Spazi e tabulazioni . Utilizza solo gli spazi.
- Dimensione del rientro . Utilizza 4 spazi per i blocchi e 8 spazi per gli avvolgimenti di riga
- Rinforzo . Ad eccezione dei valori di annotazione , una parentesi aperta va 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 dovrebbe trovarsi all'inizio del file dopo l'avviso di licenza, dovrebbe occupare l'intera riga e non dovrebbe essere rientrata. I pacchetti vengono dichiarati utilizzando il seguente formato (per la formattazione dei nomi, vedere Nomi dei pacchetti ):
package PACKAGE-NAME;
Esempio:
package android.hardware.nfc@1.0;
Dichiarazioni di funzioni
Il nome della funzione, i parametri, generates
e i valori restituiti dovrebbero essere sulla stessa riga se si adattano. Esempio:
interface IFoo { /** ... */ easyMethod(int32_t data) generates (int32_t result); };
Se non rientrano nella stessa riga, provare a inserire parametri e valori restituiti nello stesso livello di rientro e distinguere 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); }
Dettagli aggiuntivi:
- Una parentesi aperta si trova sempre sulla stessa riga del nome della funzione.
- Nessuno spazio tra il nome della funzione e la parentesi aperta.
- Nessuno spazio tra parentesi e parametri , tranne quando sono presenti avanzamenti di riga tra di essi.
- Se
generates
è sulla stessa riga della parentesi di chiusura precedente, utilizzare uno spazio precedente. Segenerates
è sulla stessa riga della successiva parentesi aperta, seguire con uno spazio. - Allinea tutti i parametri e restituisci i valori (se possibile).
- Il rientro predefinito è di 4 spazi.
- I parametri racchiusi 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 utilizza gli spazi attorno 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 stare sulla stessa riga, rientra con 8 spazi. Esempio:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
Se l'intero array di valori non può stare nella stessa riga, inserisci le interruzioni di riga dopo le parentesi graffe aperte {
e dopo ogni virgola all'interno dell'array. Posiziona la parentesi di chiusura immediatamente dopo l'ultimo valore. Non inserire le parentesi graffe se è presente un solo valore.
Se l'intero array di valori può stare nella stessa riga, non utilizzare spazi dopo le parentesi graffe aperte e prima di chiudere le parentesi graffe e utilizzare uno spazio dopo ogni virgola. Esempi:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
NON devono esserci righe vuote tra le annotazioni e la dichiarazione della funzione. Esempi:
/* Good */ @entry foo(); /* Bad */ @entry foo();
Dichiarazioni di enumerazione
Utilizzare le seguenti regole per le dichiarazioni di enumerazione:
- Se le dichiarazioni enum sono condivise con un altro pacchetto, inserisci le dichiarazioni in
types.hal
anziché incorporarle all'interno di un'interfaccia. - Utilizzare uno spazio prima e dopo i due punti e uno spazio dopo il tipo sottostante prima della parentesi graffa aperta.
- L'ultimo valore enum può o meno contenere una virgola aggiuntiva.
Dichiarazioni di struttura
Utilizzare le seguenti regole per le dichiarazioni struct:
- Se le dichiarazioni struct sono condivise con un altro pacchetto, inserisci le dichiarazioni in
types.hal
invece di incorporarle all'interno di un'interfaccia. - Utilizzare uno spazio dopo il nome del tipo di struttura prima della parentesi graffa aperta.
- Allinea i nomi dei campi (facoltativo). Esempio:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Dichiarazioni di array
Non inserire spazi tra quanto segue:
- Tipo di elemento e parentesi quadra aperta.
- Parentesi quadra aperta e dimensione dell'array.
- Dimensioni dell'array e parentesi quadra chiusa.
- Chiudere la parentesi quadra e la successiva parentesi quadra aperta, se esiste più di una dimensione.
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: anche il tipo di elemento è un
vec
). - Tipo di elemento e parentesi uncinata chiusa ( eccezione: anche il tipo di elemento è 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;