AOSP Expert & Production Engineering
4 min read

Handling Proprietary HALs

When porting AOSP to a new device or upgrading an existing device to a newer Android version, the most significant challenges revolve around hardware support. Specifically, ensuring that proprietary, closed-source Hardware Abstraction Layers (HALs) function correctly with the newer open-source framework.

Since the source code for these HALs is unavailable, developers cannot simply recompile them to fix errors. They must employ workarounds to satisfy the dynamic linker and the Android framework.

HIDL Passthrough HALs with Prebuilt .so

Early Android HALs were loaded directly into the framework process using dlopen(). With Project Treble, Android introduced HIDL (Hardware Interface Definition Layer) and Binderized HALs, where the HAL runs in its own process and communicates with the framework via binder IPC.

However, many older, proprietary HALs (like legacy camera or audio modules) were designed as standard shared libraries (.so files). To bridge this gap without rewriting the proprietary code, Android uses the "Passthrough" mode.

In a passthrough configuration, a wrapper process (the binderized service) uses dlopen() to load the proprietary .so file into its own memory space. The wrapper then translates the modern HIDL API calls from the framework into the legacy C/C++ function calls expected by the proprietary blob.

If the proprietary blob camera.vendor.so is missing a dependency or fails to load, the entire passthrough wrapper service crashes, and the hardware component becomes unavailable.

Shim Libraries for Missing Symbols

When a newer Android version removes or alters an API in a core system library (like libutils.so or libcutils.so), older proprietary HALs that link against those libraries will fail to load. The dynamic linker will report an "undefined symbol" error.

To fix this without recompiling the blob, developers create "shim" libraries. A shim is a tiny, open-source library that provides the missing symbol, acting as an adapter.

Scenario: A proprietary libaudio_custom.so requires the function String8::setTo(char const*), which was removed in Android 12.

Solution:

  1. Write the Shim (shim_audio.cpp):
    #include <utils/String8.h>
    
    extern "C" {
        // Define the missing symbol exactly as the blob expects it
        void _ZN7android7String85setToEPKc(void* obj, const char* str) {
            // Forward the call to the modern equivalent API
            static_cast<android::String8*>(obj)->setTo(str); 
        }
    }
    
  2. Compile the Shim: Build this code into a shared library, e.g., libaudio_shim.so.
  3. Inject the Shim: Use LD_PRELOAD in the init script that starts the HAL service. LD_PRELOAD forces the linker to load the shim library before any other libraries, resolving the missing symbol immediately.
# init.device.rc
service vendor.audio-hal /vendor/bin/hw/android.hardware.audio@6.0-service
    class hal
    user audioserver
    group audio
    setenv LD_PRELOAD /vendor/lib64/libaudio_shim.so

Symbol Lookup Failures and Fixes

Diagnosing why a proprietary HAL is failing requires investigating symbol lookup errors.

If a hardware feature (like the camera) isn't working, the first step is to check logcat or dmesg. A linker error looks like this:

CANNOT LINK EXECUTABLE "/vendor/bin/hw/android.hardware.camera.provider@2.4-service": 
library "libmissing_dependency.so" not found

Or:

CANNOT LINK EXECUTABLE "/vendor/bin/hw/android.hardware.camera.provider@2.4-service": 
cannot locate symbol "_ZN7android10VectorImpl19reservedVectorImpl1Ev" referenced by "/vendor/lib64/camera.qcom.so"...

Fix Strategies

  1. Missing Library: If libmissing_dependency.so is not found, you must locate it from the stock firmware extraction and add it to your proprietary-files.txt and Android.mk so it gets copied to the device.
  2. Missing Symbol (Shim): If a specific symbol like _ZN7android10VectorImpl19reservedVectorImpl1Ev (which demangles to android::VectorImpl::reservedVectorImpl1()) is missing, you must create a shim library to provide that symbol, as described above.
  3. Dependency Substitution (patchelf): If the blob is looking for an older library name (e.g., libcrypto.so.1.0.0) but your build environment provides a newer compatible version (e.g., libcrypto.so.1.1), you can use patchelf to modify the binary:
    patchelf --replace-needed libcrypto.so.1.0.0 libcrypto.so.1.1 vendor/lib64/camera.qcom.so
    

Mastering shim libraries, LD_PRELOAD, and patchelf is essential for bringing up AOSP on devices with closed-source hardware abstractions.