Стиль кода 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
. За ним должно следовать имя параметра и строка документации. - Для каждого возвращаемого значения необходимо добавить
@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;