Home | History | Annotate | Download | only in recovery
      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