Styl kodu HIDL przypomina kod C++ w środowisku Android, z wcięciami z czterema spacjami i nazwami plików o różnej wielkości liter. Deklaracje pakietów, importy i dokumenty są podobne do tych w Javie, z niewielkimi modyfikacjami.
Poniższe przykłady IFoo.hal
i types.hal
ilustrują style kodu HIDL i zapewniają szybkie łącza do szczegółów każdego stylu (pominięto 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 mieć charakter opisowy; unikać nadmiernych skrótów. Traktuj akronimy jak 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
powinien składać się z jednego słowa z małej litery opisującego 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 tą samą wersją (główną.minor), jak opisano w sekcji Wersje . -
I INTERFACE_X
powinna być nazwą interfejsu zawierającąUpperCamelCase
/PascalCase
(np.INfc
) 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ą używać następującego formatu w pełni kwalifikowanej nazwy (FQN) (określanego jako PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
Gdzie:
-
PACKAGE
to pakiet mapowany naROOT-DIRECTORY
. W szczególnościPACKAGE
to:-
android.hardware
dla podstawowych pakietów HIDL (mapowanie nahardware/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, należy je albo użyć jako podmodułów, albo zapisać w
snake_case
. - Żadne 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 powinna być pojedynczą liczbą całkowitą. HIDL wykorzystuje reguły wersjonowania semantycznego .
Import
Import może mieć jeden z trzech formatów:
- Import całych pakietów:
import PACKAGE-NAME ;
- Częściowy import:
import PACKAGE-NAME :: UDT ;
(lub, jeśli importowany typ znajduje się w tym samym pakiecie,import UDT ;
- Importy tylko typów:
import PACKAGE-NAME ::types;
PACKAGE-NAME
ma format zgodny z nazwami pakietów . types.hal
bieżącego pakietu (jeśli istnieje) jest importowany automatycznie (nie importuj go jawnie).
W pełni kwalifikowane nazwy (FQN)
W przypadku importu typu zdefiniowanego przez użytkownika należy używać pełnych nazw 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
odwołuj się do powyższego interfejsu jako INfcClientCallback
. W przeciwnym razie użyj tylko w pełni kwalifikowanej nazwy.
Grupowanie i porządkowanie importów
Użyj pustej linii po deklaracji pakietu (przed importem). Każdy import powinien zajmować jedną linię i nie powinien być wcięty. Grupuj importy 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 dostawca powinien stanowić grupę.
- Sortuj dostawców alfabetycznie.
- Importy z innych interfejsów w tym samym pakiecie (użyj prostych nazw).
Użyj pustej linii pomię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
. W pliku IFoo.hal
należy zdefiniować interfejs o nazwie IFoo
. Plik ten może zawierać definicje tylko dla interfejsu IFoo
(interfejs I NAME
powinien znajdować się 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 strukturalnych/związkowych
W przypadku nazw pól struct/union użyj lowerCamelCase
. Przykład:
struct FooReply { vec<uint8_t> replyData; }
Wpisz nazwy
Nazwy typów odnoszą się do definicji struktur/unii, definicji typów wyliczeniowych i typedef
s. W przypadku tych nazw użyj UpperCamelCase
/ PascalCase
. Przykłady:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Wartości wyliczeniowe
Wartości wyliczeniowe powinny wynosić UPPER_CASE_WITH_UNDERSCORES
. Podczas przekazywania wartości wyliczeniowych jako argumentów funkcji i zwracania ich jako zwrotów funkcji, należy używać rzeczywistego typu wyliczeniowego (a nie bazowego typu całkowitego). 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: Typ bazowy typu wyliczeniowego jest jawnie deklarowany po dwukropku. Ponieważ nie jest to zależne od kompilatora, użycie rzeczywistego typu wyliczeniowego jest jaśniejsze.
W przypadku w pełni kwalifikowanych nazw wartości wyliczeniowych między nazwą typu wyliczeniowego a nazwą wartości wyliczeniowej używany jest dwukropek :
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
W pełnej nazwie 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
W przypadku komentarza jednoliniowego //
, /* */
i /** */
są w porządku.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
- Użyj
/* */
w komentarzach. Chociaż HIDL obsługuje//
komentarze, jest to odradzane, ponieważ nie pojawiają się w generowanych wynikach. - Użyj
/** */
dla wygenerowanej dokumentacji. Można je zastosować tylko do deklaracji typu, metody, pola i wartości wyliczeniowej. 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, ... }
- Komentarze wielowierszowe rozpoczynaj znakiem
/**
w osobnej linii. Użyj*
na początku każdej linii. Zakończ komentarz znakiem*/
w osobnej linii, wyrównując gwiazdki. Przykład:/** * My multi-line * comment */
- Informacje o licencji i dzienniki zmian powinny rozpoczynać nową linię od
/*
(pojedynczej gwiazdki), używać*
na początku każdej linii i umieszczać*/
osobno w ostatniej linii (gwiazdki powinny być wyrównane). Przykład:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
Komentarze do plików
Rozpocznij każdy plik odpowiednią informacją o licencji. W przypadku podstawowych warstw HAL powinna to być licencja AOSP Apache w development/docs/copyright-templates/c.txt
. Pamiętaj, aby zaktualizować rok i użyć komentarzy wielowierszowych w stylu /* */
jak wyjaśniono powyżej.
Opcjonalnie możesz umieścić pustą linię po uwadze licencyjnej, po której nastąpi informacja o dzienniku zmian/wersji. Użyj /* */
stylu komentarzy wielowierszowych, jak wyjaśniono powyżej, umieść pustą linię po dzienniku zmian, a następnie postępuj zgodnie z deklaracją pakietu.
TODO komentarze
Treść TODO powinna zawierać ciąg TODO
wielkimi literami, po którym następuje dwukropek. Przykład:
// TODO: remove this code before foo is checked in.
Komentarze do TODO są dozwolone tylko w trakcie programowania; nie mogą one istnieć w opublikowanych interfejsach.
Komentarze do interfejsu/funkcji (dokumenty)
Użyj /** */
dla ciągów dokumentów wielowierszowych i jednowierszowych. Nie używaj //
dla ciągów dokumentów.
Dokumentacja interfejsów powinna opisywać ogólne mechanizmy interfejsu, uzasadnienie projektu, cel itp. Dokumentacja funkcji powinna być specyficzna dla danej funkcji (dokumentacja na poziomie pakietu znajduje się w pliku README w katalogu pakietów).
/** * 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:
- Do każdego parametru należy dodać
@param
. Po nim powinna następować nazwa parametru, a następnie dokument. - Do każdej zwracanej wartości należy dodać
@return
. Po nim powinna następować nazwa zwracanej wartości, a następnie ciąg dokumentów.
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.
- Białe znaki . Brak końcowych białych znaków w liniach; puste linie nie mogą zawierać białych znaków.
- Spacje a tabulatory . Używaj tylko spacji.
- Rozmiar wcięcia . Użyj 4 spacji na bloki i 8 spacji na zawijanie linii
- Usztywnienie . Z wyjątkiem wartości adnotacji , nawias otwierający znajduje się w tym samym wierszu, co poprzedni kod, ale nawias zamykający i następujący po nim średnik zajmują całą linię. Przykład:
interface INfc { close(); };
Deklaracja pakietu
Deklaracja pakietu powinna znajdować się na górze pliku, po informacji licencyjnej, powinna zajmować całą linię i nie powinna być wcięta. Pakiety deklaruje się w następującym formacie (formatowanie nazw znajdziesz w 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 tej samej linii, spróbuj umieścić parametry i zwracane wartości na tym samym poziomie wcięcia i rozróżnij generate
, aby pomóc czytelnikowi szybko zobaczyć parametry i zwrócić 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 znajduje się zawsze w tym samym wierszu, co nazwa funkcji.
- Żadnych spacji pomiędzy nazwą funkcji a nawiasem otwierającym.
- Żadnych spacji pomiędzy nawiasami i parametrami, z wyjątkiem sytuacji, gdy pomiędzy nimi znajduje się znak nowego wiersza.
- 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 nawias otwierający, należy dodać spację. - Wyrównaj wszystkie parametry i zwróć wartości (jeśli to możliwe).
- Domyślne wcięcie to 4 spacje.
- Opakowane parametry są wyrównywane do pierwszych parametrów w poprzedniej linii, w przeciwnym razie mają wcięcie 8-spacji.
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żeli adnotacje nie mieszczą się w tym samym wierszu, należy zastosować wcięcie 8 spacji. 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, po nawiasach otwartych {
i po każdym przecinku w tablicy wstaw podziały wierszy. Umieść nawias zamykający bezpośrednio po ostatniej wartości. Nie umieszczaj nawiasów klamrowych, jeśli istnieje tylko jedna wartość.
Jeśli cała tablica wartości mieści się w tym samym wierszu, nie używaj spacji po nawiasach otwartych i przed nawiasami zamykającymi, a po każdym przecinku wstaw jedną spację. Przykłady:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
Pomiędzy adnotacjami i deklaracją funkcji NIE mogą znajdować się puste linie. Przykłady:
/* Good */ @entry foo(); /* Bad */ @entry foo();
Deklaracje wyliczeniowe
Użyj następujących reguł dla deklaracji wyliczeniowych:
- Jeśli deklaracje wyliczeniowe są współdzielone z innym pakietem, umieść deklaracje w
types.hal
zamiast osadzać je w interfejsie. - Użyj spacji przed i po dwukropku oraz spacji po typie bazowym przed nawiasem otwierającym.
- Ostatnia wartość wyliczeniowa może zawierać dodatkowy przecinek lub nie.
Deklaracje struktur
Użyj następujących reguł dla deklaracji struktur:
- Jeśli deklaracje struktury są współdzielone z innym pakietem, umieść deklaracje w
types.hal
zamiast osadzać je w interfejsie. - Użyj spacji po nazwie typu struktury przed nawiasem otwierającym.
- Wyrównaj nazwy pól (opcjonalnie). Przykład:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Deklaracje tablicowe
Nie wstawiaj spacji pomiędzy następującymi elementami:
- Typ elementu i otwarty nawias kwadratowy.
- Otwórz nawias kwadratowy i rozmiar tablicy.
- Rozmiar tablicy i zamykający 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 wstawiaj spacji pomiędzy następującymi elementami:
-
vec
i otwarty nawias kątowy. - Otwarty nawias ostry i typ elementu ( Wyjątek: typ elementu to także
vec
). - Typ elementu i nawias ostry ( Wyjątek: typ elementu to także
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;