[automerger skipped] DO NOT MERGE: Build libinstall as a static library.
am: 2f8afba707 -s ours
am skip reason: SHA1 2f8afba707 skipped by user xunchang

Change-Id: Id6835cbd7845acdb91a8bb7b0888a003b01c48a3
diff --git a/Android.bp b/Android.bp
index f2230ae..0eb5fd9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -76,7 +76,6 @@
 
         // external dependencies
         "libhealthhalutils",
-        "libfstab",
     ],
 }
 
@@ -108,12 +107,10 @@
     ],
 
     srcs: [
-        "logging.cpp",
         "recovery_main.cpp",
     ],
 
     shared_libs: [
-        "libminadbd_services",
         "librecovery_ui",
     ],
 
@@ -125,6 +122,7 @@
     required: [
         "e2fsdroid.recovery",
         "librecovery_ui_ext",
+        "minadbd",
         "mke2fs.conf.recovery",
         "mke2fs.recovery",
         "recovery_deps",
@@ -140,7 +138,6 @@
     ],
 
     srcs: [
-        "logging.cpp",
         "recovery-persist.cpp",
     ],
 
@@ -152,7 +149,6 @@
 
     static_libs: [
         "libotautil",
-        "libfstab",
     ],
 
     init_rc: [
@@ -169,7 +165,6 @@
     ],
 
     srcs: [
-        "logging.cpp",
         "recovery-refresh.cpp",
     ],
 
@@ -180,7 +175,6 @@
 
     static_libs: [
         "libotautil",
-        "libfstab",
     ],
 
     init_rc: [
diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp
index e74f8ba..0f8fffa 100644
--- a/fsck_unshare_blocks.cpp
+++ b/fsck_unshare_blocks.cpp
@@ -34,7 +34,6 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/unique_fd.h>
-#include <fstab/fstab.h>
 
 #include "otautil/roots.h"
 
diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h
index 499d57a..59059cf 100644
--- a/fuse_sideload/include/fuse_provider.h
+++ b/fuse_sideload/include/fuse_provider.h
@@ -25,8 +25,8 @@
 // This is the base class to read data from source and provide the data to FUSE.
 class FuseDataProvider {
  public:
-  FuseDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size)
-      : fd_(std::move(fd)), file_size_(file_size), fuse_block_size_(block_size) {}
+  FuseDataProvider(uint64_t file_size, uint32_t block_size)
+      : file_size_(file_size), fuse_block_size_(block_size) {}
 
   virtual ~FuseDataProvider() = default;
 
@@ -37,21 +37,15 @@
     return fuse_block_size_;
   }
 
-  bool Valid() const {
-    return fd_ != -1;
-  }
-
   // Reads |fetch_size| bytes data starting from |start_block|. Puts the result in |buffer|.
   virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
                                     uint32_t start_block) const = 0;
 
-  virtual void Close() = 0;
+  virtual void Close() {}
 
  protected:
   FuseDataProvider() = default;
 
-  // The underlying source to read data from.
-  android::base::unique_fd fd_;
   // Size in bytes of the file to read.
   uint64_t file_size_ = 0;
   // Block size passed to the fuse, this is different from the block size of the block device.
@@ -61,13 +55,18 @@
 // This class reads data from a file.
 class FuseFileDataProvider : public FuseDataProvider {
  public:
-  FuseFileDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size)
-      : FuseDataProvider(std::move(fd), file_size, block_size) {}
-
   FuseFileDataProvider(const std::string& path, uint32_t block_size);
 
   bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
                             uint32_t start_block) const override;
 
+  bool Valid() const {
+    return fd_ != -1;
+  }
+
   void Close() override;
+
+ private:
+  // The underlying source to read data from.
+  android::base::unique_fd fd_;
 };
diff --git a/install/Android.bp b/install/Android.bp
index 221ce72..b18e290 100644
--- a/install/Android.bp
+++ b/install/Android.bp
@@ -19,6 +19,10 @@
         "recovery_defaults",
     ],
 
+    header_libs: [
+        "libminadbd_headers",
+    ],
+
     shared_libs: [
         "libbase",
         "libbootloader_message",
@@ -43,7 +47,6 @@
         // external dependencies
         "libvintf_recovery",
         "libvintf",
-        "libfstab",
     ],
 }
 
@@ -62,6 +65,7 @@
         "install.cpp",
         "package.cpp",
         "verifier.cpp",
+        "wipe_data.cpp",
     ],
 
     shared_libs: [
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index 5296ff6..548b6e5 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -21,24 +21,266 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <atomic>
+#include <functional>
+#include <map>
+
+#include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/memory.h>
 #include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 
 #include "fuse_sideload.h"
 #include "install/install.h"
+#include "minadbd_types.h"
 #include "recovery_ui/ui.h"
 
+using CommandFunction = std::function<bool()>;
+
 static bool SetUsbConfig(const std::string& state) {
   android::base::SetProperty("sys.usb.config", state);
   return android::base::WaitForProperty("sys.usb.state", state);
 }
 
-int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) {
+// Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors.
+static MinadbdCommands ParseMinadbdCommands(const std::string& message) {
+  if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) {
+    LOG(ERROR) << "Failed to parse command in message " << message;
+    return MinadbdCommands::kError;
+  }
+
+  auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix));
+  auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str());
+  if (cmd_code >= static_cast<uint32_t>(MinadbdCommands::kError)) {
+    LOG(ERROR) << "Unsupported command code: " << cmd_code;
+    return MinadbdCommands::kError;
+  }
+
+  return static_cast<MinadbdCommands>(cmd_code);
+}
+
+static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) {
+  char message[kMinadbdMessageSize];
+  memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
+  android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status);
+
+  if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to write message " << message;
+    return false;
+  }
+  return true;
+}
+
+// Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise.
+static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
+  // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long
+  // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME
+  // will start to exist once the host connects and starts serving a package. Poll for its
+  // appearance. (Note that inotify doesn't work with FUSE.)
+  constexpr int ADB_INSTALL_TIMEOUT = 15;
+  *result = INSTALL_ERROR;
+  for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
+    struct stat st;
+    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
+      if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
+        sleep(1);
+        continue;
+      } else {
+        ui->Print("\nTimed out waiting for fuse to be ready.\n\n");
+        break;
+      }
+    }
+    *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
+    break;
+  }
+
+  // Calling stat() on this magic filename signals the FUSE to exit.
+  struct stat st;
+  stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+  return *result == INSTALL_SUCCESS;
+}
+
+// Parses and executes the command from minadbd. Returns false if we enter an invalid state so that
+// the caller can kill the minadbd service properly.
+static bool HandleMessageFromMinadbd(
+    int socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) {
+  char buffer[kMinadbdMessageSize];
+  if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to read message from minadbd";
+    return false;
+  }
+
+  std::string message(buffer, buffer + kMinadbdMessageSize);
+  auto command_type = ParseMinadbdCommands(message);
+  if (command_type == MinadbdCommands::kError) {
+    return false;
+  }
+  if (command_map.find(command_type) == command_map.end()) {
+    LOG(ERROR) << "Unsupported command: "
+               << android::base::get_unaligned<unsigned int>(
+                      message.substr(strlen(kMinadbdCommandPrefix)).c_str());
+    return false;
+  }
+
+  // We have received a valid command, execute the corresponding function.
+  const auto& command_func = command_map.at(command_type);
+  if (!command_func()) {
+    LOG(ERROR) << "Failed to execute command " << static_cast<unsigned int>(command_type);
+    return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd);
+  }
+  return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd);
+}
+
+// TODO(xunchang) add a wrapper function and kill the minadbd service there.
+static void ListenAndExecuteMinadbdCommands(
+    pid_t minadbd_pid, android::base::unique_fd&& socket_fd,
+    const std::map<MinadbdCommands, CommandFunction>& command_map) {
+  android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC));
+  if (epoll_fd == -1) {
+    PLOG(ERROR) << "Failed to create epoll";
+    kill(minadbd_pid, SIGKILL);
+    return;
+  }
+
+  constexpr int EPOLL_MAX_EVENTS = 10;
+  struct epoll_event ev = {};
+  ev.events = EPOLLIN | EPOLLHUP;
+  ev.data.fd = socket_fd.get();
+  struct epoll_event events[EPOLL_MAX_EVENTS];
+  if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) {
+    PLOG(ERROR) << "Failed to add socket fd to epoll";
+    kill(minadbd_pid, SIGKILL);
+    return;
+  }
+
+  // Set the timeout to be 300s when waiting for minadbd commands.
+  constexpr int TIMEOUT_MILLIS = 300 * 1000;
+  while (true) {
+    // Poll for the status change of the socket_fd, and handle the message if the fd is ready to
+    // read.
+    int event_count =
+        TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS));
+    if (event_count == -1) {
+      PLOG(ERROR) << "Failed to wait for epoll events";
+      kill(minadbd_pid, SIGKILL);
+      return;
+    }
+    if (event_count == 0) {
+      LOG(ERROR) << "Timeout waiting for messages from minadbd";
+      kill(minadbd_pid, SIGKILL);
+      return;
+    }
+
+    for (int n = 0; n < event_count; n++) {
+      if (events[n].events & EPOLLHUP) {
+        LOG(INFO) << "Socket has been closed";
+        kill(minadbd_pid, SIGKILL);
+        return;
+      }
+      if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) {
+        kill(minadbd_pid, SIGKILL);
+        return;
+      }
+    }
+  }
+}
+
+// Recovery starts minadbd service as a child process, and spawns another thread to listen for the
+// message from minadbd through a socket pair. Here is an example to execute one command from adb
+// host.
+//  a. recovery                    b. listener thread               c. minadbd service
+//
+//  a1. create socket pair
+//  a2. fork minadbd service
+//                                                                c3. wait for the adb commands
+//                                                                    from host
+//                                                                c4. after receiving host commands:
+//                                                                  1) set up pre-condition (i.e.
+//                                                                     start fuse for adb sideload)
+//                                                                  2) issue command through
+//                                                                     socket.
+//                                                                  3) wait for result
+//  a5. start listener thread
+//                               b6. listen for message from
+//                                   minadbd in a loop.
+//                               b7. After receiving a minadbd
+//                                   command from socket
+//                                 1) execute the command function
+//                                 2) send the result back to
+//                                    minadbd
+//  ......
+//                                                                 c8. exit upon receiving the
+//                                                                     result
+// a9.  wait for listener thread
+//      to exit.
+//
+// a10. wait for minadbd to
+//      exit
+//                               b11. exit the listening loop
+//
+static void CreateMinadbdServiceAndExecuteCommands(
+    const std::map<MinadbdCommands, CommandFunction>& command_map) {
+  signal(SIGPIPE, SIG_IGN);
+
+  android::base::unique_fd recovery_socket;
+  android::base::unique_fd minadbd_socket;
+  if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) {
+    PLOG(ERROR) << "Failed to create socket";
+    return;
+  }
+
+  pid_t child = fork();
+  if (child == -1) {
+    PLOG(ERROR) << "Failed to fork child process";
+    return;
+  }
+  if (child == 0) {
+    recovery_socket.reset();
+    execl("/system/bin/minadbd", "minadbd", "--socket_fd",
+          std::to_string(minadbd_socket.release()).c_str(), nullptr);
+
+    _exit(EXIT_FAILURE);
+  }
+
+  minadbd_socket.reset();
+
+  // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for
+  // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon.
+  if (!SetUsbConfig("sideload")) {
+    LOG(ERROR) << "Failed to set usb config to sideload";
+    return;
+  }
+
+  std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket),
+                              std::ref(command_map));
+
+  if (listener_thread.joinable()) {
+    listener_thread.join();
+  }
+
+  int status;
+  waitpid(child, &status, 0);
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+    if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) {
+      LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n";
+    } else if (!WIFSIGNALED(status)) {
+      LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")";
+    }
+  }
+
+  signal(SIGPIPE, SIG_DFL);
+}
+
+int apply_from_adb(RecoveryUI* ui) {
   // 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.
@@ -51,66 +293,12 @@
       "\n\nNow send the package you want to apply\n"
       "to the device with \"adb sideload <filename>\"...\n");
 
-  pid_t child;
-  if ((child = fork()) == 0) {
-    execl("/system/bin/recovery", "recovery", "--adbd", nullptr);
-    _exit(EXIT_FAILURE);
-  }
+  int install_result = INSTALL_ERROR;
+  std::map<MinadbdCommands, CommandFunction> command_map{
+    { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
+  };
 
-  if (!SetUsbConfig("sideload")) {
-    LOG(ERROR) << "Failed to set usb config to sideload";
-    return INSTALL_ERROR;
-  }
-
-  // How long (in seconds) we wait for the host to start sending us a package, before timing out.
-  static constexpr int ADB_INSTALL_TIMEOUT = 300;
-
-  // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host connects and starts serving a
-  // package. Poll for its appearance. (Note that inotify doesn't work with FUSE.)
-  int result = INSTALL_ERROR;
-  int status;
-  bool waited = false;
-  for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
-    if (waitpid(child, &status, WNOHANG) != 0) {
-      result = INSTALL_ERROR;
-      waited = true;
-      break;
-    }
-
-    struct stat st;
-    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
-      if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
-        sleep(1);
-        continue;
-      } else {
-        ui->Print("\nTimed out waiting for package.\n\n");
-        result = INSTALL_ERROR;
-        kill(child, SIGKILL);
-        break;
-      }
-    }
-    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui);
-    break;
-  }
-
-  if (!waited) {
-    // Calling stat() on this magic filename signals the minadbd subprocess to shut down.
-    struct stat st;
-    stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
-
-    // TODO: there should be a way to cancel waiting for a package (by pushing some button combo on
-    // the device). For now you just have to 'adb sideload' a file that's not a valid package, like
-    // "/dev/null".
-    waitpid(child, &status, 0);
-  }
-
-  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-    if (WEXITSTATUS(status) == 3) {
-      ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
-    } else if (!WIFSIGNALED(status)) {
-      ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status));
-    }
-  }
+  CreateMinadbdServiceAndExecuteCommands(command_map);
 
   // Clean up before switching to the older state, for example setting the state
   // to none sets sys/class/android_usb/android0/enable to 0.
@@ -124,5 +312,5 @@
     }
   }
 
-  return result;
+  return install_result;
 }
diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp
index dde289f..1aa8768 100644
--- a/install/fuse_sdcard_install.cpp
+++ b/install/fuse_sdcard_install.cpp
@@ -133,7 +133,7 @@
   return run_fuse_sideload(std::move(file_data_reader)) == 0;
 }
 
-int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui) {
+int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
   if (ensure_path_mounted(SDCARD_ROOT) != 0) {
     LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
     return INSTALL_ERROR;
@@ -184,7 +184,7 @@
       }
     }
 
-    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0 /*retry_count*/, ui);
+    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui);
     break;
   }
 
diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h
index dbc8245..f7b065b 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(bool* wipe_cache, RecoveryUI* ui);
+int apply_from_adb(RecoveryUI* ui);
diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/fuse_sdcard_install.h
index 345aea4..d9214ca 100644
--- a/install/include/install/fuse_sdcard_install.h
+++ b/install/include/install/fuse_sdcard_install.h
@@ -19,4 +19,4 @@
 #include "recovery_ui/device.h"
 #include "recovery_ui/ui.h"
 
-int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui);
+int ApplyFromSdcard(Device* device, RecoveryUI* ui);
diff --git a/install/include/install/install.h b/install/include/install/install.h
index 74fb3d1..1e41b48 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -43,10 +43,11 @@
   BRICK,
 };
 
-// Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on
-// exit, caller should wipe the cache partition.
-int install_package(const std::string& package, bool* wipe_cache, bool needs_mount, int retry_count,
-                    RecoveryUI* ui);
+// Installs the given update package. This function should also wipe the cache partition after a
+// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the
+// cache.
+int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount,
+                    int retry_count, RecoveryUI* ui);
 
 // Verifies the package by ota keys. Returns true if the package is verified successfully,
 // otherwise returns false.
diff --git a/minadbd/minadbd.h b/install/include/install/wipe_data.h
similarity index 61%
copy from minadbd/minadbd.h
copy to install/include/install/wipe_data.h
index 3570a5d..b34891f 100644
--- a/minadbd/minadbd.h
+++ b/install/include/install/wipe_data.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef MINADBD_H__
-#define MINADBD_H__
+#pragma once
 
-int minadbd_main();
+#include <functional>
 
-#endif
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+
+struct selabel_handle;
+
+// Returns true on success.
+bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm);
+
+// Returns true on success.
+bool WipeData(Device* device, bool convert_fbe);
diff --git a/install/install.cpp b/install/install.cpp
index a1124c3..e2d4700 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -48,6 +48,7 @@
 
 #include "install/package.h"
 #include "install/verifier.h"
+#include "install/wipe_data.h"
 #include "otautil/error_code.h"
 #include "otautil/paths.h"
 #include "otautil/roots.h"
@@ -631,10 +632,9 @@
   return result;
 }
 
-int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, int retry_count,
-                    RecoveryUI* ui) {
+int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
+                    int retry_count, RecoveryUI* ui) {
   CHECK(!path.empty());
-  CHECK(wipe_cache != nullptr);
 
   auto start = std::chrono::system_clock::now();
 
@@ -647,8 +647,10 @@
     LOG(ERROR) << "failed to set up expected mounts for install; aborting";
     result = INSTALL_ERROR;
   } else {
-    result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,
-                                    &max_temperature, ui);
+    bool updater_wipe_cache = false;
+    result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
+                                    retry_count, &max_temperature, ui);
+    should_wipe_cache = should_wipe_cache || updater_wipe_cache;
   }
 
   // Measure the time spent to apply OTA update in seconds.
@@ -703,6 +705,12 @@
   // Write a copy into last_log.
   LOG(INFO) << log_content;
 
+  if (result == INSTALL_SUCCESS && should_wipe_cache) {
+    if (!WipeCache(ui, nullptr)) {
+      result = INSTALL_ERROR;
+    }
+  }
+
   return result;
 }
 
diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp
new file mode 100644
index 0000000..765a815
--- /dev/null
+++ b/install/wipe_data.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "install/wipe_data.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <functional>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "otautil/dirutil.h"
+#include "otautil/logging.h"
+#include "otautil/roots.h"
+#include "recovery_ui/ui.h"
+
+constexpr const char* CACHE_ROOT = "/cache";
+constexpr const char* DATA_ROOT = "/data";
+constexpr const char* METADATA_ROOT = "/metadata";
+
+static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) {
+  bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
+  bool is_data = (strcmp(volume, DATA_ROOT) == 0);
+
+  ui->SetBackground(RecoveryUI::ERASING);
+  ui->SetProgressType(RecoveryUI::INDETERMINATE);
+
+  std::vector<saved_log_file> log_files;
+  if (is_cache) {
+    // If we're reformatting /cache, we load any past logs (i.e. "/cache/recovery/last_*") and the
+    // current log ("/cache/recovery/log") into memory, so we can restore them after the reformat.
+    log_files = ReadLogFilesToMemory();
+  }
+
+  ui->Print("Formatting %s...\n", volume);
+
+  ensure_path_unmounted(volume);
+
+  int result;
+  if (is_data && convert_fbe) {
+    constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe";
+    constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
+    // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not
+    // full disk encryption.
+    if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
+      PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR;
+      return false;
+    }
+    FILE* f = fopen(CONVERT_FBE_FILE, "wbe");
+    if (!f) {
+      PLOG(ERROR) << "Failed to convert to file encryption";
+      return false;
+    }
+    fclose(f);
+    result = format_volume(volume, CONVERT_FBE_DIR);
+    remove(CONVERT_FBE_FILE);
+    rmdir(CONVERT_FBE_DIR);
+  } else {
+    result = format_volume(volume);
+  }
+
+  if (is_cache) {
+    RestoreLogFilesAfterFormat(log_files);
+  }
+
+  return (result == 0);
+}
+
+bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm_func) {
+  bool has_cache = volume_for_mount_point("/cache") != nullptr;
+  if (!has_cache) {
+    ui->Print("No /cache partition found.\n");
+    return false;
+  }
+
+  if (confirm_func && !confirm_func()) {
+    return false;
+  }
+
+  ui->Print("\n-- Wiping cache...\n");
+  bool success = EraseVolume("/cache", ui, false);
+  ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
+  return success;
+}
+
+bool WipeData(Device* device, bool convert_fbe) {
+  RecoveryUI* ui = device->GetUI();
+  ui->Print("\n-- Wiping data...\n");
+  bool success = device->PreWipeData();
+  if (success) {
+    success &= EraseVolume(DATA_ROOT, ui, convert_fbe);
+    bool has_cache = volume_for_mount_point("/cache") != nullptr;
+    if (has_cache) {
+      success &= EraseVolume(CACHE_ROOT, ui, false);
+    }
+    if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
+      success &= EraseVolume(METADATA_ROOT, ui, false);
+    }
+  }
+  if (success) {
+    success &= device->PostWipeData();
+  }
+  ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
+  return success;
+}
\ No newline at end of file
diff --git a/minadbd/Android.bp b/minadbd/Android.bp
index 9b889f4..e4f7712 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -40,7 +40,6 @@
 
     srcs: [
         "fuse_adb_provider.cpp",
-        "minadbd.cpp",
         "minadbd_services.cpp",
     ],
 
@@ -52,6 +51,36 @@
     ],
 }
 
+cc_library_headers {
+    name: "libminadbd_headers",
+    recovery_available: true,
+    // TODO create a include dir
+    export_include_dirs: [
+        ".",
+    ],
+}
+
+cc_binary {
+    name: "minadbd",
+    recovery: true,
+
+    defaults: [
+        "minadbd_defaults",
+    ],
+
+    srcs: [
+        "minadbd.cpp",
+    ],
+
+    shared_libs: [
+        "libadbd",
+        "libbase",
+        "libcrypto",
+        "libfusesideload",
+        "libminadbd_services",
+    ],
+}
+
 cc_test {
     name: "minadbd_test",
     isolated: true,
diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h
index 3fb689b..24a463d 100644
--- a/minadbd/fuse_adb_provider.h
+++ b/minadbd/fuse_adb_provider.h
@@ -14,25 +14,24 @@
  * limitations under the License.
  */
 
-#ifndef __FUSE_ADB_PROVIDER_H
-#define __FUSE_ADB_PROVIDER_H
+#pragma once
 
 #include <stdint.h>
 
-#include "android-base/unique_fd.h"
-
 #include "fuse_provider.h"
 
 // This class reads data from adb server.
 class FuseAdbDataProvider : public FuseDataProvider {
  public:
-  FuseAdbDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size)
-      : FuseDataProvider(std::move(fd), file_size, block_size) {}
+  FuseAdbDataProvider(int fd, uint64_t file_size, uint32_t block_size)
+      : FuseDataProvider(file_size, block_size), fd_(fd) {}
 
   bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
                             uint32_t start_block) const override;
 
   void Close() override;
-};
 
-#endif
+ private:
+  // The underlying source to read data from (i.e. the one that talks to the host).
+  int fd_;
+};
diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp
index 349189c..57158ad 100644
--- a/minadbd/minadbd.cpp
+++ b/minadbd/minadbd.cpp
@@ -14,30 +14,54 @@
  * limitations under the License.
  */
 
-#include "minadbd.h"
-
 #include <errno.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <strings.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
 
 #include "adb.h"
 #include "adb_auth.h"
 #include "transport.h"
 
-int minadbd_main() {
-    adb_device_banner = "sideload";
+#include "minadbd_services.h"
+#include "minadbd_types.h"
 
-    signal(SIGPIPE, SIG_IGN);
+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) {
+    LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc;
+    exit(kMinadbdArgumentsParsingError);
+  }
 
-    // We can't require authentication for sideloading. http://b/22025550.
-    auth_required = false;
+  int socket_fd;
+  if (!android::base::ParseInt(argv[2], &socket_fd)) {
+    LOG(ERROR) << "Failed to parse int in " << argv[2];
+    exit(kMinadbdArgumentsParsingError);
+  }
+  if (fcntl(socket_fd, F_GETFD, 0) == -1) {
+    PLOG(ERROR) << "Failed to get minadbd socket";
+    exit(kMinadbdSocketIOError);
+  }
+  SetMinadbdSocketFd(socket_fd);
 
-    init_transport_registration();
-    usb_init();
+  adb_device_banner = "sideload";
 
-    VLOG(ADB) << "Event loop starting";
-    fdevent_loop();
+  signal(SIGPIPE, SIG_IGN);
 
-    return 0;
+  // We can't require authentication for sideloading. http://b/22025550.
+  auth_required = false;
+
+  init_transport_registration();
+  usb_init();
+
+  VLOG(ADB) << "Event loop starting";
+  fdevent_loop();
+
+  return 0;
 }
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 6fe5c79..f2b65c0 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "minadbd_services.h"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdio.h>
@@ -27,38 +29,94 @@
 #include <string_view>
 #include <thread>
 
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
 #include "adb.h"
 #include "adb_unique_fd.h"
 #include "fdevent.h"
 #include "fuse_adb_provider.h"
 #include "fuse_sideload.h"
+#include "minadbd_types.h"
 #include "services.h"
 #include "sysdeps.h"
 
+static int minadbd_socket = -1;
+void SetMinadbdSocketFd(int socket_fd) {
+  minadbd_socket = socket_fd;
+}
+
+static bool WriteCommandToFd(MinadbdCommands cmd, int fd) {
+  char message[kMinadbdMessageSize];
+  memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix));
+  android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd);
+
+  if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to write message " << message;
+    return false;
+  }
+  return true;
+}
+
+// Blocks and reads the command status from |fd|. Returns false if the received message has a
+// format error.
+static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) {
+  char buffer[kMinadbdMessageSize];
+  if (!android::base::ReadFully(fd, buffer, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to response status from socket";
+    exit(kMinadbdSocketIOError);
+  }
+
+  std::string message(buffer, buffer + kMinadbdMessageSize);
+  if (!android::base::StartsWith(message, kMinadbdStatusPrefix)) {
+    LOG(ERROR) << "Failed to parse status in " << message;
+    return false;
+  }
+
+  *status = android::base::get_unaligned<MinadbdCommandStatus>(
+      message.substr(strlen(kMinadbdStatusPrefix)).c_str());
+  return true;
+}
+
 static void sideload_host_service(unique_fd sfd, const std::string& args) {
   int64_t file_size;
   int block_size;
   if ((sscanf(args.c_str(), "%" SCNd64 ":%d", &file_size, &block_size) != 2) || file_size <= 0 ||
       block_size <= 0) {
-    printf("bad sideload-host arguments: %s\n", args.c_str());
-    exit(1);
+    LOG(ERROR) << "bad sideload-host arguments: " << args;
+    exit(kMinadbdPackageSizeError);
   }
 
-  printf("sideload-host file size %" PRId64 " block size %d\n", file_size, block_size);
+  LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size;
 
-  auto adb_data_reader =
-      std::make_unique<FuseAdbDataProvider>(std::move(sfd), file_size, block_size);
-  int result = run_fuse_sideload(std::move(adb_data_reader));
+  if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) {
+    exit(kMinadbdSocketIOError);
+  }
 
-  printf("sideload_host finished\n");
-  exit(result == 0 ? 0 : 1);
+  auto adb_data_reader = std::make_unique<FuseAdbDataProvider>(sfd, file_size, block_size);
+  if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) {
+    LOG(ERROR) << "Failed to start fuse";
+    exit(kMinadbdFuseStartError);
+  }
+
+  MinadbdCommandStatus status;
+  if (!WaitForCommandStatus(minadbd_socket, &status)) {
+    exit(kMinadbdMessageFormatError);
+  }
+  LOG(INFO) << "Got command status: " << static_cast<unsigned int>(status);
+
+  LOG(INFO) << "sideload_host finished";
+  exit(kMinadbdSuccess);
 }
 
 unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) {
   if (name.starts_with("sideload:")) {
     // This exit status causes recovery to print a special error message saying to use a newer adb
     // (that supports sideload-host).
-    exit(3);
+    exit(kMinadbdAdbVersionError);
   } else if (name.starts_with("sideload-host:")) {
     std::string arg(name.substr(strlen("sideload-host:")));
     return create_service_thread("sideload-host",
diff --git a/minadbd/minadbd.h b/minadbd/minadbd_services.h
similarity index 89%
rename from minadbd/minadbd.h
rename to minadbd/minadbd_services.h
index 3570a5d..6835bd7 100644
--- a/minadbd/minadbd.h
+++ b/minadbd/minadbd_services.h
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-#ifndef MINADBD_H__
-#define MINADBD_H__
+#pragma once
 
-int minadbd_main();
-
-#endif
+void SetMinadbdSocketFd(int socket_fd);
diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h
new file mode 100644
index 0000000..7bd6909
--- /dev/null
+++ b/minadbd/minadbd_types.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// The message between recovery and minadbd is 8 bytes in size unless the length is explicitly
+// specified. Both the command and status has the format |prefix(4 bytes) + encoded enum(4 bytes)|.
+constexpr size_t kMinadbdMessageSize = 8;
+constexpr char const kMinadbdCommandPrefix[] = "COMD";
+constexpr char const kMinadbdStatusPrefix[] = "STAT";
+
+enum MinadbdErrorCode : int {
+  kMinadbdSuccess = 0,
+  kMinadbdArgumentsParsingError = 1,
+  kMinadbdSocketIOError = 2,
+  kMinadbdMessageFormatError = 3,
+  kMinadbdAdbVersionError = 4,
+  kMinadbdPackageSizeError = 5,
+  kMinadbdFuseStartError = 6,
+  kMinadbdUnsupportedCommandError = 7,
+  kMinadbdCommandExecutionError = 8,
+  kMinadbdErrorUnknown = 9,
+};
+
+enum class MinadbdCommandStatus : uint32_t {
+  kSuccess = 0,
+  kFailure = 1,
+};
+
+enum class MinadbdCommands : uint32_t {
+  kInstall = 0,
+  kUiPrint = 1,
+  kError = 2,
+};
+
+static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommands));
+static_assert(kMinadbdMessageSize ==
+              sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus));
diff --git a/otautil/Android.bp b/otautil/Android.bp
index b4936c0..73398c3 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -40,6 +40,7 @@
         android: {
             srcs: [
                 "dirutil.cpp",
+                "logging.cpp",
                 "mounts.cpp",
                 "parse_install_logs.cpp",
                 "roots.cpp",
@@ -61,6 +62,10 @@
                 "libfs_mgr",
                 "libselinux",
             ],
+
+            export_static_lib_headers: [
+                "libfstab",
+            ],
         },
     },
 }
diff --git a/logging.h b/otautil/include/otautil/logging.h
similarity index 79%
rename from logging.h
rename to otautil/include/otautil/logging.h
index 3cfbc7a..6083497 100644
--- a/logging.h
+++ b/otautil/include/otautil/logging.h
@@ -18,14 +18,26 @@
 #define _LOGGING_H
 
 #include <stddef.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 
 #include <string>
+#include <vector>
 
 #include <log/log_id.h>
 
 static constexpr int KEEP_LOG_COUNT = 10;
 
+struct selabel_handle;
+
+struct saved_log_file {
+  std::string name;
+  struct stat sb;
+  std::string data;
+};
+
+void SetLoggingSehandle(selabel_handle* handle);
+
 ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len,
                     void* arg);
 
@@ -41,10 +53,13 @@
 void check_and_fclose(FILE* fp, const std::string& name);
 
 void copy_log_file_to_pmsg(const std::string& source, const std::string& destination);
-void copy_log_file(const std::string& source, const std::string& destination, bool append);
-void copy_logs(bool modified_flash, bool has_cache);
+void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle);
 void reset_tmplog_offset();
 
 void save_kernel_log(const char* destination);
 
+std::vector<saved_log_file> ReadLogFilesToMemory();
+
+bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files);
+
 #endif  //_LOGGING_H
diff --git a/logging.cpp b/otautil/logging.cpp
similarity index 65%
rename from logging.cpp
rename to otautil/logging.cpp
index 48f9ec3..484f115 100644
--- a/logging.cpp
+++ b/otautil/logging.cpp
@@ -14,38 +14,51 @@
  * limitations under the License.
  */
 
-#include "logging.h"
+#include "otautil/logging.h"
 
+#include <dirent.h>
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/klog.h>
 #include <sys/types.h>
 
+#include <algorithm>
+#include <memory>
 #include <string>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 #include <private/android_filesystem_config.h> /* for AID_SYSTEM */
 #include <private/android_logger.h>            /* private pmsg functions */
+#include <selinux/label.h>
 
-#include "common.h"
 #include "otautil/dirutil.h"
 #include "otautil/paths.h"
 #include "otautil/roots.h"
 
-static constexpr const char* LOG_FILE = "/cache/recovery/log";
-static constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
-static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
-static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
+constexpr const char* LOG_FILE = "/cache/recovery/log";
+constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
+constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
+constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
 
-static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg";
-static const std::string LAST_LOG_FILTER = "recovery/last_log";
+constexpr const char* LAST_KMSG_FILTER = "recovery/last_kmsg";
+constexpr const char* LAST_LOG_FILTER = "recovery/last_log";
+
+constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
+
+static struct selabel_handle* logging_sehandle;
+
+void SetLoggingSehandle(selabel_handle* handle) {
+  logging_sehandle = handle;
+}
 
 // fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the
 // file pointer, or nullptr on error.
-static FILE* fopen_path(const std::string& path, const char* mode) {
+static FILE* fopen_path(const std::string& path, const char* mode, const selabel_handle* sehandle) {
   if (ensure_path_mounted(path) != 0) {
     LOG(ERROR) << "Can't mount " << path;
     return nullptr;
@@ -74,8 +87,8 @@
 ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */,
                     size_t len, void* arg) {
   bool* do_rotate = static_cast<bool*>(arg);
-  if (LAST_KMSG_FILTER.find(filename) != std::string::npos ||
-      LAST_LOG_FILTER.find(filename) != std::string::npos) {
+  if (std::string(LAST_KMSG_FILTER).find(filename) != std::string::npos ||
+      std::string(LAST_LOG_FILTER).find(filename) != std::string::npos) {
     *do_rotate = true;
   }
   return len;
@@ -92,8 +105,8 @@
   size_t dot = name.find_last_of('.');
   std::string sub = name.substr(0, dot);
 
-  if (LAST_KMSG_FILTER.find(sub) == std::string::npos &&
-      LAST_LOG_FILTER.find(sub) == std::string::npos) {
+  if (std::string(LAST_KMSG_FILTER).find(sub) == std::string::npos &&
+      std::string(LAST_LOG_FILTER).find(sub) == std::string::npos) {
     return __android_log_pmsg_file_write(id, prio, filename, buf, len);
   }
 
@@ -165,8 +178,9 @@
   tmplog_offset = 0;
 }
 
-void copy_log_file(const std::string& source, const std::string& destination, bool append) {
-  FILE* dest_fp = fopen_path(destination, append ? "ae" : "we");
+static void copy_log_file(const std::string& source, const std::string& destination, bool append,
+                          const selabel_handle* sehandle) {
+  FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", sehandle);
   if (dest_fp == nullptr) {
     PLOG(ERROR) << "Can't open " << destination;
   } else {
@@ -189,11 +203,11 @@
   }
 }
 
-void copy_logs(bool modified_flash, bool has_cache) {
-  // We only rotate and record the log of the current session if there are actual attempts to modify
-  // the flash, such as wipes, installs from BCB or menu selections. This is to avoid unnecessary
+void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) {
+  // We only rotate and record the log of the current session if explicitly requested. This usually
+  // happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary
   // rotation (and possible deletion) of log files, if it does not do anything loggable.
-  if (!modified_flash) {
+  if (!save_current_log) {
     return;
   }
 
@@ -211,9 +225,9 @@
   rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
 
   // Copy logs to cache so the system can find out what happened.
-  copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true);
-  copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false);
-  copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false);
+  copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true, sehandle);
+  copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false, sehandle);
+  copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false, sehandle);
   save_kernel_log(LAST_KMSG_FILE);
   chmod(LOG_FILE, 0600);
   chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM);
@@ -242,3 +256,70 @@
   buffer.resize(n);
   android::base::WriteStringToFile(buffer, destination);
 }
+
+std::vector<saved_log_file> ReadLogFilesToMemory() {
+  ensure_path_mounted("/cache");
+
+  struct dirent* de;
+  std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
+  if (!d) {
+    if (errno != ENOENT) {
+      PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
+    }
+    return {};
+  }
+
+  std::vector<saved_log_file> log_files;
+  while ((de = readdir(d.get())) != nullptr) {
+    if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
+      std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
+
+      struct stat sb;
+      if (stat(path.c_str(), &sb) != 0) {
+        PLOG(ERROR) << "Failed to stat " << path;
+        continue;
+      }
+      // Truncate files to 512kb
+      size_t read_size = std::min<size_t>(sb.st_size, 1 << 19);
+      std::string data(read_size, '\0');
+
+      android::base::unique_fd log_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY)));
+      if (log_fd == -1 || !android::base::ReadFully(log_fd, data.data(), read_size)) {
+        PLOG(ERROR) << "Failed to read log file " << path;
+        continue;
+      }
+
+      log_files.emplace_back(saved_log_file{ path, sb, data });
+    }
+  }
+
+  return log_files;
+}
+
+bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files) {
+  // Re-create the log dir and write back the log entries.
+  if (ensure_path_mounted(CACHE_LOG_DIR) != 0) {
+    PLOG(ERROR) << "Failed to mount " << CACHE_LOG_DIR;
+    return false;
+  }
+
+  if (mkdir_recursively(CACHE_LOG_DIR, 0777, false, logging_sehandle) != 0) {
+    PLOG(ERROR) << "Failed to create " << CACHE_LOG_DIR;
+    return false;
+  }
+
+  for (const auto& log : log_files) {
+    if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
+                                          log.sb.st_gid)) {
+      PLOG(ERROR) << "Failed to write to " << log.name;
+    }
+  }
+
+  // Any part of the log we'd copied to cache is now gone.
+  // Reset the pointer so we copy from the beginning of the temp
+  // log.
+  reset_tmplog_offset();
+  copy_logs(true /* save_current_log */, true /* has_cache */, logging_sehandle);
+
+  return true;
+}
diff --git a/recovery-persist.cpp b/recovery-persist.cpp
index e2a6699..294017a 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -43,7 +43,7 @@
 #include <metricslogger/metrics_logger.h>
 #include <private/android_logger.h> /* private pmsg functions */
 
-#include "logging.h"
+#include "otautil/logging.h"
 #include "otautil/parse_install_logs.h"
 
 constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log";
diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp
index aee1ca5..d41755d 100644
--- a/recovery-refresh.cpp
+++ b/recovery-refresh.cpp
@@ -42,7 +42,7 @@
 
 #include <private/android_logger.h> /* private pmsg functions */
 
-#include "logging.h"
+#include "otautil/logging.h"
 
 int main(int argc, char **argv) {
     static const char filter[] = "recovery/";
diff --git a/recovery.cpp b/recovery.cpp
index 0349184..0e6e497 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -27,7 +27,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -55,30 +54,27 @@
 #include "install/fuse_sdcard_install.h"
 #include "install/install.h"
 #include "install/package.h"
-#include "logging.h"
-#include "otautil/dirutil.h"
+#include "install/wipe_data.h"
 #include "otautil/error_code.h"
+#include "otautil/logging.h"
 #include "otautil/paths.h"
 #include "otautil/roots.h"
 #include "otautil/sysutil.h"
 #include "recovery_ui/screen_ui.h"
 #include "recovery_ui/ui.h"
 
-static constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
 static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
 static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
 static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
 static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
 
 static constexpr const char* CACHE_ROOT = "/cache";
-static constexpr const char* DATA_ROOT = "/data";
-static constexpr const char* METADATA_ROOT = "/metadata";
 
 // We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed
 // into target_files.zip. Assert the version defined in code and in Android.mk are consistent.
 static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
 
-static bool modified_flash = false;
+static bool save_current_log = false;
 std::string stage;
 const char* reason = nullptr;
 
@@ -148,7 +144,7 @@
     }
   }
 
-  copy_logs(modified_flash, has_cache);
+  copy_logs(save_current_log, has_cache, sehandle);
 
   // Reset to normal system boot so recovery won't cycle indefinitely.
   std::string err;
@@ -167,110 +163,6 @@
   sync();  // For good measure.
 }
 
-struct saved_log_file {
-  std::string name;
-  struct stat sb;
-  std::string data;
-};
-
-static bool erase_volume(const char* volume) {
-  bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
-  bool is_data = (strcmp(volume, DATA_ROOT) == 0);
-
-  ui->SetBackground(RecoveryUI::ERASING);
-  ui->SetProgressType(RecoveryUI::INDETERMINATE);
-
-  std::vector<saved_log_file> log_files;
-
-  if (is_cache) {
-    // If we're reformatting /cache, we load any past logs
-    // (i.e. "/cache/recovery/last_*") and the current log
-    // ("/cache/recovery/log") into memory, so we can restore them after
-    // the reformat.
-
-    ensure_path_mounted(volume);
-
-    struct dirent* de;
-    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
-    if (d) {
-      while ((de = readdir(d.get())) != nullptr) {
-        if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
-          std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
-
-          struct stat sb;
-          if (stat(path.c_str(), &sb) == 0) {
-            // truncate files to 512kb
-            if (sb.st_size > (1 << 19)) {
-              sb.st_size = 1 << 19;
-            }
-
-            std::string data(sb.st_size, '\0');
-            FILE* f = fopen(path.c_str(), "rbe");
-            fread(&data[0], 1, data.size(), f);
-            fclose(f);
-
-            log_files.emplace_back(saved_log_file{ path, sb, data });
-          }
-        }
-      }
-    } else {
-      if (errno != ENOENT) {
-        PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
-      }
-    }
-  }
-
-  ui->Print("Formatting %s...\n", volume);
-
-  ensure_path_unmounted(volume);
-
-  int result;
-  if (is_data && reason && strcmp(reason, "convert_fbe") == 0) {
-    static constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe";
-    static constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
-    // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not
-    // full disk encryption.
-    if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
-      PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR;
-      return false;
-    }
-    FILE* f = fopen(CONVERT_FBE_FILE, "wbe");
-    if (!f) {
-      PLOG(ERROR) << "Failed to convert to file encryption";
-      return false;
-    }
-    fclose(f);
-    result = format_volume(volume, CONVERT_FBE_DIR);
-    remove(CONVERT_FBE_FILE);
-    rmdir(CONVERT_FBE_DIR);
-  } else {
-    result = format_volume(volume);
-  }
-
-  if (is_cache) {
-    // Re-create the log dir and write back the log entries.
-    if (ensure_path_mounted(CACHE_LOG_DIR) == 0 &&
-        mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) {
-      for (const auto& log : log_files) {
-        if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
-                                              log.sb.st_gid)) {
-          PLOG(ERROR) << "Failed to write to " << log.name;
-        }
-      }
-    } else {
-      PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR;
-    }
-
-    // Any part of the log we'd copied to cache is now gone.
-    // Reset the pointer so we copy from the beginning of the temp
-    // log.
-    reset_tmplog_offset();
-    copy_logs(modified_flash, has_cache);
-  }
-
-  return (result == 0);
-}
-
 static bool yes_no(Device* device, const char* question1, const char* question2) {
   std::vector<std::string> headers{ question1, question2 };
   std::vector<std::string> items{ " No", " Yes" };
@@ -292,28 +184,6 @@
   return (chosen_item == 1);
 }
 
-// Return true on success.
-static bool wipe_data(Device* device) {
-    modified_flash = true;
-
-    ui->Print("\n-- Wiping data...\n");
-    bool success = device->PreWipeData();
-    if (success) {
-      success &= erase_volume(DATA_ROOT);
-      if (has_cache) {
-        success &= erase_volume(CACHE_ROOT);
-      }
-      if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
-        success &= erase_volume(METADATA_ROOT);
-      }
-    }
-    if (success) {
-      success &= device->PostWipeData();
-    }
-    ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
-    return success;
-}
-
 static InstallResult prompt_and_wipe_data(Device* device) {
   // Use a single string and let ScreenRecoveryUI handles the wrapping.
   std::vector<std::string> wipe_data_menu_headers{
@@ -341,7 +211,8 @@
     }
 
     if (ask_to_wipe_data(device)) {
-      if (wipe_data(device)) {
+      bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+      if (WipeData(device, convert_fbe)) {
         return INSTALL_SUCCESS;
       } else {
         return INSTALL_ERROR;
@@ -350,25 +221,6 @@
   }
 }
 
-// Return true on success.
-static bool wipe_cache(bool should_confirm, Device* device) {
-    if (!has_cache) {
-        ui->Print("No /cache partition found.\n");
-        return false;
-    }
-
-    if (should_confirm && !yes_no(device, "Wipe cache?", "  THIS CAN NOT BE UNDONE!")) {
-        return false;
-    }
-
-    modified_flash = true;
-
-    ui->Print("\n-- Wiping cache...\n");
-    bool success = erase_volume("/cache");
-    ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
-    return success;
-}
-
 // Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with
 // BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT.
 static bool secure_wipe_partition(const std::string& partition) {
@@ -653,7 +505,6 @@
             ? Device::REBOOT
             : device->InvokeMenuItem(chosen_item);
 
-    bool should_wipe_cache = false;
     switch (chosen_action) {
       case Device::NO_ACTION:
         break;
@@ -666,41 +517,40 @@
         return chosen_action;
 
       case Device::WIPE_DATA:
+        save_current_log = true;
         if (ui->IsTextVisible()) {
           if (ask_to_wipe_data(device)) {
-            wipe_data(device);
+            WipeData(device, false);
           }
         } else {
-          wipe_data(device);
+          WipeData(device, false);
           return Device::NO_ACTION;
         }
         break;
 
-      case Device::WIPE_CACHE:
-        wipe_cache(ui->IsTextVisible(), device);
+      case Device::WIPE_CACHE: {
+        save_current_log = true;
+        std::function<bool()> confirm_func = [&device]() {
+          return yes_no(device, "Wipe cache?", "  THIS CAN NOT BE UNDONE!");
+        };
+        WipeCache(ui, ui->IsTextVisible() ? confirm_func : nullptr);
         if (!ui->IsTextVisible()) return Device::NO_ACTION;
         break;
-
+      }
       case Device::APPLY_ADB_SIDELOAD:
       case Device::APPLY_SDCARD: {
-        modified_flash = true;
+        save_current_log = true;
         bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
         if (adb) {
-          status = apply_from_adb(&should_wipe_cache, ui);
+          status = apply_from_adb(ui);
         } else {
-          status = ApplyFromSdcard(device, &should_wipe_cache, ui);
-        }
-
-        if (status == INSTALL_SUCCESS && should_wipe_cache) {
-          if (!wipe_cache(false, device)) {
-            status = INSTALL_ERROR;
-          }
+          status = ApplyFromSdcard(device, ui);
         }
 
         if (status != INSTALL_SUCCESS) {
           ui->SetBackground(RecoveryUI::ERROR);
           ui->Print("Installation aborted.\n");
-          copy_logs(modified_flash, has_cache);
+          copy_logs(save_current_log, has_cache, sehandle);
         } else if (!ui->IsTextVisible()) {
           return Device::NO_ACTION;  // reboot if logs aren't visible
         } else {
@@ -987,7 +837,7 @@
   if (update_package != nullptr) {
     // It's not entirely true that we will modify the flash. But we want
     // to log the update attempt since update_package is non-NULL.
-    modified_flash = true;
+    save_current_log = true;
 
     int required_battery_level;
     if (retry_count == 0 && !is_battery_ok(&required_battery_level)) {
@@ -1009,11 +859,7 @@
         set_retry_bootloader_message(retry_count + 1, args);
       }
 
-      modified_flash = true;
-      status = install_package(update_package, &should_wipe_cache, true, retry_count, ui);
-      if (status == INSTALL_SUCCESS && should_wipe_cache) {
-        wipe_cache(false, device);
-      }
+      status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
       if (status != INSTALL_SUCCESS) {
         ui->Print("Installation aborted.\n");
 
@@ -1021,7 +867,7 @@
         // RETRY_LIMIT times before we abandon this OTA update.
         static constexpr int RETRY_LIMIT = 4;
         if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
-          copy_logs(modified_flash, has_cache);
+          copy_logs(save_current_log, has_cache, sehandle);
           retry_count += 1;
           set_retry_bootloader_message(retry_count, args);
           // Print retry count on screen.
@@ -1045,12 +891,14 @@
       }
     }
   } else if (should_wipe_data) {
-    if (!wipe_data(device)) {
+    save_current_log = true;
+    bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+    if (!WipeData(device, convert_fbe)) {
       status = INSTALL_ERROR;
     }
   } else if (should_prompt_and_wipe_data) {
     // Trigger the logging to capture the cause, even if user chooses to not wipe data.
-    modified_flash = true;
+    save_current_log = true;
 
     ui->ShowText(true);
     ui->SetBackground(RecoveryUI::ERROR);
@@ -1059,7 +907,8 @@
       ui->ShowText(false);
     }
   } else if (should_wipe_cache) {
-    if (!wipe_cache(false, device)) {
+    save_current_log = true;
+    if (!WipeCache(ui, nullptr)) {
       status = INSTALL_ERROR;
     }
   } else if (should_wipe_ab) {
@@ -1073,15 +922,11 @@
     // sideload finishes even if there are errors. Unless one turns on the
     // text display during the installation. This is to enable automated
     // testing.
+    save_current_log = true;
     if (!sideload_auto_reboot) {
       ui->ShowText(true);
     }
-    status = apply_from_adb(&should_wipe_cache, ui);
-    if (status == INSTALL_SUCCESS && should_wipe_cache) {
-      if (!wipe_cache(false, device)) {
-        status = INSTALL_ERROR;
-      }
-    }
+    status = apply_from_adb(ui);
     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 b41368d..5f3ab76 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -50,8 +50,8 @@
 
 #include "common.h"
 #include "fastboot/fastboot.h"
-#include "logging.h"
-#include "minadbd/minadbd.h"
+#include "install/wipe_data.h"
+#include "otautil/logging.h"
 #include "otautil/paths.h"
 #include "otautil/roots.h"
 #include "otautil/sysutil.h"
@@ -322,16 +322,6 @@
   // Take action to refresh pmsg contents
   __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &do_rotate);
 
-  // If this binary is started with the single argument "--adbd", instead of being the normal
-  // recovery binary, it turns into kind of a stripped-down version of adbd that only supports the
-  // 'sideload' command.  Note this must be a real argument, not anything in the command file or
-  // bootloader control block; the only way recovery should be run with this argument is when it
-  // starts a copy of itself from the apply_from_adb() function.
-  if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
-    minadbd_main();
-    return 0;
-  }
-
   time_t start = time(nullptr);
 
   // redirect_stdio should be called only in non-sideload mode. Otherwise we may have two logger
@@ -445,6 +435,8 @@
     ui->Print("Warning: No file_contexts\n");
   }
 
+  SetLoggingSehandle(sehandle);
+
   std::atomic<Device::BuiltinAction> action;
   std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action));
   listener_thread.detach();
diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp
index f5981ac..6add99f 100644
--- a/tests/component/sideload_test.cpp
+++ b/tests/component/sideload_test.cpp
@@ -22,7 +22,6 @@
 
 #include <android-base/file.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 
 #include "fuse_provider.h"
@@ -32,17 +31,26 @@
   ASSERT_EQ(0, access("/dev/fuse", R_OK | W_OK));
 }
 
+class FuseTestDataProvider : public FuseDataProvider {
+ public:
+  FuseTestDataProvider(uint64_t file_size, uint32_t block_size)
+      : FuseDataProvider(file_size, block_size) {}
+
+ private:
+  bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override {
+    return true;
+  }
+};
+
 TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {
-  auto provider_small_block =
-      std::make_unique<FuseFileDataProvider>(android::base::unique_fd(), 4096, 4095);
+  auto provider_small_block = std::make_unique<FuseTestDataProvider>(4096, 4095);
   ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_small_block)));
 
-  auto provider_large_block =
-      std::make_unique<FuseFileDataProvider>(android::base::unique_fd(), 4096, (1 << 22) + 1);
+  auto provider_large_block = std::make_unique<FuseTestDataProvider>(4096, (1 << 22) + 1);
   ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_large_block)));
 
-  auto provider_too_many_blocks = std::make_unique<FuseFileDataProvider>(
-      android::base::unique_fd(), ((1 << 18) + 1) * 4096, 4096);
+  auto provider_too_many_blocks =
+      std::make_unique<FuseTestDataProvider>(((1 << 18) + 1) * 4096, 4096);
   ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_too_many_blocks)));
 }