From b5108c372c8b92671ea5ebb4eeff00757fcee187 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 20 Aug 2018 13:40:47 -0700 Subject: Move librecovery_ui to a sub-directory This helps to expose librecovery_ui for device specific RecoveryUi. Bug: 76436783 Test: mma, unit tests pass Change-Id: Ic6c3d301d5833e4a592e6ea9d9d059bc4e4919be --- ui.cpp | 599 ----------------------------------------------------------------- 1 file changed, 599 deletions(-) delete mode 100644 ui.cpp (limited to 'ui.cpp') diff --git a/ui.cpp b/ui.cpp deleted file mode 100644 index c12a11b36..000000000 --- a/ui.cpp +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source 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. - */ - -#include "ui.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "minui/minui.h" -#include "otautil/sysutil.h" -#include "roots.h" - -using namespace std::chrono_literals; - -constexpr int UI_WAIT_KEY_TIMEOUT_SEC = 120; -constexpr const char* BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/brightness"; -constexpr const char* MAX_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/max_brightness"; -constexpr const char* BRIGHTNESS_FILE_SDM = "/sys/class/backlight/panel0-backlight/brightness"; -constexpr const char* MAX_BRIGHTNESS_FILE_SDM = - "/sys/class/backlight/panel0-backlight/max_brightness"; - -constexpr int kDefaultTouchLowThreshold = 50; -constexpr int kDefaultTouchHighThreshold = 90; - -RecoveryUI::RecoveryUI() - : brightness_normal_(50), - brightness_dimmed_(25), - brightness_file_(BRIGHTNESS_FILE), - max_brightness_file_(MAX_BRIGHTNESS_FILE), - touch_screen_allowed_(false), - fastbootd_logo_enabled_(false), - touch_low_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_low_threshold", - kDefaultTouchLowThreshold)), - touch_high_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_high_threshold", - kDefaultTouchHighThreshold)), - key_interrupted_(false), - key_queue_len(0), - key_last_down(-1), - key_long_press(false), - key_down_count(0), - enable_reboot(true), - consecutive_power_keys(0), - last_key(-1), - has_power_key(false), - has_up_key(false), - has_down_key(false), - has_touch_screen(false), - touch_slot_(0), - is_bootreason_recovery_ui_(false), - screensaver_state_(ScreensaverState::DISABLED) { - memset(key_pressed, 0, sizeof(key_pressed)); -} - -RecoveryUI::~RecoveryUI() { - ev_exit(); - input_thread_stopped_ = true; - if (input_thread_.joinable()) { - input_thread_.join(); - } -} - -void RecoveryUI::OnKeyDetected(int key_code) { - if (key_code == KEY_POWER) { - has_power_key = true; - } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) { - has_down_key = true; - } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) { - has_up_key = true; - } else if (key_code == ABS_MT_POSITION_X || key_code == ABS_MT_POSITION_Y) { - has_touch_screen = true; - } -} - -bool RecoveryUI::InitScreensaver() { - // Disabled. - if (brightness_normal_ == 0 || brightness_dimmed_ > brightness_normal_) { - return false; - } - if (access(brightness_file_.c_str(), R_OK | W_OK)) { - brightness_file_ = BRIGHTNESS_FILE_SDM; - } - if (access(max_brightness_file_.c_str(), R_OK)) { - max_brightness_file_ = MAX_BRIGHTNESS_FILE_SDM; - } - // 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)) { - PLOG(WARNING) << "Failed to read max brightness"; - 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 */) { - ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2), - touch_screen_allowed_); - - ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); - - if (touch_screen_allowed_) { - ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); - - // Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of - // "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way - // to turn on text mode. It will only be set if the recovery boot is triggered from fastboot, or - // with 'adb reboot recovery'. Note that this applies to all build variants. Otherwise the text - // mode will be turned on automatically on debuggable builds, even without a swipe. - std::string cmdline; - if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) { - is_bootreason_recovery_ui_ = cmdline.find("bootreason=recovery_ui") != std::string::npos; - } else { - // Non-fatal, and won't affect Init() result. - PLOG(WARNING) << "Failed to read /proc/cmdline"; - } - } - - if (!InitScreensaver()) { - LOG(INFO) << "Screensaver disabled"; - } - - // Create a separate thread that handles input events. - input_thread_ = std::thread([this]() { - while (!this->input_thread_stopped_) { - if (!ev_wait(500)) { - ev_dispatch(); - } - } - }); - - return true; -} - -void RecoveryUI::OnTouchDetected(int dx, int dy) { - enum SwipeDirection { UP, DOWN, RIGHT, LEFT } direction; - - // We only consider a valid swipe if: - // - the delta along one axis is below touch_low_threshold_; - // - and the delta along the other axis is beyond touch_high_threshold_. - if (abs(dy) < touch_low_threshold_ && abs(dx) > touch_high_threshold_) { - direction = dx < 0 ? SwipeDirection::LEFT : SwipeDirection::RIGHT; - } else if (abs(dx) < touch_low_threshold_ && abs(dy) > touch_high_threshold_) { - direction = dy < 0 ? SwipeDirection::UP : SwipeDirection::DOWN; - } else { - LOG(DEBUG) << "Ignored " << dx << " " << dy << " (low: " << touch_low_threshold_ - << ", high: " << touch_high_threshold_ << ")"; - return; - } - - // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui. - if (is_bootreason_recovery_ui_ && !IsTextVisible()) { - ShowText(true); - return; - } - - LOG(DEBUG) << "Swipe direction=" << direction; - switch (direction) { - case SwipeDirection::UP: - ProcessKey(KEY_UP, 1); // press up key - ProcessKey(KEY_UP, 0); // and release it - break; - - case SwipeDirection::DOWN: - ProcessKey(KEY_DOWN, 1); // press down key - ProcessKey(KEY_DOWN, 0); // and release it - break; - - case SwipeDirection::LEFT: - case SwipeDirection::RIGHT: - ProcessKey(KEY_POWER, 1); // press power key - ProcessKey(KEY_POWER, 0); // and release it - break; - }; -} - -int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { - struct input_event ev; - if (ev_get_input(fd, epevents, &ev) == -1) { - return -1; - } - - // Touch inputs handling. - // - // We handle the touch inputs by tracking the position changes between initial contacting and - // upon lifting. touch_start_X/Y record the initial positions, with touch_finger_down set. Upon - // detecting the lift, we unset touch_finger_down and detect a swipe based on position changes. - // - // Per the doc Multi-touch Protocol at below, there are two protocols. - // https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt - // - // The main difference between the stateless type A protocol and the stateful type B slot protocol - // lies in the usage of identifiable contacts to reduce the amount of data sent to userspace. The - // slot protocol (i.e. type B) sends ABS_MT_TRACKING_ID with a unique id on initial contact, and - // sends ABS_MT_TRACKING_ID -1 upon lifting the contact. Protocol A doesn't send - // ABS_MT_TRACKING_ID -1 on lifting, but the driver may additionally report BTN_TOUCH event. - // - // For protocol A, we rely on BTN_TOUCH to recognize lifting, while for protocol B we look for - // ABS_MT_TRACKING_ID being -1. - // - // Touch input events will only be available if touch_screen_allowed_ is set. - - if (ev.type == EV_SYN) { - if (touch_screen_allowed_ && ev.code == SYN_REPORT) { - // There might be multiple SYN_REPORT events. We should only detect a swipe after lifting the - // contact. - if (touch_finger_down_ && !touch_swiping_) { - touch_start_X_ = touch_X_; - touch_start_Y_ = touch_Y_; - touch_swiping_ = true; - } else if (!touch_finger_down_ && touch_swiping_) { - touch_swiping_ = false; - OnTouchDetected(touch_X_ - touch_start_X_, touch_Y_ - touch_start_Y_); - } - } - return 0; - } - - if (ev.type == EV_REL) { - if (ev.code == REL_Y) { - // accumulate the up or down motion reported by - // the trackball. When it exceeds a threshold - // (positive or negative), fake an up/down - // key event. - rel_sum += ev.value; - if (rel_sum > 3) { - ProcessKey(KEY_DOWN, 1); // press down key - ProcessKey(KEY_DOWN, 0); // and release it - rel_sum = 0; - } else if (rel_sum < -3) { - ProcessKey(KEY_UP, 1); // press up key - ProcessKey(KEY_UP, 0); // and release it - rel_sum = 0; - } - } - } else { - rel_sum = 0; - } - - if (touch_screen_allowed_ && ev.type == EV_ABS) { - if (ev.code == ABS_MT_SLOT) { - touch_slot_ = ev.value; - } - // Ignore other fingers. - if (touch_slot_ > 0) return 0; - - switch (ev.code) { - case ABS_MT_POSITION_X: - touch_X_ = ev.value; - touch_finger_down_ = true; - break; - - case ABS_MT_POSITION_Y: - touch_Y_ = ev.value; - touch_finger_down_ = true; - break; - - case ABS_MT_TRACKING_ID: - // Protocol B: -1 marks lifting the contact. - if (ev.value < 0) touch_finger_down_ = false; - break; - } - return 0; - } - - if (ev.type == EV_KEY && ev.code <= KEY_MAX) { - if (touch_screen_allowed_) { - if (ev.code == BTN_TOUCH) { - // A BTN_TOUCH with value 1 indicates the start of contact (protocol A), with 0 means - // lifting the contact. - touch_finger_down_ = (ev.value == 1); - } - - // Intentionally ignore BTN_TOUCH and BTN_TOOL_FINGER, which would otherwise trigger - // additional scrolling (because in ScreenRecoveryUI::ShowFile(), we consider keys other than - // KEY_POWER and KEY_UP as KEY_DOWN). - if (ev.code == BTN_TOUCH || ev.code == BTN_TOOL_FINGER) { - return 0; - } - } - - ProcessKey(ev.code, ev.value); - } - - return 0; -} - -// Processes a key-up or -down event. A key is "registered" when it is pressed and then released, -// with no other keypresses or releases in between. Registered keys are passed to CheckKey() to -// see if it should trigger a visibility toggle, an immediate reboot, or be queued to be processed -// next time the foreground thread wants a key (eg, for the menu). -// -// We also keep track of which keys are currently down so that CheckKey() can call IsKeyPressed() -// to see what other keys are held when a key is registered. -// -// updown == 1 for key down events; 0 for key up events -void RecoveryUI::ProcessKey(int key_code, int updown) { - bool register_key = false; - bool long_press = false; - - { - std::lock_guard lg(key_queue_mutex); - key_pressed[key_code] = updown; - if (updown) { - ++key_down_count; - key_last_down = key_code; - key_long_press = false; - std::thread time_key_thread(&RecoveryUI::TimeKey, this, key_code, key_down_count); - time_key_thread.detach(); - } else { - if (key_last_down == key_code) { - long_press = key_long_press; - register_key = true; - } - key_last_down = -1; - } - } - - bool reboot_enabled = enable_reboot; - if (register_key) { - switch (CheckKey(key_code, long_press)) { - case RecoveryUI::IGNORE: - break; - - case RecoveryUI::TOGGLE: - ShowText(!IsTextVisible()); - break; - - case RecoveryUI::REBOOT: - if (reboot_enabled) { - reboot("reboot,"); - while (true) { - pause(); - } - } - break; - - case RecoveryUI::ENQUEUE: - EnqueueKey(key_code); - break; - } - } -} - -void RecoveryUI::TimeKey(int key_code, int count) { - std::this_thread::sleep_for(750ms); // 750 ms == "long" - bool long_press = false; - { - std::lock_guard lg(key_queue_mutex); - if (key_last_down == key_code && key_down_count == count) { - long_press = key_long_press = true; - } - } - if (long_press) KeyLongPress(key_code); -} - -void RecoveryUI::EnqueueKey(int key_code) { - std::lock_guard lg(key_queue_mutex); - const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); - if (key_queue_len < queue_max) { - key_queue[key_queue_len++] = key_code; - key_queue_cond.notify_one(); - } -} - -void RecoveryUI::SetScreensaverState(ScreensaverState state) { - switch (state) { - case ScreensaverState::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_ - << "%)"; - } else { - LOG(ERROR) << "Unable to set brightness to normal"; - } - break; - case ScreensaverState::DIMMED: - 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 { - LOG(ERROR) << "Unable to set brightness to dim"; - } - break; - case ScreensaverState::OFF: - if (android::base::WriteStringToFile("0", brightness_file_)) { - LOG(INFO) << "Brightness: 0 (off)"; - screensaver_state_ = ScreensaverState::OFF; - } else { - LOG(ERROR) << "Unable to set brightness to off"; - } - break; - default: - LOG(ERROR) << "Invalid screensaver state"; - } -} - -int RecoveryUI::WaitKey() { - std::unique_lock lk(key_queue_mutex); - - // Check for a saved key queue interruption. - if (key_interrupted_) { - SetScreensaverState(ScreensaverState::NORMAL); - return static_cast(KeyError::INTERRUPTED); - } - - // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is plugged in. - do { - bool rc = key_queue_cond.wait_for(lk, std::chrono::seconds(UI_WAIT_KEY_TIMEOUT_SEC), [this] { - return this->key_queue_len != 0 || key_interrupted_; - }); - if (key_interrupted_) { - SetScreensaverState(ScreensaverState::NORMAL); - return static_cast(KeyError::INTERRUPTED); - } - if (screensaver_state_ != ScreensaverState::DISABLED) { - if (!rc) { - // Must be after a timeout. Lower the brightness level: NORMAL -> DIMMED; DIMMED -> OFF. - if (screensaver_state_ == ScreensaverState::NORMAL) { - SetScreensaverState(ScreensaverState::DIMMED); - } else if (screensaver_state_ == ScreensaverState::DIMMED) { - SetScreensaverState(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. - SetScreensaverState(ScreensaverState::NORMAL); - } - } - } while (IsUsbConnected() && key_queue_len == 0); - - int key = static_cast(KeyError::TIMED_OUT); - if (key_queue_len > 0) { - key = key_queue[0]; - memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); - } - return key; -} - -void RecoveryUI::InterruptKey() { - { - std::lock_guard lg(key_queue_mutex); - key_interrupted_ = true; - } - key_queue_cond.notify_one(); -} - -bool RecoveryUI::IsUsbConnected() { - int fd = open("/sys/class/android_usb/android0/state", O_RDONLY); - if (fd < 0) { - printf("failed to open /sys/class/android_usb/android0/state: %s\n", strerror(errno)); - return 0; - } - - char buf; - // USB is connected if android_usb state is CONNECTED or CONFIGURED. - int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C'); - if (close(fd) < 0) { - printf("failed to close /sys/class/android_usb/android0/state: %s\n", strerror(errno)); - } - return connected; -} - -bool RecoveryUI::IsKeyPressed(int key) { - std::lock_guard lg(key_queue_mutex); - int pressed = key_pressed[key]; - return pressed; -} - -bool RecoveryUI::IsLongPress() { - std::lock_guard lg(key_queue_mutex); - bool result = key_long_press; - return result; -} - -bool RecoveryUI::HasThreeButtons() { - return has_power_key && has_up_key && has_down_key; -} - -bool RecoveryUI::HasPowerKey() const { - return has_power_key; -} - -bool RecoveryUI::HasTouchScreen() const { - return has_touch_screen; -} - -void RecoveryUI::FlushKeys() { - std::lock_guard lg(key_queue_mutex); - key_queue_len = 0; -} - -RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { - { - std::lock_guard lg(key_queue_mutex); - key_long_press = false; - } - - // If we have power and volume up keys, that chord is the signal to toggle the text display. - if (HasThreeButtons() || (HasPowerKey() && HasTouchScreen() && touch_screen_allowed_)) { - if ((key == KEY_VOLUMEUP || key == KEY_UP) && IsKeyPressed(KEY_POWER)) { - return TOGGLE; - } - } else { - // Otherwise long press of any button toggles to the text display, - // and there's no way to toggle back (but that's pretty useless anyway). - if (is_long_press && !IsTextVisible()) { - return TOGGLE; - } - - // Also, for button-limited devices, a long press is translated to KEY_ENTER. - if (is_long_press && IsTextVisible()) { - EnqueueKey(KEY_ENTER); - return IGNORE; - } - } - - // Press power seven times in a row to reboot. - if (key == KEY_POWER) { - bool reboot_enabled = enable_reboot; - - if (reboot_enabled) { - ++consecutive_power_keys; - if (consecutive_power_keys >= 7) { - return REBOOT; - } - } - } else { - consecutive_power_keys = 0; - } - - last_key = key; - return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE; -} - -void RecoveryUI::KeyLongPress(int) { -} - -void RecoveryUI::SetEnableReboot(bool enabled) { - std::lock_guard lg(key_queue_mutex); - enable_reboot = enabled; -} -- cgit v1.2.3