Add AIDL Lights HAL to support multiple lights per type

This is a revision of the previous 2.0 HIDL-based light HAL.
It accomplishes 2 goals:
 1) Support more than 1 light for a given type. This allows Assistant to
    use the HAL on TV platforms that have usually 4 indicator lights.
 2) Use AIDL, which is the more modern way of writing HALs.
The previous HAL is in hardware/interfaces/light/2.0 and the new one is
in versioned as aidl, as that supports forward compatibility.

Test: atest VtsHalLightTargetTest
Bug: 142715294, 142230898
Change-Id: I08d831ca0380d8bb187e43f6d5c214810ff72f50
diff --git a/light/aidl/Android.bp b/light/aidl/Android.bp
new file mode 100644
index 0000000..916a857
--- /dev/null
+++ b/light/aidl/Android.bp
@@ -0,0 +1,18 @@
+aidl_interface {
+    name: "android.hardware.light",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/light/*.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/light/aidl/android/hardware/light/BrightnessMode.aidl b/light/aidl/android/hardware/light/BrightnessMode.aidl
new file mode 100644
index 0000000..bc29699
--- /dev/null
+++ b/light/aidl/android/hardware/light/BrightnessMode.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.light;
+
+@VintfStability
+enum BrightnessMode {
+    /**
+     * Light brightness is managed by a user setting.
+     */
+    USER = 0,
+
+    /**
+     * Light brightness is managed by a light sensor. This is typically used
+     * to control the display backlight, but not limited to it. HALs and
+     * hardware implementations are free to support sensor for other lights or
+     * none whatsoever.
+     */
+    SENSOR = 1,
+
+    /**
+     * Use a low-persistence mode for display backlights, where the pixel
+     * color transition times are lowered.
+     *
+     * When set, the device driver must switch to a mode optimized for low display
+     * persistence that is intended to be used when the device is being treated as a
+     * head mounted display (HMD). The actual display brightness in this mode is
+     * implementation dependent, and any value set for color in LightState may be
+     * overridden by the HAL implementation.
+     *
+     * For an optimal HMD viewing experience, the display must meet the following
+     * criteria in this mode:
+     * - Gray-to-Gray, White-to-Black, and Black-to-White switching time must be ≤ 3 ms.
+     * - The display must support low-persistence with ≤ 3.5 ms persistence.
+     *   Persistence is defined as the amount of time for which a pixel is
+     *   emitting light for a single frame.
+     * - Any "smart panel" or other frame buffering options that increase display
+     *   latency are disabled.
+     * - Display brightness is set so that the display is still visible to the user
+     *   under normal indoor lighting.
+     * - The display must update at 60 Hz at least, but higher refresh rates are
+     *   recommended for low latency.
+     *
+     */
+    LOW_PERSISTENCE = 2,
+}
diff --git a/light/aidl/android/hardware/light/FlashMode.aidl b/light/aidl/android/hardware/light/FlashMode.aidl
new file mode 100644
index 0000000..00c6b6a
--- /dev/null
+++ b/light/aidl/android/hardware/light/FlashMode.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.light;
+
+@VintfStability
+enum FlashMode {
+    /**
+     * Keep the light steady on or off.
+     */
+    NONE = 0,
+    /**
+     * Flash the light at specified rate, potentially using a software-based
+     * implementation.
+     */
+    TIMED = 1,
+    /**
+     * Flash the light using hardware flashing support. This may or may not
+     * support a user-defined flashing rate or other features.
+     */
+    HARDWARE = 2,
+}
diff --git a/light/aidl/android/hardware/light/HwLight.aidl b/light/aidl/android/hardware/light/HwLight.aidl
new file mode 100644
index 0000000..43fdb4b
--- /dev/null
+++ b/light/aidl/android/hardware/light/HwLight.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.light;
+
+import android.hardware.light.LightType;
+
+/**
+ * A description of a single light. Multiple lights can map to the same physical
+ * LED. Separate physical LEDs are always represented by separate instances.
+ */
+@VintfStability
+parcelable HwLight {
+    /**
+     * Integer ID used for controlling this light
+     */
+    int id;
+
+    /**
+     * For a group of lights of the same logical type, sorting by ordinal should
+     * be give their physical order. No other meaning is carried by it.
+     */
+    int ordinal;
+
+    /**
+     * Logical type use of this light.
+     */
+    LightType type;
+}
diff --git a/light/aidl/android/hardware/light/HwLightState.aidl b/light/aidl/android/hardware/light/HwLightState.aidl
new file mode 100644
index 0000000..24d3250
--- /dev/null
+++ b/light/aidl/android/hardware/light/HwLightState.aidl
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.light;
+
+import android.hardware.light.BrightnessMode;
+import android.hardware.light.FlashMode;
+
+/**
+ * The parameters that can be set for a given light.
+ *
+ * Not all lights must support all parameters. If you
+ * can do something backward-compatible, do it.
+ */
+@VintfStability
+parcelable HwLightState {
+    /**
+     * The color of the LED in ARGB.
+     *
+     * The implementation of this in the HAL and hardware is a best-effort one.
+     *   - If a light can only do red or green and blue is requested, green
+     *     should be shown.
+     *   - If only a brightness ramp is supported, then this formula applies:
+     *      unsigned char brightness = ((77*((color>>16)&0x00ff))
+     *              + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
+     *   - If only on and off are supported, 0 is off, anything else is on.
+     *
+     * The high byte should be ignored. Callers should set it to 0xff (which
+     * would correspond to 255 alpha).
+     */
+    int color;
+
+    /**
+     * To flash the light at a given rate, set flashMode to FLASH_TIMED.
+     */
+    FlashMode flashMode;
+
+    /**
+     * flashOnMs should be set to the number of milliseconds to turn the
+     * light on, before it's turned off.
+     */
+    int flashOnMs;
+
+    /**
+     * flashOfMs should be set to the number of milliseconds to turn the
+     * light off, before it's turned back on.
+     */
+    int flashOffMs;
+
+    BrightnessMode brightnessMode;
+}
diff --git a/light/aidl/android/hardware/light/ILights.aidl b/light/aidl/android/hardware/light/ILights.aidl
new file mode 100644
index 0000000..2253f73
--- /dev/null
+++ b/light/aidl/android/hardware/light/ILights.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.light;
+
+import android.hardware.light.HwLightState;
+import android.hardware.light.HwLight;
+
+/**
+ * Allows controlling logical lights/indicators, mapped to LEDs in a
+ * hardware-specific manner by the HAL implementation.
+ */
+@VintfStability
+interface ILights {
+    /**
+     * Set light identified by id to the provided state.
+     *
+     * If control over an invalid light is requested, this method exists with
+     * EX_UNSUPPORTED_OPERATION. Control over supported lights is done on a
+     * device-specific best-effort basis and unsupported sub-features will not
+     * be reported.
+     *
+     * @param id ID of logical light to set as returned by getLights()
+     * @param state describes what the light should look like.
+     */
+    void setLightState(in int id, in HwLightState state);
+
+    /**
+     * Discover what lights are supported by the HAL implementation.
+     *
+     * @return List of available lights
+     */
+    HwLight[] getLights();
+}
diff --git a/light/aidl/android/hardware/light/LightType.aidl b/light/aidl/android/hardware/light/LightType.aidl
new file mode 100644
index 0000000..9a7f656
--- /dev/null
+++ b/light/aidl/android/hardware/light/LightType.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.light;
+
+/**
+ * These light IDs correspond to logical lights, not physical.
+ * So for example, if your INDICATOR light is in line with your
+ * BUTTONS, it might make sense to also light the INDICATOR
+ * light to a reasonable color when the BUTTONS are lit.
+ */
+@VintfStability
+enum LightType {
+    BACKLIGHT = 0,
+    KEYBOARD = 1,
+    BUTTONS = 2,
+    BATTERY = 3,
+    NOTIFICATIONS = 4,
+    ATTENTION = 5,
+    BLUETOOTH = 6,
+    WIFI = 7,
+    MICROPHONE = 8,
+}
diff --git a/light/aidl/default/Android.bp b/light/aidl/default/Android.bp
new file mode 100644
index 0000000..ae3f463
--- /dev/null
+++ b/light/aidl/default/Android.bp
@@ -0,0 +1,16 @@
+cc_binary {
+    name: "android.hardware.lights-service.example",
+    relative_install_path: "hw",
+    init_rc: ["lights-default.rc"],
+    vintf_fragments: ["lights-default.xml"],
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.light-ndk_platform",
+    ],
+    srcs: [
+        "Lights.cpp",
+        "main.cpp",
+    ],
+}
diff --git a/light/aidl/default/Lights.cpp b/light/aidl/default/Lights.cpp
new file mode 100644
index 0000000..74747d5
--- /dev/null
+++ b/light/aidl/default/Lights.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Lights.h"
+
+#include <android-base/logging.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace light {
+
+ndk::ScopedAStatus Lights::setLightState(int id, const HwLightState& state) {
+    LOG(INFO) << "Lights setting state for id=" << id << " to color " << std::hex << state.color;
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Lights::getLights(std::vector<HwLight>* /*lights*/) {
+    LOG(INFO) << "Lights reporting supported lights";
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace light
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/light/aidl/default/Lights.h b/light/aidl/default/Lights.h
new file mode 100644
index 0000000..8cba5a1
--- /dev/null
+++ b/light/aidl/default/Lights.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/light/BnLights.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace light {
+
+// Default implementation that reports no supported lights.
+class Lights : public BnLights {
+    ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override;
+    ndk::ScopedAStatus getLights(std::vector<HwLight>* types) override;
+};
+
+}  // namespace light
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/light/aidl/default/lights-default.rc b/light/aidl/default/lights-default.rc
new file mode 100644
index 0000000..687ec97
--- /dev/null
+++ b/light/aidl/default/lights-default.rc
@@ -0,0 +1,5 @@
+service vendor.light-default /vendor/bin/hw/android.hardware.lights-service.example
+    class hal
+    user nobody
+    group nobody
+    shutdown critical
diff --git a/light/aidl/default/lights-default.xml b/light/aidl/default/lights-default.xml
new file mode 100644
index 0000000..db604d6
--- /dev/null
+++ b/light/aidl/default/lights-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.light</name>
+        <fqname>ILights/default</fqname>
+    </hal>
+</manifest>
diff --git a/light/aidl/default/main.cpp b/light/aidl/default/main.cpp
new file mode 100644
index 0000000..a860bf4
--- /dev/null
+++ b/light/aidl/default/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Lights.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::aidl::android::hardware::light::Lights;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<Lights> lights = ndk::SharedRefBase::make<Lights>();
+
+    const std::string instance = std::string() + Lights::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(lights->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reached
+}
diff --git a/light/aidl/vts/functional/Android.bp b/light/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..3dd8cf6
--- /dev/null
+++ b/light/aidl/vts/functional/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalLightTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsHalLightTargetTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+    ],
+    static_libs: [
+        "android.hardware.light-cpp",
+    ],
+    test_suites: [
+        "vts-core",
+    ],
+}
diff --git a/light/aidl/vts/functional/VtsHalLightTargetTest.cpp b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp
new file mode 100644
index 0000000..3c26278
--- /dev/null
+++ b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "light_aidl_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <android-base/logging.h>
+#include <android/hardware/light/ILights.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <unistd.h>
+#include <set>
+
+using android::ProcessState;
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::light::BrightnessMode;
+using android::hardware::light::FlashMode;
+using android::hardware::light::HwLight;
+using android::hardware::light::HwLightState;
+using android::hardware::light::ILights;
+using android::hardware::light::LightType;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+const std::set<LightType> kAllTypes{android::enum_range<LightType>().begin(),
+                                    android::enum_range<LightType>().end()};
+
+class LightsAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        lights = android::waitForDeclaredService<ILights>(String16(GetParam().c_str()));
+        ASSERT_NE(lights, nullptr);
+        ASSERT_TRUE(lights->getLights(&supportedLights).isOk());
+    }
+
+    sp<ILights> lights;
+    std::vector<HwLight> supportedLights;
+
+    virtual void TearDown() override {
+        for (const HwLight& light : supportedLights) {
+            HwLightState off;
+            off.color = 0x00000000;
+            off.flashMode = FlashMode::NONE;
+            off.brightnessMode = BrightnessMode::USER;
+            EXPECT_TRUE(lights->setLightState(light.id, off).isOk());
+        }
+
+        // must leave the device in a useable condition
+        for (const HwLight& light : supportedLights) {
+            if (light.type == LightType::BACKLIGHT) {
+                HwLightState backlightOn;
+                backlightOn.color = 0xFFFFFFFF;
+                backlightOn.flashMode = FlashMode::TIMED;
+                backlightOn.brightnessMode = BrightnessMode::USER;
+                EXPECT_TRUE(lights->setLightState(light.id, backlightOn).isOk());
+            }
+        }
+    }
+};
+
+/**
+ * Ensure all reported lights actually work.
+ */
+TEST_P(LightsAidl, TestSupported) {
+    HwLightState whiteFlashing;
+    whiteFlashing.color = 0xFFFFFFFF;
+    whiteFlashing.flashMode = FlashMode::TIMED;
+    whiteFlashing.flashOnMs = 100;
+    whiteFlashing.flashOffMs = 50;
+    whiteFlashing.brightnessMode = BrightnessMode::USER;
+    for (const HwLight& light : supportedLights) {
+        EXPECT_TRUE(lights->setLightState(light.id, whiteFlashing).isOk());
+    }
+}
+
+/**
+ * Ensure all reported lights have one of the supported types.
+ */
+TEST_P(LightsAidl, TestSupportedLightTypes) {
+    for (const HwLight& light : supportedLights) {
+        EXPECT_TRUE(kAllTypes.find(light.type) != kAllTypes.end());
+    }
+}
+
+/**
+ * Ensure all lights have a unique id.
+ */
+TEST_P(LightsAidl, TestUniqueIds) {
+    std::set<int> ids;
+    for (const HwLight& light : supportedLights) {
+        EXPECT_TRUE(ids.find(light.id) == ids.end());
+        ids.insert(light.id);
+    }
+}
+
+/**
+ * Ensure all lights have a unique ordinal for a given type.
+ */
+TEST_P(LightsAidl, TestUniqueOrdinalsForType) {
+    std::map<int, std::set<int>> ordinalsByType;
+    for (const HwLight& light : supportedLights) {
+        auto& ordinals = ordinalsByType[(int)light.type];
+        EXPECT_TRUE(ordinals.find(light.ordinal) == ordinals.end());
+        ordinals.insert(light.ordinal);
+    }
+}
+
+/**
+ * Ensure EX_UNSUPPORTED_OPERATION is returned if LOW_PERSISTENCE is not supported.
+ */
+TEST_P(LightsAidl, TestLowPersistence) {
+    HwLightState lowPersistence;
+    lowPersistence.color = 0xFF123456;
+    lowPersistence.flashMode = FlashMode::TIMED;
+    lowPersistence.flashOnMs = 100;
+    lowPersistence.flashOffMs = 50;
+    lowPersistence.brightnessMode = BrightnessMode::LOW_PERSISTENCE;
+    for (const HwLight& light : supportedLights) {
+        Status status = lights->setLightState(light.id, lowPersistence);
+        EXPECT_TRUE(status.isOk() || Status::EX_UNSUPPORTED_OPERATION == status.exceptionCode());
+    }
+}
+
+/**
+ * Ensure EX_UNSUPPORTED_OPERATION is returns for an invalid light id.
+ */
+TEST_P(LightsAidl, TestInvalidLightIdUnsupported) {
+    int maxId = INT_MIN;
+    for (const HwLight& light : supportedLights) {
+        maxId = std::max(maxId, light.id);
+    }
+
+    Status status = lights->setLightState(maxId + 1, HwLightState());
+    EXPECT_TRUE(status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION);
+}
+
+INSTANTIATE_TEST_SUITE_P(Lights, LightsAidl,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(ILights::descriptor)),
+                         android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/light/utils/Android.bp b/light/utils/Android.bp
index 4c287e4..e901129 100644
--- a/light/utils/Android.bp
+++ b/light/utils/Android.bp
@@ -23,7 +23,11 @@
     shared_libs: [
         "android.hardware.light@2.0",
         "libbase",
+        "libbinder",
         "libhidlbase",
         "libutils",
     ],
+    static_libs: [
+        "android.hardware.light-cpp",
+    ],
 }
diff --git a/light/utils/main.cpp b/light/utils/main.cpp
index b834132..b9b6489 100644
--- a/light/utils/main.cpp
+++ b/light/utils/main.cpp
@@ -19,34 +19,23 @@
 
 #include <android-base/logging.h>
 #include <android/hardware/light/2.0/ILight.h>
+#include <android/hardware/light/ILights.h>
+#include <binder/IServiceManager.h>
+
+using android::sp;
+using android::waitForVintfService;
+using android::binder::Status;
+using android::hardware::hidl_vec;
+
+namespace V2_0 = android::hardware::light::V2_0;
+namespace aidl = android::hardware::light;
 
 void error(const std::string& msg) {
     LOG(ERROR) << msg;
     std::cerr << msg << std::endl;
 }
 
-int main(int argc, char* argv[]) {
-    using ::android::hardware::hidl_vec;
-    using ::android::hardware::light::V2_0::Brightness;
-    using ::android::hardware::light::V2_0::Flash;
-    using ::android::hardware::light::V2_0::ILight;
-    using ::android::hardware::light::V2_0::LightState;
-    using ::android::hardware::light::V2_0::Status;
-    using ::android::hardware::light::V2_0::Type;
-    using ::android::sp;
-
-    sp<ILight> service = ILight::getService();
-    if (service == nullptr) {
-        error("Could not retrieve light service.");
-        return -1;
-    }
-
-    static LightState off = {
-            .color = 0u,
-            .flashMode = Flash::NONE,
-            .brightnessMode = Brightness::USER,
-    };
-
+int parseArgs(int argc, char* argv[], unsigned int* color) {
     if (argc > 2) {
         error("Usage: blank_screen [color]");
         return -1;
@@ -54,25 +43,78 @@
 
     if (argc > 1) {
         char* col_ptr;
-        unsigned int col_new;
 
-        col_new = strtoul(argv[1], &col_ptr, 0);
+        *color = strtoul(argv[1], &col_ptr, 0);
         if (*col_ptr != '\0') {
             error("Failed to convert " + std::string(argv[1]) + " to number");
             return -1;
         }
-        off.color = col_new;
+
+        return 0;
     }
 
-    service->getSupportedTypes([&](const hidl_vec<Type>& types) {
-        for (Type type : types) {
-            Status ret = service->setLight(type, off);
-            if (ret != Status::SUCCESS) {
-                error("Failed to shut off screen for type " +
+    *color = 0u;
+    return 0;
+}
+
+void setToColorAidl(sp<aidl::ILights> hal, unsigned int color) {
+    static aidl::HwLightState off;
+    off.color = color;
+    off.flashMode = aidl::FlashMode::NONE;
+    off.brightnessMode = aidl::BrightnessMode::USER;
+
+    std::vector<aidl::HwLight> lights;
+    Status status = hal->getLights(&lights);
+    if (!status.isOk()) {
+        error("Failed to list lights");
+        return;
+    }
+
+    for (auto light : lights) {
+        Status setStatus = hal->setLightState(light.id, off);
+        if (!setStatus.isOk()) {
+            error("Failed to shut off light id " + std::to_string(light.id));
+        }
+    }
+}
+
+void setToColorHidl(sp<V2_0::ILight> hal, unsigned int color) {
+    static V2_0::LightState off = {
+            .color = color,
+            .flashMode = V2_0::Flash::NONE,
+            .brightnessMode = V2_0::Brightness::USER,
+    };
+
+    hal->getSupportedTypes([&](const hidl_vec<V2_0::Type>& types) {
+        for (auto type : types) {
+            V2_0::Status ret = hal->setLight(type, off);
+            if (ret != V2_0::Status::SUCCESS) {
+                error("Failed to shut off light for type " +
                       std::to_string(static_cast<int>(type)));
             }
         }
     });
+}
 
-    return 0;
+int main(int argc, char* argv[]) {
+    unsigned int inputColor;
+    int result = parseArgs(argc, argv, &inputColor);
+    if (result != 0) {
+        return result;
+    }
+
+    auto aidlHal = waitForVintfService<aidl::ILights>();
+    if (aidlHal != nullptr) {
+        setToColorAidl(aidlHal, inputColor);
+        return 0;
+    }
+
+    sp<V2_0::ILight> hidlHal = V2_0::ILight::getService();
+    if (hidlHal != nullptr) {
+        setToColorHidl(hidlHal, inputColor);
+        return 0;
+    }
+
+    error("Could not retrieve light service.");
+    return -1;
 }