-
Notifications
You must be signed in to change notification settings - Fork 11
AIDL HALs
Android Interface Definition Language (AIDL) is used to define interfaces for communication between Android components. In the context of HALs (Hardware Abstraction Layers), AIDL replaced HIDL as the preferred interface definition language starting from Android 11/12.
AIDL HALs provide:
- Type-safe IPC between framework and vendor code
- Stable ABI through Vintf (Vendor Interface) stability guarantees
- Multi-language support - C++, Java, Kotlin, and NDK backends
- Callback support for asynchronous notifications
Defines methods that can be called remotely. Use @VintfStability for HAL interfaces.
Defines data containers that can be passed across process boundaries. Use @VintfStability and optionally @JavaDerive for auto-generated helpers.
Use @Backing(type="int") or @Backing(type="byte") to specify the underlying type.
| Annotation | Purpose |
|---|---|
@VintfStability |
Marks interface as stable across vendor/framework boundary |
@Backing(type="int") |
Specifies enum backing type |
@JavaDerive(toString=true) |
Auto-generates toString() in Java/Kotlin |
@nullable |
Field or return value can be null |
oneway |
Method returns immediately, no response expected |
hardware/interfaces/<category>/aidl/
├── Android.bp # Interface build definition
├── android/hardware/<category>/ # AIDL files
│ ├── IFoo.aidl
│ ├── FooConfig.aidl # Parcelable
│ └── FooMode.aidl # Enum
└── default/ # Default implementation (optional)
├── Android.bp
├── Foo.cpp
├── Foo.h
├── service.cpp
├── android.hardware.foo-service.default.xml
└── android.hardware.foo-service.default.rc
Use aidl_interface module type. Key properties:
-
stability: "vintf"- Required for HAL interfaces -
vendor_available: true- Allows vendor partition usage -
frozen: true- Locks interface for stability - Backend configuration for java/ndk/cpp
Use cc_binary with:
-
vintf_fragments- VINTF manifest file -
init_rc- Init script -
vendor: true- Installs to vendor partition - Link against generated AIDL library:
<interface-name>-V<version>-ndk
- Include the generated BnXxx base class header
- Inherit from
BnXxx(e.g.,BnFoo) - Override all interface methods with
ndk::ScopedAStatusreturn type - Use
ndk::SharedRefBase::make<>()for instance creation
- Use
ndk::ScopedAStatus::ok()for success - Use
ndk::ScopedAStatus::fromExceptionCode()for errors - Output parameters use pointers (e.g.,
bool* _aidl_return)
- Call
ABinderProcess_setThreadPoolMaxThreadCount() - Create service instance with
ndk::SharedRefBase::make<>() - Register with
AServiceManager_addService()using<descriptor>/defaultinstance name - Call
ABinderProcess_joinThreadPool()to run
Important: AServiceManager_addService() returns binder_exception_t, not binder_status_t. Check against EX_NONE, not STATUS_OK:
binder_exception_t status = AServiceManager_addService(service->asBinder().get(), instance.c_str());
CHECK_EQ(status, EX_NONE);The AIDL compiler generates:
-
BnXxx- Server base class (native binder) -
BpXxx- Client proxy -
IXxx- Interface definition withdescriptorconstant -
IXxx.Stub(Java) - Server stub class
Required XML file declaring the HAL:
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.foo</name>
<version>1</version>
<fqname>IFoo/default</fqname>
</hal>
</manifest>Defines how the service starts:
-
class hal- Start with HAL class -
user/group- Run as system or dedicated user -
oneshot- For single-execution services
- Build the fully-qualified name:
<DESCRIPTOR>/default - Use
ServiceManager.getService()to get IBinder - Use
IXxx.Stub.asInterface()to convert to typed interface
- Always check for null service reference
- Wrap calls in try-catch for
RemoteExceptionandServiceSpecificException - Handle errors appropriately
- Use
AServiceManager_getService()to get binder - Wrap in
ndk::SpAIBinder - Use
IXxx::fromBinder()to get typed interface - Check for null and call methods through the pointer
- Define callback interface (e.g.,
IFooCallback) - Main interface has
registerCallback()andunregisterCallback()methods - Client implements callback, server stores reference and calls methods on events
- Use
onewayfor callback methods to avoid blocking
Inherit from BnFooCallback and override methods. Register with service after getting it.
| Exception Code | Use Case |
|---|---|
EX_UNSUPPORTED_OPERATION |
Feature not supported on device |
EX_ILLEGAL_ARGUMENT |
Invalid input parameter |
EX_ILLEGAL_STATE |
Invalid state for operation |
EX_SERVICE_SPECIFIC |
Custom error with status code |
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
return ndk::ScopedAStatus::fromServiceSpecificError(ERROR_CODE);throw new ServiceSpecificException(ERROR_CODE, "message");- Set
frozen: trueafter initial release - Add new version to
versions_with_infoarray - Maintain backward compatibility - new versions must work with old clients
- Deprecate old methods with
@deprecatedannotation, don't remove them
Critical: VINTF-stable interfaces (stability: "vintf") must be frozen before the service can register. Without frozen: true and a matching version in versions_with_info, assemble_vintf silently strips the HAL entry from the manifest fragment, producing an empty <manifest version="9.0" type="device"/>. The service then fails with status=-3 (EX_ILLEGAL_ARGUMENT) on AServiceManager_addService. Use m <interface-name>-freeze-api to freeze.
When adding a custom AIDL HAL as a dependency to a large module like SurfaceFlinger, be aware that the module's sources may be compiled by multiple build targets — not just the main binary. For example, SurfaceFlinger.cpp is compiled by both libsurfaceflinger and the layertracegenerator tool (via :libsurfaceflinger_sources filegroup). If the AIDL library is only added to one target's shared_libs, the others will fail with a missing header error.
Best practice: Create a dedicated cc_defaults for the custom AIDL deps and have every target that compiles the relevant sources inherit from it. This avoids duplicating the library list across unrelated Android.bp files and ensures new consumers automatically pick up the deps.
// In the module's Android.bp, define a defaults block for custom deps:
cc_defaults {
name: "surfaceflinger_custom_deps",
shared_libs: [
"custom.hardware.display.ltpo-V2-ndk",
"custom.hardware.biometrics.fingerprint.udfps-V1-ndk",
],
}
// Main binary inherits it:
cc_defaults {
name: "libsurfaceflinger_defaults",
defaults: ["surfaceflinger_custom_deps"],
...
}
// Tool that compiles the same sources also inherits it:
cc_binary {
name: "layertracegenerator",
defaults: ["surfaceflinger_custom_deps", ...],
...
}
This is especially important for emulator builds, which may build tools (like layertracegenerator) that device builds skip.
Interface with getter and setter for a boolean or integer value.
Complex configuration passed as a parcelable containing multiple fields.
createSession() returns an ISession interface for ongoing operations (common in biometrics).
Register callback to receive unsolicited events from HAL.
service list | grep hardware.foo
dumpsys vintfUse LOG_TAG in C++ to identify log source:
logcat -s <LOG_TAG>| Issue | Check |
|---|---|
| Service not found | init.rc spelling, VINTF manifest, SELinux |
| DeadObjectException | Service crashed, check logs |
| Permission denied | SELinux policy for service access |
| Method not found | Version mismatch between client and service |
| Header | Purpose |
|---|---|
<android/binder_manager.h> |
Service manager (register/lookup) |
<android/binder_process.h> |
Thread pool management |
<aidl/.../BnXxx.h> |
Generated server base class |
<aidl/.../IXxx.h> |
Generated interface definition |
HAL services need appropriate SELinux labels:
- Service binary labeled as
hal_foo_default_exec - Service process labeled as
hal_foo_default - Allow rules for binder communication
AIDL HALs use the service_manager (not hwservice_manager like HIDL). The key components are:
- Attributes - Define client/server/domain attributes for the HAL
- Service type - Declares the service in service_manager
- Service context - Maps AIDL interface name to SELinux type
- Domain - The process domain for the HAL service
- File context - Labels the service binary
| File | Purpose |
|---|---|
public/attributes |
Declare HAL attributes |
public/te_macros |
Helper macros for attribute creation |
dynamic/service.te |
Service type declaration |
dynamic/service_contexts |
Map interface name → SELinux type |
dynamic/hal_<name>.te |
Binder rules and service association |
vendor/hal_<name>_default.te |
Domain definition for implementation |
vendor/file_contexts |
Label the service binary |
device/<org>/sepolicy/common/
├── public/
│ ├── attributes # HAL attribute declarations
│ └── te_macros # Custom macros
├── dynamic/ # Used when vendor partition is prebuilt
│ ├── hal_<name>.te # Binder call rules + service association
│ ├── service.te # Service type declarations
│ └── service_contexts # Interface name → type mapping
├── vendor/ # Used when building vendor partition
│ ├── hal_<name>_default.te # Domain definition
│ └── file_contexts # Binary labeling
├── private/ # System_ext private policies
└── sepolicy.mk # Build system integration
# SPDX-FileCopyrightText: 2026 The halogenOS Project
# SPDX-License-Identifier: Apache-2.0
hal_attribute_custom(my_hal)
Or manually without macro:
attribute hal_my_hal;
expandattribute hal_my_hal true;
attribute hal_my_hal_client;
expandattribute hal_my_hal_client true;
attribute hal_my_hal_server;
expandattribute hal_my_hal_server false;
# SPDX-FileCopyrightText: 2026 The halogenOS Project
# SPDX-License-Identifier: Apache-2.0
type hal_my_hal_service, hal_service_type, service_manager_type;
# SPDX-FileCopyrightText: 2026 The halogenOS Project
# SPDX-License-Identifier: Apache-2.0
com.example.my.IFoo/default u:object_r:hal_my_hal_service:s0
The format is: <AIDL descriptor>/<instance> where descriptor is the full package path to the interface.
# SPDX-FileCopyrightText: 2026 The halogenOS Project
# SPDX-License-Identifier: Apache-2.0
# Allow binder IPC from client to server
binder_call(hal_my_hal_client, hal_my_hal_server)
# Associate the HAL attribute with the service type
hal_attribute_service(hal_my_hal, hal_my_hal_service)
# SPDX-FileCopyrightText: 2026 The halogenOS Project
# SPDX-License-Identifier: Apache-2.0
type hal_my_hal_default, domain;
hal_server_domain(hal_my_hal_default, hal_my_hal)
type hal_my_hal_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_my_hal_default)
binder_call(hal_my_hal_default, servicemanager)
# SPDX-FileCopyrightText: 2026 The halogenOS Project
# SPDX-License-Identifier: Apache-2.0
/vendor/bin/hw/com\.example\.my\.foo-service\.default u:object_r:hal_my_hal_default_exec:s0
#
# SPDX-FileCopyrightText: 2026 The halogenOS Project
# SPDX-License-Identifier: Apache-2.0
#
ifeq ($(TARGET_COPY_OUT_VENDOR), vendor)
ifeq ($(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),)
TARGET_USES_PREBUILT_VENDOR_SEPOLICY ?= true
endif
endif
SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS += \
device/<org>/sepolicy/common/public
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS += \
device/<org>/sepolicy/common/private
ifeq ($(TARGET_USES_PREBUILT_VENDOR_SEPOLICY), true)
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS += \
device/<org>/sepolicy/common/dynamic
else
BOARD_VENDOR_SEPOLICY_DIRS += \
device/<org>/sepolicy/common/dynamic \
device/<org>/sepolicy/common/vendor
endifTo allow a client (e.g., system_server, platform_app) to use the HAL:
# In private/system_server.te or private/platform_app.te
hal_client_domain(system_server, hal_my_hal)
For device-specific HAL implementations, add to the device's sepolicy/vendor/file_contexts:
/vendor/bin/hw/com\.example\.my\.foo-service\.device_name u:object_r:hal_my_hal_default_exec:s0
The device implementation reuses the same _exec type from common sepolicy - no additional domain rules needed.
# Check if service is labeled correctly
ls -Z /vendor/bin/hw/my-service
# Check service manager contexts
service list
# Check for denials
dmesg | grep avc
logcat -b events -d | grep avc
# Audit2allow for generating rules
audit2allow -p /sys/fs/selinux/policy -i /dev/stdin| Error | Cause | Solution |
|---|---|---|
ServiceManager: No match found |
Missing service_contexts entry | Add interface mapping |
avc: denied { add } for service |
Wrong service type | Check service.te declaration |
avc: denied { find } for service |
Client not in hal_client_domain | Add hal_client_domain() |
avc: denied { call } for binder |
Missing binder_call rules | Add binder_call() in hal .te |
The custom SELinux policy is located at device/custom/sepolicy/common/ and is included globally via product/halogenOS/config/BoardConfigCustom.mk.
device/custom/sepolicy/common/
├── public/
│ ├── attributes # hal_attribute_custom() macro usage
│ └── te_macros # hal_attribute_custom macro definition
├── dynamic/
│ ├── hal_custom_media_codec.te
│ ├── service.te
│ └── service_contexts
├── vendor/
│ ├── file_contexts
│ └── hal_custom_media_codec_default.te
├── private/ # (empty, for future use)
└── sepolicy.mk
| Component | Pattern | Example |
|---|---|---|
| Attributes |
hal_<name>, hal_<name>_client, hal_<name>_server
|
hal_custom_media_codec |
| Service type | hal_<name>_service |
hal_custom_media_codec_service |
| Domain | hal_<name>_default |
hal_custom_media_codec_default |
| Exec type | hal_<name>_default_exec |
hal_custom_media_codec_default_exec |
Located in public/te_macros:
define(`hal_attribute_custom', `
attribute hal_$1;
expandattribute hal_$1 true;
attribute hal_$1_client;
expandattribute hal_$1_client true;
attribute hal_$1_server;
expandattribute hal_$1_server false;
')
Usage:
hal_attribute_custom(my_new_hal)
AIDL Interface: custom.media.codec.ICodecFeatures
| File | Content |
|---|---|
public/attributes |
hal_attribute_custom(custom_media_codec) |
dynamic/service.te |
type hal_custom_media_codec_service, hal_service_type, service_manager_type; |
dynamic/service_contexts |
custom.media.codec.ICodecFeatures/default u:object_r:hal_custom_media_codec_service:s0 |
dynamic/hal_custom_media_codec.te |
binder_call(...) + hal_attribute_service(...)
|
vendor/hal_custom_media_codec_default.te |
Domain + exec type + init_daemon_domain()
|
vendor/file_contexts |
Labels custom.media.codec.features-service.default
|
- Add
hal_attribute_custom(<hal_name>)topublic/attributes - Add service type to
dynamic/service.te - Add interface mapping to
dynamic/service_contexts - Create
dynamic/hal_<hal_name>.tewith binder rules - Create
vendor/hal_<hal_name>_default.tewith domain definition - Add binary label to
vendor/file_contexts - For device implementations, add device binary label to device's
sepolicy/vendor/file_contexts
When a vendor ships a proprietary AIDL HAL as a prebuilt binary, the interface definition can be reconstructed:
-
VINTF manifest — Check
vendor/etc/vintf/manifest/*.xmlfor the<hal>entry:<hal format="aidl"> <name>vendor.example.hardware.foo</name> <fqname>IFoo/default</fqname> </hal>
-
Demangled symbols — Run
nm -DCon the binary and look forBnXxx/BpXxx/IXxxsymbols:nm -DC service_binary | grep "BnFoo\|IFoo"
-
Shared library deps —
readelf -dshows the AIDL stub library name:readelf -d service_binary | grep NEEDED # e.g. vendor.example.hardware.foo-V1-ndk.so
If not listed, the stubs are statically linked.
-
Log strings —
stringson the binary often reveals method names in log messages:strings service_binary | grep -oP 'Tag: \K\w+'
-
Sysfs paths — Log strings often reference the sysfs nodes each method reads/writes, revealing the purpose of each method.
-
Parameter types — Format strings like
val = %dindicate int parameters,val = %sindicates string.
Instead of writing directly to sysfs nodes that a vendor HAL also manages (causing conflicts), a custom device-specific HAL can call the vendor HAL via binder:
- Reconstruct the minimal AIDL definition with only the methods you need
- Build it as an
aidl_interfacemodule with matching package name and version - In your HAL implementation, use
AServiceManager_getService()to get the vendor HAL - Call methods through the typed interface
Caveat: Transaction codes in AIDL are assigned by method declaration order. If the reconstructed AIDL has methods in a different order than the original, calls will invoke the wrong methods. Verify by testing on-device or by analyzing the onTransact dispatch in the binary.