From f8f10ac731fae5c1c84b795ddd2c7da11bb3c850 Mon Sep 17 00:00:00 2001 From: Bruno Martins Date: Tue, 10 Jul 2018 00:18:09 +0100 Subject: [PATCH] sdm710-common: Add a custom lights HAL * This overrides stock liblights and fixes LCD backlight issues. * Also fix lights HAL denials Change-Id: Iafa2ba2338932879b7a1dc480b50559e3328fd21 --- Android.bp | 3 + light/Android.bp | 31 +++ light/Light.cpp | 189 ++++++++++++++++++ light/Light.h | 62 ++++++ ...ardware.light@2.0-service.realme_sdm710.rc | 21 ++ light/service.cpp | 62 ++++++ sdm710.mk | 4 + sepolicy/private/file.te | 2 + sepolicy/private/file_contexts | 3 + sepolicy/private/hal_light_sdm710.te | 20 ++ 10 files changed, 397 insertions(+) create mode 100644 Android.bp create mode 100644 light/Android.bp create mode 100644 light/Light.cpp create mode 100644 light/Light.h create mode 100644 light/android.hardware.light@2.0-service.realme_sdm710.rc create mode 100644 light/service.cpp create mode 100644 sepolicy/private/hal_light_sdm710.te diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..71a09a3 --- /dev/null +++ b/Android.bp @@ -0,0 +1,3 @@ +subdirs = [ + "light" +] diff --git a/light/Android.bp b/light/Android.bp new file mode 100644 index 0000000..52fab9f --- /dev/null +++ b/light/Android.bp @@ -0,0 +1,31 @@ +// Copyright (C) 2018 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. + +cc_binary { + relative_install_path: "hw", + defaults: ["hidl_defaults"], + name: "android.hardware.light@2.0-service.realme_sdm710", + init_rc: ["android.hardware.light@2.0-service.realme_sdm710.rc"], + srcs: ["service.cpp", "Light.cpp"], + shared_libs: [ + "android.hardware.light@2.0", + "libbase", + "libhardware", + "libhidlbase", + "libhidltransport", + "libhwbinder", + "liblog", + "libutils", + ], +} diff --git a/light/Light.cpp b/light/Light.cpp new file mode 100644 index 0000000..f7334a4 --- /dev/null +++ b/light/Light.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2018 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. + */ + +#define LOG_TAG "LightsService" + +#include "Light.h" + +#include +#include +#include + +namespace android { +namespace hardware { +namespace light { +namespace V2_0 { +namespace implementation { + +/* + * Write value to path and close file. + */ +template +static void set(const std::string& path, const T& value) { + std::ofstream file(path); + file << value; +} + +/* + * Read from path and close file. + * Return def in case of any failure. + */ +template +static T get(const std::string& path, const T& def) { + std::ifstream file(path); + T result; + + file >> result; + return file.fail() ? def : result; +} + +static constexpr int kDefaultMaxBrightness = 255; +static constexpr int kRampSteps = 50; +static constexpr int kRampMaxStepDurationMs = 5; + +static uint32_t getBrightness(const LightState& state) { + uint32_t alpha, red, green, blue; + + // Extract brightness from AARRGGBB + alpha = (state.color >> 24) & 0xff; + + // Retrieve each of the RGB colors + red = (state.color >> 16) & 0xff; + green = (state.color >> 8) & 0xff; + blue = state.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; +} + +static uint32_t rgbToBrightness(const LightState& state) { + uint32_t color = state.color & 0x00ffffff; + return ((77 * ((color >> 16) & 0xff)) + + (150 * ((color >> 8) & 0xff)) + + (29 * (color & 0xff))) >> 8; +} + +Light::Light() { + mLights.emplace(Type::ATTENTION, std::bind(&Light::handleNotification, this, std::placeholders::_1, 0)); + mLights.emplace(Type::BACKLIGHT, std::bind(&Light::handleBacklight, this, std::placeholders::_1)); + mLights.emplace(Type::BATTERY, std::bind(&Light::handleNotification, this, std::placeholders::_1, 1)); + mLights.emplace(Type::NOTIFICATIONS, std::bind(&Light::handleNotification, this, std::placeholders::_1, 2)); +} + +void Light::handleBacklight(const LightState& state) { + int maxBrightness = get("/sys/class/backlight/panel0-backlight/max_brightness", -1); + if (maxBrightness < 0) { + maxBrightness = kDefaultMaxBrightness; + } + uint32_t sentBrightness = rgbToBrightness(state); + uint32_t brightness = sentBrightness * maxBrightness / kDefaultMaxBrightness; + LOG(DEBUG) << "Writing backlight brightness " << brightness + << " (orig " << sentBrightness << ")"; + set("/sys/class/backlight/panel0-backlight/brightness", brightness); +} + +void Light::handleNotification(const LightState& state, size_t index) { + mLightStates.at(index) = state; + + LightState stateToUse = mLightStates.front(); + for (const auto& lightState : mLightStates) { + if (lightState.color & 0xffffff) { + stateToUse = lightState; + break; + } + } + + uint32_t whiteBrightness = getBrightness(stateToUse); + + uint32_t onMs = stateToUse.flashMode == Flash::TIMED ? stateToUse.flashOnMs : 0; + uint32_t offMs = stateToUse.flashMode == Flash::TIMED ? stateToUse.flashOffMs : 0; + + auto getScaledDutyPercent = [](int brightness) -> std::string { + std::string output; + for (int i = 0; i <= kRampSteps; i++) { + if (i != 0) { + output += ","; + } + output += std::to_string(i * 100 * brightness / (kDefaultMaxBrightness * kRampSteps)); + } + return output; + }; + + // Disable blinking to start + set("/sys/class/leds/white/blink", 0); + + if (onMs > 0 && offMs > 0) { + uint32_t pauseLo, pauseHi, stepDuration; + if (kRampMaxStepDurationMs * kRampSteps > onMs) { + stepDuration = onMs / kRampSteps; + pauseHi = 0; + } else { + stepDuration = kRampMaxStepDurationMs; + pauseHi = onMs - kRampSteps * stepDuration; + pauseLo = offMs - kRampSteps * stepDuration; + } + + set("/sys/class/leds/white/start_idx", 0); + set("/sys/class/leds/white/duty_pcts", getScaledDutyPercent(whiteBrightness)); + set("/sys/class/leds/white/pause_lo", pauseLo); + set("/sys/class/leds/white/pause_hi", pauseHi); + set("/sys/class/leds/white/ramp_step_ms", stepDuration); + + // Start blinking + set("/sys/class/leds/white/blink", 1); + } else { + set("/sys/class/leds/white/brightness", whiteBrightness); + } +} + +Return Light::setLight(Type type, const LightState& state) { + auto it = mLights.find(type); + + if (it == mLights.end()) { + return Status::LIGHT_NOT_SUPPORTED; + } + + // Lock global mutex until light state is updated. + std::lock_guard lock(mLock); + + it->second(state); + + return Status::SUCCESS; +} + +Return Light::getSupportedTypes(getSupportedTypes_cb _hidl_cb) { + std::vector types; + + for (auto const& light : mLights) { + types.push_back(light.first); + } + + _hidl_cb(types); + + return Void(); +} + +} // namespace implementation +} // namespace V2_0 +} // namespace light +} // namespace hardware +} // namespace android diff --git a/light/Light.h b/light/Light.h new file mode 100644 index 0000000..4e1d3df --- /dev/null +++ b/light/Light.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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. + */ + +#ifndef ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H +#define ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H + +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace light { +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(); + + Return setLight(Type type, const LightState& state) override; + Return getSupportedTypes(getSupportedTypes_cb _hidl_cb) override; + + private: + void handleBacklight(const LightState& state); + void handleNotification(const LightState& state, size_t index); + + std::mutex mLock; + std::unordered_map> mLights; + std::array mLightStates; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace light +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H diff --git a/light/android.hardware.light@2.0-service.realme_sdm710.rc b/light/android.hardware.light@2.0-service.realme_sdm710.rc new file mode 100644 index 0000000..338ad8e --- /dev/null +++ b/light/android.hardware.light@2.0-service.realme_sdm710.rc @@ -0,0 +1,21 @@ +on boot + chown system system /sys/class/leds/white/brightness + + chown system system /sys/class/leds/white/blink + chown system system /sys/class/leds/white/duty_pcts + 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/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 + +service light-hal-2-0 /system/bin/hw/android.hardware.light@2.0-service.realme_sdm710 + class hal + user system + group system diff --git a/light/service.cpp b/light/service.cpp new file mode 100644 index 0000000..828c8f4 --- /dev/null +++ b/light/service.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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. + */ + +#define LOG_TAG "android.hardware.light@2.0-service.realme_sdm710" + +#include +#include + +#include "Light.h" + +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() { + status_t status; + sp service = nullptr; + + LOG(INFO) << "Light HAL service 2.0 is starting."; + + service = new Light(); + if (service == nullptr) { + LOG(ERROR) << "Can not create an instance of Light HAL Iface, exiting."; + goto shutdown; + } + + configureRpcThreadpool(1, true /*callerWillJoin*/); + + status = service->registerAsService(); + if (status != OK) { + LOG(ERROR) << "Could not register service for Light HAL Iface (" << status << ")"; + goto shutdown; + } + + LOG(INFO) << "Light HAL service is ready."; + joinRpcThreadpool(); + // Should not pass this line + +shutdown: + // In normal operation, we don't expect the thread pool to exit + LOG(ERROR) << "Light HAL service is shutting down."; + return 1; +} diff --git a/sdm710.mk b/sdm710.mk index 7bccd7a..051971a 100644 --- a/sdm710.mk +++ b/sdm710.mk @@ -39,6 +39,10 @@ PRODUCT_PACKAGES += \ PRODUCT_PACKAGES += \ ims-ext-common +# Lights +PRODUCT_PACKAGES += \ + android.hardware.light@2.0-service.realme_sdm710 + # Net PRODUCT_PACKAGES += \ netutils-wrapper-1.0 diff --git a/sepolicy/private/file.te b/sepolicy/private/file.te index d74de02..6e890db 100644 --- a/sepolicy/private/file.te +++ b/sepolicy/private/file.te @@ -2,3 +2,5 @@ type adsprpcd_file, file_type; type bt_firmware_file, file_type; type firmware_file, file_type; type persist_file, file_type; +type sysfs_white_led, sysfs_type, fs_type; + diff --git a/sepolicy/private/file_contexts b/sepolicy/private/file_contexts index d8ede0c..57764e0 100644 --- a/sepolicy/private/file_contexts +++ b/sepolicy/private/file_contexts @@ -1,3 +1,6 @@ +# Custom HALs +/system/bin/hw/android\.hardware\.light@2\.0-service\.realme_sdm710 u:object_r:hal_light_sdm710_exec:s0 + # Files in rootfs /bt_firmware(/.*)? u:object_r:bt_firmware_file:s0 /dsp(/.*)? u:object_r:adsprpcd_file:s0 diff --git a/sepolicy/private/hal_light_sdm710.te b/sepolicy/private/hal_light_sdm710.te new file mode 100644 index 0000000..dfcf946 --- /dev/null +++ b/sepolicy/private/hal_light_sdm710.te @@ -0,0 +1,20 @@ +type hal_light_sdm710, coredomain, domain; + +# Allow a base set of permissions required for a domain to offer a +# HAL implementation of the specified type over HwBinder. +typeattribute hal_light_sdm710 halserverdomain; +typeattribute hal_light_sdm710 hal_light_server; + +# HwBinder IPC from client to server, and callbacks +binder_call(hal_light_client, hal_light_server) +binder_call(hal_light_server, hal_light_client) + +add_hwservice(hal_light_server, hal_light_hwservice) +allow hal_light_client hal_light_hwservice:hwservice_manager find; + +type hal_light_sdm710_exec, exec_type, file_type; +init_daemon_domain(hal_light_sdm710) + +allow hal_light_sdm710 { sysfs_graphics sysfs_white_led }:lnk_file read; +allow hal_light_sdm710 { sysfs_graphics sysfs_white_led }:file rw_file_perms; +allow hal_light_sdm710 { sysfs_graphics sysfs_leds sysfs_white_led }:dir r_dir_perms;