Стиль кода HIDL напоминает код C++ в фреймворке Android, с отступами в 4 пробела и именами файлов в смешанном регистре. Объявления пакетов, импорты и строки документации аналогичны тем, что используются в Java, с небольшими изменениями.
Приведенные ниже примеры для IFoo.hal и types.hal иллюстрируют стили кода HIDL и содержат быстрые ссылки на подробную информацию о каждом стиле ( IFooClientCallback.hal , IBar.hal и 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; }; |
Правила именования
Названия функций, переменных и файлов должны быть описательными; избегайте чрезмерных сокращений. Акронимы следует рассматривать как слова (например, используйте INfc вместо INFC ).
Структура каталогов и именование файлов
Структура каталогов должна выглядеть следующим образом:
-
ROOT-DIRECTORY-
MODULE-
SUBMODULE(необязательный, может быть более одного уровня)-
VERSION-
Android.mk -
I INTERFACE_1 .hal -
I INTERFACE_2 .hal -
… -
I INTERFACE_N .hal -
types.hal(необязательно)
-
-
-
-
Где:
-
ROOT-DIRECTORY:-
hardware/interfacesдля основных пакетов HIDL. -
vendor/ VENDOR /interfacesдля пакетов от поставщиков, гдеVENDORобозначает поставщика SoC или OEM/ODM.
-
-
MODULEдолжно представлять собой одно слово, написанное строчными буквами, описывающее подсистему (например,nfc). Если требуется более одного слова, используйте вложенныйSUBMODULE. Вложенность может быть более одного уровня. -
VERSIONдолжен точно соответствовать версии (major.minor), описанной в разделе «Версии» . -
I INTERFACE_Xдолжно быть именем интерфейса, написанным в форматеUpperCamelCase/PascalCase(например,INfc), как описано в разделе «Имена интерфейсов» .
Пример:
-
hardware/interfaces-
nfc-
1.0-
Android.mk -
INfc.hal -
INfcClientCallback.hal -
types.hal
-
-
-
Примечание: Все файлы должны иметь права на неисполняемый доступ (в Git).
Названия пакетов
Имена пакетов должны соответствовать следующему формату полного квалифицированного имени (FQN) (обозначаемому как PACKAGE-NAME ):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
Где:
-
PACKAGE— это пакет, который соответствуетROOT-DIRECTORY. В частности,PACKAGE— это:-
android.hardwareдля основных пакетов HIDL (сопоставление сhardware/interfaces). -
vendor. VENDOR .hardwareдля пакетов от поставщиков, гдеVENDORобозначает поставщика SoC или OEM/ODM (сопоставление сvendor/ VENDOR /interfaces).
-
-
MODULE [. SUBMODULE [. SUBMODULE […]]]@ VERSION— это точно такие же имена папок, как и в структуре, описанной в разделе «Структура каталогов» . - Названия пакетов должны быть написаны строчными буквами. Если они состоят более чем из одного слова, эти слова следует использовать либо как названия подмодулей, либо писать в
snake_case. - Проходы запрещены.
Полное квалифицированное имя (FQN) всегда используется в объявлениях пакетов.
Версии
Версии должны иметь следующий формат:
MAJOR.MINOR
И MAJOR и MINOR версии должны представлять собой одно целое число. HIDL использует правила семантического версионирования .
Импорт
Импорт может иметь один из следующих трех форматов:
- Импорт всего пакета:
import PACKAGE-NAME ; - Частичный импорт:
import PACKAGE-NAME :: UDT ;(или, если импортируемый тип находится в том же пакете,import UDT ; - Импорт только типов:
import PACKAGE-NAME ::types;
PACKAGE-NAME соответствует формату, используемому в названиях пакетов . types.hal текущего пакета (если он существует) импортируется автоматически (не нужно импортировать его явно).
Полные квалифицированные наименования (FQN)
Используйте полные имена для импорта пользовательских типов только в случае необходимости. Опускайте PACKAGE-NAME если тип импорта находится в том же пакете. Полное имя не должно содержать пробелов. Пример полного имени:
android.hardware.nfc@1.0::INfcClientCallback
В другом файле, расположенном в папке android.hardware.nfc@1.0 , укажите в качестве интерфейса INfcClientCallback . В противном случае используйте только полное имя интерфейса.
Группировка и упорядочивание импорта
После объявления пакета (перед импортами) оставьте пустую строку. Каждый импорт должен занимать отдельную строку и не должен иметь отступа. Группируйте импорты в следующем порядке:
- Другие пакеты
android.hardware(используйте полные имена). - Другой
vendor. VENDOR(используйте полные квалифицированные имена).- Каждый поставщик должен представлять собой группу.
- Расположите поставщиков в алфавитном порядке.
- Импорт из других интерфейсов в том же пакете (используйте простые имена).
Между группами используйте пустую строку. Внутри каждой группы отсортируйте импортные товары в алфавитном порядке. Пример:
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;
Названия интерфейсов
Имена интерфейсов должны начинаться с буквы I , за которой следует имя в формате UpperCamelCase / PascalCase . Интерфейс с именем IFoo должен быть определен в файле IFoo.hal . Этот файл может содержать определения только для интерфейса IFoo ( I NAME должно быть в I NAME .hal ).
Функции
Для имен функций, аргументов и имен возвращаемых переменных используйте lowerCamelCase . Пример:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Названия структурных и объединенных полей
Для имен полей структур или объединений используйте lowerCamelCase . Пример:
struct FooReply {
vec<uint8_t> replyData;
}Названия типов
Имена типов относятся к определениям структур или объединений, определениям типов перечислений и определениям typedef ). Для этих имен используйте UpperCamelCase / PascalCase . Примеры:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Значения перечисления
Значения перечисления должны быть UPPER_CASE_WITH_UNDERSCORES . При передаче значений перечисления в качестве аргументов функции и их возврате в качестве возвращаемого значения функции используйте фактический тип перечисления (а не базовый целочисленный тип). Пример:
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 };
Примечание: базовый тип перечисления явно указывается после двоеточия. Поскольку это не зависит от компилятора, использование фактического типа перечисления более понятно.
Для полных имен значений перечисления между именем типа перечисления и именем значения перечисления используется двоеточие :
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
В полном квалифицированном имени не должно быть пробелов. Используйте полное квалифицированное имя только при необходимости и опускайте ненужные части. Пример:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
Комментарии
Для однострочного комментария подойдут // , /* */ и /** */ .
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
- Используйте
/* */для комментариев. Хотя HIDL поддерживает//для комментариев, их использование не рекомендуется, поскольку они не отображаются в сгенерированном выводе. - Используйте
/** */для генерации документации. Эти параметры применимы только к объявлениям типов, методов, полей и значений перечислений. Пример:/** 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, ... }
- Многострочные комментарии начинаются с
/**на отдельной строке. В начале каждой строки используйте*. Заканчиваем комментарий*/на отдельной строке, выравнивая звездочки. Пример:/** * My multi-line * comment */
- В уведомлениях о лицензировании и списках изменений новая строка должна начинаться с
/*(одной звездочки), в начале каждой строки должен использоваться*, а*/должен располагаться на последней строке отдельно (звездочки должны быть выровнены). Пример:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
Комментарии к файлу
Каждый файл должен начинаться с соответствующего уведомления о лицензировании. Для основных HAL это должна быть лицензия AOSP Apache, расположенная в development/docs/copyright-templates/c.txt . Не забудьте обновить год и использовать многострочные комментарии в стиле /* */ как описано выше.
При желании после уведомления о лицензии можно добавить пустую строку, за которой следует информация об изменениях/версиях. Используйте многострочные комментарии в стиле /* */ , как описано выше, добавьте пустую строку после информации об изменениях, а затем укажите описание пакета.
TODO комментарии
В полях TODO следует писать строку TODO заглавными буквами, после чего следует двоеточие. Пример:
// TODO: remove this code before foo is checked in.
Комментарии типа TODO разрешены только на этапе разработки; они не должны присутствовать в опубликованных интерфейсах.
Комментарии к интерфейсу и функциям (документационные строки)
Используйте /** */ для многострочных и однострочных строк документации. Не используйте // для строк документации.
Документация для интерфейсов должна описывать общие механизмы интерфейса, обоснование проектирования, назначение и т. д. Документация для функций должна быть специфичной для данной функции (документация на уровне пакета размещается в файле README в каталоге пакета).
/** * 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(); };
Для каждого параметра/возвращаемого значения необходимо добавить аннотации @param и @return :
- Для каждого параметра необходимо добавить
@param. За ним должно следовать имя параметра, а затем строка документации (docstring). - Для каждого возвращаемого значения необходимо добавить
@return. За ней должно следовать имя возвращаемого значения, а затем строка документации (docstring).
Пример:
/** * 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);
Правила форматирования
К общим правилам форматирования относятся:
- Длина строки . Каждая строка текста должна содержать не более 100 столбцов.
- Пробелы . В конце строк не должно быть пробелов; пустые строки не должны содержать пробелов.
- Пробелы против табуляции . Используйте только пробелы.
- Размер отступа . Используйте 4 пробела для блоков и 8 пробелов для переноса строк.
- Фигурные скобки . За исключением значений аннотаций , открывающая фигурная скобка располагается на той же строке, что и предыдущий код, а закрывающая фигурная скобка и следующая точка с запятой занимают всю строку. Пример:
interface INfc { close(); };
декларация упаковки
Объявление пакета должно располагаться в начале файла после уведомления о лицензии, занимать всю строку и не иметь отступов. Пакеты объявляются в следующем формате (форматирование имен см. в разделе «Имена пакетов »):
package PACKAGE-NAME;
Пример:
package android.hardware.nfc@1.0;
Объявления функций
Название функции, параметры, generates и возвращаемые значения должны располагаться на одной строке, если это возможно. Пример:
interface IFoo {
/** ... */
easyMethod(int32_t data) generates (int32_t result);
};Если параметры и возвращаемые значения не помещаются на одной строке, постарайтесь расположить их на одном уровне отступа и выделить generate , чтобы читатель мог быстро увидеть параметры и возвращаемые значения. Пример:
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);
}Дополнительные сведения:
- Открывающая скобка всегда находится на той же строке, что и имя функции.
- Между названием функции и открывающей скобкой пробелы отсутствуют.
- Между скобками и параметрами не должно быть пробелов, за исключением случаев, когда между ними используются символы перевода строки.
- Если
generatesнаходится на той же строке, что и предыдущая закрывающая скобка, используйте пробел перед ней. Еслиgeneratesнаходится на той же строке, что и следующая открывающая скобка, добавьте пробел. - Выровняйте все параметры и возвращаемые значения (если это возможно).
- Отступ по умолчанию составляет 4 пробела.
- Параметры, перенесенные на следующую строку, выравниваются по первому параметру на предыдущей строке, в противном случае они имеют отступ в 8 пробелов.
Аннотации
Для аннотаций используйте следующий формат:
@annotate(keyword = value, keyword = {value, value, value})
Сортируйте аннотации в алфавитном порядке и используйте пробелы вокруг знаков равенства. Пример:
@callflow(key = value) @entry @exit
Убедитесь, что аннотация занимает всю строку. Примеры:
/* Good */ @entry @exit /* Bad */ @entry @exit
Если аннотации не помещаются на одной строке, сделайте отступ в 8 пробелов. Пример:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
Если весь массив значений не помещается на одной строке, добавьте переносы строк после открывающих фигурных скобок { и после каждой запятой внутри массива. Закрывающие скобки размещайте сразу после последнего значения. Не ставьте фигурные скобки, если значение всего одно.
Если весь массив значений помещается на одной строке, не используйте пробелы после открывающих и закрывающих фигурных скобок, а после каждой запятой используйте один пробел. Примеры:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
Между аннотациями и объявлением функции НЕ должно быть пустых строк. Примеры:
/* Good */ @entry foo(); /* Bad */ @entry foo();
Объявления перечислений
Для объявления перечислений используйте следующие правила:
- Если объявления перечислений используются совместно с другим пакетом, поместите их в файл
types.hal, а не встраивайте в интерфейс. - Используйте пробел до и после двоеточия, а также пробел после основного типа перед открытой скобкой.
- В последнем значении перечисления может отсутствовать лишняя запятая.
Декларации структуры
Для объявления структур используйте следующие правила:
- Если объявления структур используются совместно с другим пакетом, поместите их в файл
types.hal, а не встраивайте в интерфейс. - После имени типа структуры перед открывающей фигурной скобкой поставьте пробел.
- Выравнивание названий полей (необязательно). Пример:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Объявления массивов
Не ставьте пробелы между следующими словами:
- Тип элемента и открытая квадратная скобка.
- Открыть квадратную скобку и размер массива.
- Размер массива и закрывающая квадратная скобка.
- Закройте квадратную скобку и следующую за ней открытую квадратную скобку, если размерность превышает один параметр.
Примеры:
/* Good */ int32_t[5] array; /* Good */ int32_t[5][6] multiDimArray; /* Bad */ int32_t [ 5 ] [ 6 ] array;
Векторы
Не ставьте пробелы между следующими словами:
-
vecи кронштейн с открытым углом. - Кронштейн открытого угла и тип элемента ( исключение: тип элемента также является
vec). - Тип элемента и угловой кронштейн ( исключение: тип элемента также может быть
vec) .
Примеры:
/* 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;