1 /* 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" 34 #include "device.h" 35 #include "minui/minui.h" 36 #include "screen_ui.h" 37 #include "ui.h" 38 39 #define UI_WAIT_KEY_TIMEOUT_SEC 120 40 41 // There's only (at most) one of these objects, and global callbacks 42 // (for pthread_create, and the input event system) need to find it, 43 // so use a global variable. 44 static RecoveryUI* self = NULL; 45 46 RecoveryUI::RecoveryUI() : 47 key_queue_len(0), 48 key_last_down(-1) { 49 pthread_mutex_init(&key_queue_mutex, NULL); 50 pthread_cond_init(&key_queue_cond, NULL); 51 self = this; 52 } 53 54 void RecoveryUI::Init() { 55 ev_init(input_callback, NULL); 56 pthread_create(&input_t, NULL, input_thread, NULL); 57 } 58 59 60 int RecoveryUI::input_callback(int fd, short revents, void* data) 61 { 62 struct input_event ev; 63 int ret; 64 65 ret = ev_get_input(fd, revents, &ev); 66 if (ret) 67 return -1; 68 69 if (ev.type == EV_SYN) { 70 return 0; 71 } else if (ev.type == EV_REL) { 72 if (ev.code == REL_Y) { 73 // accumulate the up or down motion reported by 74 // the trackball. When it exceeds a threshold 75 // (positive or negative), fake an up/down 76 // key event. 77 self->rel_sum += ev.value; 78 if (self->rel_sum > 3) { 79 self->process_key(KEY_DOWN, 1); // press down key 80 self->process_key(KEY_DOWN, 0); // and release it 81 self->rel_sum = 0; 82 } else if (self->rel_sum < -3) { 83 self->process_key(KEY_UP, 1); // press up key 84 self->process_key(KEY_UP, 0); // and release it 85 self->rel_sum = 0; 86 } 87 } 88 } else { 89 self->rel_sum = 0; 90 } 91 92 if (ev.type == EV_KEY && ev.code <= KEY_MAX) 93 self->process_key(ev.code, ev.value); 94 95 return 0; 96 } 97 98 // Process a key-up or -down event. A key is "registered" when it is 99 // pressed and then released, with no other keypresses or releases in 100 // between. Registered keys are passed to CheckKey() to see if it 101 // should trigger a visibility toggle, an immediate reboot, or be 102 // queued to be processed next time the foreground thread wants a key 103 // (eg, for the menu). 104 // 105 // We also keep track of which keys are currently down so that 106 // CheckKey can call IsKeyPressed to see what other keys are held when 107 // a key is registered. 108 // 109 // updown == 1 for key down events; 0 for key up events 110 void RecoveryUI::process_key(int key_code, int updown) { 111 bool register_key = false; 112 113 pthread_mutex_lock(&key_queue_mutex); 114 key_pressed[key_code] = updown; 115 if (updown) { 116 key_last_down = key_code; 117 } else { 118 if (key_last_down == key_code) 119 register_key = true; 120 key_last_down = -1; 121 } 122 pthread_mutex_unlock(&key_queue_mutex); 123 124 if (register_key) { 125 switch (CheckKey(key_code)) { 126 case RecoveryUI::IGNORE: 127 break; 128 129 case RecoveryUI::TOGGLE: 130 ShowText(!IsTextVisible()); 131 break; 132 133 case RecoveryUI::REBOOT: 134 android_reboot(ANDROID_RB_RESTART, 0, 0); 135 break; 136 137 case RecoveryUI::ENQUEUE: 138 pthread_mutex_lock(&key_queue_mutex); 139 const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); 140 if (key_queue_len < queue_max) { 141 key_queue[key_queue_len++] = key_code; 142 pthread_cond_signal(&key_queue_cond); 143 } 144 pthread_mutex_unlock(&key_queue_mutex); 145 break; 146 } 147 } 148 } 149 150 // Reads input events, handles special hot keys, and adds to the key queue. 151 void* RecoveryUI::input_thread(void *cookie) 152 { 153 for (;;) { 154 if (!ev_wait(-1)) 155 ev_dispatch(); 156 } 157 return NULL; 158 } 159 160 int RecoveryUI::WaitKey() 161 { 162 pthread_mutex_lock(&key_queue_mutex); 163 164 // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is 165 // plugged in. 166 do { 167 struct timeval now; 168 struct timespec timeout; 169 gettimeofday(&now, NULL); 170 timeout.tv_sec = now.tv_sec; 171 timeout.tv_nsec = now.tv_usec * 1000; 172 timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC; 173 174 int rc = 0; 175 while (key_queue_len == 0 && rc != ETIMEDOUT) { 176 rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, 177 &timeout); 178 } 179 } while (usb_connected() && key_queue_len == 0); 180 181 int key = -1; 182 if (key_queue_len > 0) { 183 key = key_queue[0]; 184 memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); 185 } 186 pthread_mutex_unlock(&key_queue_mutex); 187 return key; 188 } 189 190 // Return true if USB is connected. 191 bool RecoveryUI::usb_connected() { 192 int fd = open("/sys/class/android_usb/android0/state", O_RDONLY); 193 if (fd < 0) { 194 printf("failed to open /sys/class/android_usb/android0/state: %s\n", 195 strerror(errno)); 196 return 0; 197 } 198 199 char buf; 200 /* USB is connected if android_usb state is CONNECTED or CONFIGURED */ 201 int connected = (read(fd, &buf, 1) == 1) && (buf == 'C'); 202 if (close(fd) < 0) { 203 printf("failed to close /sys/class/android_usb/android0/state: %s\n", 204 strerror(errno)); 205 } 206 return connected; 207 } 208 209 bool RecoveryUI::IsKeyPressed(int key) 210 { 211 pthread_mutex_lock(&key_queue_mutex); 212 int pressed = key_pressed[key]; 213 pthread_mutex_unlock(&key_queue_mutex); 214 return pressed; 215 } 216 217 void RecoveryUI::FlushKeys() { 218 pthread_mutex_lock(&key_queue_mutex); 219 key_queue_len = 0; 220 pthread_mutex_unlock(&key_queue_mutex); 221 } 222 223 RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { 224 return RecoveryUI::ENQUEUE; 225 } 226