From 6278bdf3490a2f6682f5a9c47350b3d9f92a9165 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 16 Jan 2017 17:38:18 -0800 Subject: [PATCH] recovery: Add screensaver mode. While it's waiting for user input, dim or turn off the backlight to avoid OLED burn-in. The backlight brightness will be reduced after the first timeout (default 120s), and then turned off after the second. Pressing any key will take it back to the normal brightness. While the display is off, the first key input will only turn on the backlight. The most common case that triggers the screensaver is under text mode, such as waiting for menu selection or viewing recovery logs. This CL doesn't change the brightness while it's installing updates or performing wipes under UI mode. When it encounters any install error under UI mode (user builds): - If it's NOT USB connected, it will reboot automatically after the first timeout (same as before); - If it's USB connected, it will dim and turn off the display per the change in this CL. Bug: 34077703 Test: Boot a device with the new recovery image. Wait for timeout. Change-Id: I0c14907e60340a7f037adb6e464942d099ada08b --- ui.cpp | 131 ++++++++++++++++++++++++++++++++++++++++++++++----------- ui.h | 15 +++++++ 2 files changed, 121 insertions(+), 25 deletions(-) diff --git a/ui.cpp b/ui.cpp index a0f741e5..5efdc5a4 100644 --- a/ui.cpp +++ b/ui.cpp @@ -32,7 +32,11 @@ #include +#include +#include +#include #include +#include #include #include @@ -40,11 +44,15 @@ #include "roots.h" #include "device.h" -#define UI_WAIT_KEY_TIMEOUT_SEC 120 +static constexpr int UI_WAIT_KEY_TIMEOUT_SEC = 120; +static constexpr const char* BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/brightness"; +static constexpr const char* MAX_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/max_brightness"; RecoveryUI::RecoveryUI() : locale_(""), rtl_locale_(false), + brightness_normal_(50), + brightness_dimmed_(25), key_queue_len(0), key_last_down(-1), key_long_press(false), @@ -54,7 +62,8 @@ RecoveryUI::RecoveryUI() last_key(-1), has_power_key(false), has_up_key(false), - has_down_key(false) { + has_down_key(false), + screensaver_state_(ScreensaverState::DISABLED) { pthread_mutex_init(&key_queue_mutex, nullptr); pthread_cond_init(&key_queue_cond, nullptr); memset(key_pressed, 0, sizeof(key_pressed)); @@ -80,6 +89,40 @@ static void* InputThreadLoop(void*) { return nullptr; } +bool RecoveryUI::InitScreensaver() { + // Disabled. + if (brightness_normal_ == 0 || brightness_dimmed_ > brightness_normal_) { + return false; + } + + // Set the initial brightness level based on the max brightness. Note that reading the initial + // value from BRIGHTNESS_FILE doesn't give the actual brightness value (bullhead, sailfish), so + // we don't have a good way to query the default value. + std::string content; + if (!android::base::ReadFileToString(MAX_BRIGHTNESS_FILE, &content)) { + LOG(WARNING) << "Failed to read max brightness: " << content; + return false; + } + + unsigned int max_value; + if (!android::base::ParseUint(android::base::Trim(content), &max_value)) { + LOG(WARNING) << "Failed to parse max brightness: " << content; + return false; + } + + brightness_normal_value_ = max_value * brightness_normal_ / 100.0; + brightness_dimmed_value_ = max_value * brightness_dimmed_ / 100.0; + if (!android::base::WriteStringToFile(std::to_string(brightness_normal_value_), + BRIGHTNESS_FILE)) { + PLOG(WARNING) << "Failed to set brightness"; + return false; + } + + LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_ << "%)"; + screensaver_state_ = ScreensaverState::NORMAL; + return true; +} + bool RecoveryUI::Init(const std::string& locale) { // Set up the locale info. SetLocale(locale); @@ -88,6 +131,10 @@ bool RecoveryUI::Init(const std::string& locale) { ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + if (!InitScreensaver()) { + LOG(INFO) << "Screensaver disabled"; + } + pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr); return true; } @@ -220,31 +267,65 @@ void RecoveryUI::EnqueueKey(int key_code) { } int RecoveryUI::WaitKey() { - pthread_mutex_lock(&key_queue_mutex); + pthread_mutex_lock(&key_queue_mutex); - // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is - // plugged in. - do { - struct timeval now; - struct timespec timeout; - gettimeofday(&now, nullptr); - timeout.tv_sec = now.tv_sec; - timeout.tv_nsec = now.tv_usec * 1000; - timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC; + // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is + // plugged in. + do { + struct timeval now; + struct timespec timeout; + gettimeofday(&now, nullptr); + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = now.tv_usec * 1000; + timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC; - int rc = 0; - while (key_queue_len == 0 && rc != ETIMEDOUT) { - rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout); - } - } while (IsUsbConnected() && key_queue_len == 0); - - int key = -1; - if (key_queue_len > 0) { - key = key_queue[0]; - memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); + int rc = 0; + while (key_queue_len == 0 && rc != ETIMEDOUT) { + rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout); } - pthread_mutex_unlock(&key_queue_mutex); - return key; + + if (screensaver_state_ != ScreensaverState::DISABLED) { + if (rc == ETIMEDOUT) { + // Lower the brightness level: NORMAL -> DIMMED; DIMMED -> OFF. + if (screensaver_state_ == ScreensaverState::NORMAL) { + if (android::base::WriteStringToFile(std::to_string(brightness_dimmed_value_), + BRIGHTNESS_FILE)) { + LOG(INFO) << "Brightness: " << brightness_dimmed_value_ << " (" << brightness_dimmed_ + << "%)"; + screensaver_state_ = ScreensaverState::DIMMED; + } + } else if (screensaver_state_ == ScreensaverState::DIMMED) { + if (android::base::WriteStringToFile("0", BRIGHTNESS_FILE)) { + LOG(INFO) << "Brightness: 0 (off)"; + screensaver_state_ = ScreensaverState::OFF; + } + } + } else if (screensaver_state_ != ScreensaverState::NORMAL) { + // Drop the first key if it's changing from OFF to NORMAL. + if (screensaver_state_ == ScreensaverState::OFF) { + if (key_queue_len > 0) { + memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); + } + } + + // Reset the brightness to normal. + if (android::base::WriteStringToFile(std::to_string(brightness_normal_value_), + BRIGHTNESS_FILE)) { + screensaver_state_ = ScreensaverState::NORMAL; + LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_ + << "%)"; + } + } + } + } while (IsUsbConnected() && key_queue_len == 0); + + int key = -1; + if (key_queue_len > 0) { + key = key_queue[0]; + memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); + } + pthread_mutex_unlock(&key_queue_mutex); + return key; } bool RecoveryUI::IsUsbConnected() { @@ -330,7 +411,7 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { } last_key = key; - return IsTextVisible() ? ENQUEUE : IGNORE; + return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE; } void RecoveryUI::KeyLongPress(int) { diff --git a/ui.h b/ui.h index 53ce0602..823eb657 100644 --- a/ui.h +++ b/ui.h @@ -130,6 +130,13 @@ class RecoveryUI { std::string locale_; bool rtl_locale_; + // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% + // of the max_brightness). Because the absolute values may vary across devices. These two + // values can be configured via subclassing. Setting brightness_normal_ to 0 to disable + // screensaver. + unsigned int brightness_normal_; + unsigned int brightness_dimmed_; + private: // Key event input queue pthread_mutex_t key_queue_mutex; @@ -167,6 +174,14 @@ class RecoveryUI { void time_key(int key_code, int count); void SetLocale(const std::string&); + + enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF }; + ScreensaverState screensaver_state_; + // The following two contain the absolute values computed from brightness_normal_ and + // brightness_dimmed_ respectively. + unsigned int brightness_normal_value_; + unsigned int brightness_dimmed_value_; + bool InitScreensaver(); }; #endif // RECOVERY_UI_H