Стиль кода 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
. - Пробелы не допускаются.
Полное имя всегда используется в объявлениях пакетов.
Версии
Версии должны иметь следующий формат:
MAJOR.MINOR
И MAJOR , и MINOR версии должны быть одним целым числом. HIDL использует семантические правила версионирования .
Импорт
Импорт имеет один из следующих трех форматов:
- Импорт целых пакетов:
import PACKAGE-NAME ;
- Частичный импорт:
import PACKAGE-NAME :: UDT ;
(или, если импортируемый тип находится в том же пакете,import UDT ;
- Импорт только типов:
import PACKAGE-NAME ::types;
PACKAGE-NAME
соответствует формату в Package names . Файл types.hal
текущего пакета (если он существует) импортируется автоматически (не импортируйте его явно).
Полностью квалифицированные имена (FQN)
Используйте полностью квалифицированные имена для импорта пользовательского типа только при необходимости. Опустите PACKAGE-NAME
если тип импорта находится в том же пакете. FQN не должно содержать пробелов. Пример полностью квалифицированного имени:
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; }
Названия типов
Имена типов ссылаются на определения структур или объединений, определения типов enum и typedef
s. Для этих имен используйте 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 };
Примечание: базовый тип типа enum явно объявляется после двоеточия. Поскольку он не зависит от компилятора, использование фактического типа enum более понятно.
Для полностью квалифицированных имен значений перечисления между именем типа перечисления и именем значения перечисления ставится двоеточие :
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
необходимо добавить для каждого параметра. За ним должно следовать имя параметра, а затем строка документации. -
@return
необходимо добавлять для каждого возвращаемого значения. За ним должно следовать имя возвращаемого значения, а затем строка документации.
Пример:
/** * 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();
Декларации перечислений
Используйте следующие правила для объявлений перечислений:
- Если объявления enum используются совместно с другим пакетом, поместите объявления в
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;