Styl kodu HIDL przypomina kod C++ na platformie Androida, zawierający 4 spacje i nazwy plików z różną wielkością liter. Deklaracje pakietów, importy i ciągi dokumentacyjne są podobne do tych w Javie, ale z drobnymi modyfikacjami.
Poniższe przykłady dla IFoo.hal
i types.hal
ilustrować style kodu HIDL i udostępniać szybkie linki do szczegółowych informacji o każdym z nich
(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. unikać
stosowanie zbyt krótkich skrótów. Traktuj akronimy jako słowa (na przykład używaj zamiast nich INfc
)
z INFC
).
Struktura katalogów i nazewnictwo plików
Struktura katalogów powinna wyglądać tak:
ROOT-DIRECTORY
MODULE
SUBMODULE
(opcjonalny; może być więcej niż 1) poziomu)VERSION
Android.mk
IINTERFACE_1.hal
IINTERFACE_2.hal
…
IINTERFACE_N.hal
types.hal
(opcjonalnie)
Gdzie:
ROOT-DIRECTORY
to:hardware/interfaces
za podstawowe pakiety HIDL.vendor/VENDOR/interfaces
w przypadku pakietów dostawców, gdzieVENDOR
odnosi się do dostawcy układów SOC OEM/ODM,
- Pole
MODULE
powinno zawierać jedno małe słowo, które opisuje podsystem (np.nfc
). Jeśli potrzebne jest więcej niż jedno słowo, użyj Zagnieżdżone:SUBMODULE
. Może być więcej niż jeden poziom zagnieżdżania. - Wartość
VERSION
powinna być dokładnie taka sama (duża.podrzędna) zgodnie z opisem 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ą zawierać plik (w Git).
Nazwy pakietów
Nazwy pakietów muszą zawierać tę pełną i jednoznaczną nazwę
Format (FQN) (nazywany PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
Gdzie:
PACKAGE
to pakiet, który mapuje naROOT-DIRECTORY
W szczególnościPACKAGE
to:android.hardware
za podstawowe pakiety HIDL (mapowanie nahardware/interfaces
).vendor.VENDOR.hardware
w przypadku pakietów dostawców, gdzieVENDOR
oznacza dostawcę układów SOC, OEM/ODM (mapowanie) dovendor/VENDOR/interfaces
).
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
mają dokładnie te same nazwy folderów w strukturze opisanej w Struktura katalogu.- Nazwy pakietów powinny być zapisane małymi literami. Jeśli zawierają one więcej niż jedno słowo,
słowa powinny być używane jako moduły podrzędne lub zapisane w języku
snake_case
. - Spacje nie są dozwolone.
Pełna i jednoznaczna nazwa jest zawsze używana w deklaracjach pakietów.
Wersje
Wersje powinny mieć następujący format:
MAJOR.MINOR
Wersja MAJOR i MINOR powinna być jedną liczba całkowita. HIDL używa semantyki obsługi wersji.
Importy
Import może mieć jeden z trzech następujących formatów:
- Importy całego pakietu:
import PACKAGE-NAME;
- Importy częściowe:
import PACKAGE-NAME::UDT;
(lub, jeśli zaimportowano typ jest w tym samym pakiecie,import UDT;
- Importy tylko z typami:
import PACKAGE-NAME::types;
PACKAGE-NAME
ma format z
Nazwy pakietów. Obecny pakiet
Plik types.hal
(jeśli istnieje) zostanie zaimportowany automatycznie (nie importuj
bezpośrednio).
Pełne i jednoznaczne nazwy
W przypadku importu typu zdefiniowanego przez użytkownika używaj pełnych i jednoznacznych nazw tylko wtedy, gdy jest to konieczne.
Pomiń PACKAGE-NAME
, jeśli typ importu jest taki sam
pakietu SDK. Pełna i jednoznaczna 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
zapoznaj się z
powyżej interfejsu jako INfcClientCallback
. W przeciwnym razie używaj tylko atrybutu
pełną i jednoznaczną nazwę.
Grupowanie i sortowanie importów
Użyj pustego wiersza po deklaracji pakietu (przed importem). Każdy import powinny zajmować jeden wiersz i nie powinny mieć wcięcia. Importy grup w w kolejności:
- Inne pakiety
android.hardware
(użyj w pełni kwalifikowanych nazw). - Inne pakiety
vendor.VENDOR
(użyj w pełni kwalifikowanych nazwy).- Każdy dostawca powinien być grupą.
- Porządkuj dostawców alfabetycznie.
- Importy z innych interfejsów w tym samym pakiecie (użyj prostych nazw).
Między grupami używaj pustego wiersza. W każdej grupie posortuj importy w kolejności alfabetycznej. 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
, po którym następuje znak
Nazwa UpperCamelCase
/PascalCase
. Interfejs o nazwie
Pole IFoo
musi być zdefiniowane w pliku IFoo.hal
. Ten plik
może zawierać wyłącznie definicje interfejsu IFoo
(interfejs
Wartość INAME
powinna mieć wartość INAME.hal
).
Funkcje
W przypadku nazw funkcji i argumentów oraz zwracających nazwy zmiennych użyj
lowerCamelCase
Przykład:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Nazwy pól typu struct i union
W przypadku nazw pól typu struct lub Union użyj lowerCamelCase
. Przykład:
struct FooReply { vec<uint8_t> replyData; }
Wpisz nazwy
Nazwy typów odnoszą się do definicji struktury lub sumy, definicji typów wyliczeniowych oraz
typedef
W przypadku tych nazw użyj funkcji
UpperCamelCase
/PascalCase
. Przykłady:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Wartości typu wyliczeniowego
Wartości typu enum powinny wynosić UPPER_CASE_WITH_UNDERSCORES
. Po pomyślnym zakończeniu
wartości wyliczeniowych jako argumentów funkcji i zwracających je jako zwroty funkcji, użyj funkcji
rzeczywisty typ wyliczenia (nie bazowy typ 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: typ podstawowy to wyliczenie: jawnie zadeklarowanej po dwukropku. Nie jest on zależny od kompilatora, więc użycie tagu typ rzeczywistej wartości wyliczeniowej jest wyraźniejszy.
W przypadku pełnych i jednoznacznych nazw wartości wyliczeniowych używany jest dwukropek między nazwą typu wyliczenia a nazwą wartości wyliczenia:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
W pełnej i jednoznacznej nazwie nie mogą znajdować się spacje. Użyj w pełni kwalifikowanego tylko wtedy, gdy jest to konieczne. Pomiń niepotrzebne części. Przykład:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
Komentarze
W przypadku komentarza w jednym wierszu: //
, /* */
i /** */
są w porządku.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
-
Używaj konta
/* */
do komentowania. HIDL obsługuje komentarze w języku//
, odradzamy ich, ponieważ nie pojawiają się w wygenerowanych danych wyjściowych. - Aby wygenerować dokumentację, użyj
/** */
. Można je stosować tylko na deklaracje typu, metody, pól i wartości wyliczeniowych. 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 tworzenie komentarzy wielowierszowych (
/**
) w osobnym wierszu. Dodaj*
na początku każdego wiersza. Zakończ komentarz ciągiem*/
w osobnym wierszu i wyrównaj gwiazdki. Przykład:/** * My multi-line * comment */
- Powiadomienie o licencjonowaniu i logi zmian powinny zaczynać się od nowego wiersza (
/*
) (pojedynczą gwiazdka), użyj*
na początku każdego wiersza, a następnie umieść*/
w ostatnim wierszu (gwiazdki powinny być wyrównane). Przykład:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
Komentarze do plików
Każdy plik powinien zawierać odpowiednią informację o licencji. W przypadku podstawowych HAL
powinna być licencją AOSP Apache
development/docs/copyright-templates/c.txt
Pamiętaj, aby zaktualizować rok i użyć wielowierszowych komentarzy w stylu /* */
jak wyjaśniono powyżej.
Opcjonalnie możesz umieścić pusty wiersz po powiadomieniu o licencji, a następnie
dziennika zmian lub informacji o wersji. Użyj stylu /* */
wielowierszowych komentarzy (w sposób opisany powyżej) należy umieścić pusty wiersz po
dziennik zmian, a następnie deklarację pakietu.
Komentarze TODO
Zadania do wykonania powinny zawierać ciąg TODO
pisany wielkimi literami, a po nim
dwukropek. Przykład:
// TODO: remove this code before foo is checked in.
Komentarze do zadań są dozwolone tylko na etapie tworzenia aplikacji. muszą nie występują w opublikowanych interfejsach.
Komentarze do interfejsu i funkcji (ciągi znaków doc)
/** */
w przypadku wielowierszowych i jednowierszowych ciągów dokumentacyjnych. Nie używaj
//
na potrzeby ciągów dokumentacyjnych.
Ciągi dokumentów dotyczące interfejsów powinny opisywać ogólne mechanizmy interfejsu, uzasadnienia projektu, przeznaczenia itp. Ciągi znaków dla funkcji powinny być właściwych 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(); };
Do każdej z nich musisz dodać komponenty typu @param
i @return
parametr/wartość zwrotna:
- Do każdego parametru należy dodać
@param
. Powinna być z nazwą parametru i ciągiem docstring. - Dla każdej zwracanej wartości należy dodać atrybut
@return
. it po nim powinien znajdować się nazwa zwracanej wartości, a następnie ciąg znaków 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);
Reguły formatowania
Ogólne reguły formatowania obejmują:
- Długość wiersza. Każdy wiersz tekstu powinien składać się z maksymalnie 100 kolumn.
- Odstępy. Bez spacji na końcu wierszy. puste wiersze nie może zawierać odstępów.
- Pokoje a karty. Używaj tylko spacji.
- Rozmiar wcięcia. Użyj 4 spacji jako bloków i 8 spacji na zawijanie wierszy
- Wyprzedzanie. Z wyjątkiem adnotacji
, nawias klamrowy otwarty idzie w tym samym wierszu co poprzedzający
ale znajduje się w nim nawias klamrowy close, a następujący średnik
cały wiersz. Przykład:
interface INfc { close(); };
Deklaracja dotycząca pakietu
Deklaracja pakietu powinna znajdować się na początku pliku po licencji powinien zająć cały wiersz i nie powinien mieć wcięcia. Przesyłki są zadeklarowano w tym formacie (formatowanie nazw znajdziesz w sekcji Nazwy pakietów):
package PACKAGE-NAME;
Przykład:
package android.hardware.nfc@1.0;
Deklaracje funkcji
Nazwa funkcji, parametry oraz generates
i zwracane wartości powinny
znajdować się w tej samej linii, 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 podać parametry i zwrócić
na tym samym poziomie wcięcia i rozróżnić generate
, aby pomóc
czytelnik może szybko sprawdzić 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 informacje:
- Otwarte nawiasy zawsze znajdują się w tym samym wierszu co nazwa funkcji.
- Nie ma spacji między nazwą funkcji a nawiasem otwierającym.
- Nie ma spacji między nawiasami i parametrami, z wyjątkiem sytuacji, w których wyświetlają się między nimi.
- Jeśli
generates
znajduje się w tym samym wierszu co poprzedni zamykający wiersz nawias klamrowy, użyj poprzedzającej spacji. Jeśligenerates
jest na tym samym poziomie jako następnego otwierającego nawias, po którym następuje spacja. - Wyrównaj wszystkie parametry i zwracaj wartości (jeśli to możliwe).
- Domyślne wcięcie to 4 spacje.
- Parametry opakowane są wyrównane do pierwszych parametrów w poprzednim wierszu w przeciwnym razie ma wcięcie wynoszące 8 spacji.
Adnotacje
Zastosuj ten format adnotacji:
@annotate(keyword = value, keyword = {value, value, value})
Posortuj adnotacje w kolejności alfabetycznej, a wokół znaków równości wstaw spacje. Przykład:
@callflow(key = value) @entry @exit
Sprawdź, czy adnotacja zajmuje cały wiersz. Przykłady:
/* Good */ @entry @exit /* Bad */ @entry @exit
Jeśli adnotacje nie mieszczą się w tym samym wierszu, zrób wcięcie wynoszące 8 spacji. Przykład:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
Jeśli cała tablica wartości nie może się zmieścić w tym samym wierszu, znaki podziału wiersza umieść po
otwierające nawiasy klamrowe {
i po każdym przecinku w tablicy. Zamknięcie miejsca
nawias umieszczony bezpośrednio po ostatniej wartości. Nie używaj nawiasów, jeśli są:
tylko jedną wartość.
Jeśli cała tablica wartości mieści się w tym samym wierszu, nie używaj spacji po nawiasów klamrowych otwartych i przed zamknięciem – po każdym przecinku zostaw jedną spację. Przykłady:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
Między adnotacjami a funkcją NIE może być pustych wierszy tej deklaracji. Przykłady:
/* Good */ @entry foo(); /* Bad */ @entry foo();
Deklaracje typu wyliczeniowego
Deklaracja wyliczeniowa powinna być zgodna z tymi regułami:
- Jeśli deklaracje typu enum są udostępniane innemu pakietowi, umieść deklaracje
w
types.hal
zamiast umieszczania w interfejsie. - Użyj spacji przed i po dwukropku oraz spacji po typie bazowym przed otwartym nawiasem klamrowym.
- Ostatnia wartość wyliczenia może nie zawierać dodatkowego przecinka.
Deklaracje strukturalne
W przypadku deklaracji struct możesz stosować te reguły:
- Jeśli deklaracje struct są współdzielone z innym pakietem, umieść deklaracje
w
types.hal
zamiast umieszczania w interfejsie. - Wstaw spację 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 tablicowe
Nie wstawiaj spacji między:
- 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, jeśli jest ich więcej niż 1 .
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:
vec
i otwarty nawias trójkątny.- Otwierający nawias kątowy i typ elementu (wyjątek: typ elementu to także
vec
). - Typ elementu i zamykający nawias kątowy (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;