AOSP Framework & Internals
6 min read

Migration Process

Learn about Migration Process.

The End of HwBinder

Hardware vendors spent years writing HIDL interfaces for Android. Google then deprecated HIDL entirely and removed the HwBinder transport layer from newer Android releases. Older hardware implementations simply will not run on a modern Android filesystem. You have to migrate these legacy components to stable AIDL.

This migration is not a simple file rename. You must translate the interface syntax to match AIDL paradigms. The build system rules require updates to generate the correct C++ bindings. You also have to declare a new standard Binder transport in the device manifest. Finally, the hardware daemon must link against standard Binder libraries.

Breaking this migration down into discrete steps removes the ambiguity. You update the code, fix the build, patch the manifest, and rewire the daemon. We start with the most obvious change, which is translating the code itself.

Translating the Interface Syntax

HIDL uses a C++ style syntax that feels familiar to systems programmers. AIDL borrows its structure directly from Java. You cannot just copy and paste the old definitions because this paradigm shift forces you to rethink data flow. AIDL demands explicit directional tags for every parameter to optimize inter-process communication.

You begin by replacing your .hal files with .aidl files. The new syntax requires strict stability annotations.

Here is a legacy HIDL definition for a basic light service.

package android.hardware.light@2.0;

interface ILight {
    setLight(Type type, LightState state) generates (Status status);
};

Compare that to the modern AIDL equivalent.

package android.hardware.light;

@VintfStability
interface ILight {
    void setLight(in LightType type, in LightState state);
}

Notice the in tags on the parameters. AIDL uses these directional markers to know exactly how data moves across the boundary. The @VintfStability annotation is mandatory for any interface crossing the vendor boundary. The old Status return type vanishes because AIDL natively maps errors to android::binder::Status in C++.

Common Mistake: Forgetting the @VintfStability annotation will cause the build to fail when verifying the vendor interface. Always include it for hardware interfaces.

Your syntax is now valid AIDL. The code looks right, but the build system still thinks it is compiling a HIDL module.

Forcing a Stable ABI in the Build

The Soong build system uses the hidl_interface module type to generate HwBinder stubs. This older target cannot process AIDL files. You need a module that understands standard Binder and guarantees a stable Application Binary Interface across system updates.

You fix this by swapping the module type in your Android.bp file. The aidl_interface module handles standard Binder code generation. You configure it to generate C++ code that explicitly targets the NDK backend.

Modify your blueprint file to look like this.

aidl_interface {
    name: "android.hardware.light",
    vendor_available: true,
    srcs: ["android/hardware/light/*.aidl"],
    stability: "vintf",
    backend: {
        cpp: { enabled: false },
        ndk: { enabled: true },
        java: { sdk_version: "module_current" },
    },
}

The backend configuration block controls the generated output. You explicitly disable the cpp backend and enable the ndk backend. The ndk backend links against libbinder_ndk to create a stable C ABI. The older cpp backend links against libbinder, which ties your module to the specific compiler version used for the Android framework.

Warning: Leaving the cpp backend enabled for vendor HALs creates a tight coupling to the framework. A future framework update will break your vendor image.

Your module now compiles successfully. The binaries exist on the device, but the system will still fail to route calls to your HAL.

Declaring the Standard Binder Transport

Android devices use the Vendor Interface Object manifest to track hardware capabilities. The legacy manifest explicitly tells the framework to look for your HAL on the HwBinder transport. HwBinder does not exist in the new architecture.

You must update manifest.xml to reflect this new reality. The framework needs to know it should use standard Binder to talk to your service. You remove the old transport declarations entirely.

Before the migration, a HIDL manifest block looks like this.

<hal format="hidl">
    <name>android.hardware.light</name>
    <transport>hwbinder</transport>
    <version>2.0</version>
</hal>

You replace that entire block with the AIDL format.

<hal format="aidl">
    <name>android.hardware.light</name>
    <version>1</version>
    <interface>
        <name>ILight</name>
        <instance>default</instance>
    </interface>
</hal>

The <transport> tag disappears completely. Standard Binder is now implied by default. The format string changes from hidl to aidl. You also drop the minor version number because AIDL versioning strictly uses integers.

Common Mistake: Leaving a minor version number in the AIDL manifest block will cause a VINTF verification failure. Always use simple integers for AIDL versions.

Platform routing now knows exactly where to find the service. The final step is ensuring your actual daemon implementation accepts these connections.

Registration and Verification

Your legacy daemon code still tries to register itself with hwservicemanager. This old service manager refuses connections from standard Binder. The daemon will crash or hang when the framework attempts to bind to it.

You have to update the daemon to use the correct service manager. This requires rewriting the C++ registration calls in your main execution loop. You replace the old HwBinder registration with standard NDK Binder calls.

The following diagram contrasts the old and new registration paths. This visualization highlights why the daemon dependencies must change to reach the correct manager. Look at how the new path bypasses HwBinder entirely.

Legacy code forces the daemon to communicate through libhwbinder. The new path drops that library completely and links against libbinder_ndk.

You implement this by swapping out the old registerAsService method. You call AServiceManager_addService instead. The service now correctly registers with the standard servicemanager. Finally, you verify this implementation by running the Vendor Test Suite. Executing the VtsHalLightTargetTest binary proves your migration is successful and the contract holds.

This completes the transition from HIDL to stable AIDL. Syntax translation provided the foundation. Updating the build rules and manifest connected the system components. Rewriting the registration code brought the daemon to life.

The hardware is now fully integrated into the modern Android architecture. With the migration finished, you face a new problem. How do you safely update these AIDL interfaces next year without breaking older vendor images?