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, 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 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 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 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. 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 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;