Styl kodu HIDL przypomina kod C++ w strukturze systemu Android, z wcięciami na 4 spacje i nazwami plików z mieszanymi wielkościami liter. Deklaracje pakietów, importy i dokumenty są podobne do tych w Javie, z niewielkimi modyfikacjami.
Poniższe przykłady dla IFoo.hal
i types.hal
ilustrują style kodu HIDL i udostępniają szybkie łącza do szczegółów dotyczących każdego stylu ( IFooClientCallback.hal
, IBar.hal
i IBaz.hal
).
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; }; |
Konwencje nazewnictwa
Nazwy funkcji, nazwy zmiennych i nazwy plików powinny być opisowe; unikaj nadmiernego skrótu. Traktuj akronimy jako słowa (np. użyj INfc
zamiast INFC
).
Struktura katalogów i nazewnictwo plików
Struktura katalogów powinna wyglądać następująco:
-
ROOT-DIRECTORY
-
MODULE
-
SUBMODULE
(opcjonalnie, może mieć więcej niż jeden poziom)-
VERSION
-
Android.mk
-
I INTERFACE_1 .hal
-
I INTERFACE_2 .hal
-
…
-
I INTERFACE_N .hal
-
types.hal
(opcjonalnie)
-
-
-
-
Gdzie:
-
ROOT-DIRECTORY
to:-
hardware/interfaces
dla podstawowych pakietów HIDL. -
vendor/ VENDOR /interfaces
dla pakietów dostawców, gdzieVENDOR
odnosi się do dostawcy SoC lub OEM/ODM.
-
-
MODULE
powinno być jednym małym słowem opisującym podsystem (np.nfc
). Jeśli potrzebne jest więcej niż jedno słowo, użyj zagnieżdżonegoSUBMODULE
. Może istnieć więcej niż jeden poziom zagnieżdżenia. -
VERSION
powinna być dokładnie taką samą wersją (major.minor), jak opisano w sekcji Wersje . -
I INTERFACE_X
powinien być nazwą interfejsu zUpperCamelCase
/PascalCase
(npINfc
) zgodnie z opisem w Nazwy interfejsów .
Przykład:
-
hardware/interfaces
-
nfc
-
1.0
-
Android.mk
-
INfc.hal
-
INfcClientCallback.hal
-
types.hal
-
-
-
Uwaga: Wszystkie pliki muszą mieć uprawnienia niewykonywalne (w Git).
Nazwy pakietów
Nazwy pakietów muszą mieć następujący format w pełni kwalifikowanej nazwy (FQN) (nazywany PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
Gdzie:
-
PACKAGE
to pakiet, który mapuje doROOT-DIRECTORY
. W szczególnościPACKAGE
to:-
android.hardware
dla podstawowych pakietów HIDL (mapowanie dohardware/interfaces
). -
vendor. VENDOR .hardware
dla pakietów dostawców, gdzieVENDOR
odnosi się do dostawcy SoC lub OEM/ODM (mapowanie dovendor/ VENDOR /interfaces
).
-
-
MODULE [. SUBMODULE [. SUBMODULE […]]]@ VERSION
to dokładnie te same nazwy folderów w strukturze opisanej w Struktura katalogów . - Nazwy pakietów powinny być pisane małymi literami. Jeśli mają więcej niż jedno słowo, powinny być użyte jako podmoduły lub zapisane w
snake_case
. - Spacje nie są dozwolone.
FQN jest zawsze używany w deklaracjach pakietów.
Wersje
Wersje powinny mieć następujący format:
MAJOR.MINOR
Zarówno wersja MAJOR , jak i MINOR powinny być jedną liczbą całkowitą. HIDL używa semantycznych reguł wersjonowania .
Import
Import ma jeden z trzech następujących formatów:
- Import całej paczki:
import PACKAGE-NAME ;
- Import częściowy:
import PACKAGE-NAME :: UDT ;
(lub, jeśli importowany typ znajduje się w tym samym pakiecie,import UDT ;
- Importy samych typów:
import PACKAGE-NAME ::types;
PACKAGE-NAME
zgodna z formatem w Nazwy pakietów . Plik types.hal
bieżącego pakietu (jeśli istnieje) jest importowany automatycznie (nie importuj go jawnie).
W pełni kwalifikowane nazwy (FQN)
Używaj w pełni kwalifikowanych nazw dla importu typu zdefiniowanego przez użytkownika tylko wtedy, gdy jest to konieczne. Pomiń PACKAGE-NAME
jeśli typ importu znajduje się w tym samym pakiecie. FQN nie może zawierać spacji. Przykład w pełni kwalifikowanej nazwy:
android.hardware.nfc@1.0::INfcClientCallback
W innym pliku pod android.hardware.nfc@1.0
, odnieś się do powyższego interfejsu jako INfcClientCallback
. W przeciwnym razie używaj tylko w pełni kwalifikowanej nazwy.
Grupowanie i zamawianie importów
Użyj pustego wiersza po deklaracji paczki (przed importem). Każdy import powinien zajmować jedną linię i nie powinien być wcięty. Importy grupowe w następującej kolejności:
- Inne pakiety
android.hardware
(użyj w pełni kwalifikowanych nazw). - Inny
vendor. VENDOR
Pakietyvendor. VENDOR
(użyj w pełni kwalifikowanych nazw).- Każdy sprzedawca powinien być grupą.
- Zamawiaj dostawców alfabetycznie.
- Importy z innych interfejsów w tym samym pakiecie (użyj prostych nazw).
Użyj pustej linii między grupami. Wewnątrz każdej grupy posortuj importy alfabetycznie. Przykład:
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;
Nazwy interfejsów
Nazwy interfejsów muszą zaczynać się od I
, po którym następuje nazwa UpperCamelCase
/ PascalCase
. Interfejs o nazwie IFoo
musi być zdefiniowany w pliku IFoo.hal
. Ten plik może zawierać definicje tylko dla interfejsu IFoo
(interfejs I NAME
powinien być w I NAME .hal
).
Funkcje
W przypadku nazw funkcji, argumentów i nazw zmiennych zwracanych użyj lowerCamelCase
. Przykład:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Nazwy pól struktury/związków
W przypadku nazw pól struktury/unii użyj lowerCamelCase
. Przykład:
struct FooReply { vec<uint8_t> replyData; }
Wpisz nazwy
Nazwy typów odwołują się do definicji struktur/unii, definicji typu enum i typedef
. Dla tych nazw użyj UpperCamelCase
/ PascalCase
. Przykłady:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Wartości wyliczeniowe
Wartości wyliczenia powinny być UPPER_CASE_WITH_UNDERSCORES
. Podczas przekazywania wartości wyliczenia jako argumentów funkcji i zwracania ich jako zwracanych funkcji użyj rzeczywistego typu wyliczenia (nie podstawowego typu liczby całkowitej). Przykład:
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 };
Uwaga: Podstawowy typ typu wyliczenia jest jawnie zadeklarowany po dwukropku. Ponieważ nie jest zależny od kompilatora, użycie rzeczywistego typu wyliczenia jest jaśniejsze.
W przypadku w pełni kwalifikowanych nazw wartości wyliczenia dwukropek jest używany między nazwą typu wyliczenia a nazwą wartości wyliczenia:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
Wewnątrz w pełni kwalifikowanej nazwy nie może być spacji. Używaj w pełni kwalifikowanej nazwy tylko wtedy, gdy jest to konieczne i pomiń niepotrzebne części. Przykład:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
Uwagi
Dla komentarza jednowierszowego //
, /* */
i /** */
są poprawne.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
- Użyj
/* */
dla komentarzy. Chociaż HIDL obsługuje//
dla komentarzy, są one odradzane, ponieważ nie pojawiają się w generowanych danych wyjściowych. - Użyj
/** */
dla wygenerowanej dokumentacji. Można je zastosować tylko do deklaracji typu, metody, pola i wartości wyliczenia. Przykład:/** 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, ... }
- Rozpocznij komentarze wielowierszowe od
/**
w osobnym wierszu. Użyj*
na początku każdej linii. Zakończ komentarz znakiem*/
w osobnym wierszu, wyrównując gwiazdki. Przykład:/** * My multi-line * comment */
- Powiadomienie o licencjonowaniu i dzienniki zmian powinny rozpoczynać nową linię od
/*
(pojedyncza gwiazdka), używać*
na początku każdej linii i umieszczać*/
na ostatniej linii, wszystkie osobno (gwiazdki powinny się wyrównać). Przykład:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
Komentarze do plików
Rozpocznij każdy plik od odpowiedniej informacji licencyjnej. W przypadku podstawowych warstw HAL powinna to być licencja AOSP Apache w pliku development/docs/copyright-templates/c.txt
. Pamiętaj, aby zaktualizować rok i użyć /* */
stylu komentarzy wielowierszowych, jak wyjaśniono powyżej.
Możesz opcjonalnie umieścić pustą linię po powiadomieniu o licencji, po której znajduje się dziennik zmian/informacje o wersji. Użyj /* */
stylu komentarzy wieloliniowych, jak wyjaśniono powyżej, umieść pustą linię po dzienniku zmian, a następnie podaj deklarację pakietu.
TODO komentarze
TODO powinny zawierać ciąg TODO
we wszystkich wersalikach, po którym następuje dwukropek. Przykład:
// TODO: remove this code before foo is checked in.
Komentarze TODO są dozwolone tylko podczas tworzenia; nie mogą istnieć w opublikowanych interfejsach.
Komentarze interfejsu/funkcji (docstrings)
Użyj /** */
dla wieloliniowych i jednowierszowych łańcuchów dokumentacyjnych. Nie używaj //
dla dokumentów.
Dokumenty dla interfejsów powinny opisywać ogólne mechanizmy interfejsu, uzasadnienie projektu, cel, itp. Dokumenty dla funkcji powinny być specyficzne dla funkcji (dokumentacja na poziomie pakietu znajduje się w pliku README w katalogu pakietu).
/** * 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(); };
Musisz dodać @param
s i @return
s dla każdego parametru/wartości zwracanej:
-
@param
należy dodać dla każdego parametru. Po nim powinna następować nazwa parametru, a następnie docstring. -
@return
należy dodać dla każdej zwracanej wartości. Po nim powinna następować nazwa zwracanej wartości, a następnie docstring.
Przykład:
/** * 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);
Formatowanie
Ogólne zasady formatowania obejmują:
- Długość linii . Każdy wiersz tekstu powinien mieć maksymalnie 100 kolumn.
- Spacje . Brak końcowych spacji na liniach; puste wiersze nie mogą zawierać spacji.
- Spacje a tabulatory . Używaj tylko spacji.
- Rozmiar wcięcia . Użyj 4 spacji na bloki i 8 spacji na zawijanie linii
- Stężenie . Z wyjątkiem wartości adnotacji , otwarty nawias klamrowy znajduje się w tym samym wierszu co poprzedni kod, ale zamknięty nawias klamrowy, a następujący średnik zajmuje cały wiersz. Przykład:
interface INfc { close(); };
Deklaracja paczki
Deklaracja pakietu powinna znajdować się na początku pliku za informacją o licencji, powinna zajmować całą linię i nie powinna być wcięta. Pakiety są deklarowane przy użyciu następującego formatu (formatowanie nazw, patrz Nazwy pakietów ):
package PACKAGE-NAME;
Przykład:
package android.hardware.nfc@1.0;
Deklaracje funkcji
Nazwa funkcji, parametry, generates
i zwracane wartości powinny znajdować się w tym samym wierszu, jeśli pasują. Przykład:
interface IFoo { /** ... */ easyMethod(int32_t data) generates (int32_t result); };
Jeśli nie mieszczą się w tym samym wierszu, spróbuj umieścić parametry i zwracane wartości na tym samym poziomie wcięcia i rozróżnij generate
, aby ułatwić czytelnikowi szybkie zobaczenie parametrów i zwracanych wartości. Przykład:
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); }
Dodatkowe Szczegóły:
- Otwarty nawias zawsze znajduje się w tym samym wierszu co nazwa funkcji.
- Brak spacji między nazwą funkcji a otwartym nawiasem.
- Brak spacji między nawiasami i parametrami, z wyjątkiem sytuacji, gdy między nimi znajdują się znaki końca linii.
- Jeśli
generates
znajduje się w tym samym wierszu, co poprzedni nawias zamykający, użyj poprzedzającej spacji. Jeśligenerates
znajduje się w tym samym wierszu, co następny otwarty nawias, po nim należy umieścić spację. - Wyrównaj wszystkie parametry i zwróć wartości (jeśli to możliwe).
- Wcięcie domyślne to 4 spacje.
- Opakowane parametry są wyrównywane do pierwszych parametrów w poprzednim wierszu, w przeciwnym razie mają wcięcie z 8 miejscami.
Adnotacje
Użyj następującego formatu adnotacji:
@annotate(keyword = value, keyword = {value, value, value})
Sortuj adnotacje w kolejności alfabetycznej i używaj spacji wokół znaków równości. Przykład:
@callflow(key = value) @entry @exit
Upewnij się, że adnotacja zajmuje całą linię. Przykłady:
/* Good */ @entry @exit /* Bad */ @entry @exit
Jeśli adnotacje nie mieszczą się w tym samym wierszu, dodaj wcięcie 8 spacjami. Przykład:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
Jeśli cała tablica wartości nie mieści się w tym samym wierszu, wstaw podziały wierszy po otwartych nawiasach klamrowych {
i po każdym przecinku wewnątrz tablicy. Umieść nawias zamykający bezpośrednio po ostatniej wartości. Nie umieszczaj nawiasów klamrowych, jeśli jest tylko jedna wartość.
Jeśli cała tablica wartości może zmieścić się w tym samym wierszu, nie używaj spacji po otwartych nawiasach klamrowych i przed zamknięciem nawiasów klamrowych i użyj jednej spacji po każdym przecinku. Przykłady:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
Między adnotacjami a deklaracją funkcji NIE może być pustych wierszy. Przykłady:
/* Good */ @entry foo(); /* Bad */ @entry foo();
Deklaracje wyliczenia
Użyj następujących reguł dla deklaracji wyliczenia:
- Jeśli deklaracje enum są współdzielone z innym pakietem, umieść deklaracje w
types.hal
zamiast osadzania wewnątrz interfejsu. - Użyj spacji przed i po dwukropku oraz spacji po typie podstawowym przed otwartym nawiasem klamrowym.
- Ostatnia wartość wyliczenia może mieć dodatkowy przecinek lub nie.
Deklaracje struktury
Użyj następujących reguł dla deklaracji struktur:
- Jeśli deklaracje struct są współdzielone z innym pakietem, umieść deklaracje w
types.hal
zamiast osadzać wewnątrz interfejsu. - Użyj spacji po nazwie typu struktury przed otwartym nawiasem klamrowym.
- Wyrównaj nazwy pól (opcjonalnie). Przykład:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Deklaracje tablicy
Nie umieszczaj spacji między następującymi elementami:
- Typ elementu i otwarty nawias kwadratowy.
- Otwórz nawias kwadratowy i rozmiar tablicy.
- Rozmiar tablicy i zamknij nawias kwadratowy.
- Zamknij nawias kwadratowy i następny otwarty nawias kwadratowy, jeśli istnieje więcej niż jeden wymiar.
Przykłady:
/* Good */ int32_t[5] array; /* Good */ int32_t[5][6] multiDimArray; /* Bad */ int32_t [ 5 ] [ 6 ] array;
Wektory
Nie umieszczaj spacji między następującymi elementami:
-
vec
i otwarty wspornik kątowy. - Otwarty nawias kątowy i typ elementu ( Wyjątek: typ elementu to również
vec
). - Typ elementu i nawias kątowy zamykający ( wyjątek: typ elementu to również
vec
) .
Przykłady:
/* 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;