| /* |
| * Copyright (C) 2021 The LineageOS Project |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define LOG_TAG "android.hardware.lights-service.samsung" |
| |
| #include <android-base/stringprintf.h> |
| #include <fstream> |
| |
| #include "Lights.h" |
| |
| #define COLOR_MASK 0x00ffffff |
| #define MAX_INPUT_BRIGHTNESS 255 |
| |
| namespace aidl { |
| namespace android { |
| namespace hardware { |
| namespace light { |
| |
| /* |
| * Write value to path and close file. |
| */ |
| template <typename T> |
| static void set(const std::string& path, const T& value) { |
| std::ofstream file(path); |
| file << value << std::endl; |
| } |
| |
| template <typename T> |
| static T get(const std::string& path, const T& def) { |
| std::ifstream file(path); |
| T result; |
| |
| file >> result; |
| return file.fail() ? def : result; |
| } |
| |
| Lights::Lights() { |
| mLights.emplace(LightType::BACKLIGHT, |
| std::bind(&Lights::handleBacklight, this, std::placeholders::_1)); |
| #ifdef BUTTON_BRIGHTNESS_NODE |
| mLights.emplace(LightType::BUTTONS, std::bind(&Lights::handleButtons, this, std::placeholders::_1)); |
| #endif /* BUTTON_BRIGHTNESS_NODE */ |
| #ifdef LED_BLINK_NODE |
| mLights.emplace(LightType::BATTERY, std::bind(&Lights::handleBattery, this, std::placeholders::_1)); |
| mLights.emplace(LightType::NOTIFICATIONS, |
| std::bind(&Lights::handleNotifications, this, std::placeholders::_1)); |
| mLights.emplace(LightType::ATTENTION, |
| std::bind(&Lights::handleAttention, this, std::placeholders::_1)); |
| #endif /* LED_BLINK_NODE */ |
| } |
| |
| ndk::ScopedAStatus Lights::setLightState(int32_t id, const HwLightState& state) { |
| LightType type = static_cast<LightType>(id); |
| auto it = mLights.find(type); |
| |
| if (it == mLights.end()) { |
| return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); |
| } |
| |
| /* |
| * Lock global mutex until light state is updated. |
| */ |
| std::lock_guard<std::mutex> lock(mLock); |
| |
| it->second(state); |
| |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| void Lights::handleBacklight(const HwLightState& state) { |
| uint32_t max_brightness = get(PANEL_MAX_BRIGHTNESS_NODE, MAX_INPUT_BRIGHTNESS); |
| uint32_t brightness = rgbToBrightness(state); |
| |
| if (max_brightness != MAX_INPUT_BRIGHTNESS) { |
| brightness = brightness * max_brightness / MAX_INPUT_BRIGHTNESS; |
| } |
| |
| set(PANEL_BRIGHTNESS_NODE, brightness); |
| } |
| |
| #ifdef BUTTON_BRIGHTNESS_NODE |
| void Lights::handleButtons(const HwLightState& state) { |
| #ifdef VAR_BUTTON_BRIGHTNESS |
| uint32_t brightness = rgbToBrightness(state); |
| #else |
| uint32_t brightness = (state.color & COLOR_MASK) ? 1 : 0; |
| #endif |
| |
| set(BUTTON_BRIGHTNESS_NODE, brightness); |
| } |
| #endif |
| |
| #ifdef LED_BLINK_NODE |
| void Lights::handleBattery(const HwLightState& state) { |
| mBatteryState = state; |
| setNotificationLED(); |
| } |
| |
| void Lights::handleNotifications(const HwLightState& state) { |
| mNotificationState = state; |
| setNotificationLED(); |
| } |
| |
| void Lights::handleAttention(const HwLightState& state) { |
| mAttentionState = state; |
| setNotificationLED(); |
| } |
| |
| void Lights::setNotificationLED() { |
| int32_t adjusted_brightness = MAX_INPUT_BRIGHTNESS; |
| HwLightState state; |
| #ifdef LED_BLN_NODE |
| bool bln = false; |
| #endif /* LED_BLN_NODE */ |
| |
| if (mNotificationState.color & COLOR_MASK) { |
| adjusted_brightness = LED_BRIGHTNESS_NOTIFICATION; |
| state = mNotificationState; |
| #ifdef LED_BLN_NODE |
| bln = true; |
| #endif /* LED_BLN_NODE */ |
| } else if (mAttentionState.color & COLOR_MASK) { |
| adjusted_brightness = LED_BRIGHTNESS_ATTENTION; |
| state = mAttentionState; |
| if (state.flashMode == FlashMode::HARDWARE) { |
| if (state.flashOnMs > 0 && state.flashOffMs == 0) state.flashMode = FlashMode::NONE; |
| state.color = 0x000000ff; |
| } |
| if (state.flashMode == FlashMode::NONE) { |
| state.color = 0; |
| } |
| } else if (mBatteryState.color & COLOR_MASK) { |
| adjusted_brightness = LED_BRIGHTNESS_BATTERY; |
| state = mBatteryState; |
| } else { |
| set(LED_BLINK_NODE, "0x00000000 0 0"); |
| return; |
| } |
| |
| if (state.flashMode == FlashMode::NONE) { |
| state.flashOnMs = 0; |
| state.flashOffMs = 0; |
| } |
| |
| state.color = calibrateColor(state.color & COLOR_MASK, adjusted_brightness); |
| set(LED_BLINK_NODE, ::android::base::StringPrintf("0x%08x %d %d", state.color, state.flashOnMs, |
| state.flashOffMs)); |
| |
| #ifdef LED_BLN_NODE |
| if (bln) { |
| set(LED_BLN_NODE, (state.color & COLOR_MASK) ? 1 : 0); |
| } |
| #endif /* LED_BLN_NODE */ |
| } |
| |
| uint32_t Lights::calibrateColor(uint32_t color, int32_t brightness) { |
| uint32_t red = ((color >> 16) & 0xFF) * LED_ADJUSTMENT_R; |
| uint32_t green = ((color >> 8) & 0xFF) * LED_ADJUSTMENT_G; |
| uint32_t blue = (color & 0xFF) * LED_ADJUSTMENT_B; |
| |
| return (((red * brightness) / 255) << 16) + (((green * brightness) / 255) << 8) + |
| ((blue * brightness) / 255); |
| } |
| #endif /* LED_BLINK_NODE */ |
| |
| #define AutoHwLight(light) {.id = (int32_t)light, .type = light, .ordinal = 0} |
| |
| ndk::ScopedAStatus Lights::getLights(std::vector<HwLight> *_aidl_return) { |
| for (auto const& light : mLights) { |
| _aidl_return->push_back(AutoHwLight(light.first)); |
| } |
| |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| uint32_t Lights::rgbToBrightness(const HwLightState& state) { |
| uint32_t color = state.color & COLOR_MASK; |
| |
| return ((77 * ((color >> 16) & 0xff)) + (150 * ((color >> 8) & 0xff)) + (29 * (color & 0xff))) >> |
| 8; |
| } |
| |
| } // namespace light |
| } // namespace hardware |
| } // namespace android |
| } // namespace aidl |