Allow entering rescue mode via recovery UI.

Only enabled on debuggable builds.

Bug: 128415917
Test: Sideload package on taimen.
Test: Choose "Enter rescue" from recovery UI.
Change-Id: I913dbdbcffd3179e6fa72ca862f74ca8f1364b02
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index 548b6e5..f430920 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -31,6 +31,7 @@
 #include <atomic>
 #include <functional>
 #include <map>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -42,6 +43,7 @@
 #include "fuse_sideload.h"
 #include "install/install.h"
 #include "minadbd_types.h"
+#include "otautil/sysutil.h"
 #include "recovery_ui/ui.h"
 
 using CommandFunction = std::function<bool()>;
@@ -228,7 +230,7 @@
 //                               b11. exit the listening loop
 //
 static void CreateMinadbdServiceAndExecuteCommands(
-    const std::map<MinadbdCommands, CommandFunction>& command_map) {
+    const std::map<MinadbdCommands, CommandFunction>& command_map, bool rescue_mode) {
   signal(SIGPIPE, SIG_IGN);
 
   android::base::unique_fd recovery_socket;
@@ -245,9 +247,16 @@
   }
   if (child == 0) {
     recovery_socket.reset();
-    execl("/system/bin/minadbd", "minadbd", "--socket_fd",
-          std::to_string(minadbd_socket.release()).c_str(), nullptr);
-
+    std::vector<std::string> minadbd_commands = {
+      "/system/bin/minadbd",
+      "--socket_fd",
+      std::to_string(minadbd_socket.release()),
+    };
+    if (rescue_mode) {
+      minadbd_commands.push_back("--rescue");
+    }
+    auto exec_args = StringVectorToNullTerminatedArray(minadbd_commands);
+    execv(exec_args[0], exec_args.data());
     _exit(EXIT_FAILURE);
   }
 
@@ -280,7 +289,7 @@
   signal(SIGPIPE, SIG_DFL);
 }
 
-int apply_from_adb(RecoveryUI* ui) {
+int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) {
   // Save the usb state to restore after the sideload operation.
   std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
   // Clean up state and stop adbd.
@@ -289,16 +298,20 @@
     return INSTALL_ERROR;
   }
 
-  ui->Print(
-      "\n\nNow send the package you want to apply\n"
-      "to the device with \"adb sideload <filename>\"...\n");
+  if (!rescue_mode) {
+    ui->Print(
+        "\n\nNow send the package you want to apply\n"
+        "to the device with \"adb sideload <filename>\"...\n");
+  } else {
+    ui->Print("\n\nWaiting for rescue commands...\n");
+  }
 
   int install_result = INSTALL_ERROR;
   std::map<MinadbdCommands, CommandFunction> command_map{
     { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
   };
 
-  CreateMinadbdServiceAndExecuteCommands(command_map);
+  CreateMinadbdServiceAndExecuteCommands(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/install/include/install/adb_install.h b/install/include/install/adb_install.h
index f7b065b..208d0c7 100644
--- a/install/include/install/adb_install.h
+++ b/install/include/install/adb_install.h
@@ -18,4 +18,4 @@
 
 #include <recovery_ui/ui.h>
 
-int apply_from_adb(RecoveryUI* ui);
+int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode);
diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp
index 57158ad..c80d549 100644
--- a/minadbd/minadbd.cpp
+++ b/minadbd/minadbd.cpp
@@ -31,10 +31,13 @@
 #include "minadbd_services.h"
 #include "minadbd_types.h"
 
+using namespace std::string_literals;
+
 int main(int argc, char** argv) {
   android::base::InitLogging(argv, &android::base::StderrLogger);
   // TODO(xunchang) implement a command parser
-  if (argc != 3 || strcmp("--socket_fd", argv[1]) != 0) {
+  if ((argc != 3 && argc != 4) || argv[1] != "--socket_fd"s ||
+      (argc == 4 && argv[3] != "--rescue"s)) {
     LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc;
     exit(kMinadbdArgumentsParsingError);
   }
@@ -50,7 +53,12 @@
   }
   SetMinadbdSocketFd(socket_fd);
 
-  adb_device_banner = "sideload";
+  if (argc == 4) {
+    SetMinadbdRescueMode(true);
+    adb_device_banner = "rescue";
+  } else {
+    adb_device_banner = "sideload";
+  }
 
   signal(SIGPIPE, SIG_IGN);
 
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index f2b65c0..eaf88ec 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -45,10 +45,16 @@
 #include "sysdeps.h"
 
 static int minadbd_socket = -1;
+static bool rescue_mode = false;
+
 void SetMinadbdSocketFd(int socket_fd) {
   minadbd_socket = socket_fd;
 }
 
+void SetMinadbdRescueMode(bool rescue) {
+  rescue_mode = rescue;
+}
+
 static bool WriteCommandToFd(MinadbdCommands cmd, int fd) {
   char message[kMinadbdMessageSize];
   memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix));
diff --git a/minadbd/minadbd_services.h b/minadbd/minadbd_services.h
index 6835bd7..20e3410 100644
--- a/minadbd/minadbd_services.h
+++ b/minadbd/minadbd_services.h
@@ -17,3 +17,5 @@
 #pragma once
 
 void SetMinadbdSocketFd(int socket_fd);
+
+void SetMinadbdRescueMode(bool);
diff --git a/recovery.cpp b/recovery.cpp
index 0e6e497..ce29cb2 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -538,12 +538,20 @@
         break;
       }
       case Device::APPLY_ADB_SIDELOAD:
-      case Device::APPLY_SDCARD: {
+      case Device::APPLY_SDCARD:
+      case Device::ENTER_RESCUE: {
         save_current_log = true;
-        bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
-        if (adb) {
-          status = apply_from_adb(ui);
+
+        bool adb = true;
+        if (chosen_action == Device::ENTER_RESCUE) {
+          // Switch to graphics screen.
+          ui->ShowText(false);
+          status = ApplyFromAdb(ui, true /* rescue_mode */);
+          ui->ShowText(true);
+        } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) {
+          status = ApplyFromAdb(ui, false /* rescue_mode */);
         } else {
+          adb = false;
           status = ApplyFromSdcard(device, ui);
         }
 
@@ -926,7 +934,7 @@
     if (!sideload_auto_reboot) {
       ui->ShowText(true);
     }
-    status = apply_from_adb(ui);
+    status = ApplyFromAdb(ui, false /* rescue_mode */);
     ui->Print("\nInstall from ADB complete (status: %d).\n", status);
     if (sideload_auto_reboot) {
       ui->Print("Rebooting automatically.\n");
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 5f3ab76..38e1db7 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -423,6 +423,10 @@
     device->RemoveMenuItemForAction(Device::ENTER_FASTBOOT);
   }
 
+  if (!is_ro_debuggable()) {
+    device->RemoveMenuItemForAction(Device::ENTER_RESCUE);
+  }
+
   ui->SetBackground(RecoveryUI::NONE);
   if (show_text) ui->ShowText(true);
 
diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp
index ddb0118..e7ae1a3 100644
--- a/recovery_ui/device.cpp
+++ b/recovery_ui/device.cpp
@@ -37,6 +37,7 @@
   { "View recovery logs", Device::VIEW_RECOVERY_LOGS },
   { "Run graphics test", Device::RUN_GRAPHICS_TEST },
   { "Run locale test", Device::RUN_LOCALE_TEST },
+  { "Enter rescue", Device::ENTER_RESCUE },
   { "Power off", Device::SHUTDOWN },
 };
 
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index 3c44510..8f17639 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -50,6 +50,7 @@
     KEY_INTERRUPTED = 13,
     ENTER_FASTBOOT = 14,
     ENTER_RECOVERY = 15,
+    ENTER_RESCUE = 16,
   };
 
   explicit Device(RecoveryUI* ui);