Styl kodu HIDL przypomina kod C++ w ramach Androida, z odstępami 4 spacji i nazwami plików w wielkiej i małej literze. Deklaracje pakietów, importy i opis na temat pakietu są podobne do tych w języku Java, ale z niewielkimi modyfikacjami.
Poniższe przykłady dotyczące IFoo.hal
i types.hal
pokazują style kodu HIDL oraz zawierają szybkie linki do szczegółów dotyczących każdego stylu (opuszczono 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 stosowania skrótów. Traktuj akronimy jak słowa (np. używaj INfc
zamiast INFC
).
Struktura katalogów i nazewnictwo plików
Struktura katalogów 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 dostawcy, gdzieVENDOR
oznacza dostawcę SoC lub OEM/ODM.
MODULE
to jedno słowo pisane małymi literami, które opisuje podsystem (np.nfc
). Jeśli potrzebne jest więcej niż jedno słowo, użyj zagnieżdżonegoSUBMODULE
. Może być więcej niż 1 poziom zagnieżdżenia.- Wartość
VERSION
powinna być taka sama jak wersja (główna.podrzędna) opisana w sekcji Wersje. - Wartość
IINTERFACE_X
powinna być nazwą interfejsu z dodatkiemUpperCamelCase
/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 do odczytu (w Git).
nazwy pakietów,
Nazwy pakietów muszą mieć format pełnej i jednoznacznej nazwy (FQDN) (zwanej 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 SoC lub OEM/ODM (mapowaniavendor/VENDOR/interfaces
).
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
są dokładnie takie same nazwy folderów w strukturze opisanej w strukturze katalogu.- Nazwy pakietów powinny być pisane małymi literami. Jeśli składa się z więcej niż jednego słowa, słowa powinny być używane jako podmoduły lub zapisane w formie
snake_case
. - Nie można używać spacji.
W deklaracjach pakietów zawsze używa się pełnej nazwy.
Wersje
Wersje powinny mieć następujący format:
MAJOR.MINOR
Zarówno wersja MAJOR, jak i wersja MINOR powinny być reprezentowane przez jedną liczbę całkowitą. HIDL używa reguł wersji semantycznej.
Importy
Dane importowane mogą mieć jeden z tych 3 formatów:
- Importowanie całego pakietu:
import PACKAGE-NAME;
- Częściowe importowanie:
import PACKAGE-NAME::UDT;
(lub jeśli zaimportowany typ znajduje się w tym samym pakiecie:import UDT;
- Importowanie tylko typów:
import PACKAGE-NAME::types;
PACKAGE-NAME
musi być zgodny z formatem Nazwy pakietów. Aktualny pakiet types.hal
(jeśli istnieje) jest importowany automatycznie (nie importuj go wyraźnie).
Pełne nazwy (FQDN)
Pełne nazwy importowanych typów zdefiniowanych przez użytkownika używaj tylko wtedy, gdy jest to konieczne.
Pomiń PACKAGE-NAME
, jeśli typ importu znajduje się w tym samym pakiecie. Pełna nazwa domeny nie może zawierać spacji. Przykład pełnej nazwy:
android.hardware.nfc@1.0::INfcClientCallback
W innym pliku w folderze android.hardware.nfc@1.0
odnoś się do powyższego interfejsu jako INfcClientCallback
. W przeciwnym razie użyj tylko w pełni kwalifikowanej nazwy.
Grupowanie i sortowanie importów
Po deklaracji pakietu (przed importami) umieść pustą linię. Każdy import powinien zajmować 1 wiersz i nie powinien być wcięty. Grupuj importy w tej kolejności:
- inne pakiety
android.hardware
(użyj pełnych nazw). - Inne pakiety
vendor.VENDOR
(użyj pełnych 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).
Pomiędzy grupami należy umieścić pustą linię. W każdej grupie sortuj 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
, a następnie zawierać nazwę UpperCamelCase
lub 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 pliku INAME.hal
).
Funkcje
W przypadku nazw funkcji, argumentów i zmiennych zwracanych użyj lowerCamelCase
. Przykład:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Nazwy pól struktury i zbioru
W przypadku nazw pól struktury lub związku użyj lowerCamelCase
. Przykład:
struct FooReply { vec<uint8_t> replyData; }
Nazwy typów
Nazwy typów odnoszą się do definicji struktury lub definicji typu union, definicji typu enum oraz typedef
. W przypadku tych nazw użyj znaku UpperCamelCase
/PascalCase
. Przykłady:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Wartości typu wyliczeniowego
Wartości enum powinny być UPPER_CASE_WITH_UNDERSCORES
. Przekazując wartości typu enum jako argumenty funkcji i zwracając je jako wartości zwracane przez funkcję, używaj rzeczywistego typu enum (a nie podstawowego 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 podstawowy typu wyliczenia jest jawnie zadeklarowany po dwukropku. Ponieważ nie zależy od kompilatora, korzystanie z rzeczywistego typu enum jest bardziej przejrzyste.
W przypadku pełnych nazw wartości wyliczenia między nazwą typu wyliczenia a nazwą wartości wyliczenia jest używane dwukropkowe „:”:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
W pełnej nazwie nie może być spacji. Pełnej nazwy używaj tylko wtedy, gdy jest to konieczne, i pomiń niepotrzebne części. Przykład:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
Komentarze
W przypadku komentarza w jednym wierszu użyj //
, /* */
lub /** */
.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
-
Dodawaj komentarze za pomocą
/* */
. Chociaż HIDL obsługuje komentarze//
, nie zalecamy ich używania, ponieważ nie pojawiają się w wygenerowanych danych wyjściowych. - Użyj aplikacji
/** */
do wygenerowania dokumentacji. Można ich używać tylko w przypadku 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, ... }
- Komentarze wielowierszowe należy rozpoczynać znakiem
/**
w osobnym wierszu. Użyj znaku*
na początku każdego wiersza. Zakończ komentarz wpisem*/
na osobnej linii, wyrównując gwiazdki. Przykład:/** * My multi-line * comment */
- Ostrzeżenie o licencjach i zmiany w changelogu powinny zaczynać się w nowym wierszu od znaku
/*
(pojedyncza gwiazdka), znak*
powinien występować na początku każdego wiersza, a w ostatnim wierszu należy umieścić znak*/
(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ę odpowiednią informacją o licencji. W przypadku podstawowych interfejsów 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ć pusty wiersz po powiadomieniu o licencjach, a następnie informacje o zmianach lub wersjonowaniu. Użyj komentarzy wielowierszowych w stylu /* */
, jak wyjaśniono powyżej. Umieść pusty wiersz po historii zmian, a następnie dodaj deklarację pakietu.
Komentarze TODO
Lista TODO powinna zawierać ciąg tekstowy TODO
w wielkich literach, 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ą występować w publikowanych interfejsach.
Komentarze dotyczące interfejsu i funkcji (docstrings)
Używaj /** */
w przypadku wielowierszowych i jednowierszowych opisów funkcji. Nie używaj znaku //
w opisach funkcji.
Docstringi interfejsów powinny opisywać ogólne mechanizmy interfejsu, uzasadnienie projektu, przeznaczenie itp. Docstringi 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
i @return
dla każdego parametru lub wartości zwracanej:
@param
musi zostać dodany do każdego parametru. Następnie należy podać nazwę parametru, a potem docstring.- W przypadku każdej wartości zwracanej musisz dodać parametr
@return
. Po nim powinna następować nazwa wartości zwracanej, a następnie opis funkcji.
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 znaków.
- Odstępy. Brak spacji na końcu linii; puste linie nie mogą zawierać spacji.
- Spacje a tabulatory Użyj tylko spacji.
- Rozmiar wcięcia. Użyj 4 spacji dla bloków i 8 spacji dla przełamań wiersza.
- Bracing. Z wyjątkiem wartości adnotacji otwierająca klamra znajduje się na tej samej linii co poprzedni kod, ale zamykająca klamra i następujące przecinki zajmują całą linię. Przykład:
interface INfc { close(); };
Deklaracja przesyłki
Deklaracja pakietu powinna znajdować się u góry pliku po powiadomieniu o licencjach, powinna zajmować cały wiersz i nie powinna być wcięciem. Pakiety są deklarowane w następującym formacie (formatowanie nazwy – 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 znajdować się na tym samym wierszu, jeśli się mieszczą. Przykład:
interface IFoo { /** ... */ easyMethod(int32_t data) generates (int32_t result); };
Jeśli nie mieszczą się na tym samym wierszu, spróbuj umieścić parametry i wartości zwracane na tym samym poziomie wcięcia i odróżnić je za pomocą znaku generate
, aby ułatwić czytelnikowi szybkie znalezienie 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:
- Otwarta nawias znajduje się zawsze na tej samej linii co nazwa funkcji.
- Nie ma spacji między nazwą funkcji a nawiasem klamrowym otwierającym.
- Nie umieszczaj spacji między nawiasami a parametrami z wyjątkiem sytuacji, gdy między nimi znajdują się pliki danych linii.
- Jeśli
generates
znajduje się na tym samym wierszu co poprzednia klamra zamykająca, umieść przed nią spację. Jeśligenerates
znajduje się na tej samej linii co następny nawias otwierający, umieść po nim spację. - Dopasuj wszystkie parametry i wartości zwracane (jeśli to możliwe).
- Domyślne wcięcie to 4 spacje.
- Parametry zawinięte są wyrównane do pierwszych parametrów na poprzednim wierszu, a w przeciwnym razie mają wcięcie o 8 spacji.
Adnotacje
W przypadku adnotacji użyj tego formatu:
@annotate(keyword = value, keyword = {value, value, value})
Posortuj adnotacje w kolejności alfabetycznej i umieszczaj spacje 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ę na tym samym wierszu, użyj wcięcia 8 spacji. Przykład:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
Jeśli nie mieści się ona na jednym wierszu, umieść przerwy wiersza po otwierających nawiasach klamrowych {
i po każdej przecince w tablicy. Zamknij nawias bezpośrednio po ostatniej wartości. Nie umieszczaj nawiasów, jeśli występuje tylko jedna wartość.
Jeśli cały tablica wartości mieści się na tym samym wierszu, nie używaj spacji po nawiasach otwierających ani przed nawiasami zamykających i umieszczaj po przecinku pojedynczą spację. Przykłady:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
Między adnotacjami a deklaracją funkcji nie może być pustych linii. Przykłady:
/* Good */ @entry foo(); /* Bad */ @entry foo();
Deklaracje typu wyliczeniowego
W przypadku deklaracji wyliczeń należy przestrzegać tych reguł:
- Jeśli deklaracje enum są udostępniane innemu pakietowi, umieść je w
types.hal
, a nie wstawiaj w interfejsie. - Użyj spacji przed i po dwukropku oraz po typie źródłowym przed nawiasem klamrowym.
- Ostatnia wartość wyliczenia może nie mieć dodatkowej przecinki.
Deklaracje struktury
W przypadku deklaracji typu struct należy przestrzegać tych reguł:
- Jeśli deklaracje struktur są udostępniane innemu pakietowi, umieść je w
types.hal
, a nie umieszczaj w interfejsie. - Po nazwie typu struktury przed nawiasem klamrowym należy umieścić spację.
- Dopasuj nazwy pól (opcjonalnie). Przykład:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Deklaracje tablic
Nie stawiaj spacji między:
- 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 nawias kwadratowy, jeśli istnieje więcej niż 1 wymiar.
Przykłady:
/* Good */ int32_t[5] array; /* Good */ int32_t[5][6] multiDimArray; /* Bad */ int32_t [ 5 ] [ 6 ] array;
Wektory
Nie stawiaj spacji między:
vec
i otwierający nawias trójkątny.- Otwierający nawias trójkątny i typ elementu (wyjątek: typ elementu to też
vec
). - Typ elementu i zamykająca nawias kątny (wyjątek: typ elementu to 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;