blob: c8f08cdd118d53cd94757f064fdecd00a931d361 [file] [log] [blame]
Doug Zongker32a0a472011-11-01 11:00:20 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <fcntl.h>
19#include <linux/input.h>
20#include <pthread.h>
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <sys/time.h>
27#include <sys/types.h>
28#include <time.h>
29#include <unistd.h>
30
31#include <cutils/android_reboot.h>
32
33#include "common.h"
Doug Zongker9e805d62013-09-04 13:44:38 -070034#include "roots.h"
Doug Zongker32a0a472011-11-01 11:00:20 -070035#include "device.h"
36#include "minui/minui.h"
37#include "screen_ui.h"
38#include "ui.h"
39
40#define UI_WAIT_KEY_TIMEOUT_SEC 120
41
42// There's only (at most) one of these objects, and global callbacks
43// (for pthread_create, and the input event system) need to find it,
44// so use a global variable.
45static RecoveryUI* self = NULL;
46
47RecoveryUI::RecoveryUI() :
48 key_queue_len(0),
Doug Zongkerbb01d0c2012-12-17 10:52:58 -080049 key_last_down(-1),
Doug Zongkerc0441d12013-07-31 11:28:24 -070050 key_long_press(false),
Doug Zongker9e805d62013-09-04 13:44:38 -070051 key_down_count(0),
Doug Zongkerc704e062014-05-23 08:40:35 -070052 enable_reboot(true),
Doug Zongker9e805d62013-09-04 13:44:38 -070053 consecutive_power_keys(0),
54 consecutive_alternate_keys(0),
55 last_key(-1) {
Doug Zongker32a0a472011-11-01 11:00:20 -070056 pthread_mutex_init(&key_queue_mutex, NULL);
57 pthread_cond_init(&key_queue_cond, NULL);
58 self = this;
Mihai Serban187d6262014-06-06 15:23:20 +030059 memset(key_pressed, 0, sizeof(key_pressed));
Doug Zongker32a0a472011-11-01 11:00:20 -070060}
61
62void RecoveryUI::Init() {
63 ev_init(input_callback, NULL);
64 pthread_create(&input_t, NULL, input_thread, NULL);
65}
66
67
Todd Poynora5ef19f2013-09-17 13:39:10 -070068int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data)
Doug Zongker32a0a472011-11-01 11:00:20 -070069{
70 struct input_event ev;
71 int ret;
72
Todd Poynore7265df2013-09-10 16:53:12 -070073 ret = ev_get_input(fd, epevents, &ev);
Doug Zongker32a0a472011-11-01 11:00:20 -070074 if (ret)
75 return -1;
76
77 if (ev.type == EV_SYN) {
78 return 0;
79 } else if (ev.type == EV_REL) {
80 if (ev.code == REL_Y) {
81 // accumulate the up or down motion reported by
82 // the trackball. When it exceeds a threshold
83 // (positive or negative), fake an up/down
84 // key event.
85 self->rel_sum += ev.value;
86 if (self->rel_sum > 3) {
87 self->process_key(KEY_DOWN, 1); // press down key
88 self->process_key(KEY_DOWN, 0); // and release it
89 self->rel_sum = 0;
90 } else if (self->rel_sum < -3) {
91 self->process_key(KEY_UP, 1); // press up key
92 self->process_key(KEY_UP, 0); // and release it
93 self->rel_sum = 0;
94 }
95 }
96 } else {
97 self->rel_sum = 0;
98 }
99
100 if (ev.type == EV_KEY && ev.code <= KEY_MAX)
101 self->process_key(ev.code, ev.value);
102
103 return 0;
104}
105
106// Process a key-up or -down event. A key is "registered" when it is
107// pressed and then released, with no other keypresses or releases in
108// between. Registered keys are passed to CheckKey() to see if it
109// should trigger a visibility toggle, an immediate reboot, or be
110// queued to be processed next time the foreground thread wants a key
111// (eg, for the menu).
112//
113// We also keep track of which keys are currently down so that
114// CheckKey can call IsKeyPressed to see what other keys are held when
115// a key is registered.
116//
117// updown == 1 for key down events; 0 for key up events
118void RecoveryUI::process_key(int key_code, int updown) {
119 bool register_key = false;
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800120 bool long_press = false;
Doug Zongkerc704e062014-05-23 08:40:35 -0700121 bool reboot_enabled;
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800122
Doug Zongker32a0a472011-11-01 11:00:20 -0700123 pthread_mutex_lock(&key_queue_mutex);
124 key_pressed[key_code] = updown;
125 if (updown) {
Doug Zongkerc0441d12013-07-31 11:28:24 -0700126 ++key_down_count;
Doug Zongker32a0a472011-11-01 11:00:20 -0700127 key_last_down = key_code;
Doug Zongkerc0441d12013-07-31 11:28:24 -0700128 key_long_press = false;
129 pthread_t th;
130 key_timer_t* info = new key_timer_t;
131 info->ui = this;
132 info->key_code = key_code;
133 info->count = key_down_count;
134 pthread_create(&th, NULL, &RecoveryUI::time_key_helper, info);
135 pthread_detach(th);
Doug Zongker32a0a472011-11-01 11:00:20 -0700136 } else {
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800137 if (key_last_down == key_code) {
Doug Zongkerc0441d12013-07-31 11:28:24 -0700138 long_press = key_long_press;
Doug Zongker32a0a472011-11-01 11:00:20 -0700139 register_key = true;
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800140 }
Doug Zongker32a0a472011-11-01 11:00:20 -0700141 key_last_down = -1;
142 }
Doug Zongkerc704e062014-05-23 08:40:35 -0700143 reboot_enabled = enable_reboot;
Doug Zongker32a0a472011-11-01 11:00:20 -0700144 pthread_mutex_unlock(&key_queue_mutex);
145
146 if (register_key) {
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800147 NextCheckKeyIsLong(long_press);
Doug Zongker32a0a472011-11-01 11:00:20 -0700148 switch (CheckKey(key_code)) {
Doug Zongker48b5b072012-01-18 13:46:26 -0800149 case RecoveryUI::IGNORE:
150 break;
151
Doug Zongker32a0a472011-11-01 11:00:20 -0700152 case RecoveryUI::TOGGLE:
153 ShowText(!IsTextVisible());
154 break;
155
156 case RecoveryUI::REBOOT:
Doug Zongkerc704e062014-05-23 08:40:35 -0700157 if (reboot_enabled) {
158 android_reboot(ANDROID_RB_RESTART, 0, 0);
159 }
Doug Zongker32a0a472011-11-01 11:00:20 -0700160 break;
161
162 case RecoveryUI::ENQUEUE:
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800163 EnqueueKey(key_code);
Doug Zongker32a0a472011-11-01 11:00:20 -0700164 break;
Doug Zongker9e805d62013-09-04 13:44:38 -0700165
166 case RecoveryUI::MOUNT_SYSTEM:
167#ifndef NO_RECOVERY_MOUNT
168 ensure_path_mounted("/system");
169 Print("Mounted /system.");
170#endif
171 break;
Doug Zongker32a0a472011-11-01 11:00:20 -0700172 }
173 }
174}
175
Doug Zongkerc0441d12013-07-31 11:28:24 -0700176void* RecoveryUI::time_key_helper(void* cookie) {
177 key_timer_t* info = (key_timer_t*) cookie;
178 info->ui->time_key(info->key_code, info->count);
179 delete info;
180 return NULL;
181}
182
183void RecoveryUI::time_key(int key_code, int count) {
184 usleep(750000); // 750 ms == "long"
185 bool long_press = false;
186 pthread_mutex_lock(&key_queue_mutex);
187 if (key_last_down == key_code && key_down_count == count) {
188 long_press = key_long_press = true;
189 }
190 pthread_mutex_unlock(&key_queue_mutex);
191 if (long_press) KeyLongPress(key_code);
192}
193
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800194void RecoveryUI::EnqueueKey(int key_code) {
195 pthread_mutex_lock(&key_queue_mutex);
196 const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
197 if (key_queue_len < queue_max) {
198 key_queue[key_queue_len++] = key_code;
199 pthread_cond_signal(&key_queue_cond);
200 }
201 pthread_mutex_unlock(&key_queue_mutex);
202}
203
204
Doug Zongker32a0a472011-11-01 11:00:20 -0700205// Reads input events, handles special hot keys, and adds to the key queue.
206void* RecoveryUI::input_thread(void *cookie)
207{
208 for (;;) {
209 if (!ev_wait(-1))
210 ev_dispatch();
211 }
212 return NULL;
213}
214
215int RecoveryUI::WaitKey()
216{
217 pthread_mutex_lock(&key_queue_mutex);
218
219 // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
220 // plugged in.
221 do {
222 struct timeval now;
223 struct timespec timeout;
224 gettimeofday(&now, NULL);
225 timeout.tv_sec = now.tv_sec;
226 timeout.tv_nsec = now.tv_usec * 1000;
227 timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
228
229 int rc = 0;
230 while (key_queue_len == 0 && rc != ETIMEDOUT) {
231 rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex,
232 &timeout);
233 }
234 } while (usb_connected() && key_queue_len == 0);
235
236 int key = -1;
237 if (key_queue_len > 0) {
238 key = key_queue[0];
239 memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
240 }
241 pthread_mutex_unlock(&key_queue_mutex);
242 return key;
243}
244
245// Return true if USB is connected.
246bool RecoveryUI::usb_connected() {
247 int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
248 if (fd < 0) {
249 printf("failed to open /sys/class/android_usb/android0/state: %s\n",
250 strerror(errno));
251 return 0;
252 }
253
254 char buf;
255 /* USB is connected if android_usb state is CONNECTED or CONFIGURED */
256 int connected = (read(fd, &buf, 1) == 1) && (buf == 'C');
257 if (close(fd) < 0) {
258 printf("failed to close /sys/class/android_usb/android0/state: %s\n",
259 strerror(errno));
260 }
261 return connected;
262}
263
264bool RecoveryUI::IsKeyPressed(int key)
265{
266 pthread_mutex_lock(&key_queue_mutex);
267 int pressed = key_pressed[key];
268 pthread_mutex_unlock(&key_queue_mutex);
269 return pressed;
270}
271
272void RecoveryUI::FlushKeys() {
273 pthread_mutex_lock(&key_queue_mutex);
274 key_queue_len = 0;
275 pthread_mutex_unlock(&key_queue_mutex);
276}
277
Doug Zongker9e805d62013-09-04 13:44:38 -0700278// The default CheckKey implementation assumes the device has power,
279// volume up, and volume down keys.
280//
281// - Hold power and press vol-up to toggle display.
282// - Press power seven times in a row to reboot.
283// - Alternate vol-up and vol-down seven times to mount /system.
Doug Zongker32a0a472011-11-01 11:00:20 -0700284RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) {
Doug Zongker02abde52014-04-01 09:45:24 -0700285 if ((IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) || key == KEY_HOME) {
Doug Zongker9e805d62013-09-04 13:44:38 -0700286 return TOGGLE;
287 }
288
289 if (key == KEY_POWER) {
Doug Zongkerc704e062014-05-23 08:40:35 -0700290 pthread_mutex_lock(&key_queue_mutex);
291 bool reboot_enabled = enable_reboot;
292 pthread_mutex_unlock(&key_queue_mutex);
293
294 if (reboot_enabled) {
295 ++consecutive_power_keys;
296 if (consecutive_power_keys >= 7) {
297 return REBOOT;
298 }
Doug Zongker9e805d62013-09-04 13:44:38 -0700299 }
300 } else {
301 consecutive_power_keys = 0;
302 }
303
304 if ((key == KEY_VOLUMEUP &&
305 (last_key == KEY_VOLUMEDOWN || last_key == -1)) ||
306 (key == KEY_VOLUMEDOWN &&
307 (last_key == KEY_VOLUMEUP || last_key == -1))) {
308 ++consecutive_alternate_keys;
309 if (consecutive_alternate_keys >= 7) {
310 consecutive_alternate_keys = 0;
311 return MOUNT_SYSTEM;
312 }
313 } else {
314 consecutive_alternate_keys = 0;
315 }
316 last_key = key;
317
318 return ENQUEUE;
Doug Zongker32a0a472011-11-01 11:00:20 -0700319}
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800320
321void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) {
322}
Doug Zongkerc0441d12013-07-31 11:28:24 -0700323
324void RecoveryUI::KeyLongPress(int key) {
325}
Doug Zongkerc704e062014-05-23 08:40:35 -0700326
327void RecoveryUI::SetEnableReboot(bool enabled) {
328 pthread_mutex_lock(&key_queue_mutex);
329 enable_reboot = enabled;
330 pthread_mutex_unlock(&key_queue_mutex);
331}