Interfaces & Packages

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 (e.g. 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
android.hardware.* hardware/interfaces/*
android.frameworks.* frameworks/hardware/interfaces/*
android.system.* system/hardware/interfaces/*
android.hidl.* system/libhidl/transport/*

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 cannot be redeclared in user-defined interfaces or used otherwise. These methods include:

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

Importing

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; and
  • The imported entity, which too 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 in types.hal are not imported).
  • 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 in types.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 cannot 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 will be implemented as a subclass of the base object that represents the core interface they extend. The same object will be 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.

For broader compatibility with frameworks, multiple major versions of a HAL can be present on a device simultaneously. While multiple minor versions can also be present on a device, as minor versions are backwards compatible no reason exists to support more than the latest minor version for each major version. 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, the hashing concepts in Hashing with hidl-gen, the details of working with HIDL in general, and the following definitions:

Term Definition
Application Binary Interface (ABI) Application programming interface + 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, it is recommended to share interfaces between branches. This allows for maximum code re-usage and maximum testing of code across different devices and use cases.