recovery: Provide sideload cancellation

We can't use InterruptWaitKey() as it hangs recovery
when called from the minadbd listener thread, so provide
our own 'CancelWaitKey' implementation.
[forkbomb: rework for Q]

Squashed:
  recovery: Move sideload (non)cancellation to its own key

   * We use KEY_REFRESH for actually refreshing menus, which is
     useful when handling hotpluggable storage.
     We don't want that to interact with the sideload menu.

  Change-Id: Id7f5c06385e613648b3f727fc5e91eb3206406cf

Change-Id: I13f0c9ae5444652a2141442ef24258679a78d320
diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp
index 6d5943f..7d67980 100644
--- a/recovery_ui/device.cpp
+++ b/recovery_ui/device.cpp
@@ -104,6 +104,9 @@
     case KEY_BACK:
       return kGoBack;
 
+    case KEY_AGAIN:
+      return kDoSideload;
+
     default:
       // If you have all of the above buttons, any other buttons
       // are ignored. Otherwise, any button cycles the highlight.
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index 9eaccc7..98120e0 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -38,6 +38,7 @@
   static constexpr const int kInvokeItem = -4;
   static constexpr const int kGoBack = -5;
   static constexpr const int kGoHome = -6;
+  static constexpr const int kDoSideload = -7;
 
   // ENTER vs REBOOT: The latter will trigger a reboot that goes through bootloader, which allows
   // using a new bootloader / recovery image if applicable. For example, REBOOT_RESCUE goes from
diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h
index 7ff797f..5474aa0 100644
--- a/recovery_ui/include/recovery_ui/ui.h
+++ b/recovery_ui/include/recovery_ui/ui.h
@@ -156,6 +156,7 @@
   // KeyError::INTERRUPTED on a key interrupt.
   virtual int WaitKey();
 
+  virtual void CancelWaitKey();
   // Wakes up the UI if it is waiting on key input, causing WaitKey to return KeyError::INTERRUPTED.
   virtual void InterruptKey();
 
diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp
index 578dd14..58c50d1 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -1290,11 +1290,16 @@
         case Device::kGoHome:
           chosen_item = Device::kGoHome;
           break;
+        case Device::kDoSideload:
+          chosen_item = Device::kDoSideload;
+          break;
       }
     } else if (!menu_only) {
       chosen_item = action;
     }
-    if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome) {
+
+    if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
+        chosen_item == Device::kDoSideload) {
       break;
     }
   }
diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp
index 91b3edb..084b568 100644
--- a/recovery_ui/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -562,6 +562,10 @@
   return key;
 }
 
+void RecoveryUI::CancelWaitKey() {
+  EnqueueKey(KEY_AGAIN);
+}
+
 void RecoveryUI::InterruptKey() {
   {
     std::lock_guard<std::mutex> lg(key_queue_mutex);