Android 11 introduces the ability to use AIDL for HALs in Android, making it possible to implement parts of Android without HIDL. Transition HALs to use AIDL exclusively where possible (when upstream HALs use HIDL, HIDL must be used).
HALs using AIDL to communicate between framework components, such as those in
system.img
, and hardware components, such as those in vendor.img
, must use
stable AIDL. However, to communicate within a partition, for example, from one
HAL to another, there's no restriction on the IPC mechanism to use.
Motivation
AIDL has been around longer than HIDL, and is used in many other places, such as between Android framework components or in apps. Now that AIDL has stability support, it's possible to implement an entire stack with a single IPC runtime. AIDL also has a better versioning system than HIDL. Here are some advantages of AIDL:
- Using a single IPC language means having only one thing to learn, debug, optimize, and secure.
- AIDL supports in-place versioning for the owners of an interface:
- Owners can add methods to the end of interfaces, or fields to parcelables. This means it's easier to version code over the years, and also the year over year cost is smaller (types can be amended in place and there's no need for extra libraries for each interface version).
- Extension interfaces can be attached at run time rather than in the type system, so there's no need to rebase downstream extensions onto newer versions of interfaces.
- An existing AIDL interface can be used directly when its owner chooses to stabilize it. Before, an entire copy of the interface would have to be created in HIDL.
Build against the AIDL runtime
AIDL has three different backends: Java, NDK, and CPP. To use stable AIDL,
always use the system copy of libbinder
at system/lib*/libbinder.so
and
talk on /dev/binder
. For code on the vendor
image, this means that
libbinder
(from the VNDK) can't be used: this library has an unstable C++
API and unstable internals. Instead, native vendor code must use the NDK
backend of AIDL, link against libbinder_ndk
(which is backed by system
libbinder.so
), and link against the NDK libraries created by aidl_interface
entries. For the exact module names, see Module naming
rules.
Write an AIDL HAL interface
For an AIDL interface to be used between system and vendor, the interface needs two changes:
- Every type definition must be annotated with
@VintfStability
. - The
aidl_interface
declaration needs to includestability: "vintf",
.
Only the owner of an interface can make these changes.
When you make these changes, the interface must be in the
VINTF manifest in order to work. Test this (and related
requirements, such as verifying that released interfaces are frozen) using the
VTS test vts_treble_vintf_vendor_test
. You can use a
@VintfStability
interface without these requirements by calling either
AIBinder_forceDowngradeToLocalStability
in the NDK backend,
android::Stability::forceDowngradeToLocalStability
in the C++ backend,
or android.os.Binder#forceDowngradeToSystemStability
in the Java backend
on a binder object before it's sent to another process.
Additionally, for maximum code portability and to avoid potential problems such as unnecessary additional libraries, disable the CPP backend.
Note that the use of backends
in the following code example is correct, as
there are three backends (Java, NDK, and CPP). The code shows how to
disable a CPP backend:
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Find AIDL HAL interfaces
AOSP stable AIDL interfaces for HALS are within aidl
folders in the same
base directories as HIDL interfaces:
hardware/interfaces
is for interfaces typically provided by hardware.frameworks/hardware/interfaces
is for high-level interfaces provided to hardware.system/hardware/interfaces
is for low-level interfaces provided to hardware.
Put extension interfaces into other hardware/interfaces
subdirectories in vendor
or hardware
.
Extension interfaces
Android has a set of official AOSP interfaces with every release. When Android partners want to add capabilities to these interfaces, they shouldn't change these directly because this makes their Android runtime incompatible with the AOSP Android runtime. Avoid changing these interfaces so the GSI image can continue to work.
Extensions can register in two different ways:
- At runtime; see Attached extension interfaces
- As a standalone, registered globally and in VINTF
However an extension is registered, when vendor-specific (meaning not a part of upstream AOSP) components use the interface, merge conflicts aren't possible However, when downstream modifications to upstream AOSP components are made, merge conflicts can result, and the following strategies are recommended:
- Upstream the interface additions to AOSP in the next release.
- Upstream interface additions that allow further flexibility (without merge conflicts) in the next release.
Extension parcelables: ParcelableHolder
ParcelableHolder
is an instance of the Parcelable
interface that can
contain another instance of Parcelable
.
The main use case of ParcelableHolder
is to make Parcelable
extensible.
For example, image that device implementers expect to be able to extend an
AOSP-defined Parcelable
, AospDefinedParcelable
, to include their value-add
features.
Use the ParcelableHolder
interface to extend Parcelable
with your value-add
features. The ParcelableHolder
interface contains an instance of
Parcelable
. If you try to add fields to Parcelable
directly, it
causes an error:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
As seen in the preceding code, this practice is broken because the fields
added by the device implementer might have a conflict when Parcelable
is
revised in the next releases of Android.
Using ParcelableHolder
, the owner of a parcelable can define an extension
point in an instance of Parcelable
:
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Then the device implementers can define their own Parcelable
instance for
their extension:
parcelable OemDefinedParcelable {
String x;
int[] y;
}
The new Parcelable
instance can be attached to the original
Parcelable
with the ParcelableHolder
field:
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
AIDL HAL server instance names
By convention, AIDL HAL services have an instance name of the format
$package.$type/$instance
. For example, an instance of the vibrator HAL is
registered as android.hardware.vibrator.IVibrator/default
.
Write an AIDL HAL server
@VintfStability
AIDL servers must be declared in the VINTF manifest, for
example:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
Otherwise, they should register an AIDL service normally. When running VTS tests, it's expected that all declared AIDL HALs are available.
Write an AIDL client
AIDL clients must declare themselves in the compatibility matrix, for example:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
Convert an existing HAL from HIDL to AIDL
Use the hidl2aidl
tool to convert a HIDL interface to AIDL.
hidl2aidl
features:
- Create AIDL (
.aidl
) files based on the HAL (.hal
) files for the given package. - Create build rules for the newly created AIDL package with all backends enabled.
- Create translate methods in the Java, CPP, and NDK backends for translating from the HIDL types to the AIDL types.
- Create build rules for translate libraries with required dependencies.
- Create static asserts to ensure that HIDL and AIDL enumerators have the same values in the CPP and NDK backends.
Follow these steps to convert a package of HAL files to AIDL files:
Build the tool located in
system/tools/hidl/hidl2aidl
.Building this tool from the latest source provides the most complete experience. You can use the latest version to convert interfaces on older branches from previous releases:
m hidl2aidl
Execute the tool with an output directory followed by the package to be converted.
Optionally, use the
-l
argument to add the contents of a new license file to the top of all generated files. Be sure to use the correct license and date:hidl2aidl -o <output directory> -l <file with license> <package>
For example:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Read through the generated files and fix any issues with the conversion:
conversion.log
contains any unhandled issues to fix first.- The generated AIDL files might have warnings and suggestions that
need action. These comments begin with
//
. - Clean up and make improvements to the package.
- Check the
@JavaDerive
annotation for features that might be needed, such astoString
orequals
.
Build only the targets you need:
- Disable backends that won't be used. Prefer the NDK backend over the CPP backend; see Build against the AIDL runtime.
- Remove translate libraries or any of their generated code that won't be used.
See Major AIDL and HIDL differences:
- Using AIDL's built-in
Status
and exceptions typically improve the interface and remove the need for another interface-specific status type. - AIDL interface arguments in methods aren't
@nullable
by default like they were in HIDL.
- Using AIDL's built-in
SEPolicy for AIDL HALs
An AIDL service type that's visible to vendor code must have the
hal_service_type
attribute. Otherwise, the sepolicy configuration is the same
as any other AIDL service (though there are special attributes for HALs). Here's
an example definition of a HAL service context:
type hal_foo_service, service_manager_type, hal_service_type;
For most services defined by the platform, a service context with the correct
type is added already (for example, android.hardware.foo.IFoo/default
is
already marked as hal_foo_service
). However, if a framework client supports
multiple instance names, additional instance names must be added in
device-specific service_contexts
files:
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
When you create a new type of HAL, you must add HAL attributes. A specific HAL
attribute might be associated with multiple service types (each of which can
have multiple instances as just discussed). For a HAL, foo
, there is
hal_attribute(foo)
. This macro defines attributes hal_foo_client
and
hal_foo_server
. For a given domain, the hal_client_domain
and
hal_server_domain
macros associate a domain with a given HAL attribute. For
example, system server being a client of this HAL corresponds to the policy
hal_client_domain(system_server, hal_foo)
. A HAL server similarly includes
hal_server_domain(my_hal_domain, hal_foo)
.
Typically, for a given HAL attribute, also create a domain like
hal_foo_default
for reference or example HALs. However, some devices use
these domains for their own servers. Distinguishing between domains for
multiple servers matters only if there are multiple servers that serve the
same interface and need a different permission set in their implementations.
In all of these macros, hal_foo
isn't an sepolicy object. Instead, this
token is used by these macros to refer to the group of attributes associated
with a client server pair.
However, so far, hal_foo_service
and hal_foo
(the attribute pair from
hal_attribute(foo)
) aren't associated. A HAL attribute is associated
with AIDL HAL services using the hal_attribute_service
macro (HIDL HALs use
the hal_attribute_hwservice
macro), for example,
hal_attribute_service(hal_foo, hal_foo_service)
. This means that
hal_foo_client
processes can get ahold of the HAL, and hal_foo_server
processes can register the HAL. The enforcement of these registration rules is
done by the context manager (servicemanager
).
Service names might not always correspond to HAL attributes, for example,
hal_attribute_service(hal_foo, hal_foo2_service)
. In general, because
this implies the services are always used together, you can remove
the hal_foo2_service
and use hal_foo_service
for all of service
contexts. When HALs set multiple hal_attribute_service
instances, it's because
the original HAL attribute name isn't general enough and can't be changed.
Putting this all together, an example HAL looks like this:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
Attached extension interfaces
An extension can be attached to any binder interface, whether it's a top-level interface registered directly with service manager or it's a subinterface. When getting an extension, you must confirm the type of the extension is as expected. You can set extensions only from the process serving a binder.
Use attached extensions whenever an extension modifies the functionality of an existing HAL. When entirely new capability is needed, this mechanism isn't necessary, and you can register an extension interface with the service manager directly. Attached extension interfaces make the most sense when they're attached to subinterfaces, because these hierarchies can be deep or multi-instanced. Using a global extension to mirror the binder interface hierarchy of another service requires extensive bookkeeping to provide equivalent capabilities to directly attached extensions.
To set an extension on a binder, use the following APIs:
- NDK backend:
AIBinder_setExtension
- Java backend:
android.os.Binder.setExtension
- CPP backend:
android::Binder::setExtension
- Rust backend:
binder::Binder::set_extension
To get an extension on a binder, use the following APIs:
- NDK backend:
AIBinder_getExtension
- Java backend:
android.os.IBinder.getExtension
- CPP backend:
android::IBinder::getExtension
- Rust backend:
binder::Binder::get_extension
You can find more information for these APIs in the documentation of the
getExtension
function in the corresponding backend. An example of how to use
extensions is in
hardware/interfaces/tests/extension/vibrator
.
Major AIDL and HIDL differences
When using AIDL HALs or using AIDL HAL interfaces, be aware of the differences compared to writing HIDL HALs.
- The AIDL language's syntax is closer to Java. HIDL syntax is similar to C++.
- All AIDL interfaces have built-in error statuses. Instead of creating custom
status types, create constant status ints in interface files and use
EX_SERVICE_SPECIFIC
in the CPP and NDK backends andServiceSpecificException
in the Java backend. See Error handling. - AIDL doesn't automatically start thread pools when binder objects are sent. You must start them manually (see Thread management).
- AIDL doesn't abort on unchecked transport errors (HIDL
Return
aborts on unchecked errors). - AIDL can declare only one type per file.
- AIDL arguments can be specified as
in
,out
, orinout
in addition to the output parameter (there are no synchronous callbacks). - AIDL uses
fd
as the primitive type instead ofhandle
. - HIDL uses major versions for incompatible changes and minor versions for
compatible changes. In AIDL, backward-compatible changes are done in place.
AIDL has no explicit concept of major versions; instead, this is
incorporated into package names. For example, AIDL might use the package name
bluetooth2
. - AIDL doesn't inherit realtime priority by default. The
setInheritRt
function must be used per binder to enable realtime priority inheritance.
Vendor Test Suite (VTS) Tests for HALs
Android relies on the Vendor Test Suite (VTS) to verify expected HAL implementations. VTS helps ensure Android can be backward compatible with old vendor implementations. Implementations failing VTS have known compatibility issues that could prevent them from working with future versions of the OS.
There are two major parts of VTS for HALs.
1. Verify HALs on the device are known and expected by Android
This set of tests can be found in
test/vts-testcase/hal/treble/vintf
.
These tests are responsible for verifying:
- Every
@VintfStability
interface that is declared in a VINTF manifest is frozen at a known released version. This ensures both sides of the interface agree on the exact definition of that version of the interface. This is necessary for basic operation. - All HALs that are declared in a VINTF manifest are available on that device. Any client with sufficient permissions to use a declared HAL service must be able to get and use those services at any time.
- All HALs that are declared in a VINTF manifest are serving the version of the interface that they declare in the manifest.
- There are no deprecated HALs being served on a device. Android drops support for lower versions of HAL interfaces as described in FCM lifecycle.
- Required HALs are present on the device. Some HALs are required for Android to work properly.
2. Verify the expected behavior of each HAL
Each HAL interface has its own VTS tests to verify the expected behavior from its clients. The test cases run against every instance of a declared HAL interface and enforce specific behavior based on the version of the interface that's implemented.
These tests attempt to cover every aspect of the HAL implementation that the Android framework relies on, or might rely on in the future.
These tests include verifying support of features, error handling, and any other behavior a client might expect from the service.
VTS milestones for HAL development
VTS tests are expected to be kept up to date when creating or modifying Android's HAL interfaces.
VTS tests must be finished and ready to verify vendor implementations before they're frozen for Android Vendor API releases. They must be ready before the interfaces are frozen so developers can create their implementations, verify them, and provide feedback to the HAL interface developers.
VTS on Cuttlefish
When hardware isn't available, Android uses Cuttlefish as a development vehicle for HAL interfaces. This allows for scalable VTS and integration testing of Android.
hal_implementation_test
tests that Cuttlefish has implementations of the
latest HAL interface versions to make sure Android is ready to handle
the new interfaces and the VTS tests are ready to test the new vendor
implementations as soon as new hardware and devices are available.