Стиль кода 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), как описано в Versions . -
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
следует формату имен пакетов . types.hal
текущего пакета (если он существует) импортируется автоматически (не импортируйте его явно).
Полные имена (FQN)
Используйте полные имена для импорта пользовательского типа только в случае необходимости. Опустите PACKAGE-NAME
, если тип импорта находится в том же пакете. Полное доменное имя не должно содержать пробелов. Пример полного имени:
android.hardware.nfc@1.0::INfcClientCallback
В другом файле под android.hardware.nfc@1.0
обратитесь к указанному выше интерфейсу как к INfcClientCallback
. В противном случае используйте только полное имя.
Группировка и упорядочивание импорта
Используйте пустую строку после объявления пакета (перед импортом). Каждый импорт должен занимать одну строку и не иметь отступа. Сгруппируйте импорт в следующем порядке:
- Другие пакеты
android.hardware
(используйте полные имена). - Другой
vendor. VENDOR
Пакеты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 };
Примечание . Базовый тип перечислимого типа явно объявляется после двоеточия. Поскольку это не зависит от компилятора, использование фактического типа перечисления более понятно.
Для полных имен для значений перечисления между именем типа перечисления и именем значения перечисления используется двоеточие :
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
s и @return
s для каждого параметра/возвращаемого значения:
-
@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:
- Если объявления 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;