HIDL is built around interfaces, an abstract type used in object-oriented languages to define behaviors. Each interface is part of a package.
Packages
Package names can have sublevels such as package.subpackage
. The
root directory for published HIDL packages is hardware/interfaces
or vendor/vendorName
(for example, vendor/google
for Pixel
devices). The package name forms one or more subdirectories under the root
directory; all files defining a package are in the same directory. For example,
package android.hardware.example.extension.light@2.0
could be found
under hardware/interfaces/example/extension/light/2.0
.
The following table lists package prefixes and locations:
Package prefix | Location | Interface types |
---|---|---|
android.hardware.* |
hardware/interfaces/* |
HAL |
android.frameworks.* |
frameworks/hardware/interfaces/* |
frameworks/ related |
android.system.* |
system/hardware/interfaces/* |
system/ related |
android.hidl.* |
system/libhidl/transport/* |
core |
The package directory contains files with extension .hal
. Every
file must contain a package
statement naming the package and
version the file is part of. The file types.hal
, if present, does
not define an interface but instead defines data types accessible to every
interface in the package.
Interface definition
Aside from types.hal
, every other .hal
file defines
an interface. An interface is typically defined as follows:
interface IBar extends IFoo { // IFoo is another interface // embedded types struct MyStruct {/*...*/}; // interface methods create(int32_t id) generates (MyStruct s); close(); };
An interface without an explicit extends
declaration implicitly
extends from android.hidl.base@1.0::IBase
(similar to
java.lang.Object
in Java.) The IBase interface, implicitly
imported, declares several reserved methods that should not and can't be
redeclared in user-defined interfaces or used otherwise. These methods
include:
ping
interfaceChain
interfaceDescriptor
notifySyspropsChanged
linkToDeath
unlinkToDeath
setHALInstrumentation
getDebugInfo
debug
getHashChain
Import process
The import
statement is HIDL mechanism to access package
interfaces and types in another package. An import
statement
concerns itself with two entities:
- The importing entity, which can be either a package or an interface
- The imported entity, which can be either a package or an interface
The importing entity is determined by the location of the
import
statement. When the statement is inside a package's
types.hal
, what is being imported is visible by the entire package;
this is a package-level import. When the statement is inside an
interface file, the importing entity is the interface itself; this is an
interface-level import.
The imported entity is determined by the value after the import
keyword. The value need not be a fully qualified name; if a component is
omitted, it is automatically filled with information from the current package.
For fully qualified values, the following import cases are supported:
- Whole-package imports. If the value is a package name and a version (syntax described below), then the entire package is imported into the importing entity.
- Partial imports. If the value is:
- An interface, the package's
types.hal
and that interface are imported into the importing entity. - A UDT defined in
types.hal
, then only that UDT is imported into the importing entity (other types intypes.hal
are not imported).
- An interface, the package's
- Types-only imports. If the value uses the syntax of a
partial import described above, but with the keyword
types
instead of an Interface name, only the UDTs intypes.hal
of the designated package are imported.
The importing entity gets access to a combination of:
- The imported package's common UDTs defined in
types.hal
; - The imported package's interfaces (for a whole-package import) or specified interface (for a partial import) for the purposes of invoking them, passing handles to them and/or inheriting from them.
The import statement uses the fully qualified-type-name syntax to provide the name and version of the package or interface being imported:
import android.hardware.nfc@1.0; // import a whole package import android.hardware.example@1.0::IQuux; // import an interface and types.hal import android.hardware.example@1.0::types; // import just types.hal
Interface inheritance
An interface can be an extension of a previously-defined interface. Extensions can be one of the following three types:
- Interface can add functionality to another one, incorporating its API unchanged.
- Package can add functionality to another one, incorporating its API unchanged.
- Interface can import types from a package or from a specific interface.
An interface can extend only one other interface (no multiple inheritance).
Each interface in a package with a non-zero minor version number must extend an
interface in the previous version of the package. For example, if an interface
IBar
in version 4.0 of package derivative
is based on
(extends) an interface IFoo
in version 1.2 of package
original
, and a version 1.3 of package original
is
created, IBar
version 4.1 can't extend version 1.3 of
IFoo
. Instead, IBar
version 4.1 must extend
IBar
version 4.0, which is tied to IFoo
version 1.2.
IBar
version 5.0 could extend IFoo
version 1.3, if
desired.
Interface extensions do not imply library dependence or cross-HAL inclusion in the generated code—they simply import the data structure and method definitions at the HIDL level. Every method in a HAL must be implemented in that HAL.
Vendor extensions
In some cases, vendor extensions are implemented as a subclass of the base object that represents the core interface they extend. The same object is registered under the base HAL name and version, and under the extension's (vendor) HAL name and version.
Versioning
Packages are versioned, and interfaces have the version of their package. Versions are expressed in two integers, major.minor.
- Major versions are not backwards compatible. Incrementing the major version number resets the minor version number to 0.
- Minor versions are backwards compatible. Incrementing the minor number indicates the newer version is fully backward compatible with the previous version. New data structures and methods can be added, but no existing data structures or method signatures may be changed.
Multiple major or minor versions of a HAL can be present on a device simultaneously. However, a minor version should be preferred over a major version because client code that works with a previous minor version interface also works with later minor versions of that same interface. For more details on versioning and vendor extensions, see HIDL Versioning.
Interface layout summary
This section summarizes how to manage a HIDL interface package (such as
hardware/interfaces
) and consolidates information presented
throughout the HIDL section. Before reading, ensure you are familiar with
HIDL versioning,
hashing with
hidl-gen concepts, the details of working with
HIDL in general, and the following definitions:
Term | Definition |
---|---|
application Binary Interface (ABI) | Application programming interface plus any binary linkages required. |
fully qualified name (fqName) | Name to distinguish a hidl type. Example:
android.hardware.foo@1.0::IFoo . |
package | Package containing a HIDL interface and types. Example:
android.hardware.foo@1.0 . |
package root | Root package that contains the HIDL interfaces. Example: the HIDL interface
android.hardware is in the package root
android.hardware.foo@1.0 . |
package root path | Location in the Android source tree where a package root maps to. |
For more definitions, see HIDL Terminology.
Every file can be found from the package root mapping and its fully qualified name
Package roots are specified to hidl-gen
as the argument
-r android.hardware:hardware/interfaces
. For example, if the
package is vendor.awesome.foo@1.0::IFoo
and hidl-gen
is sent -r vendor.awesome:some/device/independent/path/interfaces
,
then the interface file should be located in
$ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal
.
In practice, it is recommended for a vendor or OEM named awesome
to put their standard interfaces in vendor.awesome
. After a package
path has been selected, it must not be changed as this is baked into the ABI of
the interface.
Package path mapping should be unique
For example, if you have -rsome.package:$PATH_A
and
-rsome.package:$PATH_B
, $PATH_A
must be equal to
$PATH_B
for a consistent interface directory (this also makes
versioning interfaces much
easier).
Package root must have a versioning file
If you create a package path such as
-r vendor.awesome:vendor/awesome/interfaces
, you should also
create the file
$ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt
, which
should contain hashes of interfaces made using the -Lhash
option in
hidl-gen
(this is discussed extensively in
Hashing with
hidl-gen).
Interfaces go in device-independent locations
In practice, we recommend sharing interfaces between branches. This allows for maximum code re-usage and maximum testing of code across different devices and use cases.