HIDL 代码样式类似于 Android 框架中的 C++ 代码,采用 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
IINTERFACE_1.hal
IINTERFACE_2.hal
…
IINTERFACE_N.hal
types.hal
(可选)
其中
ROOT-DIRECTORY
是hardware/interfaces
,适用于核心 HIDL 软件包。vendor/VENDOR/interfaces
用于供应商软件包,其中VENDOR
指的是 SoC 供应商或 OEM/ODM。
MODULE
应该是一个描述子系统的单个小写单词(例如,nfc
)。如果需要多个单词,请使用嵌套的SUBMODULE
。可以有多层嵌套。VERSION
应该与版本中描述的版本(主版本号.次版本号)完全相同。IINTERFACE_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
。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
接口的定义(接口 INAME
应在 INAME.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,这应该是 development/docs/copyright-templates/c.txt
中的 AOSP Apache 许可证。请记住更新年份并使用如上所述的 /* */
样式多行注释。
您可以选择在许可证声明后放置一个空行,后跟更改日志/版本信息。使用如上所述的 /* */
样式多行注释,在更改日志后放置空行,然后跟随软件包声明。
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();
枚举声明
对枚举声明使用以下规则
- 如果枚举声明与另一个软件包共享,请将声明放在
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;