Sensors Multi-HAL

The Sensors Multi-HAL is a framework that allows sensors HALs to run alongside other sensor HALs. The Sensors Multi-HAL dynamically loads sensors sub-HALs stored as dynamic libraries on the vendor partition and gives them a callback object that can handle posting events and acquiring and releasing the wake lock. A sensors sub-HAL is a sensors HAL that is built into a shared object on the vendor partition and is used by the multi-HAL framework. These sub-HALs don't depend on one another or on the multi-HAL code that contains the main function for the process.

Sensors Multi-HAL 2.1, available on devices running Android 11 or higher, is an iteration of Sensors Multi-HAL 2.0 that supports loading sub-HALs that can expose the hinge angle sensor type. To support this sensor type, sub-HALs must use the sub-HAL APIs defined in the 2.1 SubHal header.

For devices running Android 13 or higher that use the Sensors AIDL HAL, you can use the multi-HAL shim layer to allow multi-HAL capability. For implementation details, see Using the Sensors Multi-HAL with the Sensors AIDL HAL.

Difference between Sensors Multi-HAL 2 and Sensors HAL 2

Sensors Multi-HAL 2, available on devices running Android 10 or higher, introduces several abstractions on top of Sensors HAL 2 to make it easier to interact with HAL APIs. Sensors Multi-HAL 2 introduces the HalProxy class to handle implementing the Sensors HAL 2 interface and the V2_1/SubHal (or V2_0/SubHal) interface to allow HalProxy to interact with sub-HALs.

The ISensorsSubHal interface is different from the 2.1/ISensors.hal (or 2.0/ISensors.hal) interface in the following ways:

  • The initialize method passes a IHalProxyCallback class instead of two FMQs and ISensorsCallback.
  • Sub-HALs must implement a debug function for providing debugging information in bug reports.
  • Sub-HALs must implement a name function so the loaded sub-HAL can be distinguished from other sub-HALs.

The main difference between Sensors Multi-HAL 2 and Sensors HAL 2 is in the initialize functions. Instead of providing FMQs, the IHalProxyCallback interface provides two methods, one method to post sensor events to the sensors framework and one method to create wake locks. Under the hood, the Sensors Multi-HAL manages all interactions with the FMQs to ensure timely delivery of sensor events for all sub-HALs. It's strongly recommended that sub-HALs use the createScopedWakelock method to delegate the burden of timing out wake locks to the Sensors Multi-HAL and to centralize wake lock usage to one common wake lock for the entire Sensors Multi-HAL, which minimizes locking and unlocking calls.

Sensors Multi-HAL 2 also has some built-in safety features. It handles situations where the sensor FMQ is full or where the Android sensor framework restarts and the sensor state needs to be reset. Additionally, when events are posted to the HalProxy class but the sensor framework is unable to accept the events immediately, the Sensors Multi-HAL can move the events to a background thread to allow work to continue across all sub-HALs while waiting for the events to be posted.

Source code and reference implementation

All Sensors Multi-HAL code is available in hardware/interfaces/sensors/common/default/2.X/multihal/. Here are pointers to some resources.

  • HalProxy.h: The HalProxy object is instantiated by Sensors multi-HAL and handles the passing of data from the sub-HALs to the sensor framework.
  • HalProxy.cpp: The implementation of HalProxy contains all logic needed to multiplex communication between sub-HALs and the sensor framework.
  • SubHal.h: The ISensorsSubHal interface defines the interface that sub-HALs must follow to be compatible with HalProxy. The sub-HAL implements the initialize method so that the HalProxyCallback object can be used for postEvents and createScopedWakelock.

    For Multi-HAL 2.0 implementations, use version 2.0 of SubHal.h.

  • hardware/interfaces/sensors/common/default/2.X/multihal/tests/: These unit tests verify the HalProxy implementation.

  • hardware/interfaces/sensors/common/default/2.X/multihal/tests/fake_subhal/: This example sub-HAL implementation uses fake sensors to generate fake data. Useful for testing how multiple sub-HALs interact on a device.

Implementation

This section describes how to implement Sensors Multi-HAL in the following situations:

Use the Sensors Multi-HAL with the Sensors AIDL HAL

To allow multi-HAL capability with the Sensors AIDL HAL, import the AIDL Multi-HAL shim layer module, which is found in hardware/interfaces/sensors/aidl/default/multihal/. The module handles the conversion between AIDL and HIDL sensors HAL definition types and defines a wrapper around the multi-HAL interface described in Implementing Sensors Multi-HAL 2.1. The AIDL multi-HAL shim layer is compatible with devices that implement Sensors Multi-HAL 2.1.

The AIDL multi-HAL shim layer allows you to expose the head tracker and limited-axis IMU sensor types in the Sensors AIDL HAL. To use these sensor types defined by the AIDL HAL interface, set the type field in the SensorInfo struct in the getSensorsList_2_1() implementation. This is safe because the integer-backed sensor type fields of the AIDL and HIDL sensors HAL don't overlap.

Implement Sensors Multi-HAL 2.1

To implement Sensors Multi-HAL 2.1 on a new device, follow these steps:

  1. Implement the ISensorsSubHal interface as described in SubHal.h.
  2. Implement the sensorsHalGetSubHal_2_1 method in SubHal.h.
  3. Add a cc_library_shared target to build the newly implemented sub-HAL. When adding the target:

    1. Ensure the target is pushed to somewhere on the vendor partition of the device.
    2. In the config file located at /vendor/etc/sensors/hals.conf, add the path to the library on a new line. If necessary, create the hals.conf file.

    For an example Android.bp entry for building a sub-HAL library, see hardware/interfaces/sensors/common/default/2.X/multihal/tests/Android.bp.

  4. Remove all android.hardware.sensors entries from the manifest.xml file, which contains the list of supported HALs on the device.

  5. Remove all android.hardware.sensors service and service.rc files from the device.mk file and add android.hardware.sensors@2.1-service.multihal and android.hardware.sensors@2.1-service.multihal.rc to PRODUCT_PACKAGES.

On boot, HalProxy starts, looks for the newly implemented sub-HAL, and initializes it by calling sensorsHalGetSubHal_2_1.

Port from Sensors Multi-HAL 2.0 to Multi-HAL 2.1

To port from Multi-HAL 2.0 to Multi-HAL 2.1, implement the SubHal interface and recompile your sub-HAL.

These are the differences between the 2.0 and 2.1 SubHal interfaces:

  • IHalProxyCallback uses the types created in version 2.1 of the ISensors.hal specification.
  • The initialize() function passes a new IHalProxyCallback instead of the one from the 2.0 SubHal interface
  • Sub-HALs must implement getSensorsList_2_1 and injectSensorData_2_1 instead of getSensorsList and injectSensorData as these methods use the new types added in version 2.1 of the ISensors.hal specification.
  • Sub-HALs must expose sensorsHalGetSubHal_2_1 rather than sensorsHalGetSubHal for the Multi-HAL to treat them as version 2.1 sub-HALs.

Port from Sensors HAL 2.0

When upgrading to Sensors Multi-HAL 2.0 from Sensors HAL 2.0, ensure the HAL implementation meets the following requirements.

Initialize the HAL

Sensors HAL 2.0 has an initialize function that allows the sensor service to pass FMQs and a dynamic sensor callback. In Sensors Multi-HAL 2.0, the initialize() function passes a single callback that must be used to post sensor events, obtain wake locks, and notify of dynamic sensor connection and disconnections.

Post sensor events to the Multi-HAL implementation

Instead of posting sensor events through the FMQ, the sub-HAL must write sensor events to the IHalProxyCallback when sensor events are available.

WAKE_UP events

In Sensors HAL 2.0, the HAL can manage the wake lock for its implementation. In Sensors Multi-HAL 2.0, the sub-HALs allow the Multi-HAL implementation to manage wake locks and can request a wake lock to be acquired by invoking createScopedWakelock. A locked scoped wake lock must be acquired and passed to postEvents when posting wake up events to the Multi-HAL implementation.

Dynamic sensors

Sensors Multi-HAL 2.0 requires that onDynamicSensorsConnected and onDynamicSensorsDisconnected in IHalProxyCallback are called whenever dynamic sensor connections change. These callbacks are available as part of the IHalProxyCallback pointer that is provided through the initialize() function.

Port from Sensors HAL 1.0

When upgrading to Sensors Multi-HAL 2.0 from Sensors HAL 1.0, ensure the HAL implementation meets the following requirements.

Initialize the HAL

The initialize() function must be supported to establish the callback between the sub-HAL and the Multi-HAL implementation.

Expose available sensors

In Sensors Multi-HAL 2.0, the getSensorsList() function must return the same value during a single device boot, even across sensors HAL restarts. This allows the framework to attempt to reestablish sensor connections if the system server restarts. The value returned by getSensorsList() can change after the device performs a reboot.

Post sensor events to the Multi-HAL implementation

In Sensors HAL 2.0, instead of waiting for poll()to be called, the sub-HAL must proactively write sensor events to IHalProxyCallback whenever sensor events are available.

WAKE_UP events

In Sensors HAL 1.0, the HAL can manage the wake lock for its implementation. In Sensors Multi-HAL 2.0, the sub-HALs allows the Multi-HAL implementation to manage wake locks and can request a wake lock to be acquired by invoking createScopedWakelock. A locked scoped wake lock must be acquired and passed to postEvents when posting wake up events to the Multi-HAL implementation.

Dynamic sensors

In Sensors HAL 1.0, dynamic sensors are returned through the poll() function. Sensors Multi-HAL 2.0 requires that onDynamicSensorsConnected and onDynamicSensorsDisconnected in IHalProxyCallback are called whenever dynamic sensor connections change. These callbacks are available as part of the IHalProxyCallback pointer that is provided through the initialize() function.

Port from Sensors Multi-HAL 1.0

To port an existing implementation from Sensors Multi-HAL 1.0, follow these steps.

  1. Ensure that the sensors HAL config is located at /vendor/etc/sensors/hals.conf. This might involve moving the file located at /system/etc/sensors/hals.conf.
  2. Remove any references to hardware/hardware.h and hardware/sensors.h as these aren't supported for HAL 2.0.
  3. Port sub-HALs as described in Porting from Sensors Hal 1.0.
  4. Set Sensors Multi-HAL 2.0 as the designated HAL by following steps 3 and 4 in the Implementing Sensors Mutli-HAL 2.0 section.

Validation

Run VTS

When you've integrated one or more sub-HALs with Sensors Multi-Hal 2.1, use the Vendor Test Suite (VTS) to ensure your sub-HAL implementations meet all the requirements set by the Sensors HAL interface.

To run only the sensors VTS tests when VTS is set up on a host machine, execute the following commands:

vts-tradefed run commandAndExit vts \
    --skip-all-system-status-check \
    --primary-abi-only \
    --skip-preconditions \
    --module VtsHalSensorsV2_0Target && \
  vts-tradefed run commandAndExit vts \
    --skip-all-system-status-check \
    --primary-abi-only \
    --skip-preconditions \
    --module VtsHalSensorsV2_1Target

If you're running the AIDL Multi-HAL shim layer, run VtsAidlHalSensorsTargetTest.

vts-tradefed run commandAndExit vts \
    --skip-all-system-status-check \
    --primary-abi-only \
    --skip-preconditions \
    --module VtsAidlHalSensorsTargetTest

Run unit tests

The unit tests in HalProxy_test.cpp test HalProxy using fake sub-HALs that are instantiated in the unit test and aren't dynamically loaded. When creating a new sub-HAL, these tests should serve as a guide on how to add unit tests that verify that the new sub-HAL is implemented properly.

To run the tests, execute the following commands:

cd $ANDROID_BUILD_TOP/hardware/interfaces/sensors/common/default/2.X/multihal/tests
atest

Test with the fake sub-HALs

The fake sub-HALs are dummy implementations of the ISensorsSubHal interface. The sub-HALs expose different lists of sensors. When the sensors are activated, they periodically post automatically generated sensor events to HalProxy based on the intervals specified in a given sensor request.

The fake sub-HALs can be used to test how the full Multi-HAL code works with other sub-HALs loaded into the system and to stress various aspects of the Sensors Multi-HAL code.

Two fake sub-HALs are available at hardware/interfaces/sensors/common/default/2.X/multihal/tests/fake_subhal/.

To build and push the fake sub-HALs to a device, perform the following steps:

  1. Run the following commands to build and push the three different fake sub-HALs to the device:

    $ANDROID_BUILD_TOP/hardware/interfaces/sensors/common/default/2.X/multihal/tests/
    mma
    adb push \
      $ANDROID_BUILD_TOP/out/target/product/<device>/symbols/vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config1.so \
      /vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config1.so
    adb push \
      $ANDROID_BUILD_TOP/out/target/product/<device>/symbols/vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config2.so \
      /vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config2.so
    adb push \
      $ANDROID_BUILD_TOP/out/target/product/<device>/symbols/vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config3.so \
      /vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config3.so
    
  2. Update the sensors HAL config at /vendor/etc/sensors/hals.conf with the paths for the fake sub-HALs.

    /vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config1.so
    /vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config2.so
    /vendor/lib64/android.hardware.sensors@2.X-fakesubhal-config3.so
    
  3. Restart HalProxy and load the new sub-HALs listed in the config.

    adb shell stop
    adb shell start
    

Debugging

Developers can debug the framework by using the lshal command. To request the debug output of the Sensors HAL, run the following command:

adb root
adb shell lshal debug android.hardware.sensors@2.1::ISensors/default

Information about the current state of HalProxy and its sub-HALs is then output to the terminal. Shown below is an example of the command output for the HalProxy object and fake sub-HALs.

Internal values:
  Threads are running: true
  Wakelock timeout start time: 200 ms ago
  Wakelock timeout reset time: 73208 ms ago
  Wakelock ref count: 0
  # of events on pending write queue: 0
  # of non-dynamic sensors across all subhals: 8
  # of dynamic sensors across all subhals: 0
SubHals (2):
  Name: FakeSubHal-OnChange
  Debug dump:
Available sensors:
Name: Ambient Temp Sensor
Min delay: 40000
Flags: 2
Name: Light Sensor
Min delay: 200000
Flags: 2
Name: Proximity Sensor
Min delay: 200000
Flags: 3
Name: Relative Humidity Sensor
Min delay: 40000
Flags: 2
  Name: FakeSubHal-OnChange
  Debug dump:
Available sensors:
Name: Ambient Temp Sensor
Min delay: 40000
Flags: 2
Name: Light Sensor
Min delay: 200000
Flags: 2
Name: Proximity Sensor
Min delay: 200000
Flags: 3
Name: Relative Humidity Sensor
Min delay: 40000
Flags: 2

If the number specified for # of events on pending write queue is a large number (1000 or more), this indicates that there are many events pending to be written to the sensors framework. This indicates the sensor service is deadlocked or has crashed and isn't processing sensor events, or that a large batch of sensor events was recently posted from a sub-HAL.

If the wake lock ref count is greater than 0, this means HalProxy has acquired a wake lock. This should only be greater than 0 if a ScopedWakelock is intentionally being held or if wakeup events were sent to HalProxy and have not been processed by the sensor framework.

The file descriptor passed to the debug method of HalProxy is passed to each sub-HAL so developers must implement the debug method as part of the ISensorsSubHal interface.