Styl kodu HIDL przypomina kod C++ w platformie Android z 4-znakowymi wcięciami i nazwami plików z wielkimi i małymi literami. Deklaracje pakietów, instrukcje importu i ciągi dokumentujące są podobne do tych w Javie, ale z niewielkimi modyfikacjami.
Poniższe przykłady dla IFoo.hal
i types.hal
ilustrują style kodu HIDL i zawierają szybkie linki do szczegółów każdego stylu
(IFooClientCallback.hal
, IBar.hal
i IBaz.hal
zostały pominięte).
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, zmiennych i plików powinny być opisowe. Unikaj nadmiernych skrótów. Traktuj akronimy jak słowa (np. używaj INfc
zamiast INFC
).
Struktura katalogów i nazewnictwo plików
Struktura katalogu powinna wyglądać tak:
ROOT-DIRECTORY
MODULE
SUBMODULE
(opcjonalnie, może być więcej niż jeden poziom)VERSION
Android.mk
IINTERFACE_1.hal
IINTERFACE_2.hal
…
IINTERFACE_N.hal
types.hal
(opcjonalnie)
Gdzie:
ROOT-DIRECTORY
to:hardware/interfaces
w przypadku podstawowych pakietów HIDL.vendor/VENDOR/interfaces
w przypadku pakietów dostawców, gdzieVENDOR
oznacza dostawcę układu SOC lub producenta OEM/ODM.
MODULE
powinno być jednym słowem zapisanym małymi literami, które opisuje podsystem (np.nfc
). Jeśli potrzebujesz więcej niż jednego słowa, użyj zagnieżdżonego elementuSUBMODULE
. Może być więcej niż 1 poziom zagnieżdżenia.VERSION
powinna być dokładnie tą samą wersją (główną.podrzędną), która została opisana w sekcji Wersje.IINTERFACE_X
powinna być nazwą interfejsu zUpperCamelCase
/PascalCase
(np.INfc
), zgodnie z opisem w sekcji Nazwy interfejsów.
Przykład:
hardware/interfaces
nfc
1.0
Android.mk
INfc.hal
INfcClientCallback.hal
types.hal
Uwaga: wszystkie pliki muszą mieć uprawnienia inne niż wykonywalne (w Git).
Nazwy pakietów
Nazwy pakietów muszą mieć format pełnej nazwy (FQDN) (określanej jako PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
Gdzie:
PACKAGE
to pakiet, który jest zmapowany naROOT-DIRECTORY
. W szczególności:PACKAGE
to:android.hardware
w przypadku podstawowych pakietów HIDL (mapowanie nahardware/interfaces
).vendor.VENDOR.hardware
w przypadku pakietów dostawców, gdzieVENDOR
odnosi się do dostawcy układu SoC lub producenta OEM/ODM (mapowanie navendor/VENDOR/interfaces
).
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
są dokładnie takie same jak nazwy folderów w strukturze opisanej w sekcji Struktura katalogów.- Nazwy pakietów powinny być pisane małymi literami. Jeśli nazwy składają się z więcej niż jednego słowa, powinny być używane jako podmoduły lub zapisywane w formacie
snake_case
. - Spacje są niedozwolone.
W deklaracjach pakietów zawsze używana jest pełna nazwa.
Wersje
Wersje powinny mieć format:
MAJOR.MINOR
Zarówno wersja MAJOR, jak i MINOR powinna być pojedynczą liczbą całkowitą. HIDL używa reguł wersjonowania semantycznego.
Import
Import może mieć jeden z tych 3 formatów:
- Importy całych pakietów:
import PACKAGE-NAME;
- Importy częściowe:
import PACKAGE-NAME::UDT;
(lub, jeśli importowany typ znajduje się w tym samym pakiecie,import UDT;
- Importowanie tylko typów:
import PACKAGE-NAME::types;
Symbol PACKAGE-NAME
jest zgodny z formatem w sekcji Nazwy pakietów. Bieżący pakiet types.hal
(jeśli istnieje) jest importowany automatycznie (nie importuj go jawnie).
Pełne nazwy (FQNs)
Używaj w pełni kwalifikowanych nazw w przypadku importowania typów zdefiniowanych przez użytkownika tylko wtedy, gdy jest to konieczne.
Pomiń PACKAGE-NAME
, jeśli typ importu znajduje się w tym samym pakiecie. W pełni kwalifikowana nazwa nie może zawierać spacji. Przykład pełnej i jednoznacznej nazwy:
android.hardware.nfc@1.0::INfcClientCallback
W innym pliku w sekcji android.hardware.nfc@1.0
odwołuj się do powyższego interfejsu jako INfcClientCallback
. W przeciwnym razie użyj tylko pełnej nazwy.
Grupowanie i sortowanie importów
Po deklaracji pakietu (przed importami) użyj pustego wiersza. Każdy import powinien zajmować jeden wiersz i nie powinien być wcięty. Importuj grupy w tej kolejności:
- Inne pakiety
android.hardware
(użyj pełnych i jednoznacznych nazw). - Inne pakiety
vendor.VENDOR
(używaj pełnych i jednoznacznych nazw).- Każdy dostawca powinien być grupą.
- Uporządkuj dostawców alfabetycznie.
- Importowanie z innych interfejsów w tym samym pakiecie (używaj prostych nazw).
Używaj pustego wiersza między grupami. W każdej grupie 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 znaku I
, a po nim musi następować nazwa UpperCamelCase
/PascalCase
. W pliku IFoo.hal
musi być zdefiniowany interfejs o nazwie IFoo
. Ten plik może zawierać definicje tylko dla interfejsu IFoo
(interfejs INAME
powinien znajdować się w INAME.hal
).
Funkcje
W przypadku nazw funkcji, argumentów i zmiennych zwracanych używaj lowerCamelCase
. Przykład:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Nazwy pól struktur i unii
W przypadku nazw pól struktur lub unii używaj znaku lowerCamelCase
. Przykład:
struct FooReply { vec<uint8_t> replyData; }
Nazwy typów
Nazwy typów odnoszą się do definicji struktur lub unii, definicji typów wyliczeniowych i typedef
. W przypadku tych nazw użyj znaku
UpperCamelCase
/PascalCase
. Przykłady:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Wartości typu wyliczeniowego
Wartości wyliczeniowe powinny być UPPER_CASE_WITH_UNDERSCORES
. Podczas przekazywania wartości wyliczeniowych jako argumentów funkcji i zwracania ich jako wartości zwracanych przez funkcję używaj 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. Używanie rzeczywistego typu wyliczeniowego jest bardziej przejrzyste, ponieważ nie zależy od kompilatora.
W przypadku w pełni kwalifikowanych nazw wartości wyliczeniowych używa się dwukropka między nazwą typu wyliczeniowego a nazwą wartości wyliczeniowej:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
W pełnej nazwie nie może być spacji. Używaj pełnej nazwy tylko wtedy, gdy jest to konieczne, i pomijaj niepotrzebne części. Przykład:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
Komentarze
W przypadku komentarza w jednym wierszu wystarczą //
, /* */
i /** */
.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
-
Używaj
/* */
do komentowania. HIDL obsługuje komentarze w formacie//
, ale nie są one zalecane, ponieważ nie pojawiają się w wygenerowanych danych wyjściowych. - W wygenerowanej dokumentacji używaj
/** */
. Można je stosować 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 zaczynaj od znaku
/**
w osobnym wierszu. Na początku każdego wiersza użyj znaku*
. Zakończ komentarz znakiem*/
w osobnym wierszu, wyrównując gwiazdki. Przykład:/** * My multi-line * comment */
- Informacje o licencji i dzienniki zmian powinny zaczynać się w nowym wierszu od znaku
/*
* (pojedyncza gwiazdka), a na początku każdego wiersza powinien znajdować się znak*
. Znak*/
powinien znajdować się w ostatnim wierszu samodzielnie (gwiazdki powinny być wyrównane). Przykład:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
Komentarze do plików
Każdy plik powinien zaczynać się od odpowiedniej informacji o licencji. W przypadku podstawowych interfejsów HAL powinna to być licencja Apache AOSP w development/docs/copyright-templates/c.txt
.
Pamiętaj, aby zaktualizować rok i użyć komentarzy wielowierszowych w stylu /* */
, jak opisano powyżej.
Opcjonalnie możesz umieścić pusty wiersz po informacji o licencji, a następnie informacje o zmianach lub wersjach. Używaj wielowierszowych komentarzy w stylu /* */
, jak opisano powyżej. Umieść pusty wiersz po dzienniku zmian, a następnie dodaj deklarację pakietu.
Komentarze TODO
Elementy TODO powinny zawierać ciąg tekstowy TODO
pisany wielkimi literami, a po nim dwukropek. Przykład:
// TODO: remove this code before foo is checked in.
Komentarze TODO są dozwolone tylko podczas tworzenia. Nie mogą występować w opublikowanych interfejsach.
Komentarze do interfejsu i funkcji (docstringi)
Używaj /** */
w przypadku wielowierszowych i jednowierszowych ciągów dokumentujących. Nie używaj //
w ciągach dokumentujących.
Ciągi dokumentujące interfejsy powinny opisywać ogólne mechanizmy interfejsu, uzasadnienie projektu, przeznaczenie itp. Ciągi dokumentujące funkcje 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
i @return
dla każdego parametru lub zwracanej wartości:
@param
musi zostać dodany do każdego parametru. Po nim powinna następować nazwa parametru, a potem ciąg dokumentujący.@return
musi być dodany do każdej wartości zwracanej. Po nim powinna nastąpić nazwa zwracanej wartości, a potem ciąg dokumentujący.
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);
Reguły formatowania
Ogólne reguły formatowania:
- Długość wiersza Każdy wiersz tekstu powinien mieć maksymalnie 100 kolumn.
- Białe znaki Brak spacji na końcu wierszy; puste wiersze nie mogą zawierać spacji.
- Spacje a tabulatory Używaj tylko spacji.
- Rozmiar wcięcia Używaj 4 spacji w blokach i 8 spacji w zawijaniu wierszy.
- Wzmocnienie Z wyjątkiem wartości adnotacji, otwierający nawias klamrowy znajduje się w tym samym wierszu co poprzedzający kod, ale zamykający nawias klamrowy i następujący po nim średnik zajmują cały wiersz. Przykład:
interface INfc { close(); };
Deklaracja dotycząca pakietu
Deklaracja pakietu powinna znajdować się u góry pliku po informacji o licencji, zajmować cały wiersz i nie powinna być wcięta. Pakiety są deklarowane w tym formacie (informacje o formatowaniu nazw znajdziesz w sekcji Nazwy pakietów):
package PACKAGE-NAME;
Przykład:
package android.hardware.nfc@1.0;
Deklaracje funkcji
Nazwa funkcji, parametry, generates
i wartości zwracane powinny mieścić się w jednym wierszu. 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 wartości zwracane na tym samym poziomie wcięcia i wyróżnić je znakiem generate
, aby ułatwić czytelnikowi szybkie rozpoznanie parametrów i wartości zwracanych. 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 informacje:
- Nawias otwierający zawsze znajduje się w tym samym wierszu co nazwa funkcji.
- Między nazwą funkcji a nawiasem otwierającym nie może być spacji.
- Między nawiasami a parametrami nie może być spacji, z wyjątkiem sytuacji, gdy między nimi występują znaki końca wiersza.
- Jeśli znak
generates
znajduje się w tym samym wierszu co poprzedni nawias zamykający, użyj spacji przed nim. Jeśli znakgenerates
znajduje się w tym samym wierszu co następny nawias otwierający, dodaj po nim spację. - Wyrównaj wszystkie parametry i wartości zwracane (jeśli to możliwe).
- Domyślne wcięcie to 4 spacje.
- Zawijane parametry są wyrównywane do pierwszych parametrów w poprzednim wierszu, w przeciwnym razie mają wcięcie o 8 spacji.
Adnotacje
W przypadku adnotacji użyj tego formatu:
@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ły wiersz. Przykłady:
/* Good */ @entry @exit /* Bad */ @entry @exit
Jeśli adnotacje nie mieszczą się w jednym wierszu, wstaw wcięcie o 8 spacji. Przykład:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
Jeśli cała tablica wartości nie mieści się w jednym wierszu, wstaw znaki końca wiersza po otwierających nawiasach klamrowych {
i po każdym przecinku w tablicy. Umieść nawias zamykający bezpośrednio po ostatniej wartości. Jeśli jest tylko jedna wartość, nie umieszczaj nawiasów klamrowych.
Jeśli cała tablica wartości mieści się w tym samym wierszu, nie używaj spacji po otwierającym nawiasie klamrowym ani przed zamykającym nawiasem klamrowym, a po każdym przecinku używaj jednej spacji. 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 wyliczeń
W przypadku deklaracji wyliczeń obowiązują te reguły:
- Jeśli deklaracje wyliczeń są udostępniane innemu pakietowi, umieść je w
types.hal
zamiast osadzać w interfejsie. - Użyj spacji przed i po dwukropku oraz spacji po typie bazowym przed nawiasem klamrowym otwierającym.
- Ostatnia wartość wyliczenia może nie mieć dodatkowego przecinka.
Deklaracje struktur
W przypadku deklaracji struktur obowiązują te reguły:
- Jeśli deklaracje struktur są udostępniane innemu pakietowi, umieść je w
types.hal
zamiast osadzać w interfejsie. - Przed nawiasem otwierającym po nazwie typu struktury wstaw spację.
- Wyrównaj nazwy pól (opcjonalnie). Przykład:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Deklaracje tablic
Nie wstawiaj spacji między tymi elementami:
- Typ elementu i nawias kwadratowy otwierający.
- Otwierający nawias kwadratowy i rozmiar tablicy.
- Rozmiar tablicy i nawias kwadratowy zamykający.
- Zamknij nawias kwadratowy i otwórz kolejny, 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 między tymi elementami:
vec
i otwierający nawias trójkątny.- Otwierający nawias trójkątny i typ elementu (Wyjątek: typ elementu to też a
vec
). - Typ elementu i zamknięcie nawiasu kątowego (wyjątek: typ elementu jest też
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;