xref: /aosp_15_r20/bootable/recovery/recovery_ui/ui.cpp (revision e7c364b630b241adcb6c7726a21055250b91fdac)
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