The HIDL code style resembles C++ code in the Android framework, with 4-space indents and mixed-case filenames. Package declarations, imports, and docstrings are similar to those in Java, with slight modifications.
The following examples for IFoo.hal
and types.hal
illustrate HIDL code styles and provide quick links to details on each style
(IFooClientCallback.hal
, IBar.hal
, and
IBaz.hal
have been omitted).
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, ERR_ARG, // invalid arguments ERR_UNKNOWN = -1, // note, no transport related errors }; struct ArgData { int32_t[20] someArray; vec<uint8_t> data; }; |
Function names, variable names, and filenames should be descriptive; avoid
over-abbreviation. Treat acronyms as words (e.g., use INfc
instead
of INFC
).
The directory structure should appear as follows:
ROOT-DIRECTORY
MODULE
SUBMODULE
(optional, could be more than one
level)
VERSION
Android.mk
IINTERFACE_1.hal
IINTERFACE_2.hal
…
IINTERFACE_N.hal
types.hal
(optional)Where:
ROOT-DIRECTORY
is:
hardware/interfaces
for core HIDL packages.vendor/VENDOR/interfaces
for vendor packages,
where VENDOR
refers to an SoC vendor or an
OEM/ODM.MODULE
should be one lowercase word that describes
the subsystem (e.g. nfc
). If more than one word is needed, use
nested SUBMODULE
. There can be more than one level of
nesting.VERSION
should be the exact same version
(major.minor) as described in Versions.IINTERFACE_X
should be the interface name with
UpperCamelCase
/PascalCase
(e.g. INfc
)
as described in Interface names.Example:
hardware/interfaces
nfc
1.0
Android.mk
INfc.hal
INfcClientCallback.hal
types.hal
Note: All files must have non-executable permissions (in Git).
Package names must use the following fully-qualified name
(FQN) format (referred to as PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
Where:
PACKAGE
is the package that maps to the
ROOT-DIRECTORY
. In particular,
PACKAGE
is:
android.hardware
for core HIDL packages (mapping to
hardware/interfaces
).vendor.VENDOR.hardware
for vendor packages, where
VENDOR
refers to an SoC vendor or an OEM/ODM (mapping
to vendor/VENDOR/interfaces
).MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
are the exact same folder names in the structure described in
Directory structure.snake_case
.
The FQN is always used in package declarations.
Versions should have the following format:
MAJOR.MINOR
Both the MAJOR and the MINOR version should be a single integer. HIDL uses semantic versioning rules.
An import has one of the following three formats:
import PACKAGE-NAME;
import
PACKAGE-NAME::UDT;
(or, if the imported
type is in the same package,import UDT;
import PACKAGE-NAME::types;
The PACKAGE-NAME
follows the format in
Package names. The current package's
types.hal
(if it exists) is automatically imported (do not import
it explicitly).
Use fully qualified names for a user-defined type import only when necessary.
Omit PACKAGE-NAME
if the import type is in the same
package. An FQN must not contain spaces. Example of a fully qualified name:
android.hardware.nfc@1.0::INfcClientCallback
In another file under android.hardware.nfc@1.0
, refer to the
above interface as INfcClientCallback
. Otherwise, use only the
fully qualified name.
Use an empty line after package declaration (before the imports). Each import should occupy a single line and should not be indented. Group imports in the following order:
android.hardware
packages (use fully qualified names).
vendor.VENDOR
packages (use fully qualified
names).
Use an empty line between groups. Inside each group, sort imports alphabetically. Example:
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;
Interface names must start with an I
, followed by an
UpperCamelCase
/PascalCase
name. An interface with name
IFoo
must be defined in the file IFoo.hal
. This file
can contain definitions only for the IFoo
interface (the interface
INAME
should be in INAME.hal
).
For function names, arguments, and return variable names, use
lowerCamelCase
. Example:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
For struct/union field names, use lowerCamelCase
. Example:
struct FooReply { vec<uint8_t> replyData; }
Type names refer to struct/union definitions, enum type definitions, and
typedef
s. For these name, use
UpperCamelCase
/PascalCase
. Examples:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
Enum values should be UPPER_CASE_WITH_UNDERSCORES
. When passing
enum values as function arguments and returning them as function returns, use
the actual enum type (not the underlying integer type). Example:
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 };
Note: The underlying type of an enum type is explicitly declared after the colon. As it is not compiler dependent, using the actual enum type is clearer.
For fully qualified names for enum values, a colon is used between the enum type name and the enum value name:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
There must not be spaces inside a fully qualified name. Use a fully qualified name only when necessary and omit unnecessary parts. Example:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
For a single line comment, both //
and /** */
are
fine.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
//
mainly for:
/** */
for generated documentation. These can be applied
only to type, method, field, and enum value declarations. Example:
/** 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, ... }
/**
, use
*
at the beginning of each line, and place */
on the
last line all on its own (asterisks should align). Example:
/** * My multi-line * comment */
/*
(a single asterisk), use *
at the beginning of each line, and place
*/
on the last line all on its own (asterisks should align).
Example:
/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
Start each file with the appropriate licensing notice. For core HALs, this
should be the AOSP Apache license in
development/docs/copyright-templates/c.txt
.
Remember to update the year and use /* */
style multi-line comments
as explained above.
You can optionally place an empty line after the license notice, followed
by a changelog/versioning information. Use /* */
style
multi-line comments as explained above, place the empty line after the
changelog, then follow with the package declaration.
TODOs should include the string TODO
in all caps followed by a
colon. Example:
// TODO: remove this code before foo is checked in.
TODO comments are allowed only during development; they must not exist in published interfaces.
Use /** */
for multi-line and single line docstrings. Do not use
//
for docstrings.
Docstrings for interfaces should describe general mechanisms of the interface, design rationale, purpose, etc. Docstrings for functions should be specific to the function (package-level documentation goes in a README file in the package directory).
/** * 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(); };
You must add @param
s and @return
s for each
parameter/return value:
@param
must be added for each parameter. It should be
followed by the name of the parameter then the docstring.@return
must be added for each return value. It
should be followed by the name of the return value then the docstring.Example:
/** * 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);
General formatting rules include:
interface INfc { close(); };
Package declaration should be at the top of the file after the license notice, should occupy the entire line, and should not be indented. Packages are declared using the following format (for name formatting, see Package names):
package PACKAGE-NAME;
Example:
package android.hardware.nfc@1.0;
Function name, parameters, generates
, and return values should
be on the same line if they fit. Example:
interface IFoo { /** ... */ easyMethod(int32_t data) generates (int32_t result); };
If they don't fit on the same line, attempt to put parameters and return
values in the same indent level and distinguish generate
to help
the reader quickly see the parameters and return values. Example:
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); }
Additional details:
generates
is on the same line as the previous closing
parenthesis, use a preceding space. If generates
is on the same
line as the next open parenthesis, follow with a space.Use the following format for annotations:
@annotate(keyword = value, keyword = {value, value, value})
Sort annotations in alphabetical order, and use spaces around equal signs. Example:
@callflow(key = value) @entry @exit
Ensure an annotation occupies the entire line. Examples:
// Good @entry @exit // Bad @entry @exit
If annotations cannot fit on the same line, indent with 8 spaces. Example:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
If the entire value array cannot fit in the same line, put line breaks after
open braces {
and after each comma inside the array. Place closing
parenthesis immediately after the last value. Do not put the braces if there is
only one value.
If the entire value array can fit in the same line, do not use spaces after open braces and before closing braces and use one space after each comma. Examples:
// Good @callflow(key = {"val", "val"}) // Bad @callflow(key = { "val","val" })
There must NOT be empty lines between annotations and the function declaration. Examples:
// Good @entry foo(); // Bad @entry foo();
Use the following rules for enum declarations:
types.hal
rather than embedding inside an interface.Use the following rules for struct declarations:
types.hal
rather than embedding inside an interface.struct MyStruct { vec<uint8_t> data; int32_t someInt; }
Do not put spaces between the following:
Examples:
// Good int32_t[5] array; // Good int32_t[5][6] multiDimArray; // Bad int32_t [ 5 ] [ 6 ] array;
Do not put spaces between the following:
vec
and open angle bracket.vec
).vec
).Examples:
// 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;