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, doveVENDOR
fa riferimento a un fornitore di SoC o a un OEM/ODM.
MODULE
deve essere una parola in minuscolo che descriva il sottosistema (ad esempionfc
). Se è necessaria più di una parola, utilizzaSUBMODULE
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 esempioINfc
) 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 aROOT-DIRECTORY
. In particolare,PACKAGE
è:android.hardware
per i pacchetti HIDL di base (mappatura 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 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:
- Altri pacchetti
android.hardware
(utilizza nomi completi). - Altri pacchetti
vendor.VENDOR
(utilizza nomi completi).- Ogni fornitore deve essere un gruppo.
- Ordina i fornitori in ordine alfabetico.
- 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. Segenerates
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;