Lo stile del codice HIDL assomiglia al codice C++ nel framework Android, con rientri di 4 spazi e nomi di file in formato misto. Le dichiarazioni di pacchetti, le importazioni e le docstring sono simili a quelle in Java, con lievi modifiche.
Gli esempi seguenti per IFoo.hal
e types.hal
illustrano gli stili di codice HIDL e forniscono link rapidi ai dettagli di ogni 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
abbreviazioni eccessive. Considera gli acronimi come parole (ad esempio, utilizza INfc
invece di INFC
).
Struttura delle directory e denominazione dei file
La struttura delle directory dovrebbe apparire nel seguente modo:
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 principali.vendor/VENDOR/interfaces
per i pacchetti del fornitore, doveVENDOR
si riferisce a un fornitore di SoC o a un OEM/ODM.
MODULE
deve essere una parola minuscola che descrive il sottosistema (ad esempio,nfc
). Se è necessaria più di una parola, utilizzaSUBMODULE
nidificati. Può esserci più di un livello di nidificazione.VERSION
deve essere esattamente la stessa versione (principale.secondaria) 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 disporre di autorizzazioni non eseguibili (in Git).
Nomi pacchetto
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 mappato aROOT-DIRECTORY
. In particolare,PACKAGE
è:android.hardware
per i pacchetti HIDL principali (mapping ahardware/interfaces
).vendor.VENDOR.hardware
per i pacchetti del fornitore, doveVENDOR
si riferisce a un fornitore di SoC o a un OEM/ODM (mapping avendor/VENDOR/interfaces
).
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
sono esattamente gli stessi nomi di cartelle nella struttura descritta in Struttura delle directory.- I nomi dei pacchetti devono essere in minuscolo. Se sono composte da più di una parola, le parole devono essere utilizzate come moduli secondari o scritte in
snake_case
. - Non sono ammessi spazi.
Il nome di dominio completo viene sempre utilizzato nelle dichiarazioni dei pacchetti.
Versioni
Le versioni devono avere il seguente formato:
MAJOR.MINOR
Sia MAJOR sia la versione MINOR devono essere un singolo numero intero. HIDL utilizza le regole di controllo delle versioni semantico.
Importazioni
Un'importazione può avere uno dei seguenti tre formati:
- Importazioni dell'intero pacchetto:
import PACKAGE-NAME;
- Importazioni parziali:
import PACKAGE-NAME::UDT;
(o, se il tipo importato si trova nello stesso pacchetto,import UDT;
- Importazioni solo tipi:
import PACKAGE-NAME::types;
PACKAGE-NAME
segue il formato indicato in
Nomi dei pacchetti. types.hal
(se esiste) del pacchetto attuale viene importato automaticamente (non importarlo
in modo esplicito).
Nomi completi (FQN)
Utilizza i nomi completi per l'importazione di un tipo definito dall'utente solo quando necessario.
Ometti PACKAGE-NAME
se il tipo di importazione si trova 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
, 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 sola riga e non deve essere rientrata. Importazioni di gruppi nel seguente ordine:
- Altri pacchetti
android.hardware
(utilizza i nomi completi). - Altri pacchetti
vendor.VENDOR
(utilizza i nomi completi).- Ogni fornitore deve essere un gruppo.
- Ordina i fornitori in ordine alfabetico.
- Importazioni da altre interfacce nello stesso pacchetto (utilizza nomi semplici).
Utilizza una riga vuota tra i gruppi. All'interno di ogni 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 I
, seguito da un nome
UpperCamelCase
/PascalCase
. Nel file IFoo.hal
deve essere definita un'interfaccia con il nome
IFoo
. Questo file
può contenere definizioni solo per l'interfaccia IFoo
(l'interfaccia
INAME
deve essere in INAME.hal
).
Funzioni
Per i nomi di funzioni, argomenti e variabili restituite, 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 unione, utilizza lowerCamelCase
. Esempio:
struct FooReply { vec<uint8_t> replyData; }
Nomi dei tipi
I nomi dei tipi si riferiscono a definizioni di struct o unione, definizioni di tipi 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
valori enum come argomenti di funzione e li restituisci come valori restituiti dalla funzione, utilizza
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 in modo esplicito dopo i due punti. Poiché non dipende dal compilatore, l'utilizzo del tipo di enumerazione effettivo è più chiaro.
Per i nomi completi dei valori enum, viene utilizzato un 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. Utilizza un nome completo solo quando necessario e ometti le parti non necessarie. Esempio:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
Commenti
Per un commento di una sola riga, //
, /* */
e /** */
vanno bene.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
-
Utilizza
/* */
per i commenti. Sebbene HIDL supporti//
per i commenti, questi sono sconsigliati perché non vengono visualizzati nell'output generato. - Utilizza
/** */
per la documentazione generata. Questi possono essere applicati solo a 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'avviso relativo alla licenza e i log delle modifiche devono iniziare una nuova riga con
/*
(un singolo asterisco), utilizzare*
all'inizio di ogni riga e inserire*/
sull'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 la notifica di licenza appropriata. Per gli HAL principali, questa
deve essere la licenza Apache AOSP in
development/docs/copyright-templates/c.txt
.
Ricorda di aggiornare l'anno e di utilizzare i commenti multilinea in stile /* */
come spiegato in precedenza.
Se vuoi, puoi inserire una riga vuota dopo l'avviso relativo alla licenza, seguita
da un log delle modifiche/informazioni sul controllo delle versioni. Utilizza commenti multiriga in stile /* */
come spiegato sopra, inserisci la riga vuota dopo il log delle modifiche, quindi segui con la dichiarazione del pacchetto.
Commenti TODO
I TODO devono includere la stringa TODO
in maiuscolo seguita da due punti. 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 multilinea e a una sola riga. Non utilizzare
//
per le docstring.
Le docstring per le interfacce devono descrivere i meccanismi generali dell'interfaccia, la logica di progettazione, lo scopo e così via. Le docstring per le funzioni devono 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(); };
Devi aggiungere @param
s e @return
s per ogni
parametro/valore restituito:
@param
deve essere aggiunto per ogni parametro. Deve essere seguito dal nome del parametro e poi dalla docstring.@return
deve essere aggiunto per ogni valore restituito. Deve essere seguito dal nome del valore restituito e poi 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 contenere al massimo 100 colonne.
- Spazi vuoti. Nessuno spazio vuoto alla fine delle righe; le righe vuote non devono contenere spazi vuoti.
- Spazi e tabulazioni. Utilizza solo spazi.
- Dimensioni rientro. Utilizza 4 spazi per i blocchi e 8 spazi per i ritorni a capo
- Supporto. Ad eccezione dei valori
dell'annotazione, una parentesi aperta si trova sulla stessa riga del codice
precedente, mentre una parentesi chiusa e il punto e virgola successivo occupano
l'intera riga. Esempio:
interface INfc { close(); };
Dichiarazione del pacchetto
La dichiarazione del pacchetto deve trovarsi nella parte superiore del file dopo l'avviso della licenza, deve occupare l'intera riga e non deve essere rientrata. I pacchetti vengono dichiarati utilizzando il seguente formato (per la formattazione dei nomi, vedi 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
trovarsi sulla stessa riga se ci stanno. 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 a distinguere generate
per aiutare il lettore a visualizzare 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.
- Nessuno spazio tra il nome della funzione e la parentesi aperta.
- Nessuno spazio tra le parentesi e i parametri tranne quando sono presenti avanzamenti riga tra loro.
- Se
generates
si trova sulla stessa riga della parentesi chiusa precedente, utilizza uno spazio precedente. Segenerates
si trova sulla stessa riga della parentesi aperta successiva, aggiungi 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 utilizza gli 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 sulla stessa riga, rientra di 8 spazi. Esempio:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
Se l'intero array di valori non può essere contenuto nella stessa riga, inserisci interruzioni di riga dopo
le parentesi graffe aperte {
e dopo ogni virgola all'interno dell'array. Inserisci la parentesi
chiusa immediatamente dopo l'ultimo valore. Non inserire le parentesi graffe se è presente
un solo valore.
Se l'intero array di valori può essere contenuto 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 esserci righe vuote tra le annotazioni e la dichiarazione della funzione. Esempi:
/* Good */ @entry foo(); /* Bad */ @entry foo();
Dichiarazioni di enumerazione
Utilizza le seguenti regole per le dichiarazioni enum:
- Se le dichiarazioni enum vengono condivise con un altro pacchetto, inseriscile
in
types.hal
anziché incorporarle in un'interfaccia. - Usa uno spazio prima e dopo i due punti e uno spazio dopo il tipo sottostante prima della parentesi graffa aperta.
- L'ultimo valore enum potrebbe non avere una virgola aggiuntiva.
Dichiarazioni di struct
Utilizza le seguenti regole per le dichiarazioni di struct:
- Se le dichiarazioni di struct vengono condivise con un altro pacchetto, inseriscile
in
types.hal
anziché incorporarle in un'interfaccia. - Utilizza uno spazio dopo il nome del tipo di struct 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 i seguenti elementi:
- Tipo di elemento e parentesi quadra aperta.
- Parentesi quadra aperta e dimensione dell'array.
- Dimensione dell'array e parentesi quadra chiusa.
- Chiudi 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 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 angolare 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;