blob: 8e5b0f0e0c1f9ee85eb26b6f495f33a5989e1c07 [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
Dees_Troy38bd7602012-09-14 13:33:53 -040031#ifdef ANDROID_RB_RESTART
Doug Zongker32a0a472011-11-01 11:00:20 -070032#include <cutils/android_reboot.h>
Dees_Troy38bd7602012-09-14 13:33:53 -040033#endif
Doug Zongker32a0a472011-11-01 11:00:20 -070034
35#include "common.h"
Doug Zongker9e805d62013-09-04 13:44:38 -070036#include "roots.h"
Doug Zongker32a0a472011-11-01 11:00:20 -070037#include "device.h"
38#include "minui/minui.h"
39#include "screen_ui.h"
40#include "ui.h"
41
42#define UI_WAIT_KEY_TIMEOUT_SEC 120
43
44// There's only (at most) one of these objects, and global callbacks
45// (for pthread_create, and the input event system) need to find it,
46// so use a global variable.
47static RecoveryUI* self = NULL;
48
49RecoveryUI::RecoveryUI() :
50 key_queue_len(0),
Doug Zongkerbb01d0c2012-12-17 10:52:58 -080051 key_last_down(-1),
Doug Zongkerc0441d12013-07-31 11:28:24 -070052 key_long_press(false),
Doug Zongker9e805d62013-09-04 13:44:38 -070053 key_down_count(0),
Doug Zongkerc704e062014-05-23 08:40:35 -070054 enable_reboot(true),
Doug Zongker9e805d62013-09-04 13:44:38 -070055 consecutive_power_keys(0),
56 consecutive_alternate_keys(0),
57 last_key(-1) {
Doug Zongker32a0a472011-11-01 11:00:20 -070058 pthread_mutex_init(&key_queue_mutex, NULL);
59 pthread_cond_init(&key_queue_cond, NULL);
60 self = this;
Mihai Serban187d6262014-06-06 15:23:20 +030061 memset(key_pressed, 0, sizeof(key_pressed));
Doug Zongker32a0a472011-11-01 11:00:20 -070062}
63
64void RecoveryUI::Init() {
65 ev_init(input_callback, NULL);
66 pthread_create(&input_t, NULL, input_thread, NULL);
67}
68
69
Todd Poynora5ef19f2013-09-17 13:39:10 -070070int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data)
Doug Zongker32a0a472011-11-01 11:00:20 -070071{
72 struct input_event ev;
73 int ret;
74
Todd Poynore7265df2013-09-10 16:53:12 -070075 ret = ev_get_input(fd, epevents, &ev);
Doug Zongker32a0a472011-11-01 11:00:20 -070076 if (ret)
77 return -1;
78
79 if (ev.type == EV_SYN) {
80 return 0;
81 } else if (ev.type == EV_REL) {
82 if (ev.code == REL_Y) {
83 // accumulate the up or down motion reported by
84 // the trackball. When it exceeds a threshold
85 // (positive or negative), fake an up/down
86 // key event.
87 self->rel_sum += ev.value;
88 if (self->rel_sum > 3) {
89 self->process_key(KEY_DOWN, 1); // press down key
90 self->process_key(KEY_DOWN, 0); // and release it
91 self->rel_sum = 0;
92 } else if (self->rel_sum < -3) {
93 self->process_key(KEY_UP, 1); // press up key
94 self->process_key(KEY_UP, 0); // and release it
95 self->rel_sum = 0;
96 }
97 }
98 } else {
99 self->rel_sum = 0;
100 }
101
102 if (ev.type == EV_KEY && ev.code <= KEY_MAX)
103 self->process_key(ev.code, ev.value);
104
105 return 0;
106}
107
108// Process a key-up or -down event. A key is "registered" when it is
109// pressed and then released, with no other keypresses or releases in
110// between. Registered keys are passed to CheckKey() to see if it
111// should trigger a visibility toggle, an immediate reboot, or be
112// queued to be processed next time the foreground thread wants a key
113// (eg, for the menu).
114//
115// We also keep track of which keys are currently down so that
116// CheckKey can call IsKeyPressed to see what other keys are held when
117// a key is registered.
118//
119// updown == 1 for key down events; 0 for key up events
120void RecoveryUI::process_key(int key_code, int updown) {
121 bool register_key = false;
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800122 bool long_press = false;
Doug Zongkerc704e062014-05-23 08:40:35 -0700123 bool reboot_enabled;
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800124
Doug Zongker32a0a472011-11-01 11:00:20 -0700125 pthread_mutex_lock(&key_queue_mutex);
126 key_pressed[key_code] = updown;
127 if (updown) {
Doug Zongkerc0441d12013-07-31 11:28:24 -0700128 ++key_down_count;
Doug Zongker32a0a472011-11-01 11:00:20 -0700129 key_last_down = key_code;
Doug Zongkerc0441d12013-07-31 11:28:24 -0700130 key_long_press = false;
131 pthread_t th;
132 key_timer_t* info = new key_timer_t;
133 info->ui = this;
134 info->key_code = key_code;
135 info->count = key_down_count;
136 pthread_create(&th, NULL, &RecoveryUI::time_key_helper, info);
137 pthread_detach(th);
Doug Zongker32a0a472011-11-01 11:00:20 -0700138 } else {
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800139 if (key_last_down == key_code) {
Doug Zongkerc0441d12013-07-31 11:28:24 -0700140 long_press = key_long_press;
Doug Zongker32a0a472011-11-01 11:00:20 -0700141 register_key = true;
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800142 }
Doug Zongker32a0a472011-11-01 11:00:20 -0700143 key_last_down = -1;
144 }
Doug Zongkerc704e062014-05-23 08:40:35 -0700145 reboot_enabled = enable_reboot;
Doug Zongker32a0a472011-11-01 11:00:20 -0700146 pthread_mutex_unlock(&key_queue_mutex);
147
148 if (register_key) {
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800149 NextCheckKeyIsLong(long_press);
Doug Zongker32a0a472011-11-01 11:00:20 -0700150 switch (CheckKey(key_code)) {
Doug Zongker48b5b072012-01-18 13:46:26 -0800151 case RecoveryUI::IGNORE:
152 break;
153
Doug Zongker32a0a472011-11-01 11:00:20 -0700154 case RecoveryUI::TOGGLE:
155 ShowText(!IsTextVisible());
156 break;
157
158 case RecoveryUI::REBOOT:
Dees_Troy38bd7602012-09-14 13:33:53 -0400159#ifdef ANDROID_RB_RESTART
Doug Zongkerc704e062014-05-23 08:40:35 -0700160 if (reboot_enabled) {
161 android_reboot(ANDROID_RB_RESTART, 0, 0);
162 }
Dees_Troy38bd7602012-09-14 13:33:53 -0400163#endif
Doug Zongker32a0a472011-11-01 11:00:20 -0700164 break;
165
166 case RecoveryUI::ENQUEUE:
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800167 EnqueueKey(key_code);
Doug Zongker32a0a472011-11-01 11:00:20 -0700168 break;
Doug Zongker9e805d62013-09-04 13:44:38 -0700169
170 case RecoveryUI::MOUNT_SYSTEM:
171#ifndef NO_RECOVERY_MOUNT
172 ensure_path_mounted("/system");
173 Print("Mounted /system.");
174#endif
175 break;
Doug Zongker32a0a472011-11-01 11:00:20 -0700176 }
177 }
178}
179
Doug Zongkerc0441d12013-07-31 11:28:24 -0700180void* RecoveryUI::time_key_helper(void* cookie) {
181 key_timer_t* info = (key_timer_t*) cookie;
182 info->ui->time_key(info->key_code, info->count);
183 delete info;
184 return NULL;
185}
186
187void RecoveryUI::time_key(int key_code, int count) {
188 usleep(750000); // 750 ms == "long"
189 bool long_press = false;
190 pthread_mutex_lock(&key_queue_mutex);
191 if (key_last_down == key_code && key_down_count == count) {
192 long_press = key_long_press = true;
193 }
194 pthread_mutex_unlock(&key_queue_mutex);
195 if (long_press) KeyLongPress(key_code);
196}
197
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800198void RecoveryUI::EnqueueKey(int key_code) {
199 pthread_mutex_lock(&key_queue_mutex);
200 const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
201 if (key_queue_len < queue_max) {
202 key_queue[key_queue_len++] = key_code;
203 pthread_cond_signal(&key_queue_cond);
204 }
205 pthread_mutex_unlock(&key_queue_mutex);
206}
207
208
Doug Zongker32a0a472011-11-01 11:00:20 -0700209// Reads input events, handles special hot keys, and adds to the key queue.
210void* RecoveryUI::input_thread(void *cookie)
211{
212 for (;;) {
213 if (!ev_wait(-1))
214 ev_dispatch();
215 }
216 return NULL;
217}
218
219int RecoveryUI::WaitKey()
220{
221 pthread_mutex_lock(&key_queue_mutex);
222
223 // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
224 // plugged in.
225 do {
226 struct timeval now;
227 struct timespec timeout;
228 gettimeofday(&now, NULL);
229 timeout.tv_sec = now.tv_sec;
230 timeout.tv_nsec = now.tv_usec * 1000;
231 timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
232
233 int rc = 0;
234 while (key_queue_len == 0 && rc != ETIMEDOUT) {
235 rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex,
236 &timeout);
237 }
238 } while (usb_connected() && key_queue_len == 0);
239
240 int key = -1;
241 if (key_queue_len > 0) {
242 key = key_queue[0];
243 memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
244 }
245 pthread_mutex_unlock(&key_queue_mutex);
246 return key;
247}
248
249// Return true if USB is connected.
250bool RecoveryUI::usb_connected() {
251 int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
252 if (fd < 0) {
253 printf("failed to open /sys/class/android_usb/android0/state: %s\n",
254 strerror(errno));
255 return 0;
256 }
257
258 char buf;
259 /* USB is connected if android_usb state is CONNECTED or CONFIGURED */
260 int connected = (read(fd, &buf, 1) == 1) && (buf == 'C');
261 if (close(fd) < 0) {
262 printf("failed to close /sys/class/android_usb/android0/state: %s\n",
263 strerror(errno));
264 }
265 return connected;
266}
267
268bool RecoveryUI::IsKeyPressed(int key)
269{
270 pthread_mutex_lock(&key_queue_mutex);
271 int pressed = key_pressed[key];
272 pthread_mutex_unlock(&key_queue_mutex);
273 return pressed;
274}
275
276void RecoveryUI::FlushKeys() {
277 pthread_mutex_lock(&key_queue_mutex);
278 key_queue_len = 0;
279 pthread_mutex_unlock(&key_queue_mutex);
280}
281
Doug Zongker9e805d62013-09-04 13:44:38 -0700282// The default CheckKey implementation assumes the device has power,
283// volume up, and volume down keys.
284//
285// - Hold power and press vol-up to toggle display.
286// - Press power seven times in a row to reboot.
287// - Alternate vol-up and vol-down seven times to mount /system.
Doug Zongker32a0a472011-11-01 11:00:20 -0700288RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) {
Doug Zongker02abde52014-04-01 09:45:24 -0700289 if ((IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) || key == KEY_HOME) {
Doug Zongker9e805d62013-09-04 13:44:38 -0700290 return TOGGLE;
291 }
292
293 if (key == KEY_POWER) {
Doug Zongkerc704e062014-05-23 08:40:35 -0700294 pthread_mutex_lock(&key_queue_mutex);
295 bool reboot_enabled = enable_reboot;
296 pthread_mutex_unlock(&key_queue_mutex);
297
298 if (reboot_enabled) {
299 ++consecutive_power_keys;
300 if (consecutive_power_keys >= 7) {
301 return REBOOT;
302 }
Doug Zongker9e805d62013-09-04 13:44:38 -0700303 }
304 } else {
305 consecutive_power_keys = 0;
306 }
307
308 if ((key == KEY_VOLUMEUP &&
309 (last_key == KEY_VOLUMEDOWN || last_key == -1)) ||
310 (key == KEY_VOLUMEDOWN &&
311 (last_key == KEY_VOLUMEUP || last_key == -1))) {
312 ++consecutive_alternate_keys;
313 if (consecutive_alternate_keys >= 7) {
314 consecutive_alternate_keys = 0;
315 return MOUNT_SYSTEM;
316 }
317 } else {
318 consecutive_alternate_keys = 0;
319 }
320 last_key = key;
321
322 return ENQUEUE;
Doug Zongker32a0a472011-11-01 11:00:20 -0700323}
Doug Zongkerbb01d0c2012-12-17 10:52:58 -0800324
325void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) {
326}
Doug Zongkerc0441d12013-07-31 11:28:24 -0700327
328void RecoveryUI::KeyLongPress(int key) {
329}
Doug Zongkerc704e062014-05-23 08:40:35 -0700330
331void RecoveryUI::SetEnableReboot(bool enabled) {
332 pthread_mutex_lock(&key_queue_mutex);
333 enable_reboot = enabled;
334 pthread_mutex_unlock(&key_queue_mutex);
335}