Home | History | Annotate | Download | only in recovery
      1 /*
      2  * Copyright (C) 2007 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 "common.h"
     32 #include <cutils/android_reboot.h>
     33 #include "minui/minui.h"
     34 #include "recovery_ui.h"
     35 
     36 #define MAX_COLS 96
     37 #define MAX_ROWS 32
     38 
     39 #define CHAR_WIDTH 10
     40 #define CHAR_HEIGHT 18
     41 
     42 #define UI_WAIT_KEY_TIMEOUT_SEC    120
     43 
     44 UIParameters ui_parameters = {
     45     6,       // indeterminate progress bar frames
     46     20,      // fps
     47     7,       // installation icon frames (0 == static image)
     48     13, 190, // installation icon overlay offset
     49 };
     50 
     51 static pthread_mutex_t gUpdateMutex = PTHREAD_MUTEX_INITIALIZER;
     52 static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS];
     53 static gr_surface *gInstallationOverlay;
     54 static gr_surface *gProgressBarIndeterminate;
     55 static gr_surface gProgressBarEmpty;
     56 static gr_surface gProgressBarFill;
     57 
     58 static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
     59     { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" },
     60     { &gBackgroundIcon[BACKGROUND_ICON_ERROR],      "icon_error" },
     61     { &gProgressBarEmpty,               "progress_empty" },
     62     { &gProgressBarFill,                "progress_fill" },
     63     { NULL,                             NULL },
     64 };
     65 
     66 static int gCurrentIcon = 0;
     67 static int gInstallingFrame = 0;
     68 
     69 static enum ProgressBarType {
     70     PROGRESSBAR_TYPE_NONE,
     71     PROGRESSBAR_TYPE_INDETERMINATE,
     72     PROGRESSBAR_TYPE_NORMAL,
     73 } gProgressBarType = PROGRESSBAR_TYPE_NONE;
     74 
     75 // Progress bar scope of current operation
     76 static float gProgressScopeStart = 0, gProgressScopeSize = 0, gProgress = 0;
     77 static double gProgressScopeTime, gProgressScopeDuration;
     78 
     79 // Set to 1 when both graphics pages are the same (except for the progress bar)
     80 static int gPagesIdentical = 0;
     81 
     82 // Log text overlay, displayed when a magic key is pressed
     83 static char text[MAX_ROWS][MAX_COLS];
     84 static int text_cols = 0, text_rows = 0;
     85 static int text_col = 0, text_row = 0, text_top = 0;
     86 static int show_text = 0;
     87 static int show_text_ever = 0;   // has show_text ever been 1?
     88 
     89 static char menu[MAX_ROWS][MAX_COLS];
     90 static int show_menu = 0;
     91 static int menu_top = 0, menu_items = 0, menu_sel = 0;
     92 
     93 // Key event input queue
     94 static pthread_mutex_t key_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
     95 static pthread_cond_t key_queue_cond = PTHREAD_COND_INITIALIZER;
     96 static int key_queue[256], key_queue_len = 0;
     97 static volatile char key_pressed[KEY_MAX + 1];
     98 
     99 // Return the current time as a double (including fractions of a second).
    100 static double now() {
    101     struct timeval tv;
    102     gettimeofday(&tv, NULL);
    103     return tv.tv_sec + tv.tv_usec / 1000000.0;
    104 }
    105 
    106 // Draw the given frame over the installation overlay animation.  The
    107 // background is not cleared or draw with the base icon first; we
    108 // assume that the frame already contains some other frame of the
    109 // animation.  Does nothing if no overlay animation is defined.
    110 // Should only be called with gUpdateMutex locked.
    111 static void draw_install_overlay_locked(int frame) {
    112     if (gInstallationOverlay == NULL) return;
    113     gr_surface surface = gInstallationOverlay[frame];
    114     int iconWidth = gr_get_width(surface);
    115     int iconHeight = gr_get_height(surface);
    116     gr_blit(surface, 0, 0, iconWidth, iconHeight,
    117             ui_parameters.install_overlay_offset_x,
    118             ui_parameters.install_overlay_offset_y);
    119 }
    120 
    121 // Clear the screen and draw the currently selected background icon (if any).
    122 // Should only be called with gUpdateMutex locked.
    123 static void draw_background_locked(int icon)
    124 {
    125     gPagesIdentical = 0;
    126     gr_color(0, 0, 0, 255);
    127     gr_fill(0, 0, gr_fb_width(), gr_fb_height());
    128 
    129     if (icon) {
    130         gr_surface surface = gBackgroundIcon[icon];
    131         int iconWidth = gr_get_width(surface);
    132         int iconHeight = gr_get_height(surface);
    133         int iconX = (gr_fb_width() - iconWidth) / 2;
    134         int iconY = (gr_fb_height() - iconHeight) / 2;
    135         gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
    136         if (icon == BACKGROUND_ICON_INSTALLING) {
    137             draw_install_overlay_locked(gInstallingFrame);
    138         }
    139     }
    140 }
    141 
    142 // Draw the progress bar (if any) on the screen.  Does not flip pages.
    143 // Should only be called with gUpdateMutex locked.
    144 static void draw_progress_locked()
    145 {
    146     if (gCurrentIcon == BACKGROUND_ICON_INSTALLING) {
    147         draw_install_overlay_locked(gInstallingFrame);
    148     }
    149 
    150     if (gProgressBarType != PROGRESSBAR_TYPE_NONE) {
    151         int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
    152         int width = gr_get_width(gProgressBarEmpty);
    153         int height = gr_get_height(gProgressBarEmpty);
    154 
    155         int dx = (gr_fb_width() - width)/2;
    156         int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
    157 
    158         // Erase behind the progress bar (in case this was a progress-only update)
    159         gr_color(0, 0, 0, 255);
    160         gr_fill(dx, dy, width, height);
    161 
    162         if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL) {
    163             float progress = gProgressScopeStart + gProgress * gProgressScopeSize;
    164             int pos = (int) (progress * width);
    165 
    166             if (pos > 0) {
    167                 gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy);
    168             }
    169             if (pos < width-1) {
    170                 gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
    171             }
    172         }
    173 
    174         if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
    175             static int frame = 0;
    176             gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
    177             frame = (frame + 1) % ui_parameters.indeterminate_frames;
    178         }
    179     }
    180 }
    181 
    182 static void draw_text_line(int row, const char* t) {
    183   if (t[0] != '\0') {
    184     gr_text(0, (row+1)*CHAR_HEIGHT-1, t);
    185   }
    186 }
    187 
    188 // Redraw everything on the screen.  Does not flip pages.
    189 // Should only be called with gUpdateMutex locked.
    190 static void draw_screen_locked(void)
    191 {
    192     draw_background_locked(gCurrentIcon);
    193     draw_progress_locked();
    194 
    195     if (show_text) {
    196         gr_color(0, 0, 0, 160);
    197         gr_fill(0, 0, gr_fb_width(), gr_fb_height());
    198 
    199         int i = 0;
    200         if (show_menu) {
    201             gr_color(64, 96, 255, 255);
    202             gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT,
    203                     gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1);
    204 
    205             for (; i < menu_top + menu_items; ++i) {
    206                 if (i == menu_top + menu_sel) {
    207                     gr_color(255, 255, 255, 255);
    208                     draw_text_line(i, menu[i]);
    209                     gr_color(64, 96, 255, 255);
    210                 } else {
    211                     draw_text_line(i, menu[i]);
    212                 }
    213             }
    214             gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
    215                     gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
    216             ++i;
    217         }
    218 
    219         gr_color(255, 255, 0, 255);
    220 
    221         for (; i < text_rows; ++i) {
    222             draw_text_line(i, text[(i+text_top) % text_rows]);
    223         }
    224     }
    225 }
    226 
    227 // Redraw everything on the screen and flip the screen (make it visible).
    228 // Should only be called with gUpdateMutex locked.
    229 static void update_screen_locked(void)
    230 {
    231     draw_screen_locked();
    232     gr_flip();
    233 }
    234 
    235 // Updates only the progress bar, if possible, otherwise redraws the screen.
    236 // Should only be called with gUpdateMutex locked.
    237 static void update_progress_locked(void)
    238 {
    239     if (show_text || !gPagesIdentical) {
    240         draw_screen_locked();    // Must redraw the whole screen
    241         gPagesIdentical = 1;
    242     } else {
    243         draw_progress_locked();  // Draw only the progress bar and overlays
    244     }
    245     gr_flip();
    246 }
    247 
    248 // Keeps the progress bar updated, even when the process is otherwise busy.
    249 static void *progress_thread(void *cookie)
    250 {
    251     double interval = 1.0 / ui_parameters.update_fps;
    252     for (;;) {
    253         double start = now();
    254         pthread_mutex_lock(&gUpdateMutex);
    255 
    256         int redraw = 0;
    257 
    258         // update the installation animation, if active
    259         // skip this if we have a text overlay (too expensive to update)
    260         if (gCurrentIcon == BACKGROUND_ICON_INSTALLING &&
    261             ui_parameters.installing_frames > 0 &&
    262             !show_text) {
    263             gInstallingFrame =
    264                 (gInstallingFrame + 1) % ui_parameters.installing_frames;
    265             redraw = 1;
    266         }
    267 
    268         // update the progress bar animation, if active
    269         // skip this if we have a text overlay (too expensive to update)
    270         if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE && !show_text) {
    271             redraw = 1;
    272         }
    273 
    274         // move the progress bar forward on timed intervals, if configured
    275         int duration = gProgressScopeDuration;
    276         if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL && duration > 0) {
    277             double elapsed = now() - gProgressScopeTime;
    278             float progress = 1.0 * elapsed / duration;
    279             if (progress > 1.0) progress = 1.0;
    280             if (progress > gProgress) {
    281                 gProgress = progress;
    282                 redraw = 1;
    283             }
    284         }
    285 
    286         if (redraw) update_progress_locked();
    287 
    288         pthread_mutex_unlock(&gUpdateMutex);
    289         double end = now();
    290         // minimum of 20ms delay between frames
    291         double delay = interval - (end-start);
    292         if (delay < 0.02) delay = 0.02;
    293         usleep((long)(delay * 1000000));
    294     }
    295     return NULL;
    296 }
    297 
    298 static int rel_sum = 0;
    299 
    300 static int input_callback(int fd, short revents, void *data)
    301 {
    302     struct input_event ev;
    303     int ret;
    304     int fake_key = 0;
    305 
    306     ret = ev_get_input(fd, revents, &ev);
    307     if (ret)
    308         return -1;
    309 
    310     if (ev.type == EV_SYN) {
    311         return 0;
    312     } else if (ev.type == EV_REL) {
    313         if (ev.code == REL_Y) {
    314             // accumulate the up or down motion reported by
    315             // the trackball.  When it exceeds a threshold
    316             // (positive or negative), fake an up/down
    317             // key event.
    318             rel_sum += ev.value;
    319             if (rel_sum > 3) {
    320                 fake_key = 1;
    321                 ev.type = EV_KEY;
    322                 ev.code = KEY_DOWN;
    323                 ev.value = 1;
    324                 rel_sum = 0;
    325             } else if (rel_sum < -3) {
    326                 fake_key = 1;
    327                 ev.type = EV_KEY;
    328                 ev.code = KEY_UP;
    329                 ev.value = 1;
    330                 rel_sum = 0;
    331             }
    332         }
    333     } else {
    334         rel_sum = 0;
    335     }
    336 
    337     if (ev.type != EV_KEY || ev.code > KEY_MAX)
    338         return 0;
    339 
    340     pthread_mutex_lock(&key_queue_mutex);
    341     if (!fake_key) {
    342         // our "fake" keys only report a key-down event (no
    343         // key-up), so don't record them in the key_pressed
    344         // table.
    345         key_pressed[ev.code] = ev.value;
    346     }
    347     const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
    348     if (ev.value > 0 && key_queue_len < queue_max) {
    349         key_queue[key_queue_len++] = ev.code;
    350         pthread_cond_signal(&key_queue_cond);
    351     }
    352     pthread_mutex_unlock(&key_queue_mutex);
    353 
    354     if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
    355         pthread_mutex_lock(&gUpdateMutex);
    356         show_text = !show_text;
    357         if (show_text) show_text_ever = 1;
    358         update_screen_locked();
    359         pthread_mutex_unlock(&gUpdateMutex);
    360     }
    361 
    362     if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
    363         android_reboot(ANDROID_RB_RESTART, 0, 0);
    364     }
    365 
    366     return 0;
    367 }
    368 
    369 // Reads input events, handles special hot keys, and adds to the key queue.
    370 static void *input_thread(void *cookie)
    371 {
    372     for (;;) {
    373         if (!ev_wait(-1))
    374             ev_dispatch();
    375     }
    376     return NULL;
    377 }
    378 
    379 void ui_init(void)
    380 {
    381     gr_init();
    382     ev_init(input_callback, NULL);
    383 
    384     text_col = text_row = 0;
    385     text_rows = gr_fb_height() / CHAR_HEIGHT;
    386     if (text_rows > MAX_ROWS) text_rows = MAX_ROWS;
    387     text_top = 1;
    388 
    389     text_cols = gr_fb_width() / CHAR_WIDTH;
    390     if (text_cols > MAX_COLS - 1) text_cols = MAX_COLS - 1;
    391 
    392     int i;
    393     for (i = 0; BITMAPS[i].name != NULL; ++i) {
    394         int result = res_create_surface(BITMAPS[i].name, BITMAPS[i].surface);
    395         if (result < 0) {
    396             LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
    397         }
    398     }
    399 
    400     gProgressBarIndeterminate = malloc(ui_parameters.indeterminate_frames *
    401                                        sizeof(gr_surface));
    402     for (i = 0; i < ui_parameters.indeterminate_frames; ++i) {
    403         char filename[40];
    404         // "indeterminate01.png", "indeterminate02.png", ...
    405         sprintf(filename, "indeterminate%02d", i+1);
    406         int result = res_create_surface(filename, gProgressBarIndeterminate+i);
    407         if (result < 0) {
    408             LOGE("Missing bitmap %s\n(Code %d)\n", filename, result);
    409         }
    410     }
    411 
    412     if (ui_parameters.installing_frames > 0) {
    413         gInstallationOverlay = malloc(ui_parameters.installing_frames *
    414                                       sizeof(gr_surface));
    415         for (i = 0; i < ui_parameters.installing_frames; ++i) {
    416             char filename[40];
    417             // "icon_installing_overlay01.png",
    418             // "icon_installing_overlay02.png", ...
    419             sprintf(filename, "icon_installing_overlay%02d", i+1);
    420             int result = res_create_surface(filename, gInstallationOverlay+i);
    421             if (result < 0) {
    422                 LOGE("Missing bitmap %s\n(Code %d)\n", filename, result);
    423             }
    424         }
    425 
    426         // Adjust the offset to account for the positioning of the
    427         // base image on the screen.
    428         if (gBackgroundIcon[BACKGROUND_ICON_INSTALLING] != NULL) {
    429             gr_surface bg = gBackgroundIcon[BACKGROUND_ICON_INSTALLING];
    430             ui_parameters.install_overlay_offset_x +=
    431                 (gr_fb_width() - gr_get_width(bg)) / 2;
    432             ui_parameters.install_overlay_offset_y +=
    433                 (gr_fb_height() - gr_get_height(bg)) / 2;
    434         }
    435     } else {
    436         gInstallationOverlay = NULL;
    437     }
    438 
    439     pthread_t t;
    440     pthread_create(&t, NULL, progress_thread, NULL);
    441     pthread_create(&t, NULL, input_thread, NULL);
    442 }
    443 
    444 void ui_set_background(int icon)
    445 {
    446     pthread_mutex_lock(&gUpdateMutex);
    447     gCurrentIcon = icon;
    448     update_screen_locked();
    449     pthread_mutex_unlock(&gUpdateMutex);
    450 }
    451 
    452 void ui_show_indeterminate_progress()
    453 {
    454     pthread_mutex_lock(&gUpdateMutex);
    455     if (gProgressBarType != PROGRESSBAR_TYPE_INDETERMINATE) {
    456         gProgressBarType = PROGRESSBAR_TYPE_INDETERMINATE;
    457         update_progress_locked();
    458     }
    459     pthread_mutex_unlock(&gUpdateMutex);
    460 }
    461 
    462 void ui_show_progress(float portion, int seconds)
    463 {
    464     pthread_mutex_lock(&gUpdateMutex);
    465     gProgressBarType = PROGRESSBAR_TYPE_NORMAL;
    466     gProgressScopeStart += gProgressScopeSize;
    467     gProgressScopeSize = portion;
    468     gProgressScopeTime = now();
    469     gProgressScopeDuration = seconds;
    470     gProgress = 0;
    471     update_progress_locked();
    472     pthread_mutex_unlock(&gUpdateMutex);
    473 }
    474 
    475 void ui_set_progress(float fraction)
    476 {
    477     pthread_mutex_lock(&gUpdateMutex);
    478     if (fraction < 0.0) fraction = 0.0;
    479     if (fraction > 1.0) fraction = 1.0;
    480     if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL && fraction > gProgress) {
    481         // Skip updates that aren't visibly different.
    482         int width = gr_get_width(gProgressBarIndeterminate[0]);
    483         float scale = width * gProgressScopeSize;
    484         if ((int) (gProgress * scale) != (int) (fraction * scale)) {
    485             gProgress = fraction;
    486             update_progress_locked();
    487         }
    488     }
    489     pthread_mutex_unlock(&gUpdateMutex);
    490 }
    491 
    492 void ui_reset_progress()
    493 {
    494     pthread_mutex_lock(&gUpdateMutex);
    495     gProgressBarType = PROGRESSBAR_TYPE_NONE;
    496     gProgressScopeStart = gProgressScopeSize = 0;
    497     gProgressScopeTime = gProgressScopeDuration = 0;
    498     gProgress = 0;
    499     update_screen_locked();
    500     pthread_mutex_unlock(&gUpdateMutex);
    501 }
    502 
    503 void ui_print(const char *fmt, ...)
    504 {
    505     char buf[256];
    506     va_list ap;
    507     va_start(ap, fmt);
    508     vsnprintf(buf, 256, fmt, ap);
    509     va_end(ap);
    510 
    511     fputs(buf, stdout);
    512 
    513     // This can get called before ui_init(), so be careful.
    514     pthread_mutex_lock(&gUpdateMutex);
    515     if (text_rows > 0 && text_cols > 0) {
    516         char *ptr;
    517         for (ptr = buf; *ptr != '\0'; ++ptr) {
    518             if (*ptr == '\n' || text_col >= text_cols) {
    519                 text[text_row][text_col] = '\0';
    520                 text_col = 0;
    521                 text_row = (text_row + 1) % text_rows;
    522                 if (text_row == text_top) text_top = (text_top + 1) % text_rows;
    523             }
    524             if (*ptr != '\n') text[text_row][text_col++] = *ptr;
    525         }
    526         text[text_row][text_col] = '\0';
    527         update_screen_locked();
    528     }
    529     pthread_mutex_unlock(&gUpdateMutex);
    530 }
    531 
    532 void ui_start_menu(char** headers, char** items, int initial_selection) {
    533     int i;
    534     pthread_mutex_lock(&gUpdateMutex);
    535     if (text_rows > 0 && text_cols > 0) {
    536         for (i = 0; i < text_rows; ++i) {
    537             if (headers[i] == NULL) break;
    538             strncpy(menu[i], headers[i], text_cols-1);
    539             menu[i][text_cols-1] = '\0';
    540         }
    541         menu_top = i;
    542         for (; i < text_rows; ++i) {
    543             if (items[i-menu_top] == NULL) break;
    544             strncpy(menu[i], items[i-menu_top], text_cols-1);
    545             menu[i][text_cols-1] = '\0';
    546         }
    547         menu_items = i - menu_top;
    548         show_menu = 1;
    549         menu_sel = initial_selection;
    550         update_screen_locked();
    551     }
    552     pthread_mutex_unlock(&gUpdateMutex);
    553 }
    554 
    555 int ui_menu_select(int sel) {
    556     int old_sel;
    557     pthread_mutex_lock(&gUpdateMutex);
    558     if (show_menu > 0) {
    559         old_sel = menu_sel;
    560         menu_sel = sel;
    561         if (menu_sel < 0) menu_sel = 0;
    562         if (menu_sel >= menu_items) menu_sel = menu_items-1;
    563         sel = menu_sel;
    564         if (menu_sel != old_sel) update_screen_locked();
    565     }
    566     pthread_mutex_unlock(&gUpdateMutex);
    567     return sel;
    568 }
    569 
    570 void ui_end_menu() {
    571     int i;
    572     pthread_mutex_lock(&gUpdateMutex);
    573     if (show_menu > 0 && text_rows > 0 && text_cols > 0) {
    574         show_menu = 0;
    575         update_screen_locked();
    576     }
    577     pthread_mutex_unlock(&gUpdateMutex);
    578 }
    579 
    580 int ui_text_visible()
    581 {
    582     pthread_mutex_lock(&gUpdateMutex);
    583     int visible = show_text;
    584     pthread_mutex_unlock(&gUpdateMutex);
    585     return visible;
    586 }
    587 
    588 int ui_text_ever_visible()
    589 {
    590     pthread_mutex_lock(&gUpdateMutex);
    591     int ever_visible = show_text_ever;
    592     pthread_mutex_unlock(&gUpdateMutex);
    593     return ever_visible;
    594 }
    595 
    596 void ui_show_text(int visible)
    597 {
    598     pthread_mutex_lock(&gUpdateMutex);
    599     show_text = visible;
    600     if (show_text) show_text_ever = 1;
    601     update_screen_locked();
    602     pthread_mutex_unlock(&gUpdateMutex);
    603 }
    604 
    605 // Return true if USB is connected.
    606 static int usb_connected() {
    607     int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
    608     if (fd < 0) {
    609         printf("failed to open /sys/class/android_usb/android0/state: %s\n",
    610                strerror(errno));
    611         return 0;
    612     }
    613 
    614     char buf;
    615     /* USB is connected if android_usb state is CONNECTED or CONFIGURED */
    616     int connected = (read(fd, &buf, 1) == 1) && (buf == 'C');
    617     if (close(fd) < 0) {
    618         printf("failed to close /sys/class/android_usb/android0/state: %s\n",
    619                strerror(errno));
    620     }
    621     return connected;
    622 }
    623 
    624 int ui_wait_key()
    625 {
    626     pthread_mutex_lock(&key_queue_mutex);
    627 
    628     // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
    629     // plugged in.
    630     do {
    631         struct timeval now;
    632         struct timespec timeout;
    633         gettimeofday(&now, NULL);
    634         timeout.tv_sec = now.tv_sec;
    635         timeout.tv_nsec = now.tv_usec * 1000;
    636         timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
    637 
    638         int rc = 0;
    639         while (key_queue_len == 0 && rc != ETIMEDOUT) {
    640             rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex,
    641                                         &timeout);
    642         }
    643     } while (usb_connected() && key_queue_len == 0);
    644 
    645     int key = -1;
    646     if (key_queue_len > 0) {
    647         key = key_queue[0];
    648         memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
    649     }
    650     pthread_mutex_unlock(&key_queue_mutex);
    651     return key;
    652 }
    653 
    654 int ui_key_pressed(int key)
    655 {
    656     // This is a volatile static array, don't bother locking
    657     return key_pressed[key];
    658 }
    659 
    660 void ui_clear_key_queue() {
    661     pthread_mutex_lock(&key_queue_mutex);
    662     key_queue_len = 0;
    663     pthread_mutex_unlock(&key_queue_mutex);
    664 }
    665