Стиль кода 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-DIRECTORYMODULESUBMODULE(необязательно, может быть несколько уровней)VERSIONAndroid.mkIINTERFACE_1.halIINTERFACE_2.hal…IINTERFACE_N.haltypes.hal(необязательно)
Место:
ROOT-DIRECTORY:hardware/interfacesдля основных пакетов HIDL.vendor/VENDOR/interfacesдля пакетов поставщика, гдеVENDOR– это поставщик процессора или производитель оригинального оборудования/разработчик дизайна.
MODULEдолжно быть одним словом, написанным строчными буквами и описывающим подсистему (например,nfc). Если требуется больше одного слова, используйте вложенныеSUBMODULE. Может быть несколько уровней вложенности.VERSIONдолжна быть той же версии (основной.промежуточной), что и в разделе Версии.IINTERFACE_X– название интерфейса сUpperCamelCase/PascalCase(например,INfc), как описано в разделе Названия интерфейсов.
Пример:
hardware/interfacesnfc1.0Android.mkINfc.halINfcClientCallback.haltypes.hal
Примечание. Все файлы должны иметь разрешения, не позволяющие их выполнение (в Git).
Названия пакетов
Названия пакетов должны быть указаны в следующем формате полного имени (FQN) (далее – PACKAGE-NAME):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
Место:
PACKAGE– пакет, который сопоставляется сROOT-DIRECTORY. В частности, значение параметра "PACKAGE":android.hardwareдля основных пакетов HIDL (соответствуетhardware/interfaces).vendor.VENDOR.hardwareдля пакетов поставщиков, гдеVENDORотносится к поставщику процессора или производителю оригинального оборудования/разработчику дизайна (соответствует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 (если он существует) импортируется автоматически (не импортируйте его явным образом).
Полные имена (FQNs)
Используйте полные имена для импорта пользовательских типов только при необходимости.
Если импортируемый тип находится в том же пакете, 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 (интерфейс INAME должен находиться в INAME.hal).
Функции
Для названий функций, аргументов и возвращаемых переменных используйте lowerCamelCase. Пример:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
Названия полей структур и объединений
Для названий полей struct или union используйте 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-уровней это должна быть лицензия Apache AOSP в файле 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находится на той же строке, что и следующая открывающая скобка, после него должен быть пробел. - Согласуйте все параметры и возвращаемые значения (если возможно).
- По умолчанию отступ составляет четыре пробела.
- Перенесенные параметры выравниваются по первым параметрам предыдущей строки, в противном случае они имеют отступ в восемь пробелов.
Аннотации
Используйте следующий формат аннотаций:
@annotate(keyword = value, keyword = {value, value, value})
Аннотации должны быть отсортированы в алфавитном порядке, а вокруг знаков равенства должны быть пробелы. Пример:
@callflow(key = value) @entry @exit
Аннотация должна занимать всю строку. Примеры:
/* Good */ @entry @exit /* Bad */ @entry @exit
Если аннотации не помещаются на одной строке, сделайте отступ в восемь пробелов. Пример:
@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;