diff options
l--------- | .clang-format | 1 | ||||
-rw-r--r-- | light/Android.bp | 14 | ||||
-rw-r--r-- | light/Light.cpp | 275 | ||||
-rw-r--r-- | light/Light.h | 46 | ||||
-rw-r--r-- | light/android.hardware.light@2.0-service.xiaomi_sdm660.rc | 12 | ||||
-rw-r--r-- | light/service.cpp | 14 |
6 files changed, 177 insertions, 185 deletions
diff --git a/.clang-format b/.clang-format new file mode 120000 index 0000000..973b2fa --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +../../../build/soong/scripts/system-clang-format
\ No newline at end of file diff --git a/light/Android.bp b/light/Android.bp index e05d16d..7a2474b 100644 --- a/light/Android.bp +++ b/light/Android.bp @@ -19,14 +19,12 @@ cc_binary { init_rc: ["android.hardware.light@2.0-service.xiaomi_sdm660.rc"], srcs: ["service.cpp", "Light.cpp"], shared_libs: [ - "libhardware", - "libhidlbase", - "libhidltransport", - "liblog", - "libhwbinder", - "libutils", - "android.hardware.light@2.0", + "libbase", + "libhidlbase", + "libhidltransport", + "libutils", + "android.hardware.light@2.0", ], relative_install_path : "hw", - proprietary: true, + vendor: true, } diff --git a/light/Light.cpp b/light/Light.cpp index e6d9349..0286b6a 100644 --- a/light/Light.cpp +++ b/light/Light.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The LineageOS Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,80 +15,59 @@ * limitations under the License. */ -//Author := dev_harsh1998, Isaac Chen +// Author := dev_harsh1998, Isaac Chen -#define LOG_TAG "android.hardware.light@2.0-service.xiaomi_sdm660" +#define LOG_TAG "android.hardware.light@2.0-impl.xiaomi_sdm660" +/* #define LOG_NDEBUG 0 */ -#include <log/log.h> -#include <fstream> #include "Light.h" -namespace android { -namespace hardware { -namespace light { -namespace V2_0 { -namespace implementation { +#include <android-base/file.h> +#include <android-base/logging.h> -#define LEDS "/sys/class/leds/" -#define LCD_LED LEDS "lcd-backlight/" -#define BRIGHTNESS "brightness" -#define WHITE LEDS "white/" -#define BLINK "blink" -#define DUTY_PCTS "duty_pcts" -#define PAUSE_HI "pause_hi" -#define PAUSE_LO "pause_lo" -#define RAMP_STEP_MS "ramp_step_ms" -#define START_IDX "start_idx" +namespace { -#define MAX_LED_BRIGHTNESS 255 -#define MAX_LCD_BRIGHTNESS 4095 +#define PPCAT_NX(A, B) A/B +#define PPCAT(A, B) PPCAT_NX(A, B) +#define STRINGIFY_INNER(x) #x +#define STRINGIFY(x) STRINGIFY_INNER(x) -/* - * 8 duty percent steps. - */ -#define RAMP_STEPS 8 -/* - * Each step will stay on for 50ms by default. - */ -#define RAMP_STEP_DURATION 50 -/* - * Each value represents a duty percent (0 - 100) for the led pwm. - */ -static int32_t BRIGHTNESS_RAMP[RAMP_STEPS] = {0, 12, 25, 37, 50, 72, 85, 100}; +#define LEDS(x) PPCAT(/sys/class/leds, x) +#define LCD_ATTR(x) STRINGIFY(PPCAT(LEDS(lcd-backlight), x)) +#define WHITE_ATTR(x) STRINGIFY(PPCAT(LEDS(white), x)) -/* - * Write value to path and close file. - */ -static void set(std::string path, std::string value) { - std::ofstream file(path); - /* Only write brightness value if stream is open, alive & well */ - if (file.is_open()) { - file << value; - } else { - /* Fire a warning a bail out */ - ALOGE("failed to write %s to %s", value.c_str(), path.c_str()); - return; - } +using ::android::base::ReadFileToString; +using ::android::base::WriteStringToFile; + +// Default max brightness +constexpr auto kDefaultMaxLedBrightness = 255; +constexpr auto kDefaultMaxScreenBrightness = 4095; + +// Each step will stay on for 50ms by default. +constexpr auto kRampStepDuration = 50; + +// Each value represents a duty percent (0 - 100) for the led pwm. +constexpr std::array kBrightnessRamp = {0, 12, 25, 37, 50, 72, 85, 100}; + +// Write value to path and close file. +bool WriteToFile(const std::string& path, uint32_t content) { + return WriteStringToFile(std::to_string(content), path); } -static void set(std::string path, int value) { - set(path, std::to_string(value)); +bool WriteToFile(const std::string& path, const std::string& content) { + return WriteStringToFile(content, path); } -static uint32_t getBrightness(const LightState& state) { - uint32_t alpha, red, green, blue; +uint32_t RgbaToBrightness(uint32_t color) { + // Extract brightness from AARRGGBB. + uint32_t alpha = (color >> 24) & 0xFF; - /* - * Extract brightness from AARRGGBB. - */ - alpha = (state.color >> 24) & 0xFF; - red = (state.color >> 16) & 0xFF; - green = (state.color >> 8) & 0xFF; - blue = state.color & 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 brightness if Alpha brightness is not 0xFF. - */ + // Scale RGB colors if a brightness has been applied by the user if (alpha != 0xFF) { red = red * alpha / 0xFF; green = green * alpha / 0xFF; @@ -97,130 +77,133 @@ static uint32_t getBrightness(const LightState& state) { return (77 * red + 150 * green + 29 * blue) >> 8; } +inline uint32_t RgbaToBrightness(uint32_t color, uint32_t max_brightness) { + return RgbaToBrightness(color) * max_brightness / 0xFF; +} + /* * Scale each value of the brightness ramp according to the * brightness of the color. */ -static std::string getScaledRamp(uint32_t brightness) { - std::string ramp, pad; +std::string GetScaledDutyPcts(uint32_t brightness) { + std::stringstream ramp; - for (auto const& step : BRIGHTNESS_RAMP) { - ramp += pad + std::to_string(step * brightness / 0xFF); - pad = ","; + for (size_t i = 0; i < kBrightnessRamp.size(); i++) { + if (i > 0) ramp << ","; + ramp << kBrightnessRamp[i] * brightness / 0xFF; } - return ramp; + return ramp.str(); } -static inline uint32_t scaleBrightness(uint32_t brightness, uint32_t maxBrightness) { - return brightness * maxBrightness / 0xFF; +inline bool IsLit(uint32_t color) { + return color & 0x00ffffff; } -static inline uint32_t getScaledBrightness(const LightState& state, uint32_t maxBrightness) { - return scaleBrightness(getBrightness(state), maxBrightness); -} +} // anonymous namespace -static void handleXiaomiBacklight(Type /*type*/, const LightState& state) { - uint32_t brightness = getScaledBrightness(state, MAX_LCD_BRIGHTNESS); - set(LCD_LED BRIGHTNESS, brightness); -} - -static void setNotification(const LightState& state) { - uint32_t whiteBrightness = getScaledBrightness(state, MAX_LED_BRIGHTNESS); - - /* Turn off the leds (initially) */ - set(WHITE BLINK, 0); - - if (state.flashMode == Flash::TIMED) { - /* - * If the flashOnMs duration is not long enough to fit ramping up - * and down at the default step duration, step duration is modified - * to fit. - */ - int32_t stepDuration = RAMP_STEP_DURATION; - int32_t pauseHi = state.flashOnMs - (stepDuration * RAMP_STEPS * 2); - int32_t pauseLo = state.flashOffMs; +namespace android { +namespace hardware { +namespace light { +namespace V2_0 { +namespace implementation { - if (pauseHi < 0) { - pauseHi = 0; - } +Light::Light() { + std::string buf; - /* White */ - set(WHITE START_IDX, 0 * RAMP_STEPS); - set(WHITE DUTY_PCTS, getScaledRamp(whiteBrightness)); - set(WHITE PAUSE_LO, pauseLo); - set(WHITE PAUSE_HI, pauseHi); - set(WHITE RAMP_STEP_MS, stepDuration); - set(WHITE BLINK, 1); + if (ReadFileToString(LCD_ATTR(max_brightness), &buf)) { + max_screen_brightness_ = std::stoi(buf); } else { - set(WHITE BRIGHTNESS, whiteBrightness); + max_screen_brightness_ = kDefaultMaxScreenBrightness; + LOG(ERROR) << "Failed to read max screen brightness, fallback to " + << kDefaultMaxLedBrightness; } -} - -static inline bool isLit(const LightState& state) { - return state.color & 0x00ffffff; -} -/* - * Keep sorted in the order of importance. - */ -static const LightState offState = {}; -static std::vector<std::pair<Type, LightState>> notificationStates = { - { Type::ATTENTION, offState }, - { Type::NOTIFICATIONS, offState }, - { Type::BATTERY, offState }, -}; - -static void handleXiaomiNotification(Type type, const LightState& state) { - for(auto it : notificationStates) { - if (it.first == type) { - it.second = state; - } - - if (isLit(it.second)) { - setNotification(it.second); - return; - } + if (ReadFileToString(WHITE_ATTR(max_brightness), &buf)) { + max_led_brightness_ = std::stoi(buf); + } else { + max_led_brightness_ = kDefaultMaxLedBrightness; + LOG(ERROR) << "Failed to read max LED brightness, fallback to " << kDefaultMaxLedBrightness; } - - setNotification(offState); } -static std::map<Type, std::function<void(Type type, const LightState&)>> lights = { - {Type::BACKLIGHT, handleXiaomiBacklight}, - {Type::NOTIFICATIONS, handleXiaomiNotification}, - {Type::BATTERY, handleXiaomiNotification}, - {Type::ATTENTION, handleXiaomiNotification}, -}; - -Light::Light() {} - Return<Status> Light::setLight(Type type, const LightState& state) { - auto it = lights.find(type); + auto it = lights_.find(type); - if (it == lights.end()) { + if (it == lights_.end()) { return Status::LIGHT_NOT_SUPPORTED; } - /* - * Lock global mutex until light state is updated. - */ - - std::lock_guard<std::mutex> lock(globalLock); it->second(type, state); + return Status::SUCCESS; } Return<void> Light::getSupportedTypes(getSupportedTypes_cb _hidl_cb) { std::vector<Type> types; - for (auto const& light : lights) types.push_back(light.first); + for (auto&& light : lights_) types.emplace_back(light.first); _hidl_cb(types); return Void(); } +void Light::setLightBacklight(Type /*type*/, const LightState& state) { + uint32_t brightness = RgbaToBrightness(state.color, max_screen_brightness_); + WriteToFile(LCD_ATTR(brightness), brightness); +} + +void Light::setLightNotification(Type type, const LightState& state) { + bool found = false; + for (auto&& [cur_type, cur_state] : notif_states_) { + if (cur_type == type) { + cur_state = state; + } + + // Fallback to battery light + if (!found && (cur_type == Type::BATTERY || IsLit(state.color))) { + found = true; + LOG(DEBUG) << __func__ << ": type=" << toString(cur_type); + applyNotificationState(state); + } + } +} + +void Light::applyNotificationState(const LightState& state) { + uint32_t white_brightness = RgbaToBrightness(state.color, max_led_brightness_); + + // Turn off the leds (initially) + WriteToFile(WHITE_ATTR(blink), 0); + + if (state.flashMode == Flash::TIMED && state.flashOnMs > 0 && state.flashOffMs > 0) { + /* + * If the flashOnMs duration is not long enough to fit ramping up + * and down at the default step duration, step duration is modified + * to fit. + */ + int32_t step_duration = kRampStepDuration; + int32_t pause_hi = state.flashOnMs - (step_duration * kBrightnessRamp.size() * 2); + if (pause_hi < 0) { + step_duration = state.flashOnMs / (kBrightnessRamp.size() * 2); + pause_hi = 0; + } + + LOG(DEBUG) << __func__ << ": color=" << std::hex << state.color << std::dec + << " onMs=" << state.flashOnMs << " offMs=" << state.flashOffMs; + + // White + WriteToFile(WHITE_ATTR(start_idx), 0); + WriteToFile(WHITE_ATTR(duty_pcts), GetScaledDutyPcts(white_brightness)); + WriteToFile(WHITE_ATTR(pause_lo), static_cast<uint32_t>(state.flashOffMs)); + WriteToFile(WHITE_ATTR(pause_hi), static_cast<uint32_t>(pause_hi)); + WriteToFile(WHITE_ATTR(ramp_step_ms), static_cast<uint32_t>(step_duration)); + WriteToFile(WHITE_ATTR(blink), 1); + } else { + WriteToFile(WHITE_ATTR(brightness), white_brightness); + } +} + } // namespace implementation } // namespace V2_0 } // namespace light diff --git a/light/Light.h b/light/Light.h index 311b7de..8427148 100644 --- a/light/Light.h +++ b/light/Light.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The LineageOS Project + * Copyright (C) 2017-2020 The LineageOS Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H -#define ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H + +#pragma once #include <android/hardware/light/2.0/ILight.h> -#include <hardware/lights.h> -#include <hidl/Status.h> -#include <map> -#include <mutex> + +#include <unordered_map> namespace android { namespace hardware { @@ -29,22 +27,38 @@ namespace V2_0 { namespace implementation { using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::hardware::hidl_vec; 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; class Light : public ILight { - public: - Light(); + public: + Light(); + + Return<Status> setLight(Type type, const LightState& state) override; + Return<void> getSupportedTypes(getSupportedTypes_cb _hidl_cb) override; - Return<Status> setLight(Type type, const LightState& state) override; - Return<void> getSupportedTypes(getSupportedTypes_cb _hidl_cb) override; + private: + void setLightBacklight(Type type, const LightState& state); + void setLightNotification(Type type, const LightState& state); + void applyNotificationState(const LightState& state); - private: - std::mutex globalLock; + uint32_t max_led_brightness_; + uint32_t max_screen_brightness_; + + std::unordered_map<Type, std::function<void(Type type, const LightState&)>> lights_{ + {Type::ATTENTION, [this](auto&&... args) { setLightNotification(args...); }}, + {Type::BACKLIGHT, [this](auto&&... args) { setLightBacklight(args...); }}, + {Type::BATTERY, [this](auto&&... args) { setLightNotification(args...); }}, + {Type::NOTIFICATIONS, [this](auto&&... args) { setLightNotification(args...); }}}; + + // Keep sorted in the order of importance. + std::array<std::pair<Type, LightState>, 3> notif_states_ = {{ + {Type::ATTENTION, {}}, + {Type::NOTIFICATIONS, {}}, + {Type::BATTERY, {}}, + }}; }; } // namespace implementation @@ -52,5 +66,3 @@ class Light : public ILight { } // namespace light } // namespace hardware } // namespace android - -#endif // ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H diff --git a/light/android.hardware.light@2.0-service.xiaomi_sdm660.rc b/light/android.hardware.light@2.0-service.xiaomi_sdm660.rc index 7dac2e6..969bdac 100644 --- a/light/android.hardware.light@2.0-service.xiaomi_sdm660.rc +++ b/light/android.hardware.light@2.0-service.xiaomi_sdm660.rc @@ -3,21 +3,19 @@ on boot chown system system /sys/class/leds/white/blink chown system system /sys/class/leds/white/brightness chown system system /sys/class/leds/white/duty_pcts + chown system system /sys/class/leds/white/max_brightness chown system system /sys/class/leds/white/pause_hi chown system system /sys/class/leds/white/pause_lo chown system system /sys/class/leds/white/ramp_step_ms chown system system /sys/class/leds/white/start_idx - chmod 660 /sys/class/leds/white/blink - chmod 660 /sys/class/leds/white/brightness - chmod 660 /sys/class/leds/white/duty_pcts - chmod 660 /sys/class/leds/white/pause_hi - chmod 660 /sys/class/leds/white/pause_lo - chmod 660 /sys/class/leds/white/ramp_step_ms - chmod 660 /sys/class/leds/white/start_idx + chown system system /sys/class/leds/lcd-backlight/max_brightness + + start vendor.light-hal-2-0 service vendor.light-hal-2-0 /vendor/bin/hw/android.hardware.light@2.0-service.xiaomi_sdm660 class hal user system group system shutdown critical + disabled diff --git a/light/service.cpp b/light/service.cpp index 8f6fa88..27a4ebf 100644 --- a/light/service.cpp +++ b/light/service.cpp @@ -1,5 +1,6 @@ /* - * Copyright 2018 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The LineageOS Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +17,7 @@ #define LOG_TAG "android.hardware.light@2.0-service.xiaomi_sdm660" +#include <android-base/logging.h> #include <hidl/HidlTransportSupport.h> #include "Light.h" @@ -23,28 +25,26 @@ using android::hardware::configureRpcThreadpool; using android::hardware::joinRpcThreadpool; -using android::hardware::light::V2_0::ILight; using android::hardware::light::V2_0::implementation::Light; using android::OK; -using android::sp; using android::status_t; int main() { - android::sp<ILight> service = new Light(); + android::sp<Light> service = new Light(); configureRpcThreadpool(1, true); status_t status = service->registerAsService(); if (status != OK) { - ALOGE("Cannot register Light HAL service."); + LOG(ERROR) << "Cannot register Light HAL service."; return 1; } - ALOGI("Light HAL service ready."); + LOG(DEBUG) << "Light HAL service ready."; joinRpcThreadpool(); - ALOGI("Light HAL service failed to join thread pool."); + LOG(ERROR) << "Light HAL service failed to join thread pool."; return 1; } |