From ee864f39eac73795a172774cde679d214cc5e109 Mon Sep 17 00:00:00 2001 From: Sebastiano Barezzi Date: Thu, 18 Apr 2024 17:34:39 +0200 Subject: [PATCH] aidl: light: Refactor Change-Id: Ib4ae8e9821dd0fb36e922e2c5f993aa6c0e28be1 --- aidl/light/Android.bp | 11 +- aidl/light/Backlight.cpp | 82 ------- aidl/light/Backlight.h | 27 --- aidl/light/BacklightDevice.cpp | 55 +++++ aidl/light/BacklightDevice.h | 66 ++++++ aidl/light/Devices.cpp | 217 ++++++++++++++++++ aidl/light/Devices.h | 51 ++++ aidl/light/IDumpable.h | 32 +++ aidl/light/LED.cpp | 39 ---- aidl/light/LED.h | 33 --- aidl/light/LedDevice.cpp | 94 ++++++++ aidl/light/LedDevice.h | 82 +++++++ aidl/light/Lights.cpp | 129 +++++------ aidl/light/Lights.h | 22 +- aidl/light/RgbLedDevice.cpp | 109 +++++++++ aidl/light/RgbLedDevice.h | 84 +++++++ aidl/light/Utils.cpp | 48 ++-- aidl/light/Utils.h | 37 ++- .../android.hardware.light-service.xiaomi.rc | 58 +++-- aidl/light/service.cpp | 4 +- 20 files changed, 941 insertions(+), 339 deletions(-) delete mode 100644 aidl/light/Backlight.cpp delete mode 100644 aidl/light/Backlight.h create mode 100644 aidl/light/BacklightDevice.cpp create mode 100644 aidl/light/BacklightDevice.h create mode 100644 aidl/light/Devices.cpp create mode 100644 aidl/light/Devices.h create mode 100644 aidl/light/IDumpable.h delete mode 100644 aidl/light/LED.cpp delete mode 100644 aidl/light/LED.h create mode 100644 aidl/light/LedDevice.cpp create mode 100644 aidl/light/LedDevice.h create mode 100644 aidl/light/RgbLedDevice.cpp create mode 100644 aidl/light/RgbLedDevice.h diff --git a/aidl/light/Android.bp b/aidl/light/Android.bp index 2ac4fc9..574480a 100644 --- a/aidl/light/Android.bp +++ b/aidl/light/Android.bp @@ -1,18 +1,22 @@ // -// Copyright (C) 2021-2022 The LineageOS Project +// Copyright (C) 2021-2024 The LineageOS Project // // SPDX-License-Identifier: Apache-2.0 // cc_binary { name: "android.hardware.light-service.xiaomi", + defaults: ["hidl_defaults"], + vendor: true, relative_install_path: "hw", init_rc: ["android.hardware.light-service.xiaomi.rc"], vintf_fragments: ["android.hardware.light-service.xiaomi.xml"], srcs: [ - "Backlight.cpp", + "BacklightDevice.cpp", + "Devices.cpp", + "LedDevice.cpp", "Lights.cpp", - "LED.cpp", + "RgbLedDevice.cpp", "Utils.cpp", "service.cpp", ], @@ -21,5 +25,4 @@ cc_binary { "libbinder_ndk", "android.hardware.light-V2-ndk", ], - vendor: true, } diff --git a/aidl/light/Backlight.cpp b/aidl/light/Backlight.cpp deleted file mode 100644 index 274c53e..0000000 --- a/aidl/light/Backlight.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2022 The LineageOS Project - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "Backlight.h" - -#include "LED.h" - -namespace aidl { -namespace android { -namespace hardware { -namespace light { - -class BacklightBrightness : public BacklightDevice { - public: - BacklightBrightness(std::string name) : mBasePath(mkBacklightBasePath + name + "/") { - if (!readFromFile(mBasePath + "max_brightness", &mMaxBrightness)) { - mMaxBrightness = kDefaultMaxBrightness; - } - }; - - void setBacklight(uint8_t value) { - writeToFile(mBasePath + "brightness", value * mMaxBrightness / 0xFF); - } - - bool exists() { return fileWriteable(mBasePath + "brightness"); } - - private: - std::string mBasePath; - uint32_t mMaxBrightness; - - inline static const std::string mkBacklightBasePath = "/sys/class/backlight/"; - inline static const uint32_t kDefaultMaxBrightness = 255; -}; - -class LEDBacklight : public BacklightDevice { - public: - LEDBacklight(std::string type) : mLED(type){}; - - void setBacklight(uint8_t value) { mLED.setBrightness(value); } - - bool exists() { return mLED.exists(); } - - private: - LED mLED; -}; - -static const std::string kBacklightDevices[] = { - "backlight", - "panel0-backlight", -}; - -static const std::string kLedDevices[] = { - "lcd-backlight", -}; - -BacklightDevice* getBacklightDevice() { - for (auto& device : kBacklightDevices) { - auto backlight = new BacklightBrightness(device); - if (backlight->exists()) { - return backlight; - } - delete backlight; - } - - for (auto& device : kLedDevices) { - auto backlight = new LEDBacklight(device); - if (backlight->exists()) { - return backlight; - } - delete backlight; - } - - return nullptr; -} - -} // namespace light -} // namespace hardware -} // namespace android -} // namespace aidl diff --git a/aidl/light/Backlight.h b/aidl/light/Backlight.h deleted file mode 100644 index 04aba6b..0000000 --- a/aidl/light/Backlight.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2022 The LineageOS Project - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "Utils.h" - -namespace aidl { -namespace android { -namespace hardware { -namespace light { - -class BacklightDevice { - public: - virtual ~BacklightDevice() = default; - - virtual void setBacklight(uint8_t value) = 0; - virtual bool exists() = 0; -}; - -BacklightDevice* getBacklightDevice(); - -} // namespace light -} // namespace hardware -} // namespace android -} // namespace aidl diff --git a/aidl/light/BacklightDevice.cpp b/aidl/light/BacklightDevice.cpp new file mode 100644 index 0000000..67ba707 --- /dev/null +++ b/aidl/light/BacklightDevice.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022-2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "BacklightDevice.h" + +#define LOG_TAG "BacklightDevice" + +#include +#include +#include "Utils.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +static const std::string kBacklightBasePath = "/sys/class/backlight/"; +static const uint32_t kDefaultMaxBrightness = 255; + +static const std::string kBrightnessNode = "brightness"; +static const std::string kMaxBrightnessNode = "max_brightness"; + +BacklightDevice::BacklightDevice(std::string name) + : mName(name), mBasePath(kBacklightBasePath + name + "/") { + if (!readFromFile(mBasePath + kMaxBrightnessNode, mMaxBrightness)) { + mMaxBrightness = kDefaultMaxBrightness; + } +}; + +std::string BacklightDevice::getName() const { + return mName; +} + +bool BacklightDevice::exists() const { + return std::ifstream(mBasePath + kBrightnessNode).good(); +} + +bool BacklightDevice::setBrightness(uint8_t value) { + return writeToFile(mBasePath + kBrightnessNode, scaleBrightness(value, mMaxBrightness)); +} + +void BacklightDevice::dump(int fd) const { + dprintf(fd, "Name: %s", mName.c_str()); + dprintf(fd, ", exists: %d", exists()); + dprintf(fd, ", base path: %s", mBasePath.c_str()); + dprintf(fd, ", max brightness: %u", mMaxBrightness); +} + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/BacklightDevice.h b/aidl/light/BacklightDevice.h new file mode 100644 index 0000000..a807fd3 --- /dev/null +++ b/aidl/light/BacklightDevice.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022-2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "IDumpable.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +/** + * A Linux backlight device. + * @see https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-class-backlight + */ +class BacklightDevice : public IDumpable { + public: + BacklightDevice() = delete; + + /** + * Constructor. + * + * @param name The name of the backlight device + */ + BacklightDevice(std::string name); + + /** + * Get the name of the backlight device. + * + * @return std::string The name of the backlight device + */ + std::string getName() const; + + /** + * Return whether this backlight device exists. + * + * @return bool true if the backlight device exists, false otherwise + */ + bool exists() const; + + /** + * Set the brightness of this backlight device. + * + * @param value The brightness value to set + * @return bool true if the brightness was set successfully, false otherwise + */ + bool setBrightness(uint8_t value); + + void dump(int fd) const override; + + private: + std::string mName; + std::string mBasePath; + uint32_t mMaxBrightness; +}; + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/Devices.cpp b/aidl/light/Devices.cpp new file mode 100644 index 0000000..d2b2a3c --- /dev/null +++ b/aidl/light/Devices.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Devices.h" + +#define LOG_TAG "Devices" + +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +static const std::string kBacklightDevices[] = { + "backlight", + "panel0-backlight", +}; + +static std::vector getBacklightDevices() { + std::vector devices; + + for (const auto& device : kBacklightDevices) { + BacklightDevice backlight(device); + if (backlight.exists()) { + LOG(INFO) << "Found backlight device: " << backlight.getName(); + devices.push_back(backlight); + } + } + + return devices; +} + +static const std::string kLedBacklightDevices[] = { + "lcd-backlight", +}; + +static std::vector getBacklightLedDevices() { + std::vector devices; + + for (const auto& device : kLedBacklightDevices) { + LedDevice backlight(device); + if (backlight.exists()) { + LOG(INFO) << "Found backlight LED device: " << backlight.getName(); + devices.push_back(backlight); + } + } + + return devices; +} + +static const std::string kButtonLedDevices[] = { + "button-backlight", + "button-backlight1", +}; + +static std::vector getButtonLedDevices() { + std::vector devices; + + for (const auto& device : kButtonLedDevices) { + LedDevice button(device); + if (button.exists()) { + LOG(INFO) << "Found button LED device: " << button.getName(); + devices.emplace_back(button); + } + } + + return devices; +} + +static const std::string kRgbLedDevices[][3] = { + {"red", "green", "blue"}, +}; + +static std::vector getNotificationRgbLedDevices() { + std::vector devices; + + for (const auto& device : kRgbLedDevices) { + LedDevice red(device[0]); + LedDevice green(device[1]); + LedDevice blue(device[2]); + + RgbLedDevice rgbLedDevice(red, green, blue); + if (rgbLedDevice.exists()) { + LOG(INFO) << "Found notification RGB LED device: " << red.getName() << ", " + << green.getName() << ", " << blue.getName(); + devices.emplace_back(red, green, blue); + } + } + + return devices; +} + +static const std::string kNotificationLedDevices[] = { + "white", +}; + +static std::vector getNotificationLedDevices() { + std::vector devices; + + for (const auto& device : kNotificationLedDevices) { + LedDevice notification(device); + if (notification.exists()) { + LOG(INFO) << "Found notification LED device: " << notification.getName(); + devices.emplace_back(notification); + } + } + + return devices; +} + +Devices::Devices() + : mBacklightDevices(getBacklightDevices()), + mBacklightLedDevices(getBacklightLedDevices()), + mButtonLedDevices(getButtonLedDevices()), + mNotificationRgbLedDevices(getNotificationRgbLedDevices()), + mNotificationLedDevices(getNotificationLedDevices()) { + if (!hasBacklightDevices()) { + LOG(INFO) << "No backlight devices found"; + } + + if (!hasButtonDevices()) { + LOG(INFO) << "No button devices found"; + } + + if (!hasNotificationDevices()) { + LOG(INFO) << "No notification devices found"; + } +} + +bool Devices::hasBacklightDevices() const { + return !mBacklightDevices.empty() || !mBacklightLedDevices.empty(); +} + +bool Devices::hasButtonDevices() const { + return !mButtonLedDevices.empty(); +} + +bool Devices::hasNotificationDevices() const { + return !mNotificationRgbLedDevices.empty() || !mNotificationLedDevices.empty(); +} + +void Devices::setBacklightColor(rgb color) { + for (auto& device : mBacklightDevices) { + device.setBrightness(color.toBrightness()); + } + for (auto& device : mBacklightLedDevices) { + device.setBrightness(color.toBrightness()); + } +} + +void Devices::setButtonsColor(rgb color) { + for (auto& device : mButtonLedDevices) { + device.setBrightness(color.toBrightness()); + } +} + +void Devices::setNotificationColor(rgb color, LightMode mode) { + for (auto& device : mNotificationRgbLedDevices) { + device.setBrightness(color, mode); + } + + for (auto& device : mNotificationLedDevices) { + device.setBrightness(color.toBrightness(), mode); + } +} + +void Devices::dump(int fd) const { + dprintf(fd, "Backlight devices:\n"); + for (const auto& device : mBacklightDevices) { + dprintf(fd, "- "); + device.dump(fd); + dprintf(fd, "\n"); + } + dprintf(fd, "\n"); + + dprintf(fd, "Backlight LED devices:\n"); + for (const auto& device : mBacklightLedDevices) { + dprintf(fd, "- "); + device.dump(fd); + dprintf(fd, "\n"); + } + dprintf(fd, "\n"); + + dprintf(fd, "Button LED devices:\n"); + for (const auto& device : mButtonLedDevices) { + dprintf(fd, "- "); + device.dump(fd); + dprintf(fd, "\n"); + } + dprintf(fd, "\n"); + + dprintf(fd, "Notification RGB LED devices:\n"); + for (const auto& device : mNotificationRgbLedDevices) { + dprintf(fd, "- "); + device.dump(fd); + dprintf(fd, "\n"); + } + dprintf(fd, "\n"); + + dprintf(fd, "Notification LED devices:\n"); + for (const auto& device : mNotificationLedDevices) { + dprintf(fd, "- "); + device.dump(fd); + dprintf(fd, "\n"); + } + + return; +} + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/Devices.h b/aidl/light/Devices.h new file mode 100644 index 0000000..530e599 --- /dev/null +++ b/aidl/light/Devices.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "BacklightDevice.h" +#include "IDumpable.h" +#include "LedDevice.h" +#include "RgbLedDevice.h" +#include "Utils.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +class Devices : public IDumpable { + public: + Devices(); + + bool hasBacklightDevices() const; + bool hasButtonDevices() const; + bool hasNotificationDevices() const; + + void setBacklightColor(rgb color); + void setButtonsColor(rgb color); + void setNotificationColor(rgb color, LightMode mode = LightMode::STATIC); + + void dump(int fd) const override; + + private: + // Backlight + std::vector mBacklightDevices; + std::vector mBacklightLedDevices; + + // Buttons + std::vector mButtonLedDevices; + + // Notifications + std::vector mNotificationRgbLedDevices; + std::vector mNotificationLedDevices; +}; + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/IDumpable.h b/aidl/light/IDumpable.h new file mode 100644 index 0000000..ef672e1 --- /dev/null +++ b/aidl/light/IDumpable.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +/** + * Interface for dumpable objects using AIDL dump()'s file descriptor. + */ +class IDumpable { + public: + virtual ~IDumpable() = default; + + /** + * Write information regarding this object to the given file descriptor using dprintf(). + * You should avoid ending newline, since the caller will add it. + * @param fd The file descriptor to write to. + */ + virtual void dump(int fd) const = 0; +}; + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/LED.cpp b/aidl/light/LED.cpp deleted file mode 100644 index 333296e..0000000 --- a/aidl/light/LED.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2021-2022 The LineageOS Project - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "LED.h" - -#include "Utils.h" - -namespace aidl { -namespace android { -namespace hardware { -namespace light { - -static const uint32_t kDefaultMaxLedBrightness = 255; - -LED::LED(std::string type) : mBasePath("/sys/class/leds/" + type + "/") { - if (!readFromFile(mBasePath + "max_brightness", &mMaxBrightness)) - mMaxBrightness = kDefaultMaxLedBrightness; - mBreath = fileWriteable(mBasePath + "breath"); -} - -bool LED::exists() { - return fileWriteable(mBasePath + "brightness"); -} - -bool LED::setBreath(uint8_t value) { - return writeToFile(mBasePath + (mBreath ? "breath" : "blink"), value); -} - -bool LED::setBrightness(uint8_t value) { - return writeToFile(mBasePath + "brightness", value * mMaxBrightness / 0xFF); -} - -} // namespace light -} // namespace hardware -} // namespace android -} // namespace aidl diff --git a/aidl/light/LED.h b/aidl/light/LED.h deleted file mode 100644 index f859239..0000000 --- a/aidl/light/LED.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2021-2022 The LineageOS Project - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -namespace aidl { -namespace android { -namespace hardware { -namespace light { - -class LED { - public: - LED(std::string type); - - bool exists(); - bool setBreath(uint8_t value); - bool setBrightness(uint8_t value); - - private: - std::string mBasePath; - uint32_t mMaxBrightness; - bool mBreath; -}; - -} // namespace light -} // namespace hardware -} // namespace android -} // namespace aidl diff --git a/aidl/light/LedDevice.cpp b/aidl/light/LedDevice.cpp new file mode 100644 index 0000000..969d736 --- /dev/null +++ b/aidl/light/LedDevice.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021-2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "LedDevice.h" + +#define LOG_TAG "LedDevice" + +#include +#include +#include "Utils.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +static const uint32_t kDefaultMaxBrightness = 255; + +static const std::string kBaseLedsPath = "/sys/class/leds/"; + +static const std::string kBrightnessNode = "brightness"; +static const std::string kMaxBrightnessNode = "max_brightness"; + +static const std::string kBreathNodes[] = { + "breath", + "blink", +}; + +LedDevice::LedDevice(std::string name) : mName(name), mBasePath(kBaseLedsPath + name + "/") { + if (!readFromFile(mBasePath + kMaxBrightnessNode, mMaxBrightness)) { + mMaxBrightness = kDefaultMaxBrightness; + } + + for (const auto& node : kBreathNodes) { + if (std::ifstream(mBasePath + node).good()) { + mBreathNode = node; + break; + } + } +} + +std::string LedDevice::getName() const { + return mName; +} + +bool LedDevice::supportsBreath() const { + return !mBreathNode.empty(); +} + +bool LedDevice::exists() const { + return std::ifstream(mBasePath + kBrightnessNode).good(); +} + +bool LedDevice::setBrightness(uint8_t value, LightMode mode) { + // Disable current blinking + if (supportsBreath()) { + writeToFile(mBasePath + mBreathNode, 0); + } + + switch (mode) { + case LightMode::BREATH: + if (supportsBreath()) { + return writeToFile(mBasePath + mBreathNode, value > 0 ? 1 : 0); + break; + } + + // Fallthrough to static mode if breath is not supported + FALLTHROUGH_INTENDED; + case LightMode::STATIC: + return writeToFile(mBasePath + kBrightnessNode, scaleBrightness(value, mMaxBrightness)); + break; + default: + LOG(ERROR) << "Unknown mode: " << mode; + return false; + break; + } +} + +void LedDevice::dump(int fd) const { + dprintf(fd, "Name: %s", mName.c_str()); + dprintf(fd, ", exists: %d", exists()); + dprintf(fd, ", base path: %s", mBasePath.c_str()); + dprintf(fd, ", max brightness: %u", mMaxBrightness); + dprintf(fd, ", supports breath: %d", supportsBreath()); + dprintf(fd, ", breath node: %s", mBreathNode.c_str()); +} + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/LedDevice.h b/aidl/light/LedDevice.h new file mode 100644 index 0000000..f655867 --- /dev/null +++ b/aidl/light/LedDevice.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021-2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "IDumpable.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +enum LightMode { + STATIC, + BREATH, +}; + +/** + * A Linux LED device. + * @see https://docs.kernel.org/leds/leds-class.html + */ +class LedDevice : public IDumpable { + public: + LedDevice() = delete; + + /** + * Constructor. + * + * @param name The name of the LED device + */ + LedDevice(std::string name); + + /** + * Get the name of the LED device. + * + * @return std::string The name of the LED device + */ + std::string getName() const; + + /** + * Return whether this LED device exists. + * + * @return bool true if the LED device exists, false otherwise + */ + bool exists() const; + + /** + * Return whether this LED device supports breathing. + * When it doesn't, calling setBrightness with LightMode::BREATH will behave like + * LightMode::STATIC. + * + * @return bool true if the LED device supports breathing, false otherwise + */ + bool supportsBreath() const; + + /** + * Set the brightness of the LED device. + * + * @param value The brightness value to set + * @param mode The light mode to use + * @return bool true if the brightness was set successfully, false otherwise + */ + bool setBrightness(uint8_t value, LightMode mode = LightMode::STATIC); + + void dump(int fd) const override; + + private: + std::string mName; + std::string mBasePath; + uint32_t mMaxBrightness; + std::string mBreathNode; +}; + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/Lights.cpp b/aidl/light/Lights.cpp index 93e4d71..8a02605 100644 --- a/aidl/light/Lights.cpp +++ b/aidl/light/Lights.cpp @@ -1,13 +1,14 @@ /* - * Copyright (C) 2021-2022 The LineageOS Project + * Copyright (C) 2021-2024 The LineageOS Project * * SPDX-License-Identifier: Apache-2.0 */ #include "Lights.h" +#define LOG_TAG "Lights" + #include -#include "LED.h" #include "Utils.h" namespace aidl { @@ -15,48 +16,23 @@ namespace android { namespace hardware { namespace light { -static const std::string kAllButtonsPaths[] = { - "/sys/class/leds/button-backlight/brightness", - "/sys/class/leds/button-backlight1/brightness", -}; - -enum led_type { - RED, - GREEN, - BLUE, - WHITE, - MAX_LEDS, -}; - -static LED kLEDs[MAX_LEDS] = { - [RED] = LED("red"), - [GREEN] = LED("green"), - [BLUE] = LED("blue"), - [WHITE] = LED("white"), -}; - #define AutoHwLight(light) \ - { .id = (int32_t)light, .type = light, .ordinal = 0 } + { .id = static_cast(light), .ordinal = 0, .type = light } Lights::Lights() { - mBacklightDevice = getBacklightDevice(); - if (mBacklightDevice) { + if (mDevices.hasBacklightDevices()) { mLights.push_back(AutoHwLight(LightType::BACKLIGHT)); } - for (auto& buttons : kAllButtonsPaths) { - if (!fileWriteable(buttons)) continue; - - mButtonsPaths.push_back(buttons); + if (mDevices.hasButtonDevices()) { + mLights.push_back(AutoHwLight(LightType::BUTTONS)); } - if (!mButtonsPaths.empty()) mLights.push_back(AutoHwLight(LightType::BUTTONS)); - - mWhiteLED = kLEDs[WHITE].exists(); - - mLights.push_back(AutoHwLight(LightType::BATTERY)); - mLights.push_back(AutoHwLight(LightType::NOTIFICATIONS)); - mLights.push_back(AutoHwLight(LightType::ATTENTION)); + if (mDevices.hasNotificationDevices()) { + mLights.push_back(AutoHwLight(LightType::BATTERY)); + mLights.push_back(AutoHwLight(LightType::NOTIFICATIONS)); + mLights.push_back(AutoHwLight(LightType::ATTENTION)); + } } ndk::ScopedAStatus Lights::setLightState(int32_t id, const HwLightState& state) { @@ -65,27 +41,22 @@ ndk::ScopedAStatus Lights::setLightState(int32_t id, const HwLightState& state) LightType type = static_cast(id); switch (type) { case LightType::BACKLIGHT: - if (mBacklightDevice) mBacklightDevice->setBacklight(color.toBrightness()); + mDevices.setBacklightColor(color); break; case LightType::BUTTONS: - for (auto& buttons : mButtonsPaths) writeToFile(buttons, color.isLit()); + mDevices.setButtonsColor(color); break; case LightType::BATTERY: + mLastBatteryState = state; + updateNotificationColor(); + break; case LightType::NOTIFICATIONS: + mLastNotificationsState = state; + updateNotificationColor(); + break; case LightType::ATTENTION: - mLEDMutex.lock(); - - if (type == LightType::BATTERY) - mLastBatteryState = state; - else if (type == LightType::NOTIFICATIONS) - mLastNotificationState = state; - else if (type == LightType::ATTENTION) - mLastAttentionState = state; - - setLED(); - - mLEDMutex.unlock(); - + mLastAttentionState = state; + updateNotificationColor(); break; default: return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); @@ -96,47 +67,59 @@ ndk::ScopedAStatus Lights::setLightState(int32_t id, const HwLightState& state) } ndk::ScopedAStatus Lights::getLights(std::vector* _aidl_return) { - for (const auto& light : mLights) _aidl_return->push_back(light); + for (const auto& light : mLights) { + _aidl_return->push_back(light); + } return ndk::ScopedAStatus::ok(); } -void Lights::setLED() { - bool rc = true; +binder_status_t Lights::dump(int fd, const char** /*args*/, uint32_t /*numArgs*/) { + dprintf(fd, "Lights AIDL:\n"); + dprintf(fd, "\n"); + + dprintf(fd, "Lights:\n"); + for (const auto& light : mLights) { + dprintf(fd, "- %d: LightType::%s\n", light.id, toString(light.type).c_str()); + } + dprintf(fd, "\n"); + + dprintf(fd, "Devices:\n"); + mDevices.dump(fd); + dprintf(fd, "\n"); + + return STATUS_OK; +} + +void Lights::updateNotificationColor() { + std::lock_guard lock(mLedMutex); bool isBatteryLit = rgb(mLastBatteryState.color).isLit(); bool isAttentionLit = rgb(mLastAttentionState.color).isLit(); const HwLightState state = isBatteryLit ? mLastBatteryState : isAttentionLit ? mLastAttentionState - : mLastNotificationState; + : mLastNotificationsState; rgb color(state.color); - uint8_t blink = (state.flashOnMs != 0 && state.flashOffMs != 0); + LightMode lightMode; switch (state.flashMode) { - case FlashMode::HARDWARE: + case FlashMode::NONE: + lightMode = LightMode::STATIC; + break; case FlashMode::TIMED: - if (mWhiteLED) { - rc = kLEDs[WHITE].setBreath(blink); - } else { - rc = kLEDs[RED].setBreath(blink && color.red); - rc &= kLEDs[GREEN].setBreath(blink && color.green); - rc &= kLEDs[BLUE].setBreath(blink && color.blue); - } - if (rc) break; - FALLTHROUGH_INTENDED; + case FlashMode::HARDWARE: + lightMode = LightMode::BREATH; + break; default: - if (mWhiteLED) { - rc = kLEDs[WHITE].setBrightness(color.toBrightness()); - } else { - rc = kLEDs[RED].setBrightness(color.red); - rc &= kLEDs[GREEN].setBrightness(color.green); - rc &= kLEDs[BLUE].setBrightness(color.blue); - } + LOG(ERROR) << "Unknown flash mode: " << static_cast(state.flashMode); + lightMode = LightMode::STATIC; break; } + mDevices.setNotificationColor(color, lightMode); + return; } diff --git a/aidl/light/Lights.h b/aidl/light/Lights.h index c9287ee..362191a 100644 --- a/aidl/light/Lights.h +++ b/aidl/light/Lights.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021-2022 The LineageOS Project + * Copyright (C) 2021-2024 The LineageOS Project * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,10 +8,7 @@ #include #include -#include "Backlight.h" - -using ::aidl::android::hardware::light::HwLight; -using ::aidl::android::hardware::light::HwLightState; +#include "Devices.h" namespace aidl { namespace android { @@ -25,20 +22,19 @@ class Lights : public BnLights { ndk::ScopedAStatus setLightState(int32_t id, const HwLightState& state) override; ndk::ScopedAStatus getLights(std::vector* _aidl_return) override; - private: - void setLED(); + binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; + private: std::vector mLights; - BacklightDevice* mBacklightDevice; - std::vector mButtonsPaths; - bool mWhiteLED; - - std::mutex mLEDMutex; + Devices mDevices; HwLightState mLastBatteryState; - HwLightState mLastNotificationState; + HwLightState mLastNotificationsState; HwLightState mLastAttentionState; + std::mutex mLedMutex; + + void updateNotificationColor(); }; } // namespace light diff --git a/aidl/light/RgbLedDevice.cpp b/aidl/light/RgbLedDevice.cpp new file mode 100644 index 0000000..6ee5bf6 --- /dev/null +++ b/aidl/light/RgbLedDevice.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "RgbLedDevice.h" + +#define LOG_TAG "RgbLedDevice" + +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +RgbLedDevice::RgbLedDevice(LedDevice red, LedDevice green, LedDevice blue) + : mRed(red), mGreen(green), mBlue(blue), mColors(Color::NONE) { + if (mRed.exists()) { + mColors |= Color::RED; + } + if (mGreen.exists()) { + mColors |= Color::GREEN; + } + if (mBlue.exists()) { + mColors |= Color::BLUE; + } +} + +bool RgbLedDevice::exists() const { + return mColors != Color::NONE; +} + +bool RgbLedDevice::supportsBreath() const { + return (!mRed.exists() || mRed.supportsBreath()) && + (!mGreen.exists() || mGreen.supportsBreath()) && + (!mBlue.exists() || mBlue.supportsBreath()); +} + +bool RgbLedDevice::setBrightness(rgb color, LightMode mode) { + bool rc = true; + + if (mColors == Color::NONE) { + LOG(ERROR) << "No LEDs found"; + return false; + } + + if (mode == LightMode::BREATH && !supportsBreath()) { + // Not all LEDs support breathing, force static mode + mode = LightMode::STATIC; + } + + if (mColors == Color::ALL) { + rc &= mRed.setBrightness(color.red, mode); + rc &= mGreen.setBrightness(color.green, mode); + rc &= mBlue.setBrightness(color.blue, mode); + } else { + // Check if we have only one LED + if (mColors == Color::RED) { + rc &= mRed.setBrightness(color.toBrightness(), mode); + } else if (mColors == Color::GREEN) { + rc &= mGreen.setBrightness(color.toBrightness(), mode); + } else if (mColors == Color::BLUE) { + rc &= mBlue.setBrightness(color.toBrightness(), mode); + } else { + // We only have two LEDs, blend the missing color in the other two + if ((mColors & Color::RED) == Color::NONE) { + rc &= mBlue.setBrightness((color.blue + color.red) / 2, mode); + rc &= mGreen.setBrightness((color.green + color.red) / 2, mode); + } else if ((mColors & Color::GREEN) == Color::NONE) { + rc &= mRed.setBrightness((color.red + color.green) / 2, mode); + rc &= mBlue.setBrightness((color.blue + color.green) / 2, mode); + } else if ((mColors & Color::BLUE) == Color::NONE) { + rc &= mRed.setBrightness((color.red + color.blue) / 2, mode); + rc &= mGreen.setBrightness((color.green + color.blue) / 2, mode); + } + } + } + + return rc; +} + +void RgbLedDevice::dump(int fd) const { + dprintf(fd, "Exists: %d", exists()); + dprintf(fd, ", supports breath: %d", supportsBreath()); + dprintf(fd, ", colors:"); + if (mColors != Color::NONE) { + if (mColors & Color::RED) { + dprintf(fd, "\nRed: "); + mRed.dump(fd); + } + if (mColors & Color::GREEN) { + dprintf(fd, "\nGreen: "); + mGreen.dump(fd); + } + if (mColors & Color::BLUE) { + dprintf(fd, "\nBlue: "); + mBlue.dump(fd); + } + } else { + dprintf(fd, " None"); + } +} + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/RgbLedDevice.h b/aidl/light/RgbLedDevice.h new file mode 100644 index 0000000..d2d0f1d --- /dev/null +++ b/aidl/light/RgbLedDevice.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "IDumpable.h" +#include "LedDevice.h" +#include "Utils.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +/** + * A pool of LED devices that will be toggled based on the wanted color. + * Support all types of LED combinations, with a maximum of 3 LEDs. + * Also supports 2 color LEDs (*cough* ASUS *cough*). + */ +class RgbLedDevice : public IDumpable { + public: + RgbLedDevice() = delete; + + /** + * Constructor. + * + * @param red The red LED device + * @param green The green LED device + * @param blue The blue LED device + */ + RgbLedDevice(LedDevice red, LedDevice green, LedDevice blue); + + /** + * Return whether this RGB LED device exists. + * This is true when at least one of the LEDs exists. + * + * @return bool true if the RGB LED device exists, false otherwise + */ + bool exists() const; + + /** + * Return whether this RGB LED device supports breathing. + * This is true when all existing LEDs support breathing. + * In case this is false, calling setBrightness with LightMode::BREATH will behave like + * LightMode::STATIC. + * + * @return bool true if the RGB LED device supports breathing, false otherwise + */ + bool supportsBreath() const; + + /** + * Set the brightness of this RGB LED device. + * + * @param color The color to set + * @param mode The mode to set + * @return bool true if the brightness was set successfully, false otherwise + */ + bool setBrightness(rgb color, LightMode mode = LightMode::STATIC); + + void dump(int fd) const override; + + enum Color { + NONE = 0, + RED = 1 << 0, + GREEN = 1 << 1, + BLUE = 1 << 2, + ALL = RED | GREEN | BLUE, + }; + + private: + LedDevice mRed; + LedDevice mGreen; + LedDevice mBlue; + + int mColors; +}; + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/Utils.cpp b/aidl/light/Utils.cpp index 8cc51fa..95626a7 100644 --- a/aidl/light/Utils.cpp +++ b/aidl/light/Utils.cpp @@ -1,49 +1,19 @@ /* - * Copyright (C) 2021-2022 The LineageOS Project + * Copyright (C) 2021-2024 The LineageOS Project * * SPDX-License-Identifier: Apache-2.0 */ #include "Utils.h" -#define LOG_TAG "android.hardware.light-service.xiaomi" - -#include -#include -#include - -using ::android::base::ReadFileToString; -using ::android::base::WriteStringToFile; - namespace aidl { namespace android { namespace hardware { namespace light { -bool fileWriteable(const std::string& file) { - return !access(file.c_str(), W_OK); -} +rgb::rgb() : red(0), green(0), blue(0) {} -bool readFromFile(const std::string& file, std::string* content) { - return ReadFileToString(file, content, true); -} - -bool readFromFile(const std::string& file, uint32_t* content) { - std::string content_str; - if (readFromFile(file, &content_str)) - *content = std::stoi(content_str); - else - return false; - return true; -} - -bool writeToFile(const std::string& file, std::string content) { - return WriteStringToFile(content, file); -} - -bool writeToFile(const std::string& file, uint32_t content) { - return writeToFile(file, std::to_string(content)); -} +rgb::rgb(uint8_t r, uint8_t g, uint8_t b) : red(r), green(g), blue(b){}; rgb::rgb(uint32_t color) { // Extract brightness from AARRGGBB. @@ -55,7 +25,7 @@ rgb::rgb(uint32_t color) { blue = color & 0xFF; // Scale RGB colors if a brightness has been applied by the user - if (alpha > 0 && alpha < 255) { + if (alpha > 0 && alpha < 0xFF) { red = red * alpha / 0xFF; green = green * alpha / 0xFF; blue = blue * alpha / 0xFF; @@ -66,8 +36,16 @@ bool rgb::isLit() { return !!red || !!green || !!blue; } +static constexpr uint8_t kRedWeight = 77; +static constexpr uint8_t kGreenWeight = 150; +static constexpr uint8_t kBlueWeight = 29; + uint8_t rgb::toBrightness() { - return (77 * red + 150 * green + 29 * blue) >> 8; + return (kRedWeight * red + kGreenWeight * green + kBlueWeight * blue) >> 8; +} + +uint32_t scaleBrightness(uint8_t brightness, uint32_t maxBrightness) { + return brightness * maxBrightness / 0xFF; } } // namespace light diff --git a/aidl/light/Utils.h b/aidl/light/Utils.h index b39d8b8..035c737 100644 --- a/aidl/light/Utils.h +++ b/aidl/light/Utils.h @@ -1,11 +1,13 @@ /* - * Copyright (C) 2021-2022 The LineageOS Project + * Copyright (C) 2021-2024 The LineageOS Project * * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include +#include #include namespace aidl { @@ -14,9 +16,9 @@ namespace hardware { namespace light { struct rgb { - rgb(uint8_t r, uint8_t g, uint8_t b) : red(r), green(g), blue(b){}; + rgb(); + rgb(uint8_t r, uint8_t g, uint8_t b); rgb(uint32_t color); - rgb() : red(0), green(0), blue(0){}; uint8_t red; uint8_t green; @@ -26,10 +28,31 @@ struct rgb { uint8_t toBrightness(); }; -bool fileWriteable(const std::string& file); -bool readFromFile(const std::string& file, std::string* content); -bool readFromFile(const std::string& file, uint32_t* content); -bool writeToFile(const std::string& file, uint32_t content); +uint32_t scaleBrightness(uint8_t brightness, uint32_t maxBrightness); + +template +bool readFromFile(const std::string& file, T& content) { + std::ifstream fileStream(file); + + if (!fileStream) { + return false; + } + + fileStream >> content; + return true; +} + +template +bool writeToFile(const std::string& file, const T content) { + std::ofstream fileStream(file); + + if (!fileStream) { + return false; + } + + fileStream << content; + return true; +} } // namespace light } // namespace hardware diff --git a/aidl/light/android.hardware.light-service.xiaomi.rc b/aidl/light/android.hardware.light-service.xiaomi.rc index 3e798c1..300e617 100644 --- a/aidl/light/android.hardware.light-service.xiaomi.rc +++ b/aidl/light/android.hardware.light-service.xiaomi.rc @@ -1,38 +1,46 @@ on early-boot - # Notification LEDs - chown system system /sys/class/leds/red/blink - chown system system /sys/class/leds/red/breath - chown system system /sys/class/leds/red/brightness - chown system system /sys/class/leds/red/max_brightness - - chown system system /sys/class/leds/green/blink - chown system system /sys/class/leds/green/breath - chown system system /sys/class/leds/green/brightness - chown system system /sys/class/leds/green/max_brightness - - chown system system /sys/class/leds/blue/blink - chown system system /sys/class/leds/blue/breath - chown system system /sys/class/leds/blue/brightness - chown system system /sys/class/leds/blue/max_brightness - - chown system system /sys/class/leds/white/blink - chown system system /sys/class/leds/white/breath - chown system system /sys/class/leds/white/brightness - chown system system /sys/class/leds/white/max_brightness - - # Backlight + # Backlight devices chown system system /sys/class/backlight/backlight/brightness chown system system /sys/class/backlight/backlight/max_brightness chown system system /sys/class/backlight/panel0-backlight/brightness chown system system /sys/class/backlight/panel0-backlight/max_brightness + # LED devices + chown system system /sys/class/leds/blue/blink + chown system system /sys/class/leds/blue/breath + chown system system /sys/class/leds/blue/brightness + chown system system /sys/class/leds/blue/max_brightness + + chown system system /sys/class/leds/button-backlight/blink + chown system system /sys/class/leds/button-backlight/breath + chown system system /sys/class/leds/button-backlight/brightness + chown system system /sys/class/leds/button-backlight/max_brightness + + chown system system /sys/class/leds/button-backlight1/blink + chown system system /sys/class/leds/button-backlight1/breath + chown system system /sys/class/leds/button-backlight1/brightness + chown system system /sys/class/leds/button-backlight1/max_brightness + + chown system system /sys/class/leds/green/blink + chown system system /sys/class/leds/green/breath + chown system system /sys/class/leds/green/brightness + chown system system /sys/class/leds/green/max_brightness + + chown system system /sys/class/leds/lcd-backlight/blink + chown system system /sys/class/leds/lcd-backlight/breath chown system system /sys/class/leds/lcd-backlight/brightness chown system system /sys/class/leds/lcd-backlight/max_brightness - # Buttons - chown system system /sys/class/leds/button-backlight/brightness - chown system system /sys/class/leds/button-backlight1/brightness + chown system system /sys/class/leds/red/blink + chown system system /sys/class/leds/red/breath + chown system system /sys/class/leds/red/brightness + chown system system /sys/class/leds/red/max_brightness + + chown system system /sys/class/leds/white/blink + chown system system /sys/class/leds/white/breath + chown system system /sys/class/leds/white/brightness + chown system system /sys/class/leds/white/max_brightness service vendor.light-default /vendor/bin/hw/android.hardware.light-service.xiaomi class hal diff --git a/aidl/light/service.cpp b/aidl/light/service.cpp index 233ab30..09038cc 100644 --- a/aidl/light/service.cpp +++ b/aidl/light/service.cpp @@ -1,11 +1,13 @@ /* - * Copyright (C) 2021 The LineageOS Project + * Copyright (C) 2021-2024 The LineageOS Project * * SPDX-License-Identifier: Apache-2.0 */ #include "Lights.h" +#define LOG_TAG "android.hardware.light-service.xiaomi" + #include #include #include