recovery: ui: Support hardware virtual keys

 * Also swipe left -> KEY_BACK

Change-Id: I6bd8054485d680df35abb86cb79f1dda683e4459
diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp
index 6e239ef..aed6f49 100644
--- a/recovery_ui/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -89,6 +89,48 @@
   }
 }
 
+void RecoveryUI::OnTouchDeviceDetected(int fd) {
+  char name[256];
+  char path[PATH_MAX];
+  char buf[4096];
+
+  memset(name, 0, sizeof(name));
+  if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
+    return;
+  }
+  sprintf(path, "/sys/board_properties/virtualkeys.%s", name);
+  int vkfd = open(path, O_RDONLY);
+  if (vkfd < 0) {
+    LOG(INFO) << "vkeys: could not open " << path;
+    return;
+  }
+  ssize_t len = read(vkfd, buf, sizeof(buf));
+  close(vkfd);
+  if (len <= 0) {
+    LOG(ERROR) << "vkeys: could not read " << path;
+    return;
+  }
+  buf[len] = '\0';
+
+  char* p = buf;
+  char* endp;
+  for (size_t n = 0; 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;
+    vkey_t vk;
+    vk.keycode = val[1];
+    vk.min_ = Point(val[2] - val[4] / 2, val[3] - val[5] / 2);
+    vk.max_ = Point(val[2] + val[4] / 2, val[3] + val[5] / 2);
+    virtual_keys_.push_back(vk);
+  }
+}
+
 void RecoveryUI::OnKeyDetected(int key_code) {
   if (key_code == KEY_POWER) {
     has_power_key = true;
@@ -147,7 +189,9 @@
   ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
 
   if (touch_screen_allowed_) {
-    ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
+    ev_iterate_touch_inputs(
+        std::bind(&RecoveryUI::OnTouchDeviceDetected, this, std::placeholders::_1),
+        std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
 
     // Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of
     // "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way
@@ -191,6 +235,14 @@
   } else if (abs(delta.x()) < touch_low_threshold_ && abs(delta.y()) > touch_high_threshold_) {
     direction = delta.y() < 0 ? SwipeDirection::UP : SwipeDirection::DOWN;
   } else {
+    for (const auto& vk : virtual_keys_) {
+      if (touch_start_.x() >= vk.min_.x() && touch_start_.x() < vk.max_.x() &&
+          touch_start_.y() >= vk.min_.y() && touch_start_.y() < vk.max_.y()) {
+        ProcessKey(vk.keycode, 1);  // press key
+        ProcessKey(vk.keycode, 0);  // and release it
+        return;
+      }
+    }
     LOG(DEBUG) << "Ignored " << delta.x() << " " << delta.y() << " (low: " << touch_low_threshold_
                << ", high: " << touch_high_threshold_ << ")";
     return;
@@ -215,6 +267,9 @@
       break;
 
     case SwipeDirection::LEFT:
+      ProcessKey(KEY_BACK, 1);  // press back key
+      ProcessKey(KEY_BACK, 0);  // and release it
+      break;
     case SwipeDirection::RIGHT:
       ProcessKey(KEY_POWER, 1);  // press power key
       ProcessKey(KEY_POWER, 0);  // and release it