aboutsummaryrefslogtreecommitdiffstats
path: root/ui.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ui.cpp')
-rw-r--r--ui.cpp552
1 files changed, 525 insertions, 27 deletions
diff --git a/ui.cpp b/ui.cpp
index 1a0b079..d1eca91 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -27,8 +27,11 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
+#include <sys/epoll.h>
+#include <cutils/properties.h>
#include <cutils/android_reboot.h>
+#include <cutils/properties.h>
#include "common.h"
#include "roots.h"
@@ -37,19 +40,113 @@
#include "screen_ui.h"
#include "ui.h"
+#include "voldclient.h"
+
+#include "messagesocket.h"
+
#define UI_WAIT_KEY_TIMEOUT_SEC 120
+/* Some extra input defines */
+#ifndef ABS_MT_ANGLE
+#define ABS_MT_ANGLE 0x38
+#endif
+
+static int string_split(char* s, char** fields, int maxfields)
+{
+ int n = 0;
+ while (n+1 < maxfields) {
+ char* p = strchr(s, ' ');
+ if (!p)
+ break;
+ *p = '\0';
+ printf("string_split: field[%d]=%s\n", n, s);
+ fields[n++] = s;
+ s = p+1;
+ }
+ fields[n] = s;
+ printf("string_split: last field[%d]=%s\n", n, s);
+ return n+1;
+}
+
+struct ms_info {
+ RecoveryUI* ui;
+ MessageSocket* sock;
+};
+
+static int message_socket_client_event(int fd, uint32_t epevents, void *data)
+{
+ ms_info* info = (ms_info*)data;
+
+ printf("message_socket client event\n");
+ if (!(epevents & EPOLLIN)) {
+ return 0;
+ }
+
+ char buf[256];
+ ssize_t nread;
+ nread = info->sock->Read(buf, sizeof(buf));
+ if (nread <= 0) {
+ ev_del_fd(fd);
+ info->ui->DialogDismiss();
+ info->sock->Close();
+ delete info->sock;
+ delete info;
+ return 0;
+ }
+
+ printf("message_socket client message <%s>\n", buf);
+
+ // Parse the message. Right now we support:
+ // dialog show <string>
+ // dialog dismiss
+ char* fields[3];
+ int nfields;
+ nfields = string_split(buf, fields, 3);
+ printf("fields=%d\n", nfields);
+ if (nfields < 2)
+ return 0;
+ printf("field[0]=%s, field[1]=%s\n", fields[0], fields[1]);
+ if (strcmp(fields[0], "dialog") == 0) {
+ if (strcmp(fields[1], "show") == 0 && nfields > 2) {
+ info->ui->DialogShowInfo(fields[2]);
+ }
+ if (strcmp(fields[1], "dismiss") == 0) {
+ info->ui->DialogDismiss();
+ }
+ }
+
+ return 0;
+}
+
+static int message_socket_listen_event(int fd, uint32_t epevents, void *data)
+{
+ ms_info* info = (ms_info*)data;
+ MessageSocket* sock = info->sock->Accept();
+ printf("message_socket_listen_event: event on %d\n", fd);
+ if (sock) {
+ printf("message_socket client connected\n");
+ ms_info* clientinfo = new ms_info;
+ clientinfo->ui = info->ui;
+ clientinfo->sock = sock;
+ ev_add_fd(sock->fd(), message_socket_client_event, clientinfo);
+ }
+ return 0;
+}
+
RecoveryUI::RecoveryUI()
: key_queue_len(0),
key_last_down(-1),
key_long_press(false),
key_down_count(0),
enable_reboot(true),
+ v_changed(0),
consecutive_power_keys(0),
last_key(-1),
has_power_key(false),
has_up_key(false),
- has_down_key(false) {
+ has_down_key(false),
+ has_back_key(false),
+ has_home_key(false) {
pthread_mutex_init(&key_queue_mutex, nullptr);
pthread_cond_init(&key_queue_cond, nullptr);
memset(key_pressed, 0, sizeof(key_pressed));
@@ -62,6 +159,12 @@ void RecoveryUI::OnKeyDetected(int key_code) {
has_down_key = true;
} else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
has_up_key = true;
+ } else if (key_code == KEY_BACK) {
+ has_back_key = true;
+ LOGI("Detected back key, disabling virtual back button\n");
+ } else if (key_code == KEY_HOMEPAGE || key_code == KEY_HOME) {
+ has_home_key = true;
+ LOGI("Detected home key, disabling virtual home button\n");
}
}
@@ -80,8 +183,15 @@ static void* InputThreadLoop(void*) {
}
void RecoveryUI::Init() {
+ calibrate_swipe();
ev_init(InputCallback, this);
+ message_socket.ServerInit();
+ ms_info* info = new ms_info;
+ info->ui = this;
+ info->sock = &message_socket;
+ ev_add_fd(message_socket.fd(), message_socket_listen_event, info);
+
ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
@@ -93,31 +203,45 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
return -1;
}
- if (ev.type == EV_SYN) {
- return 0;
- } else if (ev.type == EV_REL) {
- if (ev.code == REL_Y) {
- // accumulate the up or down motion reported by
- // the trackball. When it exceeds a threshold
- // (positive or negative), fake an up/down
- // key event.
- rel_sum += ev.value;
- if (rel_sum > 3) {
- ProcessKey(KEY_DOWN, 1); // press down key
- ProcessKey(KEY_DOWN, 0); // and release it
- rel_sum = 0;
- } else if (rel_sum < -3) {
- ProcessKey(KEY_UP, 1); // press up key
- ProcessKey(KEY_UP, 0); // and release it
- rel_sum = 0;
- }
+ input_device* dev = NULL;
+ int n;
+ for (n = 0; n < MAX_NR_INPUT_DEVICES; ++n) {
+ if (input_devices[n].fd == fd) {
+ dev = &input_devices[n];
+ break;
}
- } else {
- rel_sum = 0;
+ if (input_devices[n].fd == -1) {
+ dev = &input_devices[n];
+ memset(dev, 0, sizeof(input_device));
+ dev->fd = fd;
+ dev->tracking_id = -1;
+ calibrate_touch(dev);
+ setup_vkeys(dev);
+ break;
+ }
+ }
+ if (!dev) {
+ LOGE("input_callback: no more available input devices\n");
+ return -1;
}
- if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
- ProcessKey(ev.code, ev.value);
+ if (ev.type != EV_REL) {
+ dev->rel_sum = 0;
+ }
+
+ switch (ev.type) {
+ case EV_SYN:
+ ProcessSyn(dev, ev.code, ev.value);
+ break;
+ case EV_ABS:
+ ProcessAbs(dev, ev.code, ev.value);
+ break;
+ case EV_REL:
+ ProcessRel(dev, ev.code, ev.value);
+ break;
+ case EV_KEY:
+ ProcessKey(dev, ev.code, ev.value);
+ break;
}
return 0;
@@ -135,11 +259,14 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
// a key is registered.
//
// updown == 1 for key down events; 0 for key up events
-void RecoveryUI::ProcessKey(int key_code, int updown) {
+void RecoveryUI::ProcessKey(input_device* dev, int key_code, int updown) {
bool register_key = false;
bool long_press = false;
bool reboot_enabled;
+ if (key_code > KEY_MAX)
+ return;
+
pthread_mutex_lock(&key_queue_mutex);
key_pressed[key_code] = updown;
if (updown) {
@@ -173,8 +300,12 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
break;
case RecoveryUI::REBOOT:
+#ifndef VERIFIER_TEST
+ vdc->unmountAll();
+#endif
if (reboot_enabled) {
- android_reboot(ANDROID_RB_RESTART, 0, 0);
+ property_set(ANDROID_RB_PROPERTY, "reboot,");
+ while(1) { pause(); }
}
break;
@@ -185,6 +316,146 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
}
}
+void RecoveryUI::ProcessSyn(input_device* dev, int code, int value) {
+ /*
+ * Type A device release:
+ * 1. Lack of position update
+ * 2. BTN_TOUCH | ABS_PRESSURE | SYN_MT_REPORT
+ * 3. SYN_REPORT
+ *
+ * Type B device release:
+ * 1. ABS_MT_TRACKING_ID == -1 for "first" slot
+ * 2. SYN_REPORT
+ */
+
+ if (code == SYN_MT_REPORT) {
+ if (!dev->in_touch && (dev->saw_pos_x && dev->saw_pos_y)) {
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type a press\n");
+#endif
+ handle_press(dev);
+ }
+ dev->saw_mt_report = true;
+ return;
+ }
+ if (code == SYN_REPORT) {
+ if (dev->in_touch) {
+ handle_gestures(dev);
+ }
+ else {
+ if (dev->saw_tracking_id) {
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type b press\n");
+#endif
+ handle_press(dev);
+ }
+ }
+
+ /* Detect release */
+ if (dev->saw_mt_report) {
+ if (dev->in_touch && !dev->saw_pos_x && !dev->saw_pos_y) {
+ /* type A release */
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type a release\n");
+#endif
+ handle_release(dev);
+ dev->slot_first = 0;
+ }
+ }
+ else {
+ if (dev->in_touch && dev->saw_tracking_id && dev->tracking_id == -1 &&
+ dev->slot_current == dev->slot_first) {
+ /* type B release */
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type b release\n");
+#endif
+ handle_release(dev);
+ dev->slot_first = 0;
+ }
+ }
+
+ dev->saw_pos_x = dev->saw_pos_y = false;
+ dev->saw_mt_report = dev->saw_tracking_id = false;
+ }
+}
+
+void RecoveryUI::ProcessAbs(input_device* dev, int code, int value) {
+ if (code == ABS_MT_SLOT) {
+ dev->slot_current = value;
+ if (dev->slot_first == -1) {
+ dev->slot_first = value;
+ }
+ return;
+ }
+ if (code == ABS_MT_TRACKING_ID) {
+ /*
+ * Some devices send an initial ABS_MT_SLOT event before switching
+ * to type B events, so discard any type A state related to slot.
+ */
+ dev->saw_tracking_id = true;
+ dev->slot_first = dev->slot_current = 0;
+
+ if (value != dev->tracking_id) {
+ dev->tracking_id = value;
+ if (dev->tracking_id < 0) {
+ dev->slot_nr_active--;
+ }
+ else {
+ dev->slot_nr_active++;
+ }
+ }
+ return;
+ }
+ /*
+ * For type A devices, we "lock" onto the first coordinates by ignoring
+ * position updates from the time we see a SYN_MT_REPORT until the next
+ * SYN_REPORT
+ *
+ * For type B devices, we "lock" onto the first slot seen until all slots
+ * are released
+ */
+ if (dev->slot_nr_active == 0) {
+ /* type A */
+ if (dev->saw_pos_x && dev->saw_pos_y) {
+ return;
+ }
+ }
+ else {
+ if (dev->slot_current != dev->slot_first) {
+ return;
+ }
+ }
+ if (code == ABS_MT_POSITION_X) {
+ dev->saw_pos_x = true;
+ dev->touch_pos.x = value * fb_dimensions.x / (dev->touch_max.x - dev->touch_min.x);
+ }
+ else if (code == ABS_MT_POSITION_Y) {
+ dev->saw_pos_y = true;
+ dev->touch_pos.y = value * fb_dimensions.y / (dev->touch_max.y - dev->touch_min.y);
+ }
+}
+
+void RecoveryUI::ProcessRel(input_device* dev, int code, int value) {
+#ifdef BOARD_RECOVERY_NEEDS_REL_INPUT
+ if (code == REL_Y) {
+ // accumulate the up or down motion reported by
+ // the trackball. When it exceeds a threshold
+ // (positive or negative), fake an up/down
+ // key event.
+ dev->rel_sum += value;
+ if (dev->rel_sum > 3) {
+ process_key(dev, KEY_DOWN, 1); // press down key
+ process_key(dev, KEY_DOWN, 0); // and release it
+ dev->rel_sum = 0;
+ } else if (dev->rel_sum < -3) {
+ process_key(dev, KEY_UP, 1); // press up key
+ process_key(dev, KEY_UP, 0); // and release it
+ dev->rel_sum = 0;
+ }
+ }
+#endif
+}
+
void* RecoveryUI::time_key_helper(void* cookie) {
key_timer_t* info = (key_timer_t*) cookie;
info->ui->time_key(info->key_code, info->count);
@@ -203,7 +474,212 @@ void RecoveryUI::time_key(int key_code, int count) {
if (long_press) KeyLongPress(key_code);
}
+void RecoveryUI::calibrate_touch(input_device* dev) {
+ fb_dimensions.x = gr_fb_width();
+ fb_dimensions.y = gr_fb_height();
+
+ struct input_absinfo info;
+ memset(&info, 0, sizeof(info));
+ if (ioctl(dev->fd, EVIOCGABS(ABS_MT_POSITION_X), &info) == 0) {
+ dev->touch_min.x = info.minimum;
+ dev->touch_max.x = info.maximum;
+ dev->touch_pos.x = info.value;
+ }
+ memset(&info, 0, sizeof(info));
+ if (ioctl(dev->fd, EVIOCGABS(ABS_MT_POSITION_Y), &info) == 0) {
+ dev->touch_min.y = info.minimum;
+ dev->touch_max.y = info.maximum;
+ dev->touch_pos.y = info.value;
+ }
+#ifdef DEBUG_TOUCH
+ LOGI("calibrate_touch: fd=%d, (%d,%d)-(%d,%d) pos (%d,%d)\n", dev->fd,
+ dev->touch_min.x, dev->touch_min.y,
+ dev->touch_max.x, dev->touch_max.y,
+ dev->touch_pos.x, dev->touch_pos.y);
+#endif
+}
+
+void RecoveryUI::setup_vkeys(input_device* dev) {
+ int n;
+ char name[256];
+ char path[PATH_MAX];
+ char buf[64*MAX_NR_VKEYS];
+
+ for (n = 0; n < MAX_NR_VKEYS; ++n) {
+ dev->virtual_keys[n].keycode = -1;
+ }
+
+ memset(name, 0, sizeof(name));
+ if (ioctl(dev->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
+ LOGI("setup_vkeys: no vkeys\n");
+ return;
+ }
+ sprintf(path, "/sys/board_properties/virtualkeys.%s", name);
+ int vkfd = open(path, O_RDONLY);
+ if (vkfd < 0) {
+ LOGI("setup_vkeys: could not open %s\n", path);
+ return;
+ }
+ ssize_t len = read(vkfd, buf, sizeof(buf));
+ close(vkfd);
+ if (len <= 0) {
+ LOGE("setup_vkeys: could not read %s\n", path);
+ return;
+ }
+ buf[len] = '\0';
+
+ char* p = buf;
+ char* endp;
+ for (n = 0; n < MAX_NR_VKEYS && p < buf+len && *p == '0'; ++n) {
+ int val[6];
+ int f;
+ for (f = 0; *p && f < 6; ++f) {
+ val[f] = strtol(p, &endp, 0);
+ if (p == endp)
+ break;
+ p = endp+1;
+ }
+ if (f != 6 || val[0] != 0x01)
+ break;
+ dev->virtual_keys[n].keycode = val[1];
+ dev->virtual_keys[n].min.x = val[2] - val[4]/2;
+ dev->virtual_keys[n].min.y = val[3] - val[5]/2;
+ dev->virtual_keys[n].max.x = val[2] + val[4]/2;
+ dev->virtual_keys[n].max.y = val[3] + val[5]/2;
+
+#ifdef DEBUG_TOUCH
+ LOGI("vkey: fd=%d, [%d]=(%d,%d)-(%d,%d)\n", dev->fd,
+ dev->virtual_keys[n].keycode,
+ dev->virtual_keys[n].min.x, dev->virtual_keys[n].min.y,
+ dev->virtual_keys[n].max.x, dev->virtual_keys[n].max.y);
+#endif
+ }
+}
+
+void RecoveryUI::calibrate_swipe() {
+ char strvalue[PROPERTY_VALUE_MAX];
+ int intvalue;
+ property_get("ro.sf.lcd_density", strvalue, "160");
+ intvalue = atoi(strvalue);
+ int screen_density = (intvalue >= 160 ? intvalue : 160);
+ min_swipe_px.x = screen_density * 50 / 100; // Roughly 0.5in
+ min_swipe_px.y = screen_density * 30 / 100; // Roughly 0.3in
+#ifdef DEBUG_TOUCH
+ LOGI("calibrate_swipe: density=%d, min_swipe=(%d,%d)\n",
+ screen_density, min_swipe_px.x, min_swipe_px.y);
+#endif
+}
+
+void RecoveryUI::handle_press(input_device* dev) {
+ dev->touch_start = dev->touch_track = dev->touch_pos;
+ dev->in_touch = true;
+ dev->in_swipe = false;
+ if (dev->touch_pos.y >= gr_fb_height() - GetSysbarHeight()) {
+ SetSysbarState(1 << (3 * dev->touch_pos.x / gr_fb_width()));
+ }
+ else {
+ SetSysbarState(0);
+ }
+}
+
+void RecoveryUI::handle_release(input_device* dev) {
+ struct point diff = dev->touch_pos - dev->touch_start;
+ bool in_touch = dev->in_touch;
+ bool in_swipe = dev->in_swipe;
+
+ dev->in_touch = dev->in_swipe = false;
+
+ if (!in_swipe) {
+ int n;
+ for (n = 0; dev->virtual_keys[n].keycode != -1 && n < MAX_NR_VKEYS; ++n) {
+ vkey* vk = &dev->virtual_keys[n];
+ if (dev->touch_start.x >= vk->min.x && dev->touch_start.x < vk->max.x &&
+ dev->touch_start.y >= vk->min.y && dev->touch_start.y < vk->max.y) {
+#ifdef DEBUG_TOUCH
+ LOGI("handle_release: vkey %d\n", vk->keycode);
+#endif
+ EnqueueKey(vk->keycode);
+ return;
+ }
+ }
+
+ int sysbar_state = GetSysbarState();
+ SetSysbarState(0);
+ if (sysbar_state == SYSBAR_BACK) {
+ ProcessKey(dev, KEY_BACK, 1);
+ ProcessKey(dev, KEY_BACK, 0);
+ return;
+ }
+ if (sysbar_state == SYSBAR_HOME) {
+ ProcessKey(dev, KEY_HOME, 1);
+ ProcessKey(dev, KEY_HOME, 0);
+ return;
+ }
+ }
+
+ if (DialogShowing()) {
+ if (DialogDismissable() && !dev->in_swipe) {
+ DialogDismiss();
+ }
+ return;
+ }
+
+ if (in_swipe) {
+ if (abs(diff.x) > abs(diff.y)) {
+ if (abs(diff.x) > min_swipe_px.x) {
+ int key = (diff.x > 0 ? KEY_ENTER : KEY_BACK);
+ ProcessKey(dev, key, 1);
+ ProcessKey(dev, key, 0);
+ }
+ }
+ else {
+ /* Vertical swipe, handled realtime */
+ }
+ }
+ else {
+ int sel, start_menu_pos;
+ // Make sure touch pos is not less than menu start pos.
+ // No need to check if beyond end of menu items, since
+ // that is checked by get_menu_selection().
+ start_menu_pos = MenuItemStart();
+ if (dev->touch_pos.y >= start_menu_pos) {
+ sel = (dev->touch_pos.y - start_menu_pos)/MenuItemHeight();
+ EnqueueKey(KEY_FLAG_ABS | sel);
+ }
+ }
+}
+
+void RecoveryUI::handle_gestures(input_device* dev) {
+ struct point diff;
+ diff = dev->touch_pos - dev->touch_start;
+
+ if (abs(diff.x) > abs(diff.y)) {
+ if (abs(diff.x) > min_swipe_px.x) {
+ /* Horizontal swipe, handle it on release */
+ dev->in_swipe = true;
+ }
+ }
+ else {
+ diff.y = dev->touch_pos.y - dev->touch_track.y;
+ if (abs(diff.y) > MenuItemHeight()) {
+ dev->in_swipe = true;
+ if (!DialogShowing()) {
+ dev->touch_track = dev->touch_pos;
+ int key = (diff.y < 0) ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
+ ProcessKey(dev, key, 1);
+ ProcessKey(dev, key, 0);
+ }
+ }
+ }
+}
+
void RecoveryUI::EnqueueKey(int key_code) {
+ if (DialogShowing()) {
+ if (DialogDismissable()) {
+ DialogDismiss();
+ }
+ return;
+ }
pthread_mutex_lock(&key_queue_mutex);
const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
if (key_queue_len < queue_max) {
@@ -215,6 +691,7 @@ void RecoveryUI::EnqueueKey(int key_code) {
int RecoveryUI::WaitKey() {
pthread_mutex_lock(&key_queue_mutex);
+ int timeouts = UI_WAIT_KEY_TIMEOUT_SEC;
// Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
// plugged in.
@@ -224,13 +701,18 @@ int RecoveryUI::WaitKey() {
gettimeofday(&now, nullptr);
timeout.tv_sec = now.tv_sec;
timeout.tv_nsec = now.tv_usec * 1000;
- timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
+ timeout.tv_sec += 1;
int rc = 0;
while (key_queue_len == 0 && rc != ETIMEDOUT) {
rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout);
+ if (VolumesChanged()) {
+ pthread_mutex_unlock(&key_queue_mutex);
+ return Device::kRefresh;
+ }
+ timeouts--;
}
- } while (IsUsbConnected() && key_queue_len == 0);
+ } while ((timeouts || IsUsbConnected()) && key_queue_len == 0);
int key = -1;
if (key_queue_len > 0) {
@@ -241,6 +723,15 @@ int RecoveryUI::WaitKey() {
return key;
}
+void RecoveryUI::CancelWaitKey()
+{
+ pthread_mutex_lock(&key_queue_mutex);
+ key_queue[key_queue_len] = -2;
+ key_queue_len++;
+ pthread_cond_signal(&key_queue_cond);
+ pthread_mutex_unlock(&key_queue_mutex);
+}
+
bool RecoveryUI::IsUsbConnected() {
int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
if (fd < 0) {
@@ -335,3 +826,10 @@ void RecoveryUI::SetEnableReboot(bool enabled) {
enable_reboot = enabled;
pthread_mutex_unlock(&key_queue_mutex);
}
+
+bool RecoveryUI::VolumesChanged() {
+ int ret = v_changed;
+ if (v_changed > 0)
+ v_changed = 0;
+ return ret == 1;
+}