diff --git a/aidl/light/Android.bp b/aidl/light/Android.bp new file mode 100644 index 0000000..e22737a --- /dev/null +++ b/aidl/light/Android.bp @@ -0,0 +1,23 @@ +// +// Copyright (C) 2021 The LineageOS Project +// +// SPDX-License-Identifier: Apache-2.0 +// + +cc_binary { + name: "android.hardware.light-service.exynos9810", + relative_install_path: "hw", + init_rc: ["android.hardware.light-service.exynos9810.rc"], + vintf_fragments: ["android.hardware.light-service.exynos9810.xml"], + local_include_dirs: ["include"], + srcs: [ + "Lights.cpp", + "service.cpp", + ], + shared_libs: [ + "libbase", + "libbinder_ndk", + "android.hardware.light-V1-ndk", + ], + vendor: true, +} diff --git a/aidl/light/Lights.cpp b/aidl/light/Lights.cpp new file mode 100644 index 0000000..7d91c87 --- /dev/null +++ b/aidl/light/Lights.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2021 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_TAG "android.hardware.lights-service.exynos9810" + +#include +#include + +#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 +static void set(const std::string& path, const T& value) { + std::ofstream file(path); + file << value << std::endl; +} + +template +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(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 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 *_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 diff --git a/aidl/light/Lights.h b/aidl/light/Lights.h new file mode 100644 index 0000000..253167b --- /dev/null +++ b/aidl/light/Lights.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "samsung_lights.h" + +using ::aidl::android::hardware::light::HwLightState; +using ::aidl::android::hardware::light::HwLight; + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +class Lights : public BnLights { +public: + Lights(); + + ndk::ScopedAStatus setLightState(int32_t id, const HwLightState& state) override; + ndk::ScopedAStatus getLights(std::vector *_aidl_return) override; + +private: + void handleBacklight(const HwLightState& state); +#ifdef BUTTON_BRIGHTNESS_NODE + void handleButtons(const HwLightState& state); +#endif /* BUTTON_BRIGHTNESS_NODE */ +#ifdef LED_BLINK_NODE + void handleBattery(const HwLightState& state); + void handleNotifications(const HwLightState& state); + void handleAttention(const HwLightState& state); + void setNotificationLED(); + uint32_t calibrateColor(uint32_t color, int32_t brightness); + + HwLightState mAttentionState; + HwLightState mBatteryState; + HwLightState mNotificationState; +#endif /* LED_BLINK_NODE */ + + uint32_t rgbToBrightness(const HwLightState& state); + + std::mutex mLock; + std::unordered_map> mLights; +}; + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/light/android.hardware.light-service.exynos9810.rc b/aidl/light/android.hardware.light-service.exynos9810.rc new file mode 100644 index 0000000..08ffb7f --- /dev/null +++ b/aidl/light/android.hardware.light-service.exynos9810.rc @@ -0,0 +1,5 @@ +service vendor.light-default /vendor/bin/hw/android.hardware.light-service.exynos9810 + class hal + user system + group system + shutdown critical diff --git a/aidl/light/android.hardware.light-service.exynos9810.xml b/aidl/light/android.hardware.light-service.exynos9810.xml new file mode 100644 index 0000000..db604d6 --- /dev/null +++ b/aidl/light/android.hardware.light-service.exynos9810.xml @@ -0,0 +1,6 @@ + + + android.hardware.light + ILights/default + + diff --git a/aidl/light/include/samsung_lights.h b/aidl/light/include/samsung_lights.h new file mode 100644 index 0000000..e53876e --- /dev/null +++ b/aidl/light/include/samsung_lights.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * 2017-2022 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. + * 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 + +/* + * Board specific nodes + * + * If your kernel exposes these controls in another place, you can either + * symlink to the locations given here, or override this header in your + * device tree. + */ +#define PANEL_BRIGHTNESS_NODE "/sys/class/backlight/panel/brightness" +#define PANEL_MAX_BRIGHTNESS_NODE "/sys/class/backlight/panel/max_brightness" +#define BUTTON_BRIGHTNESS_NODE "/sys/class/sec/sec_touchkey/brightness" +#define LED_BLINK_NODE "/sys/class/sec/led/led_blink" +#define LED_BLN_NODE "/sys/class/misc/backlightnotification/notification_led" + +// Uncomment to enable variable button brightness +//#define VAR_BUTTON_BRIGHTNESS 1 + +/* + * Brightness adjustment factors + * + * If one of your device's LEDs is more powerful than the others, use these + * values to equalise them. This value is in the range 0.0-1.0. + */ +#define LED_ADJUSTMENT_R 1.0 +#define LED_ADJUSTMENT_G 1.0 +#define LED_ADJUSTMENT_B 1.0 + +/* + * Light brightness factors + * + * It might make sense for all colours to be scaled down (for example, if your + * LED is too bright). Use these values to adjust the brightness of each + * light. This value is within the range 0-255. + */ +#define LED_BRIGHTNESS_BATTERY 255 +#define LED_BRIGHTNESS_NOTIFICATION 255 +#define LED_BRIGHTNESS_ATTENTION 255 diff --git a/aidl/light/service.cpp b/aidl/light/service.cpp new file mode 100644 index 0000000..27fc419 --- /dev/null +++ b/aidl/light/service.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_TAG "android.hardware.light-service.exynos9810" + +#include "Lights.h" + +#include +#include +#include + +using ::aidl::android::hardware::light::Lights; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr lights = ndk::SharedRefBase::make(); + + 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 reach +} diff --git a/common.mk b/common.mk index a49ef6f..b82b4d6 100644 --- a/common.mk +++ b/common.mk @@ -396,6 +396,10 @@ PRODUCT_PACKAGES += \ PRODUCT_PACKAGES += \ android.hardware.vibrator-service.sm7125 +# Light +PRODUCT_PACKAGES += \ + android.hardware.light-service.exynos9810 + # Tether PRODUCT_PACKAGES += \ ipacm \ diff --git a/sepolicy/vendor/file_contexts b/sepolicy/vendor/file_contexts index 7054a03..9df999c 100644 --- a/sepolicy/vendor/file_contexts +++ b/sepolicy/vendor/file_contexts @@ -63,6 +63,10 @@ /(vendor|system/vendor)/bin/hw/android.hardware.biometrics.fingerprint@2.3-service-samsung.sm7125 u:object_r:hal_fingerprint_default_exec:s0 /(vendor|system/vendor)/bin/hw/vendor.samsung.hardware.thermal@1.0-service u:object_r:hal_thermal_default_exec:s0 /(vendor|system/vendor)/bin/hw/android.hardware.sensors@[0-9].[0-9]-service.samsung-multihal u:object_r:hal_sensors_default_exec:s0 + +# WIP +/(vendor|system/vendor)/bin/hw/android\.hardware\.light(@[0-9].[0-9])?-service\.exynos9810 u:object_r:hal_light_default_exec:s0 +/(vendor|system/vendor)/bin/hw/android\.hardware\.vibrator(@[0-9].[0-9])?-service\.exynos9810 u:object_r:hal_vibrator_default_exec:s0 /(vendor|system/vendor)/bin/hw/android\.hardware\.sensors-service(\.exynos9810-multihal)? u:object_r:hal_sensors_default_exec:s0 /(vendor|system/vendor)/bin/hw/android.hardware.vibrator-service.sm7125 u:object_r:hal_vibrator_default_exec:s0