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 "roots.h"
     35 #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.
     45 static RecoveryUI* self = NULL;
     46 
     47 RecoveryUI::RecoveryUI() :
     48     key_queue_len(0),
     49     key_last_down(-1),
     50     key_long_press(false),
     51     key_down_count(0),
     52     enable_reboot(true),
     53     consecutive_power_keys(0),
     54     consecutive_alternate_keys(0),
     55     last_key(-1) {
     56     pthread_mutex_init(&key_queue_mutex, NULL);
     57     pthread_cond_init(&key_queue_cond, NULL);
     58     self = this;
     59     memset(key_pressed, 0, sizeof(key_pressed));
     60 }
     61 
     62 void RecoveryUI::Init() {
     63     ev_init(input_callback, NULL);
     64     pthread_create(&input_t, NULL, input_thread, NULL);
     65 }
     66 
     67 
     68 int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data)
     69 {
     70     struct input_event ev;
     71     int ret;
     72 
     73     ret = ev_get_input(fd, epevents, &ev);
     74     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
    118 void RecoveryUI::process_key(int key_code, int updown) {
    119     bool register_key = false;
    120     bool long_press = false;
    121     bool reboot_enabled;
    122 
    123     pthread_mutex_lock(&key_queue_mutex);
    124     key_pressed[key_code] = updown;
    125     if (updown) {
    126         ++key_down_count;
    127         key_last_down = key_code;
    128         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);
    136     } else {
    137         if (key_last_down == key_code) {
    138             long_press = key_long_press;
    139             register_key = true;
    140         }
    141         key_last_down = -1;
    142     }
    143     reboot_enabled = enable_reboot;
    144     pthread_mutex_unlock(&key_queue_mutex);
    145 
    146     if (register_key) {
    147         NextCheckKeyIsLong(long_press);
    148         switch (CheckKey(key_code)) {
    149           case RecoveryUI::IGNORE:
    150             break;
    151 
    152           case RecoveryUI::TOGGLE:
    153             ShowText(!IsTextVisible());
    154             break;
    155 
    156           case RecoveryUI::REBOOT:
    157             if (reboot_enabled) {
    158                 android_reboot(ANDROID_RB_RESTART, 0, 0);
    159             }
    160             break;
    161 
    162           case RecoveryUI::ENQUEUE:
    163             EnqueueKey(key_code);
    164             break;
    165 
    166           case RecoveryUI::MOUNT_SYSTEM:
    167 #ifndef NO_RECOVERY_MOUNT
    168             ensure_path_mounted("/system");
    169             Print("Mounted /system.");
    170 #endif
    171             break;
    172         }
    173     }
    174 }
    175 
    176 void* 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 
    183 void 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 
    194 void 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 
    205 // Reads input events, handles special hot keys, and adds to the key queue.
    206 void* RecoveryUI::input_thread(void *cookie)
    207 {
    208     for (;;) {
    209         if (!ev_wait(-1))
    210             ev_dispatch();
    211     }
    212     return NULL;
    213 }
    214 
    215 int 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.
    246 bool 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 
    264 bool 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 
    272 void RecoveryUI::FlushKeys() {
    273     pthread_mutex_lock(&key_queue_mutex);
    274     key_queue_len = 0;
    275     pthread_mutex_unlock(&key_queue_mutex);
    276 }
    277 
    278 // 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.
    284 RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) {
    285     if ((IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) || key == KEY_HOME) {
    286         return TOGGLE;
    287     }
    288 
    289     if (key == KEY_POWER) {
    290         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             }
    299         }
    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;
    319 }
    320 
    321 void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) {
    322 }
    323 
    324 void RecoveryUI::KeyLongPress(int key) {
    325 }
    326 
    327 void RecoveryUI::SetEnableReboot(bool enabled) {
    328     pthread_mutex_lock(&key_queue_mutex);
    329     enable_reboot = enabled;
    330     pthread_mutex_unlock(&key_queue_mutex);
    331 }
    332