Support wipe command in rescue mode

Bug: 131037235
Test: unit tests pass, run `adb rescue wipe`
Change-Id: I22668f2c98fe2d9195d2561f961c28a7c08e712c
(cherry picked from commit fedeef6f6d1f7b8f1e5a8b9e77f8dc21ef6b3c95)
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index 9dfe040..4dd1f1b 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -43,6 +43,7 @@
 
 #include "fuse_sideload.h"
 #include "install/install.h"
+#include "install/wipe_data.h"
 #include "minadbd_types.h"
 #include "otautil/sysutil.h"
 #include "recovery_ui/device.h"
@@ -330,7 +331,7 @@
   signal(SIGPIPE, SIG_DFL);
 }
 
-int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action) {
+int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) {
   // 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.
@@ -339,13 +340,7 @@
     return INSTALL_ERROR;
   }
 
-  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");
-  }
+  RecoveryUI* ui = device->GetUI();
 
   int install_result = INSTALL_ERROR;
   std::map<MinadbdCommand, CommandFunction> command_map{
@@ -363,6 +358,18 @@
       std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) },
   };
 
+  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");
+    command_map.emplace(MinadbdCommand::kWipeData, [&device]() {
+      bool result = WipeData(device, false);
+      return std::make_pair(result, true);
+    });
+  }
+
   CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode);
 
   // Clean up before switching to the older state, for example setting the state
diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h
index 49b32b5..3a0a817 100644
--- a/install/include/install/adb_install.h
+++ b/install/include/install/adb_install.h
@@ -17,9 +17,8 @@
 #pragma once
 
 #include <recovery_ui/device.h>
-#include <recovery_ui/ui.h>
 
 // Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum
 // InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with
 // the reboot target set in reboot_action.
-int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action);
+int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action);
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 9b1999d..1c4c0f4 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -104,7 +104,7 @@
   if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 ||
       !android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) {
     LOG(ERROR) << "bad sideload-host arguments: " << args;
-    return kMinadbdPackageSizeError;
+    return kMinadbdHostCommandArgumentError;
   }
 
   LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size;
@@ -124,17 +124,17 @@
     return kMinadbdMessageFormatError;
   }
 
-  // Signal host-side adb to stop. For sideload mode, we always send kSideloadServiceExitSuccess
+  // Signal host-side adb to stop. For sideload mode, we always send kMinadbdServicesExitSuccess
   // (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on
   // install error.
   if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) {
-    if (!android::base::WriteFully(sfd, kSideloadServiceExitSuccess,
-                                   strlen(kSideloadServiceExitSuccess))) {
+    if (!android::base::WriteFully(sfd, kMinadbdServicesExitSuccess,
+                                   strlen(kMinadbdServicesExitSuccess))) {
       return kMinadbdHostSocketIOError;
     }
   } else {
-    if (!android::base::WriteFully(sfd, kSideloadServiceExitFailure,
-                                   strlen(kSideloadServiceExitFailure))) {
+    if (!android::base::WriteFully(sfd, kMinadbdServicesExitFailure,
+                                   strlen(kMinadbdServicesExitFailure))) {
       return kMinadbdHostSocketIOError;
     }
   }
@@ -200,6 +200,34 @@
   }
 }
 
+static void WipeDeviceService(unique_fd fd, const std::string& args) {
+  auto pieces = android::base::Split(args, ":");
+  if (pieces.size() != 2 || pieces[0] != "userdata") {
+    LOG(ERROR) << "Failed to parse wipe device command arguments " << args;
+    exit(kMinadbdHostCommandArgumentError);
+  }
+
+  size_t message_size;
+  if (!android::base::ParseUint(pieces[1], &message_size) ||
+      message_size < strlen(kMinadbdServicesExitSuccess)) {
+    LOG(ERROR) << "Failed to parse wipe device message size in " << args;
+    exit(kMinadbdHostCommandArgumentError);
+  }
+
+  WriteCommandToFd(MinadbdCommand::kWipeData, minadbd_socket);
+  MinadbdCommandStatus status;
+  if (!WaitForCommandStatus(minadbd_socket, &status)) {
+    exit(kMinadbdMessageFormatError);
+  }
+
+  std::string response = (status == MinadbdCommandStatus::kSuccess) ? kMinadbdServicesExitSuccess
+                                                                    : kMinadbdServicesExitFailure;
+  response += std::string(message_size - response.size(), '\0');
+  if (!android::base::WriteFully(fd, response.c_str(), response.size())) {
+    exit(kMinadbdHostSocketIOError);
+  }
+}
+
 unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) {
   // Common services that are supported both in sideload and rescue modes.
   if (ConsumePrefix(&name, "reboot:")) {
@@ -225,7 +253,13 @@
       std::string args(name);
       return create_service_thread(
           "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args));
+    } else if (ConsumePrefix(&name, "rescue-wipe:")) {
+      // rescue-wipe:target:<message-size>
+      std::string args(name);
+      return create_service_thread("rescue-wipe",
+                                   std::bind(WipeDeviceService, std::placeholders::_1, args));
     }
+
     return unique_fd{};
   }
 
diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp
index 593180b..f878737 100644
--- a/minadbd/minadbd_services_test.cpp
+++ b/minadbd/minadbd_services_test.cpp
@@ -122,7 +122,7 @@
 
 TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) {
   ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"),
-              ::testing::ExitedWithCode(kMinadbdPackageSizeError), "");
+              ::testing::ExitedWithCode(kMinadbdHostCommandArgumentError), "");
 }
 
 TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) {
diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h
index b370b79..99fd45e 100644
--- a/minadbd/minadbd_types.h
+++ b/minadbd/minadbd_types.h
@@ -30,7 +30,7 @@
   kMinadbdSocketIOError = 2,
   kMinadbdMessageFormatError = 3,
   kMinadbdAdbVersionError = 4,
-  kMinadbdPackageSizeError = 5,
+  kMinadbdHostCommandArgumentError = 5,
   kMinadbdFuseStartError = 6,
   kMinadbdUnsupportedCommandError = 7,
   kMinadbdCommandExecutionError = 8,
@@ -51,6 +51,8 @@
   kRebootFastboot = 4,
   kRebootRecovery = 5,
   kRebootRescue = 6,
+  kWipeCache = 7,
+  kWipeData = 8,
 
   // Last but invalid command.
   kError,
diff --git a/recovery.cpp b/recovery.cpp
index f9b3bfc..5fc673e 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -551,9 +551,9 @@
         if (chosen_action == Device::ENTER_RESCUE) {
           // Switch to graphics screen.
           ui->ShowText(false);
-          status = ApplyFromAdb(ui, true /* rescue_mode */, &reboot_action);
+          status = ApplyFromAdb(device, true /* rescue_mode */, &reboot_action);
         } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) {
-          status = ApplyFromAdb(ui, false /* rescue_mode */, &reboot_action);
+          status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action);
         } else {
           adb = false;
           status = ApplyFromSdcard(device, ui);
@@ -946,7 +946,7 @@
     if (!sideload_auto_reboot) {
       ui->ShowText(true);
     }
-    status = ApplyFromAdb(ui, false /* rescue_mode */, &next_action);
+    status = ApplyFromAdb(device, false /* rescue_mode */, &next_action);
     ui->Print("\nInstall from ADB complete (status: %d).\n", status);
     if (sideload_auto_reboot) {
       status = INSTALL_REBOOT;
@@ -954,7 +954,7 @@
     }
   } else if (rescue) {
     save_current_log = true;
-    status = ApplyFromAdb(ui, true /* rescue_mode */, &next_action);
+    status = ApplyFromAdb(device, true /* rescue_mode */, &next_action);
     ui->Print("\nInstall from ADB complete (status: %d).\n", status);
   } else if (fsck_unshare_blocks) {
     if (!do_fsck_unshare_blocks()) {