From af9f8b4d97af56b5abcb622aaecebce59179bc0b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 28 Jul 2017 00:05:40 -0700 Subject: ui: Move the support for touch inputs into RecoveryUI. - Added detection for EV_ABS events in minui/events.cpp, if it's allowed; - Added listening and processing touch inputs in ui.cpp; - Fixed an issue in recognizing swipe with multi-touch protocol A; - Changed the logic in RecoveryUI::ProcessKey() to be swipe-aware. It now allows turning on text mode with + . The last change also fixed an issue on devices with protocol A: prior to this CL, user may accidentally toggle the text mode during an OTA. Because it was considered as a single-button device, a long tap that sent BTN_TOUCH event would turn on text mode. Test: Allow detecting touch inputs. Swiping (up, down, enter) works on angler, angelfish, dorado respectively. Bug: 36169090 Bug: 64307776 Change-Id: I4bc882b99114ce4ab414f8bdb8f4f7a525b8a8fd (cherry picked from commit 5f8dd9951d986b65d98d6a9ea38003427e9e46df) --- Android.mk | 12 ++++ minui/events.cpp | 47 +++++++++++---- minui/include/minui/minui.h | 3 +- ui.cpp | 139 ++++++++++++++++++++++++++++++++++++++++++-- ui.h | 24 ++++++++ 5 files changed, 209 insertions(+), 16 deletions(-) diff --git a/Android.mk b/Android.mk index 3eed7a696..967b9dfbe 100644 --- a/Android.mk +++ b/Android.mk @@ -108,6 +108,18 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 endif +ifneq ($(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD),) +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_LOW_THRESHOLD=$(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD) +else +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_LOW_THRESHOLD=50 +endif + +ifneq ($(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD),) +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=$(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD) +else +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=90 +endif + ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) else diff --git a/minui/events.cpp b/minui/events.cpp index 0e1fd44a0..24c2a8277 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -53,36 +53,37 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; } -int ev_init(ev_callback input_cb) { - bool epollctlfail = false; - +int ev_init(ev_callback input_cb, bool allow_touch_inputs) { g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS); if (g_epoll_fd == -1) { return -1; } + bool epollctlfail = false; DIR* dir = opendir("/dev/input"); - if (dir != NULL) { + if (dir != nullptr) { dirent* de; while ((de = readdir(dir))) { - // Use unsigned long to match ioctl's parameter type. - unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT - - // fprintf(stderr,"/dev/input/%s\n", de->d_name); if (strncmp(de->d_name, "event", 5)) continue; int fd = openat(dirfd(dir), de->d_name, O_RDONLY); if (fd == -1) continue; + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT + // Read the evbits of the input device. if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { close(fd); continue; } - // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. + // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also + // allowed if allow_touch_inputs is set. if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { - close(fd); - continue; + if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { + close(fd); + continue; + } } epoll_event ev; @@ -231,3 +232,27 @@ void ev_iterate_available_keys(const std::function& f) { } } } + +void ev_iterate_touch_inputs(const std::function& action) { + for (size_t i = 0; i < ev_dev_count; ++i) { + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {}; // NOLINT + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + continue; + } + if (!test_bit(EV_ABS, ev_bits)) { + continue; + } + + unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)] = {}; // NOLINT + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_ABS, KEY_MAX), key_bits) == -1) { + continue; + } + + for (int key_code = 0; key_code <= KEY_MAX; ++key_code) { + if (test_bit(key_code, key_bits)) { + action(key_code); + } + } + } +} diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index 78dd4cb98..017ddde75 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -74,10 +74,11 @@ struct input_event; using ev_callback = std::function; using ev_set_key_callback = std::function; -int ev_init(ev_callback input_cb); +int ev_init(ev_callback input_cb, bool allow_touch_inputs = false); void ev_exit(); int ev_add_fd(int fd, ev_callback cb); void ev_iterate_available_keys(const std::function& f); +void ev_iterate_touch_inputs(const std::function& action); int ev_sync_key_state(const ev_set_key_callback& set_key_cb); // 'timeout' has the same semantics as poll(2). diff --git a/ui.cpp b/ui.cpp index 30b42a19a..eadcdd433 100644 --- a/ui.cpp +++ b/ui.cpp @@ -54,6 +54,9 @@ RecoveryUI::RecoveryUI() rtl_locale_(false), brightness_normal_(50), brightness_dimmed_(25), + touch_screen_allowed_(false), + kTouchLowThreshold(RECOVERY_UI_TOUCH_LOW_THRESHOLD), + kTouchHighThreshold(RECOVERY_UI_TOUCH_HIGH_THRESHOLD), key_queue_len(0), key_last_down(-1), key_long_press(false), @@ -64,6 +67,8 @@ RecoveryUI::RecoveryUI() has_power_key(false), has_up_key(false), has_down_key(false), + has_touch_screen(false), + touch_slot_(0), screensaver_state_(ScreensaverState::DISABLED) { pthread_mutex_init(&key_queue_mutex, nullptr); pthread_cond_init(&key_queue_cond, nullptr); @@ -77,6 +82,8 @@ void RecoveryUI::OnKeyDetected(int key_code) { 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; } } @@ -128,10 +135,15 @@ bool RecoveryUI::Init(const std::string& locale) { // Set up the locale info. SetLocale(locale); - ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2)); + 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)); + } + if (!InitScreensaver()) { LOG(INFO) << "Screensaver disabled"; } @@ -140,15 +152,85 @@ bool RecoveryUI::Init(const std::string& locale) { 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 kTouchLowThreshold; + // - and the delta along the other axis is beyond kTouchHighThreshold. + if (abs(dy) < kTouchLowThreshold && abs(dx) > kTouchHighThreshold) { + direction = dx < 0 ? SwipeDirection::LEFT : SwipeDirection::RIGHT; + } else if (abs(dx) < kTouchLowThreshold && abs(dy) > kTouchHighThreshold) { + direction = dy < 0 ? SwipeDirection::UP : SwipeDirection::DOWN; + } else { + LOG(DEBUG) << "Ignored " << dx << " " << dy << " (low: " << kTouchLowThreshold + << ", high: " << kTouchHighThreshold << ")"; + 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; - } else if (ev.type == EV_REL) { + } + + 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 @@ -169,7 +251,48 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { 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); } @@ -365,6 +488,14 @@ 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() { pthread_mutex_lock(&key_queue_mutex); key_queue_len = 0; @@ -377,8 +508,8 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { pthread_mutex_unlock(&key_queue_mutex); // If we have power and volume up keys, that chord is the signal to toggle the text display. - if (HasThreeButtons()) { - if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) { + if (HasThreeButtons() || (HasPowerKey() && HasTouchScreen() && touch_screen_allowed_)) { + if ((key == KEY_VOLUMEUP || key == KEY_UP) && IsKeyPressed(KEY_POWER)) { return TOGGLE; } } else { diff --git a/ui.h b/ui.h index 7eb04aec8..5cda7af0f 100644 --- a/ui.h +++ b/ui.h @@ -82,6 +82,12 @@ class RecoveryUI { // otherwise. virtual bool HasThreeButtons(); + // Returns true if it has a power key. + virtual bool HasPowerKey() const; + + // Returns true if it supports touch inputs. + virtual bool HasTouchScreen() const; + // Erases any queued-up keys. virtual void FlushKeys(); @@ -129,7 +135,14 @@ class RecoveryUI { unsigned int brightness_normal_; unsigned int brightness_dimmed_; + // Whether we should listen for touch inputs (default: false). + bool touch_screen_allowed_; + private: + // The sensitivity when detecting a swipe. + const int kTouchLowThreshold; + const int kTouchHighThreshold; + // Key event input queue pthread_mutex_t key_queue_mutex; pthread_cond_t key_queue_cond; @@ -147,6 +160,16 @@ class RecoveryUI { bool has_power_key; bool has_up_key; bool has_down_key; + bool has_touch_screen; + + // Touch event related variables. See the comments in RecoveryUI::OnInputEvent(). + int touch_slot_; + int touch_X_; + int touch_Y_; + int touch_start_X_; + int touch_start_Y_; + bool touch_finger_down_; + bool touch_swiping_; struct key_timer_t { RecoveryUI* ui; @@ -157,6 +180,7 @@ class RecoveryUI { pthread_t input_thread_; void OnKeyDetected(int key_code); + void OnTouchDetected(int dx, int dy); int OnInputEvent(int fd, uint32_t epevents); void ProcessKey(int key_code, int updown); -- cgit v1.2.3 From 937e884ca113a5e775a3cb37a3f9dbe2174d3376 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 31 Jul 2017 23:15:09 -0700 Subject: ui: Check for bootreason=recovery_ui. Some wear bootloaders are passing bootreason=recovery_ui when booting into recovery from fastboot, or via 'adb reboot recovery'. Allow turning on text mode with a swipe for such a bootreason. Since we will turn on text mode automatically for debuggable builds, this bootreason mainly handles the case for user builds. Note this change only applies to devices that allow touch screen inputs. Bug: 36169090 Bug: 64307776 Test: Build and boot into user build recovery image. Toggle on text mode with a swipe. Change-Id: I55f19aed7b210352f8370de19935b4772cc12095 (cherry picked from commit 046aae29d9b0d2cdf24ad0567146991c3864c140) --- ui.cpp | 20 ++++++++++++++++++++ ui.h | 1 + 2 files changed, 21 insertions(+) diff --git a/ui.cpp b/ui.cpp index eadcdd433..e80d7ed04 100644 --- a/ui.cpp +++ b/ui.cpp @@ -69,6 +69,7 @@ RecoveryUI::RecoveryUI() has_down_key(false), has_touch_screen(false), touch_slot_(0), + is_bootreason_recovery_ui_(false), screensaver_state_(ScreensaverState::DISABLED) { pthread_mutex_init(&key_queue_mutex, nullptr); pthread_cond_init(&key_queue_cond, nullptr); @@ -142,6 +143,19 @@ bool RecoveryUI::Init(const std::string& locale) { 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()) { @@ -168,6 +182,12 @@ void RecoveryUI::OnTouchDetected(int dx, int dy) { 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: diff --git a/ui.h b/ui.h index 5cda7af0f..3d9afece0 100644 --- a/ui.h +++ b/ui.h @@ -170,6 +170,7 @@ class RecoveryUI { int touch_start_Y_; bool touch_finger_down_; bool touch_swiping_; + bool is_bootreason_recovery_ui_; struct key_timer_t { RecoveryUI* ui; -- cgit v1.2.3 From 5c2bc51bf26bbd88253c464340d9191ce247961b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 5 Aug 2017 18:24:07 -0700 Subject: Import translations. DO NOT MERGE Change-Id: Ia215c30c6912ffa1e3270543f6ff4fc27f4260e4 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- tools/recovery_l10n/res/values-en-rXC/strings.xml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tools/recovery_l10n/res/values-en-rXC/strings.xml diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml new file mode 100644 index 000000000..2d528b3fb --- /dev/null +++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml @@ -0,0 +1,9 @@ + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Installing system update‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎Erasing‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎" + -- cgit v1.2.3