1*e7c364b6SAndroid Build Coastguard Worker /*
2*e7c364b6SAndroid Build Coastguard Worker * Copyright (C) 2011 The Android Open Source Project
3*e7c364b6SAndroid Build Coastguard Worker *
4*e7c364b6SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*e7c364b6SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*e7c364b6SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*e7c364b6SAndroid Build Coastguard Worker *
8*e7c364b6SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*e7c364b6SAndroid Build Coastguard Worker *
10*e7c364b6SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*e7c364b6SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*e7c364b6SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*e7c364b6SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*e7c364b6SAndroid Build Coastguard Worker * limitations under the License.
15*e7c364b6SAndroid Build Coastguard Worker */
16*e7c364b6SAndroid Build Coastguard Worker
17*e7c364b6SAndroid Build Coastguard Worker #include "recovery_ui/ui.h"
18*e7c364b6SAndroid Build Coastguard Worker
19*e7c364b6SAndroid Build Coastguard Worker #include <errno.h>
20*e7c364b6SAndroid Build Coastguard Worker #include <fcntl.h>
21*e7c364b6SAndroid Build Coastguard Worker #include <stdio.h>
22*e7c364b6SAndroid Build Coastguard Worker #include <stdlib.h>
23*e7c364b6SAndroid Build Coastguard Worker #include <string.h>
24*e7c364b6SAndroid Build Coastguard Worker #include <sys/time.h>
25*e7c364b6SAndroid Build Coastguard Worker #include <sys/types.h>
26*e7c364b6SAndroid Build Coastguard Worker #include <time.h>
27*e7c364b6SAndroid Build Coastguard Worker #include <unistd.h>
28*e7c364b6SAndroid Build Coastguard Worker
29*e7c364b6SAndroid Build Coastguard Worker #include <chrono>
30*e7c364b6SAndroid Build Coastguard Worker #include <functional>
31*e7c364b6SAndroid Build Coastguard Worker #include <string>
32*e7c364b6SAndroid Build Coastguard Worker #include <thread>
33*e7c364b6SAndroid Build Coastguard Worker
34*e7c364b6SAndroid Build Coastguard Worker #include <android-base/file.h>
35*e7c364b6SAndroid Build Coastguard Worker #include <android-base/logging.h>
36*e7c364b6SAndroid Build Coastguard Worker #include <android-base/parseint.h>
37*e7c364b6SAndroid Build Coastguard Worker #include <android-base/properties.h>
38*e7c364b6SAndroid Build Coastguard Worker #include <android-base/strings.h>
39*e7c364b6SAndroid Build Coastguard Worker
40*e7c364b6SAndroid Build Coastguard Worker #include "minui/minui.h"
41*e7c364b6SAndroid Build Coastguard Worker #include "otautil/sysutil.h"
42*e7c364b6SAndroid Build Coastguard Worker
43*e7c364b6SAndroid Build Coastguard Worker using namespace std::chrono_literals;
44*e7c364b6SAndroid Build Coastguard Worker
45*e7c364b6SAndroid Build Coastguard Worker constexpr int UI_WAIT_KEY_TIMEOUT_SEC = 120;
46*e7c364b6SAndroid Build Coastguard Worker constexpr const char* DEFAULT_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/brightness";
47*e7c364b6SAndroid Build Coastguard Worker constexpr const char* DEFAULT_MAX_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/max_brightness";
48*e7c364b6SAndroid Build Coastguard Worker constexpr const char* BRIGHTNESS_FILE_SDM = "/sys/class/backlight/panel0-backlight/brightness";
49*e7c364b6SAndroid Build Coastguard Worker constexpr const char* MAX_BRIGHTNESS_FILE_SDM =
50*e7c364b6SAndroid Build Coastguard Worker "/sys/class/backlight/panel0-backlight/max_brightness";
51*e7c364b6SAndroid Build Coastguard Worker constexpr const char* BRIGHTNESS_FILE_PWM = "/sys/class/backlight/pwm-backlight.0/brightness";
52*e7c364b6SAndroid Build Coastguard Worker constexpr const char* MAX_BRIGHTNESS_FILE_PWM =
53*e7c364b6SAndroid Build Coastguard Worker "/sys/class/backlight/pwm-backlight.0/max_brightness";
54*e7c364b6SAndroid Build Coastguard Worker
55*e7c364b6SAndroid Build Coastguard Worker constexpr int kDefaultTouchLowThreshold = 50;
56*e7c364b6SAndroid Build Coastguard Worker constexpr int kDefaultTouchHighThreshold = 90;
57*e7c364b6SAndroid Build Coastguard Worker
58*e7c364b6SAndroid Build Coastguard Worker constexpr int kDefaultNormalBrightnessPercent = 50;
59*e7c364b6SAndroid Build Coastguard Worker constexpr int kDefaultDimmedBrightnessPercent = 25;
60*e7c364b6SAndroid Build Coastguard Worker
RecoveryUI()61*e7c364b6SAndroid Build Coastguard Worker RecoveryUI::RecoveryUI()
62*e7c364b6SAndroid Build Coastguard Worker : brightness_normal_(android::base::GetIntProperty("ro.recovery.ui.brightness_normal_percent",
63*e7c364b6SAndroid Build Coastguard Worker kDefaultNormalBrightnessPercent)),
64*e7c364b6SAndroid Build Coastguard Worker brightness_dimmed_(android::base::GetIntProperty("ro.recovery.ui.brightness_dimmed_percent",
65*e7c364b6SAndroid Build Coastguard Worker kDefaultDimmedBrightnessPercent)),
66*e7c364b6SAndroid Build Coastguard Worker brightness_file_(
67*e7c364b6SAndroid Build Coastguard Worker android::base::GetProperty("ro.recovery.ui.brightness_file", DEFAULT_BRIGHTNESS_FILE)),
68*e7c364b6SAndroid Build Coastguard Worker max_brightness_file_(android::base::GetProperty("ro.recovery.ui.max_brightness_file",
69*e7c364b6SAndroid Build Coastguard Worker DEFAULT_MAX_BRIGHTNESS_FILE)),
70*e7c364b6SAndroid Build Coastguard Worker touch_screen_allowed_(false),
71*e7c364b6SAndroid Build Coastguard Worker fastbootd_logo_enabled_(false),
72*e7c364b6SAndroid Build Coastguard Worker touch_low_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_low_threshold",
73*e7c364b6SAndroid Build Coastguard Worker kDefaultTouchLowThreshold)),
74*e7c364b6SAndroid Build Coastguard Worker touch_high_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_high_threshold",
75*e7c364b6SAndroid Build Coastguard Worker kDefaultTouchHighThreshold)),
76*e7c364b6SAndroid Build Coastguard Worker key_interrupted_(false),
77*e7c364b6SAndroid Build Coastguard Worker key_queue_len(0),
78*e7c364b6SAndroid Build Coastguard Worker key_last_down(-1),
79*e7c364b6SAndroid Build Coastguard Worker key_long_press(false),
80*e7c364b6SAndroid Build Coastguard Worker key_down_count(0),
81*e7c364b6SAndroid Build Coastguard Worker enable_reboot(true),
82*e7c364b6SAndroid Build Coastguard Worker consecutive_power_keys(0),
83*e7c364b6SAndroid Build Coastguard Worker has_power_key(false),
84*e7c364b6SAndroid Build Coastguard Worker has_up_key(false),
85*e7c364b6SAndroid Build Coastguard Worker has_down_key(false),
86*e7c364b6SAndroid Build Coastguard Worker has_touch_screen(false),
87*e7c364b6SAndroid Build Coastguard Worker touch_slot_(0),
88*e7c364b6SAndroid Build Coastguard Worker is_bootreason_recovery_ui_(false),
89*e7c364b6SAndroid Build Coastguard Worker screensaver_state_(ScreensaverState::DISABLED) {
90*e7c364b6SAndroid Build Coastguard Worker memset(key_pressed, 0, sizeof(key_pressed));
91*e7c364b6SAndroid Build Coastguard Worker }
92*e7c364b6SAndroid Build Coastguard Worker
~RecoveryUI()93*e7c364b6SAndroid Build Coastguard Worker RecoveryUI::~RecoveryUI() {
94*e7c364b6SAndroid Build Coastguard Worker ev_exit();
95*e7c364b6SAndroid Build Coastguard Worker input_thread_stopped_ = true;
96*e7c364b6SAndroid Build Coastguard Worker if (input_thread_.joinable()) {
97*e7c364b6SAndroid Build Coastguard Worker input_thread_.join();
98*e7c364b6SAndroid Build Coastguard Worker }
99*e7c364b6SAndroid Build Coastguard Worker }
100*e7c364b6SAndroid Build Coastguard Worker
OnKeyDetected(int key_code)101*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::OnKeyDetected(int key_code) {
102*e7c364b6SAndroid Build Coastguard Worker if (key_code == KEY_POWER) {
103*e7c364b6SAndroid Build Coastguard Worker has_power_key = true;
104*e7c364b6SAndroid Build Coastguard Worker } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) {
105*e7c364b6SAndroid Build Coastguard Worker has_down_key = true;
106*e7c364b6SAndroid Build Coastguard Worker } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
107*e7c364b6SAndroid Build Coastguard Worker has_up_key = true;
108*e7c364b6SAndroid Build Coastguard Worker } else if (key_code == ABS_MT_POSITION_X || key_code == ABS_MT_POSITION_Y) {
109*e7c364b6SAndroid Build Coastguard Worker has_touch_screen = true;
110*e7c364b6SAndroid Build Coastguard Worker }
111*e7c364b6SAndroid Build Coastguard Worker }
112*e7c364b6SAndroid Build Coastguard Worker
InitScreensaver()113*e7c364b6SAndroid Build Coastguard Worker bool RecoveryUI::InitScreensaver() {
114*e7c364b6SAndroid Build Coastguard Worker // Disabled.
115*e7c364b6SAndroid Build Coastguard Worker if (brightness_normal_ == 0 || brightness_dimmed_ > brightness_normal_) {
116*e7c364b6SAndroid Build Coastguard Worker return false;
117*e7c364b6SAndroid Build Coastguard Worker }
118*e7c364b6SAndroid Build Coastguard Worker if (access(brightness_file_.c_str(), R_OK | W_OK)) {
119*e7c364b6SAndroid Build Coastguard Worker if (!access(BRIGHTNESS_FILE_SDM, R_OK | W_OK)) {
120*e7c364b6SAndroid Build Coastguard Worker brightness_file_ = BRIGHTNESS_FILE_SDM;
121*e7c364b6SAndroid Build Coastguard Worker } else {
122*e7c364b6SAndroid Build Coastguard Worker brightness_file_ = BRIGHTNESS_FILE_PWM;
123*e7c364b6SAndroid Build Coastguard Worker }
124*e7c364b6SAndroid Build Coastguard Worker }
125*e7c364b6SAndroid Build Coastguard Worker
126*e7c364b6SAndroid Build Coastguard Worker if (access(max_brightness_file_.c_str(), R_OK)) {
127*e7c364b6SAndroid Build Coastguard Worker if (!access(MAX_BRIGHTNESS_FILE_SDM, R_OK)) {
128*e7c364b6SAndroid Build Coastguard Worker max_brightness_file_ = MAX_BRIGHTNESS_FILE_SDM;
129*e7c364b6SAndroid Build Coastguard Worker } else {
130*e7c364b6SAndroid Build Coastguard Worker max_brightness_file_ = MAX_BRIGHTNESS_FILE_PWM;
131*e7c364b6SAndroid Build Coastguard Worker }
132*e7c364b6SAndroid Build Coastguard Worker }
133*e7c364b6SAndroid Build Coastguard Worker // Set the initial brightness level based on the max brightness. Note that reading the initial
134*e7c364b6SAndroid Build Coastguard Worker // value from BRIGHTNESS_FILE doesn't give the actual brightness value (bullhead, sailfish), so
135*e7c364b6SAndroid Build Coastguard Worker // we don't have a good way to query the default value.
136*e7c364b6SAndroid Build Coastguard Worker std::string content;
137*e7c364b6SAndroid Build Coastguard Worker if (!android::base::ReadFileToString(max_brightness_file_, &content)) {
138*e7c364b6SAndroid Build Coastguard Worker PLOG(WARNING) << "Failed to read max brightness";
139*e7c364b6SAndroid Build Coastguard Worker return false;
140*e7c364b6SAndroid Build Coastguard Worker }
141*e7c364b6SAndroid Build Coastguard Worker
142*e7c364b6SAndroid Build Coastguard Worker unsigned int max_value;
143*e7c364b6SAndroid Build Coastguard Worker if (!android::base::ParseUint(android::base::Trim(content), &max_value)) {
144*e7c364b6SAndroid Build Coastguard Worker LOG(WARNING) << "Failed to parse max brightness: " << content;
145*e7c364b6SAndroid Build Coastguard Worker return false;
146*e7c364b6SAndroid Build Coastguard Worker }
147*e7c364b6SAndroid Build Coastguard Worker
148*e7c364b6SAndroid Build Coastguard Worker brightness_normal_value_ = max_value * brightness_normal_ / 100.0;
149*e7c364b6SAndroid Build Coastguard Worker brightness_dimmed_value_ = max_value * brightness_dimmed_ / 100.0;
150*e7c364b6SAndroid Build Coastguard Worker if (!android::base::WriteStringToFile(std::to_string(brightness_normal_value_),
151*e7c364b6SAndroid Build Coastguard Worker brightness_file_)) {
152*e7c364b6SAndroid Build Coastguard Worker PLOG(WARNING) << "Failed to set brightness";
153*e7c364b6SAndroid Build Coastguard Worker return false;
154*e7c364b6SAndroid Build Coastguard Worker }
155*e7c364b6SAndroid Build Coastguard Worker
156*e7c364b6SAndroid Build Coastguard Worker LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_ << "%)";
157*e7c364b6SAndroid Build Coastguard Worker screensaver_state_ = ScreensaverState::NORMAL;
158*e7c364b6SAndroid Build Coastguard Worker return true;
159*e7c364b6SAndroid Build Coastguard Worker }
160*e7c364b6SAndroid Build Coastguard Worker
Init(const std::string &)161*e7c364b6SAndroid Build Coastguard Worker bool RecoveryUI::Init(const std::string& /* locale */) {
162*e7c364b6SAndroid Build Coastguard Worker ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2),
163*e7c364b6SAndroid Build Coastguard Worker touch_screen_allowed_);
164*e7c364b6SAndroid Build Coastguard Worker
165*e7c364b6SAndroid Build Coastguard Worker ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
166*e7c364b6SAndroid Build Coastguard Worker
167*e7c364b6SAndroid Build Coastguard Worker if (touch_screen_allowed_) {
168*e7c364b6SAndroid Build Coastguard Worker ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
169*e7c364b6SAndroid Build Coastguard Worker
170*e7c364b6SAndroid Build Coastguard Worker // Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of
171*e7c364b6SAndroid Build Coastguard Worker // "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way
172*e7c364b6SAndroid Build Coastguard Worker // to turn on text mode. It will only be set if the recovery boot is triggered from fastboot, or
173*e7c364b6SAndroid Build Coastguard Worker // with 'adb reboot recovery'. Note that this applies to all build variants. Otherwise the text
174*e7c364b6SAndroid Build Coastguard Worker // mode will be turned on automatically on debuggable builds, even without a swipe.
175*e7c364b6SAndroid Build Coastguard Worker std::string cmdline;
176*e7c364b6SAndroid Build Coastguard Worker if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
177*e7c364b6SAndroid Build Coastguard Worker is_bootreason_recovery_ui_ = cmdline.find("bootreason=recovery_ui") != std::string::npos;
178*e7c364b6SAndroid Build Coastguard Worker } else {
179*e7c364b6SAndroid Build Coastguard Worker // Non-fatal, and won't affect Init() result.
180*e7c364b6SAndroid Build Coastguard Worker PLOG(WARNING) << "Failed to read /proc/cmdline";
181*e7c364b6SAndroid Build Coastguard Worker }
182*e7c364b6SAndroid Build Coastguard Worker }
183*e7c364b6SAndroid Build Coastguard Worker
184*e7c364b6SAndroid Build Coastguard Worker if (!InitScreensaver()) {
185*e7c364b6SAndroid Build Coastguard Worker LOG(INFO) << "Screensaver disabled";
186*e7c364b6SAndroid Build Coastguard Worker }
187*e7c364b6SAndroid Build Coastguard Worker
188*e7c364b6SAndroid Build Coastguard Worker // Create a separate thread that handles input events.
189*e7c364b6SAndroid Build Coastguard Worker input_thread_ = std::thread([this]() {
190*e7c364b6SAndroid Build Coastguard Worker while (!this->input_thread_stopped_) {
191*e7c364b6SAndroid Build Coastguard Worker if (!ev_wait(500)) {
192*e7c364b6SAndroid Build Coastguard Worker ev_dispatch();
193*e7c364b6SAndroid Build Coastguard Worker }
194*e7c364b6SAndroid Build Coastguard Worker }
195*e7c364b6SAndroid Build Coastguard Worker });
196*e7c364b6SAndroid Build Coastguard Worker
197*e7c364b6SAndroid Build Coastguard Worker return true;
198*e7c364b6SAndroid Build Coastguard Worker }
199*e7c364b6SAndroid Build Coastguard Worker
200*e7c364b6SAndroid Build Coastguard Worker enum SwipeDirection { UP, DOWN, RIGHT, LEFT };
201*e7c364b6SAndroid Build Coastguard Worker
FlipSwipeDirection(SwipeDirection direction)202*e7c364b6SAndroid Build Coastguard Worker static SwipeDirection FlipSwipeDirection(SwipeDirection direction) {
203*e7c364b6SAndroid Build Coastguard Worker switch (direction) {
204*e7c364b6SAndroid Build Coastguard Worker case UP:
205*e7c364b6SAndroid Build Coastguard Worker return SwipeDirection::DOWN;
206*e7c364b6SAndroid Build Coastguard Worker case DOWN:
207*e7c364b6SAndroid Build Coastguard Worker return SwipeDirection::UP;
208*e7c364b6SAndroid Build Coastguard Worker case RIGHT:
209*e7c364b6SAndroid Build Coastguard Worker return SwipeDirection::LEFT;
210*e7c364b6SAndroid Build Coastguard Worker case LEFT:
211*e7c364b6SAndroid Build Coastguard Worker return SwipeDirection::RIGHT;
212*e7c364b6SAndroid Build Coastguard Worker }
213*e7c364b6SAndroid Build Coastguard Worker }
214*e7c364b6SAndroid Build Coastguard Worker
OnTouchDetected(int dx,int dy)215*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::OnTouchDetected(int dx, int dy) {
216*e7c364b6SAndroid Build Coastguard Worker SwipeDirection direction;
217*e7c364b6SAndroid Build Coastguard Worker
218*e7c364b6SAndroid Build Coastguard Worker // We only consider a valid swipe if:
219*e7c364b6SAndroid Build Coastguard Worker // - the delta along one axis is below touch_low_threshold_;
220*e7c364b6SAndroid Build Coastguard Worker // - and the delta along the other axis is beyond touch_high_threshold_.
221*e7c364b6SAndroid Build Coastguard Worker if (abs(dy) < touch_low_threshold_ && abs(dx) > touch_high_threshold_) {
222*e7c364b6SAndroid Build Coastguard Worker direction = dx < 0 ? SwipeDirection::LEFT : SwipeDirection::RIGHT;
223*e7c364b6SAndroid Build Coastguard Worker } else if (abs(dx) < touch_low_threshold_ && abs(dy) > touch_high_threshold_) {
224*e7c364b6SAndroid Build Coastguard Worker direction = dy < 0 ? SwipeDirection::UP : SwipeDirection::DOWN;
225*e7c364b6SAndroid Build Coastguard Worker } else {
226*e7c364b6SAndroid Build Coastguard Worker LOG(DEBUG) << "Ignored " << dx << " " << dy << " (low: " << touch_low_threshold_
227*e7c364b6SAndroid Build Coastguard Worker << ", high: " << touch_high_threshold_ << ")";
228*e7c364b6SAndroid Build Coastguard Worker return;
229*e7c364b6SAndroid Build Coastguard Worker }
230*e7c364b6SAndroid Build Coastguard Worker
231*e7c364b6SAndroid Build Coastguard Worker // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui.
232*e7c364b6SAndroid Build Coastguard Worker if (is_bootreason_recovery_ui_ && !IsTextVisible()) {
233*e7c364b6SAndroid Build Coastguard Worker ShowText(true);
234*e7c364b6SAndroid Build Coastguard Worker return;
235*e7c364b6SAndroid Build Coastguard Worker }
236*e7c364b6SAndroid Build Coastguard Worker
237*e7c364b6SAndroid Build Coastguard Worker // Flip swipe direction if screen is rotated upside down
238*e7c364b6SAndroid Build Coastguard Worker if (gr_get_rotation() == GRRotation::DOWN) {
239*e7c364b6SAndroid Build Coastguard Worker direction = FlipSwipeDirection(direction);
240*e7c364b6SAndroid Build Coastguard Worker }
241*e7c364b6SAndroid Build Coastguard Worker
242*e7c364b6SAndroid Build Coastguard Worker LOG(DEBUG) << "Swipe direction=" << direction;
243*e7c364b6SAndroid Build Coastguard Worker switch (direction) {
244*e7c364b6SAndroid Build Coastguard Worker case SwipeDirection::UP:
245*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_UP, 1); // press up key
246*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_UP, 0); // and release it
247*e7c364b6SAndroid Build Coastguard Worker break;
248*e7c364b6SAndroid Build Coastguard Worker
249*e7c364b6SAndroid Build Coastguard Worker case SwipeDirection::DOWN:
250*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_DOWN, 1); // press down key
251*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_DOWN, 0); // and release it
252*e7c364b6SAndroid Build Coastguard Worker break;
253*e7c364b6SAndroid Build Coastguard Worker
254*e7c364b6SAndroid Build Coastguard Worker case SwipeDirection::LEFT:
255*e7c364b6SAndroid Build Coastguard Worker case SwipeDirection::RIGHT:
256*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_POWER, 1); // press power key
257*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_POWER, 0); // and release it
258*e7c364b6SAndroid Build Coastguard Worker break;
259*e7c364b6SAndroid Build Coastguard Worker };
260*e7c364b6SAndroid Build Coastguard Worker }
261*e7c364b6SAndroid Build Coastguard Worker
OnInputEvent(int fd,uint32_t epevents)262*e7c364b6SAndroid Build Coastguard Worker int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
263*e7c364b6SAndroid Build Coastguard Worker struct input_event ev;
264*e7c364b6SAndroid Build Coastguard Worker if (ev_get_input(fd, epevents, &ev) == -1) {
265*e7c364b6SAndroid Build Coastguard Worker return -1;
266*e7c364b6SAndroid Build Coastguard Worker }
267*e7c364b6SAndroid Build Coastguard Worker
268*e7c364b6SAndroid Build Coastguard Worker // Touch inputs handling.
269*e7c364b6SAndroid Build Coastguard Worker //
270*e7c364b6SAndroid Build Coastguard Worker // We handle the touch inputs by tracking the position changes between initial contacting and
271*e7c364b6SAndroid Build Coastguard Worker // upon lifting. touch_start_X/Y record the initial positions, with touch_finger_down set. Upon
272*e7c364b6SAndroid Build Coastguard Worker // detecting the lift, we unset touch_finger_down and detect a swipe based on position changes.
273*e7c364b6SAndroid Build Coastguard Worker //
274*e7c364b6SAndroid Build Coastguard Worker // Per the doc Multi-touch Protocol at below, there are two protocols.
275*e7c364b6SAndroid Build Coastguard Worker // https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
276*e7c364b6SAndroid Build Coastguard Worker //
277*e7c364b6SAndroid Build Coastguard Worker // The main difference between the stateless type A protocol and the stateful type B slot protocol
278*e7c364b6SAndroid Build Coastguard Worker // lies in the usage of identifiable contacts to reduce the amount of data sent to userspace. The
279*e7c364b6SAndroid Build Coastguard Worker // slot protocol (i.e. type B) sends ABS_MT_TRACKING_ID with a unique id on initial contact, and
280*e7c364b6SAndroid Build Coastguard Worker // sends ABS_MT_TRACKING_ID -1 upon lifting the contact. Protocol A doesn't send
281*e7c364b6SAndroid Build Coastguard Worker // ABS_MT_TRACKING_ID -1 on lifting, but the driver may additionally report BTN_TOUCH event.
282*e7c364b6SAndroid Build Coastguard Worker //
283*e7c364b6SAndroid Build Coastguard Worker // For protocol A, we rely on BTN_TOUCH to recognize lifting, while for protocol B we look for
284*e7c364b6SAndroid Build Coastguard Worker // ABS_MT_TRACKING_ID being -1.
285*e7c364b6SAndroid Build Coastguard Worker //
286*e7c364b6SAndroid Build Coastguard Worker // Touch input events will only be available if touch_screen_allowed_ is set.
287*e7c364b6SAndroid Build Coastguard Worker
288*e7c364b6SAndroid Build Coastguard Worker if (ev.type == EV_SYN) {
289*e7c364b6SAndroid Build Coastguard Worker if (touch_screen_allowed_ && ev.code == SYN_REPORT) {
290*e7c364b6SAndroid Build Coastguard Worker // There might be multiple SYN_REPORT events. We should only detect a swipe after lifting the
291*e7c364b6SAndroid Build Coastguard Worker // contact.
292*e7c364b6SAndroid Build Coastguard Worker if (touch_finger_down_ && !touch_swiping_) {
293*e7c364b6SAndroid Build Coastguard Worker touch_start_X_ = touch_X_;
294*e7c364b6SAndroid Build Coastguard Worker touch_start_Y_ = touch_Y_;
295*e7c364b6SAndroid Build Coastguard Worker touch_swiping_ = true;
296*e7c364b6SAndroid Build Coastguard Worker } else if (!touch_finger_down_ && touch_swiping_) {
297*e7c364b6SAndroid Build Coastguard Worker touch_swiping_ = false;
298*e7c364b6SAndroid Build Coastguard Worker OnTouchDetected(touch_X_ - touch_start_X_, touch_Y_ - touch_start_Y_);
299*e7c364b6SAndroid Build Coastguard Worker }
300*e7c364b6SAndroid Build Coastguard Worker }
301*e7c364b6SAndroid Build Coastguard Worker return 0;
302*e7c364b6SAndroid Build Coastguard Worker }
303*e7c364b6SAndroid Build Coastguard Worker
304*e7c364b6SAndroid Build Coastguard Worker if (ev.type == EV_REL) {
305*e7c364b6SAndroid Build Coastguard Worker if (ev.code == REL_Y) {
306*e7c364b6SAndroid Build Coastguard Worker // accumulate the up or down motion reported by
307*e7c364b6SAndroid Build Coastguard Worker // the trackball. When it exceeds a threshold
308*e7c364b6SAndroid Build Coastguard Worker // (positive or negative), fake an up/down
309*e7c364b6SAndroid Build Coastguard Worker // key event.
310*e7c364b6SAndroid Build Coastguard Worker rel_sum += ev.value;
311*e7c364b6SAndroid Build Coastguard Worker if (rel_sum > 3) {
312*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_DOWN, 1); // press down key
313*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_DOWN, 0); // and release it
314*e7c364b6SAndroid Build Coastguard Worker rel_sum = 0;
315*e7c364b6SAndroid Build Coastguard Worker } else if (rel_sum < -3) {
316*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_UP, 1); // press up key
317*e7c364b6SAndroid Build Coastguard Worker ProcessKey(KEY_UP, 0); // and release it
318*e7c364b6SAndroid Build Coastguard Worker rel_sum = 0;
319*e7c364b6SAndroid Build Coastguard Worker }
320*e7c364b6SAndroid Build Coastguard Worker }
321*e7c364b6SAndroid Build Coastguard Worker } else {
322*e7c364b6SAndroid Build Coastguard Worker rel_sum = 0;
323*e7c364b6SAndroid Build Coastguard Worker }
324*e7c364b6SAndroid Build Coastguard Worker
325*e7c364b6SAndroid Build Coastguard Worker if (touch_screen_allowed_ && ev.type == EV_ABS) {
326*e7c364b6SAndroid Build Coastguard Worker if (ev.code == ABS_MT_SLOT) {
327*e7c364b6SAndroid Build Coastguard Worker touch_slot_ = ev.value;
328*e7c364b6SAndroid Build Coastguard Worker }
329*e7c364b6SAndroid Build Coastguard Worker // Ignore other fingers.
330*e7c364b6SAndroid Build Coastguard Worker if (touch_slot_ > 0) return 0;
331*e7c364b6SAndroid Build Coastguard Worker
332*e7c364b6SAndroid Build Coastguard Worker switch (ev.code) {
333*e7c364b6SAndroid Build Coastguard Worker case ABS_MT_POSITION_X:
334*e7c364b6SAndroid Build Coastguard Worker touch_X_ = ev.value;
335*e7c364b6SAndroid Build Coastguard Worker touch_finger_down_ = true;
336*e7c364b6SAndroid Build Coastguard Worker break;
337*e7c364b6SAndroid Build Coastguard Worker
338*e7c364b6SAndroid Build Coastguard Worker case ABS_MT_POSITION_Y:
339*e7c364b6SAndroid Build Coastguard Worker touch_Y_ = ev.value;
340*e7c364b6SAndroid Build Coastguard Worker touch_finger_down_ = true;
341*e7c364b6SAndroid Build Coastguard Worker break;
342*e7c364b6SAndroid Build Coastguard Worker
343*e7c364b6SAndroid Build Coastguard Worker case ABS_MT_TRACKING_ID:
344*e7c364b6SAndroid Build Coastguard Worker // Protocol B: -1 marks lifting the contact.
345*e7c364b6SAndroid Build Coastguard Worker if (ev.value < 0) touch_finger_down_ = false;
346*e7c364b6SAndroid Build Coastguard Worker break;
347*e7c364b6SAndroid Build Coastguard Worker }
348*e7c364b6SAndroid Build Coastguard Worker return 0;
349*e7c364b6SAndroid Build Coastguard Worker }
350*e7c364b6SAndroid Build Coastguard Worker
351*e7c364b6SAndroid Build Coastguard Worker if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
352*e7c364b6SAndroid Build Coastguard Worker if (touch_screen_allowed_) {
353*e7c364b6SAndroid Build Coastguard Worker if (ev.code == BTN_TOUCH) {
354*e7c364b6SAndroid Build Coastguard Worker // A BTN_TOUCH with value 1 indicates the start of contact (protocol A), with 0 means
355*e7c364b6SAndroid Build Coastguard Worker // lifting the contact.
356*e7c364b6SAndroid Build Coastguard Worker touch_finger_down_ = (ev.value == 1);
357*e7c364b6SAndroid Build Coastguard Worker }
358*e7c364b6SAndroid Build Coastguard Worker
359*e7c364b6SAndroid Build Coastguard Worker // Intentionally ignore BTN_TOUCH and BTN_TOOL_FINGER, which would otherwise trigger
360*e7c364b6SAndroid Build Coastguard Worker // additional scrolling (because in ScreenRecoveryUI::ShowFile(), we consider keys other than
361*e7c364b6SAndroid Build Coastguard Worker // KEY_POWER and KEY_UP as KEY_DOWN).
362*e7c364b6SAndroid Build Coastguard Worker if (ev.code == BTN_TOUCH || ev.code == BTN_TOOL_FINGER) {
363*e7c364b6SAndroid Build Coastguard Worker return 0;
364*e7c364b6SAndroid Build Coastguard Worker }
365*e7c364b6SAndroid Build Coastguard Worker }
366*e7c364b6SAndroid Build Coastguard Worker
367*e7c364b6SAndroid Build Coastguard Worker ProcessKey(ev.code, ev.value);
368*e7c364b6SAndroid Build Coastguard Worker }
369*e7c364b6SAndroid Build Coastguard Worker
370*e7c364b6SAndroid Build Coastguard Worker // For Lid switch handle
371*e7c364b6SAndroid Build Coastguard Worker if (ev.type == EV_SW) {
372*e7c364b6SAndroid Build Coastguard Worker SetSwCallback(ev.code, ev.value);
373*e7c364b6SAndroid Build Coastguard Worker }
374*e7c364b6SAndroid Build Coastguard Worker
375*e7c364b6SAndroid Build Coastguard Worker return 0;
376*e7c364b6SAndroid Build Coastguard Worker }
377*e7c364b6SAndroid Build Coastguard Worker
378*e7c364b6SAndroid Build Coastguard Worker // Processes a key-up or -down event. A key is "registered" when it is pressed and then released,
379*e7c364b6SAndroid Build Coastguard Worker // with no other keypresses or releases in between. Registered keys are passed to CheckKey() to
380*e7c364b6SAndroid Build Coastguard Worker // see if it should trigger a visibility toggle, an immediate reboot, or be queued to be processed
381*e7c364b6SAndroid Build Coastguard Worker // next time the foreground thread wants a key (eg, for the menu).
382*e7c364b6SAndroid Build Coastguard Worker //
383*e7c364b6SAndroid Build Coastguard Worker // We also keep track of which keys are currently down so that CheckKey() can call IsKeyPressed()
384*e7c364b6SAndroid Build Coastguard Worker // to see what other keys are held when a key is registered.
385*e7c364b6SAndroid Build Coastguard Worker //
386*e7c364b6SAndroid Build Coastguard Worker // updown == 1 for key down events; 0 for key up events
ProcessKey(int key_code,int updown)387*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::ProcessKey(int key_code, int updown) {
388*e7c364b6SAndroid Build Coastguard Worker bool register_key = false;
389*e7c364b6SAndroid Build Coastguard Worker bool long_press = false;
390*e7c364b6SAndroid Build Coastguard Worker
391*e7c364b6SAndroid Build Coastguard Worker {
392*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_press_mutex);
393*e7c364b6SAndroid Build Coastguard Worker key_pressed[key_code] = updown;
394*e7c364b6SAndroid Build Coastguard Worker if (updown) {
395*e7c364b6SAndroid Build Coastguard Worker ++key_down_count;
396*e7c364b6SAndroid Build Coastguard Worker key_last_down = key_code;
397*e7c364b6SAndroid Build Coastguard Worker key_long_press = false;
398*e7c364b6SAndroid Build Coastguard Worker std::thread time_key_thread(&RecoveryUI::TimeKey, this, key_code, key_down_count);
399*e7c364b6SAndroid Build Coastguard Worker time_key_thread.detach();
400*e7c364b6SAndroid Build Coastguard Worker } else {
401*e7c364b6SAndroid Build Coastguard Worker if (key_last_down == key_code) {
402*e7c364b6SAndroid Build Coastguard Worker long_press = key_long_press;
403*e7c364b6SAndroid Build Coastguard Worker register_key = true;
404*e7c364b6SAndroid Build Coastguard Worker }
405*e7c364b6SAndroid Build Coastguard Worker key_last_down = -1;
406*e7c364b6SAndroid Build Coastguard Worker }
407*e7c364b6SAndroid Build Coastguard Worker }
408*e7c364b6SAndroid Build Coastguard Worker
409*e7c364b6SAndroid Build Coastguard Worker bool reboot_enabled = enable_reboot;
410*e7c364b6SAndroid Build Coastguard Worker if (register_key) {
411*e7c364b6SAndroid Build Coastguard Worker switch (CheckKey(key_code, long_press)) {
412*e7c364b6SAndroid Build Coastguard Worker case RecoveryUI::IGNORE:
413*e7c364b6SAndroid Build Coastguard Worker break;
414*e7c364b6SAndroid Build Coastguard Worker
415*e7c364b6SAndroid Build Coastguard Worker case RecoveryUI::TOGGLE:
416*e7c364b6SAndroid Build Coastguard Worker ShowText(!IsTextVisible());
417*e7c364b6SAndroid Build Coastguard Worker break;
418*e7c364b6SAndroid Build Coastguard Worker
419*e7c364b6SAndroid Build Coastguard Worker case RecoveryUI::REBOOT:
420*e7c364b6SAndroid Build Coastguard Worker if (reboot_enabled) {
421*e7c364b6SAndroid Build Coastguard Worker Reboot("userrequested,recovery,ui");
422*e7c364b6SAndroid Build Coastguard Worker }
423*e7c364b6SAndroid Build Coastguard Worker break;
424*e7c364b6SAndroid Build Coastguard Worker
425*e7c364b6SAndroid Build Coastguard Worker case RecoveryUI::ENQUEUE:
426*e7c364b6SAndroid Build Coastguard Worker EnqueueKey(key_code);
427*e7c364b6SAndroid Build Coastguard Worker break;
428*e7c364b6SAndroid Build Coastguard Worker }
429*e7c364b6SAndroid Build Coastguard Worker }
430*e7c364b6SAndroid Build Coastguard Worker }
431*e7c364b6SAndroid Build Coastguard Worker
TimeKey(int key_code,int count)432*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::TimeKey(int key_code, int count) {
433*e7c364b6SAndroid Build Coastguard Worker std::this_thread::sleep_for(750ms); // 750 ms == "long"
434*e7c364b6SAndroid Build Coastguard Worker bool long_press = false;
435*e7c364b6SAndroid Build Coastguard Worker {
436*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_press_mutex);
437*e7c364b6SAndroid Build Coastguard Worker if (key_last_down == key_code && key_down_count == count) {
438*e7c364b6SAndroid Build Coastguard Worker long_press = key_long_press = true;
439*e7c364b6SAndroid Build Coastguard Worker }
440*e7c364b6SAndroid Build Coastguard Worker }
441*e7c364b6SAndroid Build Coastguard Worker if (long_press) KeyLongPress(key_code);
442*e7c364b6SAndroid Build Coastguard Worker }
443*e7c364b6SAndroid Build Coastguard Worker
EnqueueKey(int key_code)444*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::EnqueueKey(int key_code) {
445*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_queue_mutex);
446*e7c364b6SAndroid Build Coastguard Worker const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
447*e7c364b6SAndroid Build Coastguard Worker if (key_queue_len < queue_max) {
448*e7c364b6SAndroid Build Coastguard Worker key_queue[key_queue_len++] = key_code;
449*e7c364b6SAndroid Build Coastguard Worker key_queue_cond.notify_one();
450*e7c364b6SAndroid Build Coastguard Worker }
451*e7c364b6SAndroid Build Coastguard Worker }
452*e7c364b6SAndroid Build Coastguard Worker
SetScreensaverState(ScreensaverState state)453*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::SetScreensaverState(ScreensaverState state) {
454*e7c364b6SAndroid Build Coastguard Worker switch (state) {
455*e7c364b6SAndroid Build Coastguard Worker case ScreensaverState::NORMAL:
456*e7c364b6SAndroid Build Coastguard Worker if (android::base::WriteStringToFile(std::to_string(brightness_normal_value_),
457*e7c364b6SAndroid Build Coastguard Worker brightness_file_)) {
458*e7c364b6SAndroid Build Coastguard Worker screensaver_state_ = ScreensaverState::NORMAL;
459*e7c364b6SAndroid Build Coastguard Worker LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_
460*e7c364b6SAndroid Build Coastguard Worker << "%)";
461*e7c364b6SAndroid Build Coastguard Worker } else {
462*e7c364b6SAndroid Build Coastguard Worker LOG(WARNING) << "Unable to set brightness to normal";
463*e7c364b6SAndroid Build Coastguard Worker }
464*e7c364b6SAndroid Build Coastguard Worker break;
465*e7c364b6SAndroid Build Coastguard Worker case ScreensaverState::DIMMED:
466*e7c364b6SAndroid Build Coastguard Worker if (android::base::WriteStringToFile(std::to_string(brightness_dimmed_value_),
467*e7c364b6SAndroid Build Coastguard Worker brightness_file_)) {
468*e7c364b6SAndroid Build Coastguard Worker LOG(INFO) << "Brightness: " << brightness_dimmed_value_ << " (" << brightness_dimmed_
469*e7c364b6SAndroid Build Coastguard Worker << "%)";
470*e7c364b6SAndroid Build Coastguard Worker screensaver_state_ = ScreensaverState::DIMMED;
471*e7c364b6SAndroid Build Coastguard Worker } else {
472*e7c364b6SAndroid Build Coastguard Worker LOG(WARNING) << "Unable to set brightness to dim";
473*e7c364b6SAndroid Build Coastguard Worker }
474*e7c364b6SAndroid Build Coastguard Worker break;
475*e7c364b6SAndroid Build Coastguard Worker case ScreensaverState::OFF:
476*e7c364b6SAndroid Build Coastguard Worker if (android::base::WriteStringToFile("0", brightness_file_)) {
477*e7c364b6SAndroid Build Coastguard Worker LOG(INFO) << "Brightness: 0 (off)";
478*e7c364b6SAndroid Build Coastguard Worker screensaver_state_ = ScreensaverState::OFF;
479*e7c364b6SAndroid Build Coastguard Worker } else {
480*e7c364b6SAndroid Build Coastguard Worker LOG(WARNING) << "Unable to set brightness to off";
481*e7c364b6SAndroid Build Coastguard Worker }
482*e7c364b6SAndroid Build Coastguard Worker break;
483*e7c364b6SAndroid Build Coastguard Worker default:
484*e7c364b6SAndroid Build Coastguard Worker LOG(ERROR) << "Invalid screensaver state";
485*e7c364b6SAndroid Build Coastguard Worker }
486*e7c364b6SAndroid Build Coastguard Worker }
487*e7c364b6SAndroid Build Coastguard Worker
WaitKey()488*e7c364b6SAndroid Build Coastguard Worker int RecoveryUI::WaitKey() {
489*e7c364b6SAndroid Build Coastguard Worker std::unique_lock<std::mutex> lk(key_queue_mutex);
490*e7c364b6SAndroid Build Coastguard Worker
491*e7c364b6SAndroid Build Coastguard Worker // Check for a saved key queue interruption.
492*e7c364b6SAndroid Build Coastguard Worker if (key_interrupted_) {
493*e7c364b6SAndroid Build Coastguard Worker SetScreensaverState(ScreensaverState::NORMAL);
494*e7c364b6SAndroid Build Coastguard Worker return static_cast<int>(KeyError::INTERRUPTED);
495*e7c364b6SAndroid Build Coastguard Worker }
496*e7c364b6SAndroid Build Coastguard Worker
497*e7c364b6SAndroid Build Coastguard Worker // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is plugged in.
498*e7c364b6SAndroid Build Coastguard Worker do {
499*e7c364b6SAndroid Build Coastguard Worker bool rc = key_queue_cond.wait_for(lk, std::chrono::seconds(UI_WAIT_KEY_TIMEOUT_SEC), [this] {
500*e7c364b6SAndroid Build Coastguard Worker return this->key_queue_len != 0 || key_interrupted_;
501*e7c364b6SAndroid Build Coastguard Worker });
502*e7c364b6SAndroid Build Coastguard Worker if (key_interrupted_) {
503*e7c364b6SAndroid Build Coastguard Worker SetScreensaverState(ScreensaverState::NORMAL);
504*e7c364b6SAndroid Build Coastguard Worker return static_cast<int>(KeyError::INTERRUPTED);
505*e7c364b6SAndroid Build Coastguard Worker }
506*e7c364b6SAndroid Build Coastguard Worker if (screensaver_state_ != ScreensaverState::DISABLED) {
507*e7c364b6SAndroid Build Coastguard Worker if (!rc) {
508*e7c364b6SAndroid Build Coastguard Worker // Must be after a timeout. Lower the brightness level: NORMAL -> DIMMED; DIMMED -> OFF.
509*e7c364b6SAndroid Build Coastguard Worker if (screensaver_state_ == ScreensaverState::NORMAL) {
510*e7c364b6SAndroid Build Coastguard Worker SetScreensaverState(ScreensaverState::DIMMED);
511*e7c364b6SAndroid Build Coastguard Worker } else if (screensaver_state_ == ScreensaverState::DIMMED) {
512*e7c364b6SAndroid Build Coastguard Worker SetScreensaverState(ScreensaverState::OFF);
513*e7c364b6SAndroid Build Coastguard Worker }
514*e7c364b6SAndroid Build Coastguard Worker } else if (screensaver_state_ != ScreensaverState::NORMAL) {
515*e7c364b6SAndroid Build Coastguard Worker // Drop the first key if it's changing from OFF to NORMAL.
516*e7c364b6SAndroid Build Coastguard Worker if (screensaver_state_ == ScreensaverState::OFF) {
517*e7c364b6SAndroid Build Coastguard Worker if (key_queue_len > 0) {
518*e7c364b6SAndroid Build Coastguard Worker memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
519*e7c364b6SAndroid Build Coastguard Worker }
520*e7c364b6SAndroid Build Coastguard Worker }
521*e7c364b6SAndroid Build Coastguard Worker
522*e7c364b6SAndroid Build Coastguard Worker // Reset the brightness to normal.
523*e7c364b6SAndroid Build Coastguard Worker SetScreensaverState(ScreensaverState::NORMAL);
524*e7c364b6SAndroid Build Coastguard Worker }
525*e7c364b6SAndroid Build Coastguard Worker }
526*e7c364b6SAndroid Build Coastguard Worker } while (IsUsbConnected() && key_queue_len == 0);
527*e7c364b6SAndroid Build Coastguard Worker
528*e7c364b6SAndroid Build Coastguard Worker int key = static_cast<int>(KeyError::TIMED_OUT);
529*e7c364b6SAndroid Build Coastguard Worker if (key_queue_len > 0) {
530*e7c364b6SAndroid Build Coastguard Worker key = key_queue[0];
531*e7c364b6SAndroid Build Coastguard Worker memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
532*e7c364b6SAndroid Build Coastguard Worker }
533*e7c364b6SAndroid Build Coastguard Worker return key;
534*e7c364b6SAndroid Build Coastguard Worker }
535*e7c364b6SAndroid Build Coastguard Worker
InterruptKey()536*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::InterruptKey() {
537*e7c364b6SAndroid Build Coastguard Worker {
538*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_queue_mutex);
539*e7c364b6SAndroid Build Coastguard Worker key_interrupted_ = true;
540*e7c364b6SAndroid Build Coastguard Worker }
541*e7c364b6SAndroid Build Coastguard Worker key_queue_cond.notify_one();
542*e7c364b6SAndroid Build Coastguard Worker }
543*e7c364b6SAndroid Build Coastguard Worker
IsUsbConnected()544*e7c364b6SAndroid Build Coastguard Worker bool RecoveryUI::IsUsbConnected() {
545*e7c364b6SAndroid Build Coastguard Worker int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
546*e7c364b6SAndroid Build Coastguard Worker if (fd < 0) {
547*e7c364b6SAndroid Build Coastguard Worker printf("failed to open /sys/class/android_usb/android0/state: %s\n", strerror(errno));
548*e7c364b6SAndroid Build Coastguard Worker return 0;
549*e7c364b6SAndroid Build Coastguard Worker }
550*e7c364b6SAndroid Build Coastguard Worker
551*e7c364b6SAndroid Build Coastguard Worker char buf;
552*e7c364b6SAndroid Build Coastguard Worker // USB is connected if android_usb state is CONNECTED or CONFIGURED.
553*e7c364b6SAndroid Build Coastguard Worker int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C');
554*e7c364b6SAndroid Build Coastguard Worker if (close(fd) < 0) {
555*e7c364b6SAndroid Build Coastguard Worker printf("failed to close /sys/class/android_usb/android0/state: %s\n", strerror(errno));
556*e7c364b6SAndroid Build Coastguard Worker }
557*e7c364b6SAndroid Build Coastguard Worker return connected;
558*e7c364b6SAndroid Build Coastguard Worker }
559*e7c364b6SAndroid Build Coastguard Worker
IsKeyPressed(int key)560*e7c364b6SAndroid Build Coastguard Worker bool RecoveryUI::IsKeyPressed(int key) {
561*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_press_mutex);
562*e7c364b6SAndroid Build Coastguard Worker int pressed = key_pressed[key];
563*e7c364b6SAndroid Build Coastguard Worker return pressed;
564*e7c364b6SAndroid Build Coastguard Worker }
565*e7c364b6SAndroid Build Coastguard Worker
IsLongPress()566*e7c364b6SAndroid Build Coastguard Worker bool RecoveryUI::IsLongPress() {
567*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_press_mutex);
568*e7c364b6SAndroid Build Coastguard Worker bool result = key_long_press;
569*e7c364b6SAndroid Build Coastguard Worker return result;
570*e7c364b6SAndroid Build Coastguard Worker }
571*e7c364b6SAndroid Build Coastguard Worker
HasThreeButtons() const572*e7c364b6SAndroid Build Coastguard Worker bool RecoveryUI::HasThreeButtons() const {
573*e7c364b6SAndroid Build Coastguard Worker return has_power_key && has_up_key && has_down_key;
574*e7c364b6SAndroid Build Coastguard Worker }
575*e7c364b6SAndroid Build Coastguard Worker
HasPowerKey() const576*e7c364b6SAndroid Build Coastguard Worker bool RecoveryUI::HasPowerKey() const {
577*e7c364b6SAndroid Build Coastguard Worker return has_power_key;
578*e7c364b6SAndroid Build Coastguard Worker }
579*e7c364b6SAndroid Build Coastguard Worker
HasTouchScreen() const580*e7c364b6SAndroid Build Coastguard Worker bool RecoveryUI::HasTouchScreen() const {
581*e7c364b6SAndroid Build Coastguard Worker return has_touch_screen;
582*e7c364b6SAndroid Build Coastguard Worker }
583*e7c364b6SAndroid Build Coastguard Worker
FlushKeys()584*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::FlushKeys() {
585*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_queue_mutex);
586*e7c364b6SAndroid Build Coastguard Worker key_queue_len = 0;
587*e7c364b6SAndroid Build Coastguard Worker }
588*e7c364b6SAndroid Build Coastguard Worker
CheckKey(int key,bool is_long_press)589*e7c364b6SAndroid Build Coastguard Worker RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
590*e7c364b6SAndroid Build Coastguard Worker {
591*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_press_mutex);
592*e7c364b6SAndroid Build Coastguard Worker key_long_press = false;
593*e7c364b6SAndroid Build Coastguard Worker }
594*e7c364b6SAndroid Build Coastguard Worker
595*e7c364b6SAndroid Build Coastguard Worker // If we have power and volume up keys, that chord is the signal to toggle the text display.
596*e7c364b6SAndroid Build Coastguard Worker if (HasThreeButtons() || (HasPowerKey() && HasTouchScreen() && touch_screen_allowed_)) {
597*e7c364b6SAndroid Build Coastguard Worker if ((key == KEY_VOLUMEUP || key == KEY_UP) && IsKeyPressed(KEY_POWER)) {
598*e7c364b6SAndroid Build Coastguard Worker return TOGGLE;
599*e7c364b6SAndroid Build Coastguard Worker }
600*e7c364b6SAndroid Build Coastguard Worker } else {
601*e7c364b6SAndroid Build Coastguard Worker // Otherwise long press of any button toggles to the text display,
602*e7c364b6SAndroid Build Coastguard Worker // and there's no way to toggle back (but that's pretty useless anyway).
603*e7c364b6SAndroid Build Coastguard Worker if (is_long_press && !IsTextVisible()) {
604*e7c364b6SAndroid Build Coastguard Worker return TOGGLE;
605*e7c364b6SAndroid Build Coastguard Worker }
606*e7c364b6SAndroid Build Coastguard Worker
607*e7c364b6SAndroid Build Coastguard Worker // Also, for button-limited devices, a long press is translated to KEY_ENTER.
608*e7c364b6SAndroid Build Coastguard Worker if (is_long_press && IsTextVisible()) {
609*e7c364b6SAndroid Build Coastguard Worker EnqueueKey(KEY_ENTER);
610*e7c364b6SAndroid Build Coastguard Worker return IGNORE;
611*e7c364b6SAndroid Build Coastguard Worker }
612*e7c364b6SAndroid Build Coastguard Worker }
613*e7c364b6SAndroid Build Coastguard Worker
614*e7c364b6SAndroid Build Coastguard Worker // Press power seven times in a row to reboot.
615*e7c364b6SAndroid Build Coastguard Worker if (key == KEY_POWER) {
616*e7c364b6SAndroid Build Coastguard Worker bool reboot_enabled = enable_reboot;
617*e7c364b6SAndroid Build Coastguard Worker
618*e7c364b6SAndroid Build Coastguard Worker if (reboot_enabled) {
619*e7c364b6SAndroid Build Coastguard Worker ++consecutive_power_keys;
620*e7c364b6SAndroid Build Coastguard Worker if (consecutive_power_keys >= 7) {
621*e7c364b6SAndroid Build Coastguard Worker return REBOOT;
622*e7c364b6SAndroid Build Coastguard Worker }
623*e7c364b6SAndroid Build Coastguard Worker }
624*e7c364b6SAndroid Build Coastguard Worker } else {
625*e7c364b6SAndroid Build Coastguard Worker consecutive_power_keys = 0;
626*e7c364b6SAndroid Build Coastguard Worker }
627*e7c364b6SAndroid Build Coastguard Worker
628*e7c364b6SAndroid Build Coastguard Worker return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
629*e7c364b6SAndroid Build Coastguard Worker }
630*e7c364b6SAndroid Build Coastguard Worker
KeyLongPress(int)631*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::KeyLongPress(int) {}
632*e7c364b6SAndroid Build Coastguard Worker
SetEnableReboot(bool enabled)633*e7c364b6SAndroid Build Coastguard Worker void RecoveryUI::SetEnableReboot(bool enabled) {
634*e7c364b6SAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(key_press_mutex);
635*e7c364b6SAndroid Build Coastguard Worker enable_reboot = enabled;
636*e7c364b6SAndroid Build Coastguard Worker }
637