diff --git a/lights/Lights.cpp b/lights/Lights.cpp index 74747d5..40432f7 100644 --- a/lights/Lights.cpp +++ b/lights/Lights.cpp @@ -16,23 +16,166 @@ #include "Lights.h" +#include #include +#include + +using ::android::base::WriteStringToFile; 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); +#define LED_PATH(led) "/sys/class/leds/" led "/" + +static const std::string led_paths[] { + [RED] = LED_PATH("red"), + [GREEN] = LED_PATH("green"), + [BLUE] = LED_PATH("blue"), + [WHITE] = LED_PATH("white"), +}; + +static const std::string kLCDFile = "/sys/class/backlight/panel0-backlight/brightness"; + +#define AutoHwLight(light) {.id = (int)light, .type = light, .ordinal = 0} + +// List of supported lights +const static std::vector kAvailableLights = { + AutoHwLight(LightType::BACKLIGHT), + AutoHwLight(LightType::BATTERY), + AutoHwLight(LightType::NOTIFICATIONS) +}; + +Lights::Lights() { + mWhiteLed = !access((led_paths[WHITE] + "brightness").c_str(), W_OK); } -ndk::ScopedAStatus Lights::getLights(std::vector* /*lights*/) { - LOG(INFO) << "Lights reporting supported lights"; +// AIDL methods +ndk::ScopedAStatus Lights::setLightState(int id, const HwLightState& state) { + switch (id) { + case (int)LightType::BACKLIGHT: + WriteToFile(kLCDFile, RgbaToBrightness(state.color)); + break; + case (int)LightType::BATTERY: + mBattery = state; + handleSpeakerBatteryLocked(); + break; + case (int)LightType::NOTIFICATIONS: + mNotification = state; + handleSpeakerBatteryLocked(); + break; + default: + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + break; + } + return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Lights::getLights(std::vector* lights) { + for (auto i = kAvailableLights.begin(); i != kAvailableLights.end(); i++) { + lights->push_back(*i); + } + return ndk::ScopedAStatus::ok(); +} + +// device methods +void Lights::setSpeakerLightLocked(const HwLightState& state) { + uint32_t alpha, red, green, blue; + uint32_t blink; + bool rc = true; + + // Extract brightness from AARRGGBB + alpha = (state.color >> 24) & 0xFF; + red = (state.color >> 16) & 0xFF; + green = (state.color >> 8) & 0xFF; + blue = state.color & 0xFF; + + // Scale RGB brightness if Alpha brightness is not 0xFF + if (alpha != 0xFF) { + red = (red * alpha) / 0xFF; + green = (green * alpha) / 0xFF; + blue = (blue * alpha) / 0xFF; + } + + blink = (state.flashOnMs != 0 && state.flashOffMs != 0); + + switch (state.flashMode) { + case FlashMode::HARDWARE: + case FlashMode::TIMED: + if (mWhiteLed) { + rc = setLedBreath(WHITE, blink); + } else { + if (!!red) + rc = setLedBreath(RED, blink); + if (!!green) + rc &= setLedBreath(GREEN, blink); + if (!!blue) + rc &= setLedBreath(BLUE, blink); + } + if (rc) + break; + FALLTHROUGH_INTENDED; + case FlashMode::NONE: + default: + if (mWhiteLed) { + rc = setLedBrightness(WHITE, RgbaToBrightness(state.color)); + } else { + rc = setLedBrightness(RED, red); + rc &= setLedBrightness(GREEN, green); + rc &= setLedBrightness(BLUE, blue); + } + break; + } + + return; +} + +void Lights::handleSpeakerBatteryLocked() { + if (IsLit(mBattery.color)) + return setSpeakerLightLocked(mBattery); + else + return setSpeakerLightLocked(mNotification); +} + +bool Lights::setLedBreath(led_type led, uint32_t value) { + return WriteToFile(led_paths[led] + "breath", value); +} + +bool Lights::setLedBrightness(led_type led, uint32_t value) { + return WriteToFile(led_paths[led] + "brightness", value); +} + +// Utils +bool Lights::IsLit(uint32_t color) { + return color & 0x00ffffff; +} + +uint32_t Lights::RgbaToBrightness(uint32_t color) { + // Extract brightness from AARRGGBB. + uint32_t alpha = (color >> 24) & 0xFF; + + // Retrieve each of the RGB colors + uint32_t red = (color >> 16) & 0xFF; + uint32_t green = (color >> 8) & 0xFF; + uint32_t blue = color & 0xFF; + + // Scale RGB colors if a brightness has been applied by the user + if (alpha != 0xFF) { + red = red * alpha / 0xFF; + green = green * alpha / 0xFF; + blue = blue * alpha / 0xFF; + } + + return (77 * red + 150 * green + 29 * blue) >> 8; +} + +// Write value to path and close file. +bool Lights::WriteToFile(const std::string& path, uint32_t content) { + return WriteStringToFile(std::to_string(content), path); +} + } // namespace light } // namespace hardware } // namespace android diff --git a/lights/Lights.h b/lights/Lights.h index 8cba5a1..4be3577 100644 --- a/lights/Lights.h +++ b/lights/Lights.h @@ -23,10 +23,34 @@ namespace android { namespace hardware { namespace light { -// Default implementation that reports no supported lights. +enum led_type { + RED, + GREEN, + BLUE, + WHITE, +}; + class Lights : public BnLights { +public: + Lights(); + ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override; ndk::ScopedAStatus getLights(std::vector* types) override; + +private: + void setSpeakerLightLocked(const HwLightState& state); + void handleSpeakerBatteryLocked(); + + bool setLedBreath(led_type led, uint32_t value); + bool setLedBrightness(led_type led, uint32_t value); + + bool IsLit(uint32_t color); + uint32_t RgbaToBrightness(uint32_t color); + bool WriteToFile(const std::string& path, uint32_t content); + + bool mWhiteLed; + HwLightState mNotification; + HwLightState mBattery; }; } // namespace light diff --git a/lights/lights-xiaomi_atoll.rc b/lights/lights-xiaomi_atoll.rc index 1e9fa47..a5fb952 100644 --- a/lights/lights-xiaomi_atoll.rc +++ b/lights/lights-xiaomi_atoll.rc @@ -1,5 +1,10 @@ +on early-boot + chown system system /sys/class/leds/white/breath + chown system system /sys/class/leds/white/brightness + chown system system /sys/class/backlight/panel0-backlight/brightness + service vendor.light-xiaomi_atoll /vendor/bin/hw/android.hardware.lights-service.xiaomi_atoll class hal - user nobody - group nobody + user system + group system shutdown critical