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]
Change-Id: I13f0c9ae5444652a2141442ef24258679a78d320
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index 9497df5..84711d3 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -110,6 +110,7 @@
         break;
       }
     }
+    ui->CancelWaitKey();
     *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
     break;
   }
@@ -272,7 +273,7 @@
 //                               b11. exit the listening loop
 //
 static void CreateMinadbdServiceAndExecuteCommands(
-    RecoveryUI* ui, const std::map<MinadbdCommand, CommandFunction>& command_map,
+    Device* device, const std::map<MinadbdCommand, CommandFunction>& command_map,
     bool rescue_mode) {
   signal(SIGPIPE, SIG_IGN);
 
@@ -312,8 +313,23 @@
     return;
   }
 
+  RecoveryUI* ui = device->GetUI();
   std::thread listener_thread(ListenAndExecuteMinadbdCommands, ui, child,
                               std::move(recovery_socket), std::ref(command_map));
+
+  if (ui->IsTextVisible()) {
+    std::vector<std::string> headers{ rescue_mode ? "Rescue mode" : "ADB Sideload" };
+    std::vector<std::string> entries{ "Cancel" };
+    size_t chosen_item = ui->ShowMenu(
+        headers, entries, 0, true,
+        std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
+
+    if (chosen_item != Device::kRefresh) {
+      // Kill minadbd if 'cancel' was selected, to abort sideload.
+      kill(child, SIGKILL);
+    }
+  }
+
   if (listener_thread.joinable()) {
     listener_thread.join();
   }
@@ -372,7 +388,7 @@
     ui->Print("\n\nWaiting for rescue commands...\n");
   }
 
-  CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode);
+  CreateMinadbdServiceAndExecuteCommands(device, command_map, rescue_mode);
 
   // Clean up before switching to the older state, for example setting the state
   // to none sets sys/class/android_usb/android0/enable to 0.
diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp
index cee07a4..e09431a 100644
--- a/recovery_ui/device.cpp
+++ b/recovery_ui/device.cpp
@@ -103,6 +103,9 @@
     case KEY_BACK:
       return kGoBack;
 
+    case KEY_REFRESH:
+      return kRefresh;
+
     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 7efa075..d532d86 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -35,6 +35,7 @@
   static constexpr const int kInvokeItem = -4;
   static constexpr const int kGoBack = -5;
   static constexpr const int kGoHome = -6;
+  static constexpr const int kRefresh = -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 58fef6c..a53576a 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 112620c..96b454a 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -1237,6 +1237,9 @@
           break;
         case Device::kNoAction:
           break;
+        case Device::kRefresh:
+          chosen_item = Device::kRefresh;
+          break;
         case Device::kGoBack:
           chosen_item = Device::kGoBack;
           break;
@@ -1247,7 +1250,9 @@
     } 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::kRefresh) {
       break;
     }
   }
diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp
index aced5b0..17f28df 100644
--- a/recovery_ui/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -554,6 +554,10 @@
   return key;
 }
 
+void RecoveryUI::CancelWaitKey() {
+  EnqueueKey(KEY_REFRESH);
+}
+
 void RecoveryUI::InterruptKey() {
   {
     std::lock_guard<std::mutex> lg(key_queue_mutex);