Snap for 6198741 from 9e040fc1f42ce548384ee33f519dbe8e95fe48b4 to sdk-release

Change-Id: I0054fe35d4ff8cd284f6c6a56a840561f71b14be
diff --git a/Android.bp b/Android.bp
index 421ece2..1667ffb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -79,7 +79,7 @@
     srcs: [
         "daemon.cpp",
         "gsi_service.cpp",
-        "gsi_installer.cpp",
+        "partition_installer.cpp",
     ],
     required: [
         "mke2fs",
@@ -88,19 +88,23 @@
         "gsid.rc",
     ],
     shared_libs: [
-        "gsi_aidl_interface-cpp",
         "libbase",
         "libbinder",
+        "libcrypto",
+        "liblog",
+    ],
+    static_libs: [
+        "gsi_aidl_interface-cpp",
+        "libavb",
+        "libcutils",
+        "libdm",
         "libext4_utils",
         "libfs_mgr",
         "libgsi",
-        "liblog",
+        "libgsid",
         "liblp",
         "libutils",
-    ],
-    static_libs: [
-        "libdm",
-        "libfiemap_passthrough",
+        "libc++fs",
     ],
     local_include_dirs: ["include"],
 }
@@ -119,12 +123,17 @@
 filegroup {
     name: "gsiservice_aidl",
     srcs: [
-        "aidl/android/gsi/GsiInstallParams.aidl",
+        "aidl/android/gsi/AvbPublicKey.aidl",
         "aidl/android/gsi/GsiProgress.aidl",
-        "aidl/android/gsi/IImageService.aidl",
         "aidl/android/gsi/IGsid.aidl",
         "aidl/android/gsi/IGsiService.aidl",
+        "aidl/android/gsi/IImageService.aidl",
+        "aidl/android/gsi/IProgressCallback.aidl",
         "aidl/android/gsi/MappedImage.aidl",
     ],
     path: "aidl",
 }
+
+vts_config {
+    name: "VtsGsiBootTest",
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 21d0b45..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsGsiBootTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/OWNERS b/OWNERS
index 3ef0a70..8a0a10e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,3 @@
 dvander@google.com
 sspatil@google.com
+elsk@google.com
diff --git a/aidl/android/gsi/AvbPublicKey.aidl b/aidl/android/gsi/AvbPublicKey.aidl
new file mode 100644
index 0000000..519ec80
--- /dev/null
+++ b/aidl/android/gsi/AvbPublicKey.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.gsi;
+
+/** {@hide} */
+parcelable AvbPublicKey {
+    /* Raw data bytes. */
+    byte[] bytes;
+    /* SHA-1 digest of the key. */
+    byte[] sha1;
+}
diff --git a/aidl/android/gsi/GsiInstallParams.aidl b/aidl/android/gsi/GsiInstallParams.aidl
deleted file mode 100644
index 6e46da4..0000000
--- a/aidl/android/gsi/GsiInstallParams.aidl
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-package android.gsi;
-
-/** {@hide} */
-parcelable GsiInstallParams {
-    /**
-     * The directory to install GSI images under. This must be either an empty
-     * string (which will use the default /data/gsi), "/data/gsi", or a mount
-     * under /mnt/media_rw. It may end in a trailing slash.
-     */
-    @utf8InCpp String installDir;
-
-    /* The size of the on-disk GSI image. */
-    long gsiSize;
-
-    /* The desired size of the userdata partition. */
-    long userdataSize;
-
-    /* If false, a userdata image is only created if one does not already
-     * exist. If the size is zero, a default size of 8GiB is used. If there is
-     * an existing image smaller than the desired size, it may be resized
-     * automatically.
-     */
-    boolean wipeUserdata;
-}
-
diff --git a/aidl/android/gsi/IGsiService.aidl b/aidl/android/gsi/IGsiService.aidl
index 6aed1c1..65050fe 100644
--- a/aidl/android/gsi/IGsiService.aidl
+++ b/aidl/android/gsi/IGsiService.aidl
@@ -16,7 +16,7 @@
 
 package android.gsi;
 
-import android.gsi.GsiInstallParams;
+import android.gsi.AvbPublicKey;
 import android.gsi.GsiProgress;
 import android.gsi.IImageService;
 import android.os.ParcelFileDescriptor;
@@ -78,9 +78,12 @@
      *
      * @param oneShot       If true, the GSI will boot once and then disable itself.
      *                      It can still be re-enabled again later with setGsiBootable.
+     * @param dsuSlot       The DSU slot to be enabled. Possible values are available
+     *                      with the getInstalledDsuSlots()
+     *
      * @return              INSTALL_* error code.
      */
-    int enableGsi(boolean oneShot);
+    int enableGsi(boolean oneShot, @utf8InCpp String dsuSlot);
 
     /**
      * @return              True if Gsi is enabled
@@ -122,29 +125,55 @@
     boolean isGsiRunning();
 
     /**
+     * Returns the active DSU slot if there is any DSU installed, empty string otherwise.
+     */
+    @utf8InCpp String getActiveDsuSlot();
+
+    /**
      * If a GSI is installed, returns the directory where the installed images
      * are located. Otherwise, returns an empty string.
      */
-     @utf8InCpp String getInstalledGsiImageDir();
+    @utf8InCpp String getInstalledGsiImageDir();
 
     /**
-     * Begin a GSI installation.
+     * Returns all installed DSU slots.
+     */
+    @utf8InCpp List<String> getInstalledDsuSlots();
+
+    /**
+     * Open a DSU installation
      *
-     * This is a replacement for startGsiInstall, in order to supply additional
-     * options.
+     * @param installDir The directory to install DSU images under. This must be
+     *     either an empty string (which will use the default /data/gsi),
+     *     "/data/gsi", or a mount under /mnt/media_rw. It may end in a trailing slash.
      *
      * @return              0 on success, an error code on failure.
      */
-    int beginGsiInstall(in GsiInstallParams params);
+    int openInstall(in @utf8InCpp String installDir);
 
     /**
-     * Wipe the userdata of an existing GSI install. This will not work if the
-     * GSI is currently running. The userdata image will not be removed, but the
-     * first block will be zeroed ensuring that the next GSI boot formats /data.
+     * Close a DSU installation. An installation is complete after the close been invoked.
+     */
+    int closeInstall();
+
+    /**
+     * Create a DSU partition within the current installation
+     *
+     * @param name The DSU partition name
+     * @param size Bytes in the partition
+     * @param readOnly True if the partition is readOnly when DSU is running
+     */
+    int createPartition(in @utf8InCpp String name, long size, boolean readOnly);
+
+    /**
+     * Wipe a partition. This will not work if the GSI is currently running.
+     * The partition will not be removed, but the first block will be zeroed.
+     *
+     * @param name The DSU partition name
      *
      * @return              0 on success, an error code on failure.
      */
-    int wipeGsiUserdata();
+    int zeroPartition(in @utf8InCpp String name);
 
     /**
      * Open a handle to an IImageService for the given metadata and data storage paths.
@@ -154,4 +183,26 @@
      *                      /metadata/gsi/{prefix}.
      */
     IImageService openImageService(@utf8InCpp String prefix);
+
+    /**
+     * Dump diagnostic information about device-mapper devices. This is intended
+     * for dumpstate.
+     */
+    @utf8InCpp String dumpDeviceMapperDevices();
+
+    /**
+     * Retrieve AVB public key from the current mapped partition.
+     * This works only while partition device is mapped and the end-of-partition
+     * AVB footer has been written.
+     * A call to createPartition() does the following things:
+     * 1. Close the previous partition installer, thus unmap the partition.
+     * 2. Open a new partition installer.
+     * 3. Create and map the new partition.
+     *
+     * In other words, getAvbPublicKey() works between two createPartition() calls.
+     *
+     * @param dst           Output the AVB public key.
+     * @return              0 on success, an error code on failure.
+     */
+    int getAvbPublicKey(out AvbPublicKey dst);
 }
diff --git a/aidl/android/gsi/IImageService.aidl b/aidl/android/gsi/IImageService.aidl
index 1195c00..c8c5a9d 100644
--- a/aidl/android/gsi/IImageService.aidl
+++ b/aidl/android/gsi/IImageService.aidl
@@ -16,7 +16,9 @@
 
 package android.gsi;
 
+import android.gsi.AvbPublicKey;
 import android.gsi.MappedImage;
+import android.gsi.IProgressCallback;
 
 /** {@hide} */
 interface IImageService {
@@ -25,6 +27,11 @@
     const int CREATE_IMAGE_READONLY = 0x1;
     const int CREATE_IMAGE_ZERO_FILL = 0x2;
 
+    /* Successfully returned */
+    const int IMAGE_OK = 0;
+    /* Generic error code */
+    const int IMAGE_ERROR = 1;
+
     /**
      * Create an image that can be mapped as a block device.
      *
@@ -35,15 +42,20 @@
      *                      free, the call will fail.
      * @param readonly      If readonly, MapBackingImage() will configure the device as
      *                      readonly.
-     * @return              True on success, false otherwise.
+     * @param on_progress   Progress callback. It is invoked when there is an interesting update.
+     *                      For each invocation, |current| is the number of bytes actually written,
+     *                      and |total| is set to |size|.
+     * @throws ServiceSpecificException if any error occurs. Exception code is a
+     *                      FiemapStatus::ErrorCode value.
      */
-    void createBackingImage(@utf8InCpp String name, long size, int flags);
+    void createBackingImage(@utf8InCpp String name, long size, int flags,
+                            @nullable IProgressCallback on_progress);
 
     /**
      * Delete an image created with createBackingImage.
      *
      * @param name          Image name as passed to createBackingImage().
-     * @return              True on success, false otherwise.
+     * @throws ServiceSpecificException if any error occurs.
      */
     void deleteBackingImage(@utf8InCpp String name);
 
@@ -81,6 +93,18 @@
     boolean isImageMapped(@utf8InCpp String name);
 
     /**
+     * Retrieve AVB public key from an image.
+     * If the image is already mapped then it works the same as
+     * IGsiService::getAvbPublicKey(). Otherwise this will attempt to
+     * map / unmap the partition image upon enter / return.
+     *
+     * @param name          Image name as passed to createBackingImage().
+     * @param dst           Output of the AVB public key.
+     * @return              0 on success, an error code on failure.
+     */
+    int getAvbPublicKey(@utf8InCpp String name, out AvbPublicKey dst);
+
+    /**
      * Get all installed backing image names
      *
      * @return list of installed backing image names
@@ -95,7 +119,23 @@
      * @param bytes         Number of zeros to be written, starting from the
      *                      beginning. If bytes is equal to 0, then the whole
      *                      image file is filled with zeros.
-     * @return              True on success, false otherwise.
+     * @throws ServiceSpecificException if any error occurs. Exception code is a
+     *                      FiemapStatus::ErrorCode value.
      */
     void zeroFillNewImage(@utf8InCpp String name, long bytes);
+
+    /**
+     * Find and remove all images in the containing folder of this instance.
+     */
+    void removeAllImages();
+
+    /**
+     * Remove all images that were marked as disabled in recovery.
+     */
+    void removeDisabledImages();
+
+    /**
+     * Return the block device path of a mapped image, or an empty string if not mapped.
+     */
+    @utf8InCpp String getMappedImageDevice(@utf8InCpp String name);
 }
diff --git a/aidl/android/gsi/IProgressCallback.aidl b/aidl/android/gsi/IProgressCallback.aidl
new file mode 100644
index 0000000..b7acefc
--- /dev/null
+++ b/aidl/android/gsi/IProgressCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.gsi;
+
+/** {@hide} */
+interface IProgressCallback {
+    /**
+     * Report progress for a long-running task.
+     *
+     * The percentage can be computed using current / total. The task is
+     * done when current == total.
+     *
+     * Different APIs may have different meanings for the parameters; see
+     * specific APIs for details.
+     *
+     * @param current   a value indicating the current progress. Should be treated as uint64.
+     * @param total     a value indicating 100% progress. Should be treated as uint64.
+     */
+    void onProgress(long current, long total);
+}
diff --git a/daemon.cpp b/daemon.cpp
index 3fa7b3f..782d2ed 100644
--- a/daemon.cpp
+++ b/daemon.cpp
@@ -16,6 +16,7 @@
 
 #include <getopt.h>
 
+#include <iostream>
 #include <string>
 
 #include <android-base/logging.h>
@@ -23,6 +24,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <libgsi/libgsi.h>
+#include <libgsi/libgsid.h>
 
 #include "gsi_service.h"
 
@@ -30,12 +32,34 @@
 using android::sp;
 using namespace std::literals;
 
+static int DumpDeviceMapper() {
+    auto service = android::gsi::GetGsiService();
+    if (!service) {
+        std::cerr << "Could not start IGsiService.\n";
+        return 1;
+    }
+    std::string output;
+    auto status = service->dumpDeviceMapperDevices(&output);
+    if (!status.isOk()) {
+        std::cerr << "Could not dump device-mapper devices: " << status.exceptionMessage().c_str()
+                  << "\n";
+        return 1;
+    }
+    std::cout << output;
+    return 0;
+}
+
 int main(int argc, char** argv) {
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
-    if (argc > 1 && argv[1] == "run-startup-tasks"s) {
-        android::gsi::GsiService::RunStartupTasks();
-        exit(0);
+    if (argc > 1) {
+        if (argv[1] == "run-startup-tasks"s) {
+            android::gsi::GsiService::RunStartupTasks();
+            exit(0);
+        } else if (argv[1] == "dump-device-mapper"s) {
+            int rc = DumpDeviceMapper();
+            exit(rc);
+        }
     }
 
     android::gsi::Gsid::Register();
diff --git a/file_paths.h b/file_paths.h
index b596704..1ae7817 100644
--- a/file_paths.h
+++ b/file_paths.h
@@ -16,22 +16,26 @@
 
 #pragma once
 
+#include <filesystem>
+
 namespace android {
 namespace gsi {
 
 static constexpr char kDefaultDsuImageFolder[] = "/data/gsi/dsu/";
 static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
 
-static constexpr char kDsuMetadataDir[] = "/metadata/gsi/dsu";
-static constexpr char kDsuOneShotBootFile[] = "/metadata/gsi/dsu/one_shot_boot";
-static constexpr char kDsuInstallDirFile[] = "/metadata/gsi/dsu/install_dir";
+static inline std::string MetadataDir(const std::string& dsu_slot) {
+    return std::filesystem::path(DSU_METADATA_PREFIX) / dsu_slot;
+}
+
+static constexpr char kDsuOneShotBootFile[] = DSU_METADATA_PREFIX "one_shot_boot";
 
 // This file can contain the following values:
 //   [int]      - boot attempt counter, starting from 0
 //   "ok"       - boot was marked successful
 //   "disabled" - boot into GSI no longer allowed
 //   "wipe"     - boot into GSI not allowed; next reboot will delete gsi
-static constexpr char kDsuInstallStatusFile[] = "/metadata/gsi/dsu/install_status";
+static constexpr char kDsuInstallStatusFile[] = DSU_METADATA_PREFIX "install_status";
 
 }  // namespace gsi
 }  // namespace android
diff --git a/gsi_installer.cpp b/gsi_installer.cpp
deleted file mode 100644
index 27e6dd0..0000000
--- a/gsi_installer.cpp
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * 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 "gsi_installer.h"
-
-#include <sys/statvfs.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr_dm_linear.h>
-#include <libdm/dm.h>
-#include <libgsi/libgsi.h>
-
-#include "file_paths.h"
-#include "gsi_service.h"
-#include "libgsi_private.h"
-
-namespace android {
-namespace gsi {
-
-using namespace std::literals;
-using namespace android::dm;
-using namespace android::fiemap;
-using namespace android::fs_mgr;
-using android::base::unique_fd;
-
-// The default size of userdata.img for GSI.
-// We are looking for /data to have atleast 40% free space
-static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
-// Default userdata image size.
-static constexpr int64_t kDefaultUserdataSize = int64_t(2) * 1024 * 1024 * 1024;
-
-GsiInstaller::GsiInstaller(GsiService* service, const GsiInstallParams& params)
-    : service_(service),
-      install_dir_(params.installDir),
-      gsi_size_(params.gsiSize),
-      wipe_userdata_(params.wipeUserdata) {
-    userdata_size_ = (params.userdataSize) ? params.userdataSize : kDefaultUserdataSize;
-    images_ = ImageManager::Open(kDsuMetadataDir, install_dir_);
-
-    // Only rm userdata_gsi if one didn't already exist.
-    if (wipe_userdata_ || !images_->BackingImageExists("userdata_gsi")) {
-        wipe_userdata_on_failure_ = true;
-    }
-}
-
-GsiInstaller::GsiInstaller(GsiService* service, const std::string& install_dir)
-    : service_(service), install_dir_(install_dir) {
-    images_ = ImageManager::Open(kDsuMetadataDir, install_dir_);
-
-    // The install already exists, so always mark it as succeeded.
-    succeeded_ = true;
-}
-
-GsiInstaller::~GsiInstaller() {
-    if (!succeeded_) {
-        // Close open handles before we remove files.
-        system_device_ = nullptr;
-        PostInstallCleanup(images_.get());
-
-        GsiService::RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
-    }
-    if (IsAshmemMapped()) {
-        UnmapAshmem();
-    }
-}
-
-void GsiInstaller::PostInstallCleanup() {
-    auto manager = ImageManager::Open(kDsuMetadataDir, GsiService::GetInstalledImageDir());
-    if (!manager) {
-        LOG(ERROR) << "Could not open image manager";
-        return;
-    }
-    return PostInstallCleanup(manager.get());
-}
-
-void GsiInstaller::PostInstallCleanup(ImageManager* manager) {
-    if (manager->IsImageMapped("userdata_gsi")) {
-        manager->UnmapImageDevice("userdata_gsi");
-    }
-    if (manager->IsImageMapped("system_gsi")) {
-        manager->UnmapImageDevice("system_gsi");
-    }
-}
-
-int GsiInstaller::StartInstall() {
-    if (int status = PerformSanityChecks()) {
-        return status;
-    }
-    if (int status = PreallocateFiles()) {
-        return status;
-    }
-    if (!FormatUserdata()) {
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    // Map system_gsi so we can write to it.
-    system_device_ = OpenPartition("system_gsi");
-    if (!system_device_) {
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    // Clear the progress indicator.
-    service_->UpdateProgress(IGsiService::STATUS_NO_OPERATION, 0);
-    return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::PerformSanityChecks() {
-    if (!images_) {
-        LOG(ERROR) << "unable to create image manager";
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-    if (gsi_size_ < 0) {
-        LOG(ERROR) << "image size " << gsi_size_ << " is negative";
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-    if (android::gsi::IsGsiRunning()) {
-        LOG(ERROR) << "cannot install gsi inside a live gsi";
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    struct statvfs sb;
-    if (statvfs(install_dir_.c_str(), &sb)) {
-        PLOG(ERROR) << "failed to read file system stats";
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    // This is the same as android::vold::GetFreebytes() but we also
-    // need the total file system size so we open code it here.
-    uint64_t free_space = 1ULL * sb.f_bavail * sb.f_frsize;
-    uint64_t fs_size = sb.f_blocks * sb.f_frsize;
-    if (free_space <= (gsi_size_ + userdata_size_)) {
-        LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
-        return IGsiService::INSTALL_ERROR_NO_SPACE;
-    }
-    // We are asking for 40% of the /data to be empty.
-    // TODO: may be not hard code it like this
-    double free_space_percent = ((1.0 * free_space) / fs_size) * 100;
-    if (free_space_percent < kMinimumFreeSpaceThreshold) {
-        LOG(ERROR) << "free space " << static_cast<uint64_t>(free_space_percent)
-                   << "% is below the minimum threshold of " << kMinimumFreeSpaceThreshold << "%";
-        return IGsiService::INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
-    }
-    return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::PreallocateFiles() {
-    if (wipe_userdata_) {
-        images_->DeleteBackingImage("userdata_gsi");
-    }
-    images_->DeleteBackingImage("system_gsi");
-
-    // Create fallocated files.
-    if (int status = PreallocateUserdata()) {
-        return status;
-    }
-    if (int status = PreallocateSystem()) {
-        return status;
-    }
-
-    service_->UpdateProgress(IGsiService::STATUS_COMPLETE, 0);
-    return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::PreallocateUserdata() {
-    if (wipe_userdata_ || !images_->BackingImageExists("userdata_gsi")) {
-        service_->StartAsyncOperation("create userdata", userdata_size_);
-        if (!CreateImage("userdata_gsi", userdata_size_, false)) {
-            LOG(ERROR) << "Could not create userdata image";
-            return IGsiService::INSTALL_ERROR_GENERIC;
-        }
-
-        // Signal that we need to reformat userdata.
-        wipe_userdata_ = true;
-    }
-    return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::PreallocateSystem() {
-    service_->StartAsyncOperation("create system", gsi_size_);
-
-    if (!CreateImage("system_gsi", gsi_size_, true)) {
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-    return IGsiService::INSTALL_OK;
-}
-
-bool GsiInstaller::CreateImage(const std::string& name, uint64_t size, bool readonly) {
-    auto progress = [this](uint64_t bytes, uint64_t /* total */) -> bool {
-        service_->UpdateProgress(IGsiService::STATUS_WORKING, bytes);
-        if (service_->should_abort()) return false;
-        return true;
-    };
-    int flags = ImageManager::CREATE_IMAGE_DEFAULT;
-    if (readonly) {
-        flags |= ImageManager::CREATE_IMAGE_READONLY;
-    }
-    return images_->CreateBackingImage(name, size, flags, std::move(progress));
-}
-
-std::unique_ptr<MappedDevice> GsiInstaller::OpenPartition(const std::string& name) {
-    return MappedDevice::Open(images_.get(), 10s, name);
-}
-
-bool GsiInstaller::CommitGsiChunk(int stream_fd, int64_t bytes) {
-    service_->StartAsyncOperation("write gsi", gsi_size_);
-
-    if (bytes < 0) {
-        LOG(ERROR) << "chunk size " << bytes << " is negative";
-        return false;
-    }
-
-    static const size_t kBlockSize = 4096;
-    auto buffer = std::make_unique<char[]>(kBlockSize);
-
-    int progress = -1;
-    uint64_t remaining = bytes;
-    while (remaining) {
-        size_t max_to_read = std::min(static_cast<uint64_t>(kBlockSize), remaining);
-        ssize_t rv = TEMP_FAILURE_RETRY(read(stream_fd, buffer.get(), max_to_read));
-        if (rv < 0) {
-            PLOG(ERROR) << "read gsi chunk";
-            return false;
-        }
-        if (rv == 0) {
-            LOG(ERROR) << "no bytes left in stream";
-            return false;
-        }
-        if (!CommitGsiChunk(buffer.get(), rv)) {
-            return false;
-        }
-        CHECK(static_cast<uint64_t>(rv) <= remaining);
-        remaining -= rv;
-
-        // Only update the progress when the % (or permille, in this case)
-        // significantly changes.
-        int new_progress = ((gsi_size_ - remaining) * 1000) / gsi_size_;
-        if (new_progress != progress) {
-            service_->UpdateProgress(IGsiService::STATUS_WORKING, gsi_size_ - remaining);
-        }
-    }
-
-    service_->UpdateProgress(IGsiService::STATUS_COMPLETE, gsi_size_);
-    return true;
-}
-
-bool GsiInstaller::IsFinishedWriting() {
-    return gsi_bytes_written_ == gsi_size_;
-}
-
-bool GsiInstaller::IsAshmemMapped() {
-    return ashmem_data_ != MAP_FAILED;
-}
-
-bool GsiInstaller::CommitGsiChunk(const void* data, size_t bytes) {
-    if (static_cast<uint64_t>(bytes) > gsi_size_ - gsi_bytes_written_) {
-        // We cannot write past the end of the image file.
-        LOG(ERROR) << "chunk size " << bytes << " exceeds remaining image size (" << gsi_size_
-                   << " expected, " << gsi_bytes_written_ << " written)";
-        return false;
-    }
-    if (service_->should_abort()) {
-        return false;
-    }
-    if (!android::base::WriteFully(system_device_->fd(), data, bytes)) {
-        PLOG(ERROR) << "write failed";
-        return false;
-    }
-    gsi_bytes_written_ += bytes;
-    return true;
-}
-
-bool GsiInstaller::MapAshmem(int fd, size_t size) {
-    ashmem_size_ = size;
-    ashmem_data_ = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-    return ashmem_data_ != MAP_FAILED;
-}
-
-void GsiInstaller::UnmapAshmem() {
-    if (munmap(ashmem_data_, ashmem_size_) != 0) {
-        PLOG(ERROR) << "cannot munmap";
-        return;
-    }
-    ashmem_data_ = MAP_FAILED;
-    ashmem_size_ = -1;
-}
-
-bool GsiInstaller::CommitGsiChunk(size_t bytes) {
-    if (!IsAshmemMapped()) {
-        PLOG(ERROR) << "ashmem is not mapped";
-        return false;
-    }
-    bool success = CommitGsiChunk(ashmem_data_, bytes);
-    if (success && IsFinishedWriting()) {
-        UnmapAshmem();
-    }
-    return success;
-}
-
-bool GsiInstaller::SetBootMode(bool one_shot) {
-    if (one_shot) {
-        if (!android::base::WriteStringToFile("1", kDsuOneShotBootFile)) {
-            PLOG(ERROR) << "write " << kDsuOneShotBootFile;
-            return false;
-        }
-    } else if (!access(kDsuOneShotBootFile, F_OK)) {
-        std::string error;
-        if (!android::base::RemoveFileIfExists(kDsuOneShotBootFile, &error)) {
-            LOG(ERROR) << error;
-            return false;
-        }
-    }
-    return true;
-}
-
-bool GsiInstaller::CreateInstallStatusFile() {
-    if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) {
-        PLOG(ERROR) << "write " << kDsuInstallStatusFile;
-        return false;
-    }
-    return true;
-}
-
-bool GsiInstaller::FormatUserdata() {
-    auto device = OpenPartition("userdata_gsi");
-    if (!device) {
-        return false;
-    }
-
-    // libcutils checks the first 4K, no matter the block size.
-    std::string zeroes(4096, 0);
-    if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
-        PLOG(ERROR) << "write userdata_gsi";
-        return false;
-    }
-    return true;
-}
-
-int GsiInstaller::SetGsiBootable(bool one_shot) {
-    if (gsi_bytes_written_ != gsi_size_) {
-        // We cannot boot if the image is incomplete.
-        LOG(ERROR) << "image incomplete; expected " << gsi_size_ << " bytes, waiting for "
-                   << (gsi_size_ - gsi_bytes_written_) << " bytes";
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    if (fsync(system_device_->fd())) {
-        PLOG(ERROR) << "fsync failed for system_gsi";
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-    system_device_ = {};
-
-    // If files moved (are no longer pinned), the metadata file will be invalid.
-    // This check can be removed once b/133967059 is fixed.
-    if (!images_->Validate()) {
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    // Remember the installation directory.
-    if (!android::base::WriteStringToFile(install_dir_, kDsuInstallDirFile)) {
-        PLOG(ERROR) << "write failed: " << kDsuInstallDirFile;
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    // Note: create the install status file last, since this is the actual boot
-    // indicator.
-    if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    succeeded_ = true;
-    return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::CheckInstallState() {
-    std::vector<std::string> gsi_images = {"system_gsi", "userdata_gsi"};
-    for (const auto& image : gsi_images) {
-        if (!images_->PartitionExists(image) || !images_->BackingImageExists(image)) {
-            return IGsiService::INSTALL_ERROR_GENERIC;
-        }
-    }
-    return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::ReenableGsi(bool one_shot) {
-    if (IsGsiRunning()) {
-        if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
-            return IGsiService::INSTALL_ERROR_GENERIC;
-        }
-        return IGsiService::INSTALL_OK;
-    }
-
-    if (int error = CheckInstallState()) {
-        return error;
-    }
-    if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-    return IGsiService::INSTALL_OK;
-}
-
-int GsiInstaller::WipeUserdata() {
-    if (int error = CheckInstallState()) {
-        return error;
-    }
-
-    auto device = OpenPartition("userdata_gsi");
-    if (!device) {
-        return IGsiService::INSTALL_ERROR_GENERIC;
-    }
-
-    // Wipe the first 1MiB of the device, ensuring both the first block and
-    // the superblock are destroyed.
-    static constexpr uint64_t kEraseSize = 1024 * 1024;
-
-    std::string zeroes(4096, 0);
-    uint64_t erase_size = std::min(kEraseSize, get_block_device_size(device->fd()));
-    for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
-        if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
-            PLOG(ERROR) << "write userdata_gsi";
-            return IGsiService::INSTALL_ERROR_GENERIC;
-        }
-    }
-    return IGsiService::INSTALL_OK;
-}
-
-}  // namespace gsi
-}  // namespace android
diff --git a/gsi_service.cpp b/gsi_service.cpp
index dcf2d83..3705c5b 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -18,16 +18,19 @@
 
 #include <errno.h>
 #include <linux/fs.h>
+#include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <array>
 #include <chrono>
 #include <string>
 #include <vector>
 
+#include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
@@ -36,7 +39,10 @@
 #include <android/gsi/IGsiService.h>
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
+#include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
 
 #include "file_paths.h"
@@ -48,11 +54,24 @@
 using namespace std::literals;
 using namespace android::fs_mgr;
 using namespace android::fiemap;
+using android::base::ReadFileToString;
+using android::base::ReadFullyAtOffset;
+using android::base::RemoveFileIfExists;
 using android::base::StringPrintf;
 using android::base::unique_fd;
+using android::base::WriteStringToFd;
+using android::base::WriteStringToFile;
+using android::dm::DeviceMapper;
+
+static std::mutex sInstanceLock;
 
 android::wp<GsiService> GsiService::sInstance;
 
+// Default userdata image size.
+static constexpr int64_t kDefaultUserdataSize = int64_t(2) * 1024 * 1024 * 1024;
+
+static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst);
+
 void Gsid::Register() {
     auto ret = android::BinderService<Gsid>::publish();
     if (ret != android::OK) {
@@ -67,11 +86,10 @@
 
 GsiService::GsiService(Gsid* parent) : parent_(parent) {
     progress_ = {};
-    GsiInstaller::PostInstallCleanup();
 }
 
 GsiService::~GsiService() {
-    std::lock_guard<std::mutex> guard(parent_->lock());
+    std::lock_guard<std::mutex> guard(sInstanceLock);
 
     if (sInstance == this) {
         // No more consumers, gracefully shut down gsid.
@@ -80,7 +98,7 @@
 }
 
 android::sp<IGsiService> GsiService::Get(Gsid* parent) {
-    std::lock_guard<std::mutex> guard(parent->lock());
+    std::lock_guard<std::mutex> guard(sInstanceLock);
 
     android::sp<GsiService> service = sInstance.promote();
     if (!service) {
@@ -102,23 +120,88 @@
         if (!status.isOk()) return status;                            \
     } while (0)
 
-binder::Status GsiService::beginGsiInstall(const GsiInstallParams& given_params,
-                                           int* _aidl_return) {
+int GsiService::SaveInstallation(const std::string& installation) {
+    auto dsu_slot = GetDsuSlot(installation);
+    auto install_dir_file = DsuInstallDirFile(dsu_slot);
+    auto metadata_dir = android::base::Dirname(install_dir_file);
+    if (access(metadata_dir.c_str(), F_OK) != 0) {
+        if (mkdir(metadata_dir.c_str(), 0777) != 0) {
+            PLOG(ERROR) << "Failed to mkdir " << metadata_dir;
+            return INSTALL_ERROR_GENERIC;
+        }
+    }
+    auto fd = android::base::unique_fd(
+            open(install_dir_file.c_str(), O_RDWR | O_SYNC | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
+    if (!WriteStringToFd(installation, fd)) {
+        PLOG(ERROR) << "write failed: " << DsuInstallDirFile(dsu_slot);
+        return INSTALL_ERROR_GENERIC;
+    }
+    return INSTALL_OK;
+}
+
+binder::Status GsiService::openInstall(const std::string& install_dir, int* _aidl_return) {
+    ENFORCE_SYSTEM;
+    std::lock_guard<std::mutex> guard(parent_->lock());
+    if (IsGsiRunning()) {
+        *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
+    }
+    install_dir_ = install_dir;
+    if (int status = ValidateInstallParams(install_dir_)) {
+        *_aidl_return = status;
+        return binder::Status::ok();
+    }
+    std::string message;
+    auto dsu_slot = GetDsuSlot(install_dir_);
+    if (!RemoveFileIfExists(GetCompleteIndication(dsu_slot), &message)) {
+        LOG(ERROR) << message;
+    }
+    // Remember the installation directory before allocate any resource
+    *_aidl_return = SaveInstallation(install_dir_);
+    return binder::Status::ok();
+}
+
+binder::Status GsiService::closeInstall(int* _aidl_return) {
+    ENFORCE_SYSTEM;
+    std::lock_guard<std::mutex> guard(parent_->lock());
+    auto dsu_slot = GetDsuSlot(install_dir_);
+    std::string file = GetCompleteIndication(dsu_slot);
+    if (!WriteStringToFile("OK", file)) {
+        PLOG(ERROR) << "write failed: " << file;
+        *_aidl_return = INSTALL_ERROR_GENERIC;
+    }
+    *_aidl_return = INSTALL_OK;
+    return binder::Status::ok();
+}
+
+binder::Status GsiService::createPartition(const ::std::string& name, int64_t size, bool readOnly,
+                                           int32_t* _aidl_return) {
     ENFORCE_SYSTEM;
     std::lock_guard<std::mutex> guard(parent_->lock());
 
-    // Make sure any interrupted installations are cleaned up.
+    if (install_dir_.empty()) {
+        PLOG(ERROR) << "open is required for createPartition";
+        *_aidl_return = INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
+    }
+
+    // Make sure a pending interrupted installations are cleaned up.
     installer_ = nullptr;
 
     // Do some precursor validation on the arguments before diving into the
     // install process.
-    GsiInstallParams params = given_params;
-    if (int status = ValidateInstallParams(&params)) {
-        *_aidl_return = status;
+    if (size % LP_SECTOR_SIZE) {
+        LOG(ERROR) << " size " << size << " is not a multiple of " << LP_SECTOR_SIZE;
+        *_aidl_return = INSTALL_ERROR_GENERIC;
         return binder::Status::ok();
     }
 
-    installer_ = std::make_unique<GsiInstaller>(this, params);
+    if (size == 0 && name == "userdata") {
+        size = kDefaultUserdataSize;
+    }
+    installer_ = std::make_unique<PartitionInstaller>(this, install_dir_, name,
+                                                      GetDsuSlot(install_dir_), size, readOnly);
+    progress_ = {};
     int status = installer_->StartInstall();
     if (status != INSTALL_OK) {
         installer_ = nullptr;
@@ -165,6 +248,9 @@
     ENFORCE_SYSTEM;
     std::lock_guard<std::mutex> guard(progress_lock_);
 
+    if (installer_ == nullptr) {
+        progress_ = {};
+    }
     *_aidl_return = progress_;
     return binder::Status::ok();
 }
@@ -183,6 +269,7 @@
 
 binder::Status GsiService::setGsiAshmem(const ::android::os::ParcelFileDescriptor& ashmem,
                                         int64_t size, bool* _aidl_return) {
+    ENFORCE_SYSTEM;
     if (!installer_) {
         *_aidl_return = false;
         return binder::Status::ok();
@@ -191,13 +278,21 @@
     return binder::Status::ok();
 }
 
-binder::Status GsiService::enableGsi(bool one_shot, int* _aidl_return) {
+binder::Status GsiService::enableGsi(bool one_shot, const std::string& dsuSlot, int* _aidl_return) {
     std::lock_guard<std::mutex> guard(parent_->lock());
 
+    if (!WriteStringToFile(dsuSlot, kDsuActiveFile)) {
+        PLOG(ERROR) << "write failed: " << GetDsuSlot(install_dir_);
+        *_aidl_return = INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
+    }
     if (installer_) {
         ENFORCE_SYSTEM;
-        if (int error = installer_->SetGsiBootable(one_shot)) {
-            *_aidl_return = error;
+        installer_ = {};
+        // Note: create the install status file last, since this is the actual boot
+        // indicator.
+        if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+            *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
         } else {
             *_aidl_return = INSTALL_OK;
         }
@@ -217,7 +312,7 @@
     if (!GetInstallStatus(&boot_key)) {
         *_aidl_return = false;
     } else {
-        *_aidl_return = (boot_key == kInstallStatusOk);
+        *_aidl_return = (boot_key != kInstallStatusDisabled);
     }
     return binder::Status::ok();
 }
@@ -226,19 +321,13 @@
     ENFORCE_SYSTEM_OR_SHELL;
     std::lock_guard<std::mutex> guard(parent_->lock());
 
-    // Just in case an install was left hanging.
-    std::string install_dir;
-    if (installer_) {
-        install_dir = installer_->install_dir();
-    } else {
-        install_dir = GetInstalledImageDir();
-    }
-
+    std::string install_dir = GetActiveInstalledImageDir();
     if (IsGsiRunning()) {
         // Can't remove gsi files while running.
         *_aidl_return = UninstallGsi();
     } else {
-        *_aidl_return = RemoveGsiFiles(install_dir, true /* wipeUserdata */);
+        installer_ = {};
+        *_aidl_return = RemoveGsiFiles(install_dir);
     }
     return binder::Status::ok();
 }
@@ -291,13 +380,26 @@
     ENFORCE_SYSTEM;
     std::lock_guard<std::mutex> guard(parent_->lock());
 
-    if (IsGsiInstalled()) {
-        *_aidl_return = GetInstalledImageDir();
-    }
+    *_aidl_return = GetActiveInstalledImageDir();
     return binder::Status::ok();
 }
 
-binder::Status GsiService::wipeGsiUserdata(int* _aidl_return) {
+binder::Status GsiService::getActiveDsuSlot(std::string* _aidl_return) {
+    ENFORCE_SYSTEM_OR_SHELL;
+    std::lock_guard<std::mutex> guard(parent_->lock());
+
+    *_aidl_return = GetActiveDsuSlot();
+    return binder::Status::ok();
+}
+
+binder::Status GsiService::getInstalledDsuSlots(std::vector<std::string>* _aidl_return) {
+    ENFORCE_SYSTEM;
+    std::lock_guard<std::mutex> guard(parent_->lock());
+    *_aidl_return = GetInstalledDsuSlots();
+    return binder::Status::ok();
+}
+
+binder::Status GsiService::zeroPartition(const std::string& name, int* _aidl_return) {
     ENFORCE_SYSTEM_OR_SHELL;
     std::lock_guard<std::mutex> guard(parent_->lock());
 
@@ -306,15 +408,89 @@
         return binder::Status::ok();
     }
 
-    auto installer = std::make_unique<GsiInstaller>(this, GetInstalledImageDir());
-    *_aidl_return = installer->WipeUserdata();
+    std::string install_dir = GetActiveInstalledImageDir();
+    *_aidl_return = PartitionInstaller::WipeWritable(GetDsuSlot(install_dir), install_dir, name);
 
     return binder::Status::ok();
 }
 
-static binder::Status BinderError(const std::string& message) {
-    return binder::Status::fromExceptionCode(binder::Status::EX_SERVICE_SPECIFIC,
-                                             String8(message.c_str()));
+static binder::Status BinderError(const std::string& message,
+                                  FiemapStatus::ErrorCode status = FiemapStatus::ErrorCode::ERROR) {
+    return binder::Status::fromServiceSpecificError(static_cast<int32_t>(status), message.c_str());
+}
+
+binder::Status GsiService::dumpDeviceMapperDevices(std::string* _aidl_return) {
+    ENFORCE_SYSTEM_OR_SHELL;
+
+    auto& dm = DeviceMapper::Instance();
+
+    std::vector<DeviceMapper::DmBlockDevice> devices;
+    if (!dm.GetAvailableDevices(&devices)) {
+        return BinderError("Could not list devices");
+    }
+
+    std::stringstream text;
+    for (const auto& device : devices) {
+        text << "Device " << device.name() << " (" << device.Major() << ":" << device.Minor()
+             << ")\n";
+
+        std::vector<DeviceMapper::TargetInfo> table;
+        if (!dm.GetTableInfo(device.name(), &table)) {
+            continue;
+        }
+
+        for (const auto& target : table) {
+            const auto& spec = target.spec;
+            auto target_type = DeviceMapper::GetTargetType(spec);
+            text << "    " << target_type << " " << spec.sector_start << " " << spec.length << " "
+                 << target.data << "\n";
+        }
+    }
+
+    *_aidl_return = text.str();
+    return binder::Status::ok();
+}
+
+binder::Status GsiService::getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_return) {
+    ENFORCE_SYSTEM;
+    std::lock_guard<std::mutex> guard(parent_->lock());
+
+    if (!installer_) {
+        *_aidl_return = INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
+    }
+    int fd = installer_->GetPartitionFd();
+    if (!GetAvbPublicKeyFromFd(fd, dst)) {
+        LOG(ERROR) << "Failed to extract AVB public key";
+        *_aidl_return = INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
+    }
+    *_aidl_return = INSTALL_OK;
+    return binder::Status::ok();
+}
+
+bool GsiService::CreateInstallStatusFile() {
+    if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) {
+        PLOG(ERROR) << "write " << kDsuInstallStatusFile;
+        return false;
+    }
+    return true;
+}
+
+bool GsiService::SetBootMode(bool one_shot) {
+    if (one_shot) {
+        if (!android::base::WriteStringToFile("1", kDsuOneShotBootFile)) {
+            PLOG(ERROR) << "write " << kDsuOneShotBootFile;
+            return false;
+        }
+    } else if (!access(kDsuOneShotBootFile, F_OK)) {
+        std::string error;
+        if (!android::base::RemoveFileIfExists(kDsuOneShotBootFile, &error)) {
+            LOG(ERROR) << error;
+            return false;
+        }
+    }
+    return true;
 }
 
 static binder::Status UidSecurityError() {
@@ -327,14 +503,20 @@
   public:
     ImageService(GsiService* service, std::unique_ptr<ImageManager>&& impl, uid_t uid);
     binder::Status getAllBackingImages(std::vector<std::string>* _aidl_return);
-    binder::Status createBackingImage(const std::string& name, int64_t size, int flags) override;
+    binder::Status createBackingImage(const std::string& name, int64_t size, int flags,
+                                      const sp<IProgressCallback>& on_progress) override;
     binder::Status deleteBackingImage(const std::string& name) override;
     binder::Status mapImageDevice(const std::string& name, int32_t timeout_ms,
                                   MappedImage* mapping) override;
     binder::Status unmapImageDevice(const std::string& name) override;
     binder::Status backingImageExists(const std::string& name, bool* _aidl_return) override;
     binder::Status isImageMapped(const std::string& name, bool* _aidl_return) override;
+    binder::Status getAvbPublicKey(const std::string& name, AvbPublicKey* dst,
+                                   int32_t* _aidl_return) override;
     binder::Status zeroFillNewImage(const std::string& name, int64_t bytes) override;
+    binder::Status removeAllImages() override;
+    binder::Status removeDisabledImages() override;
+    binder::Status getMappedImageDevice(const std::string& name, std::string* device) override;
 
   private:
     bool CheckUid();
@@ -353,13 +535,28 @@
     return binder::Status::ok();
 }
 
-binder::Status ImageService::createBackingImage(const std::string& name, int64_t size, int flags) {
+binder::Status ImageService::createBackingImage(const std::string& name, int64_t size, int flags,
+                                                const sp<IProgressCallback>& on_progress) {
     if (!CheckUid()) return UidSecurityError();
 
     std::lock_guard<std::mutex> guard(parent_->lock());
 
-    if (!impl_->CreateBackingImage(name, size, flags, nullptr)) {
-        return BinderError("Failed to create");
+    std::function<bool(uint64_t, uint64_t)> callback;
+    if (on_progress) {
+        callback = [on_progress](uint64_t current, uint64_t total) -> bool {
+            auto status = on_progress->onProgress(static_cast<int64_t>(current),
+                                                  static_cast<int64_t>(total));
+            if (!status.isOk()) {
+                LOG(ERROR) << "progress callback returned: " << status.toString8().string();
+                return false;
+            }
+            return true;
+        };
+    }
+
+    auto res = impl_->CreateBackingImage(name, size, flags, std::move(callback));
+    if (!res.is_ok()) {
+        return BinderError("Failed to create: " + res.string(), res.error_code());
     }
     return binder::Status::ok();
 }
@@ -416,6 +613,46 @@
     return binder::Status::ok();
 }
 
+binder::Status ImageService::getAvbPublicKey(const std::string& name, AvbPublicKey* dst,
+                                             int32_t* _aidl_return) {
+    if (!CheckUid()) return UidSecurityError();
+
+    std::lock_guard<std::mutex> guard(parent_->lock());
+
+    std::string device_path;
+    std::unique_ptr<MappedDevice> mapped_device;
+    if (!impl_->IsImageMapped(name)) {
+        mapped_device = MappedDevice::Open(impl_.get(), 10s, name);
+        if (!mapped_device) {
+            PLOG(ERROR) << "Fail to map image: " << name;
+            *_aidl_return = IMAGE_ERROR;
+            return binder::Status::ok();
+        }
+        device_path = mapped_device->path();
+    } else {
+        if (!impl_->GetMappedImageDevice(name, &device_path)) {
+            PLOG(ERROR) << "GetMappedImageDevice() failed";
+            *_aidl_return = IMAGE_ERROR;
+            return binder::Status::ok();
+        }
+    }
+    android::base::unique_fd fd(open(device_path.c_str(), O_RDONLY | O_CLOEXEC));
+    if (!fd.ok()) {
+        PLOG(ERROR) << "Fail to open mapped device: " << device_path;
+        *_aidl_return = IMAGE_ERROR;
+        return binder::Status::ok();
+    }
+    bool ok = GetAvbPublicKeyFromFd(fd.get(), dst);
+    fd = {};
+    if (!ok) {
+        LOG(ERROR) << "Failed to extract AVB public key";
+        *_aidl_return = IMAGE_ERROR;
+        return binder::Status::ok();
+    }
+    *_aidl_return = IMAGE_OK;
+    return binder::Status::ok();
+}
+
 binder::Status ImageService::zeroFillNewImage(const std::string& name, int64_t bytes) {
     if (!CheckUid()) return UidSecurityError();
 
@@ -424,8 +661,39 @@
     if (bytes < 0) {
         return BinderError("Cannot use negative values");
     }
-    if (!impl_->ZeroFillNewImage(name, bytes)) {
-        return BinderError("Failed to fill image with zeros");
+    auto res = impl_->ZeroFillNewImage(name, bytes);
+    if (!res.is_ok()) {
+        return BinderError("Failed to fill image with zeros: " + res.string(), res.error_code());
+    }
+    return binder::Status::ok();
+}
+
+binder::Status ImageService::removeAllImages() {
+    if (!CheckUid()) return UidSecurityError();
+
+    std::lock_guard<std::mutex> guard(parent_->lock());
+    if (!impl_->RemoveAllImages()) {
+        return BinderError("Failed to remove all images");
+    }
+    return binder::Status::ok();
+}
+
+binder::Status ImageService::removeDisabledImages() {
+    if (!CheckUid()) return UidSecurityError();
+
+    std::lock_guard<std::mutex> guard(parent_->lock());
+    if (!impl_->RemoveDisabledImages()) {
+        return BinderError("Failed to remove disabled images");
+    }
+    return binder::Status::ok();
+}
+
+binder::Status ImageService::getMappedImageDevice(const std::string& name, std::string* device) {
+    if (!CheckUid()) return UidSecurityError();
+
+    std::lock_guard<std::mutex> guard(parent_->lock());
+    if (!impl_->GetMappedImageDevice(name, device)) {
+        *device = "";
     }
     return binder::Status::ok();
 }
@@ -441,14 +709,20 @@
 
     auto in_metadata_dir = kImageMetadataPrefix + prefix;
     auto in_data_dir = kImageDataPrefix + prefix;
+    auto install_dir_file = DsuInstallDirFile(GetDsuSlot(prefix));
 
+    std::string in_data_dir_tmp;
+    if (android::base::ReadFileToString(install_dir_file, &in_data_dir_tmp)) {
+        in_data_dir = in_data_dir_tmp;
+        LOG(INFO) << "load " << install_dir_file << ":" << in_data_dir;
+    }
     std::string metadata_dir, data_dir;
     if (!android::base::Realpath(in_metadata_dir, &metadata_dir)) {
-        PLOG(ERROR) << "realpath failed: " << metadata_dir;
+        PLOG(ERROR) << "realpath failed for metadata: " << in_metadata_dir;
         return BinderError("Invalid path");
     }
     if (!android::base::Realpath(in_data_dir, &data_dir)) {
-        PLOG(ERROR) << "realpath failed: " << data_dir;
+        PLOG(ERROR) << "realpath failed for data: " << in_data_dir;
         return BinderError("Invalid path");
     }
     if (!android::base::StartsWith(metadata_dir, kImageMetadataPrefix) ||
@@ -503,27 +777,27 @@
     return info.f_type == MSDOS_SUPER_MAGIC;
 }
 
-int GsiService::ValidateInstallParams(GsiInstallParams* params) {
+int GsiService::ValidateInstallParams(std::string& install_dir) {
     // If no install path was specified, use the default path. We also allow
     // specifying the top-level folder, and then we choose the correct location
     // underneath.
-    if (params->installDir.empty() || params->installDir == "/data/gsi") {
-        params->installDir = kDefaultDsuImageFolder;
+    if (install_dir.empty() || install_dir == "/data/gsi") {
+        install_dir = kDefaultDsuImageFolder;
     }
 
     // Normalize the path and add a trailing slash.
-    std::string origInstallDir = params->installDir;
-    if (!android::base::Realpath(origInstallDir, &params->installDir)) {
+    std::string origInstallDir = install_dir;
+    if (!android::base::Realpath(origInstallDir, &install_dir)) {
         PLOG(ERROR) << "realpath failed: " << origInstallDir;
         return INSTALL_ERROR_GENERIC;
     }
     // Ensure the path ends in / for consistency.
-    if (!android::base::EndsWith(params->installDir, "/")) {
-        params->installDir += "/";
+    if (!android::base::EndsWith(install_dir, "/")) {
+        install_dir += "/";
     }
 
     // Currently, we can only install to /data/gsi/ or external storage.
-    if (IsExternalStoragePath(params->installDir)) {
+    if (IsExternalStoragePath(install_dir)) {
         Fstab fstab;
         if (!ReadDefaultFstab(&fstab)) {
             LOG(ERROR) << "cannot read default fstab";
@@ -538,28 +812,38 @@
             LOG(ERROR) << "cannot install GSIs to external media if verity uses check_at_most_once";
             return INSTALL_ERROR_GENERIC;
         }
-    } else if (params->installDir != kDefaultDsuImageFolder) {
-        LOG(ERROR) << "cannot install GSI to " << params->installDir;
-        return INSTALL_ERROR_GENERIC;
-    }
-
-    if (params->gsiSize % LP_SECTOR_SIZE) {
-        LOG(ERROR) << "GSI size " << params->gsiSize << " is not a multiple of " << LP_SECTOR_SIZE;
-        return INSTALL_ERROR_GENERIC;
-    }
-    if (params->userdataSize % LP_SECTOR_SIZE) {
-        LOG(ERROR) << "userdata size " << params->userdataSize << " is not a multiple of "
-                   << LP_SECTOR_SIZE;
+    } else if (install_dir != kDefaultDsuImageFolder) {
+        LOG(ERROR) << "cannot install DSU to " << install_dir;
         return INSTALL_ERROR_GENERIC;
     }
     return INSTALL_OK;
 }
 
+std::string GsiService::GetActiveDsuSlot() {
+    if (!install_dir_.empty()) {
+        return GetDsuSlot(install_dir_);
+    } else {
+        std::string active_dsu;
+        return GetActiveDsu(&active_dsu) ? active_dsu : "";
+    }
+}
+
+std::string GsiService::GetActiveInstalledImageDir() {
+    // Just in case an install was left hanging.
+    if (installer_) {
+        return installer_->install_dir();
+    } else {
+        return GetInstalledImageDir();
+    }
+}
+
 std::string GsiService::GetInstalledImageDir() {
     // If there's no install left, just return /data/gsi since that's where
     // installs go by default.
+    std::string active_dsu;
     std::string dir;
-    if (android::base::ReadFileToString(kDsuInstallDirFile, &dir)) {
+    if (GetActiveDsu(&active_dsu) &&
+        android::base::ReadFileToString(DsuInstallDirFile(active_dsu), &dir)) {
         return dir;
     }
     return kDefaultDsuImageFolder;
@@ -570,7 +854,6 @@
         LOG(ERROR) << "no gsi installed - cannot re-enable";
         return INSTALL_ERROR_GENERIC;
     }
-
     std::string boot_key;
     if (!GetInstallStatus(&boot_key)) {
         PLOG(ERROR) << "read " << kDsuInstallStatusFile;
@@ -580,28 +863,43 @@
         LOG(ERROR) << "GSI is not currently disabled";
         return INSTALL_ERROR_GENERIC;
     }
-
-    installer_ = std::make_unique<GsiInstaller>(this, GetInstalledImageDir());
-    return installer_->ReenableGsi(one_shot);
+    if (IsGsiRunning()) {
+        if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+            return IGsiService::INSTALL_ERROR_GENERIC;
+        }
+        return IGsiService::INSTALL_OK;
+    }
+    if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+    return IGsiService::INSTALL_OK;
 }
 
-bool GsiService::RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata) {
+bool GsiService::RemoveGsiFiles(const std::string& install_dir) {
     bool ok = true;
-    if (auto manager = ImageManager::Open(kDsuMetadataDir, install_dir)) {
-        ok &= manager->DeleteBackingImage("system_gsi");
-        if (wipeUserdata) {
-            ok &= manager->DeleteBackingImage("userdata_gsi");
+    auto active_dsu = GetDsuSlot(install_dir);
+    if (auto manager = ImageManager::Open(MetadataDir(active_dsu), install_dir)) {
+        std::vector<std::string> images = manager->GetAllBackingImages();
+        for (auto&& image : images) {
+            if (!android::base::EndsWith(image, kDsuPostfix)) {
+                continue;
+            }
+            if (manager->IsImageMapped(image)) {
+                ok &= manager->UnmapImageDevice(image);
+            }
+            ok &= manager->DeleteBackingImage(image);
         }
     }
-
+    auto dsu_slot = GetDsuSlot(install_dir);
     std::vector<std::string> files{
             kDsuInstallStatusFile,
             kDsuOneShotBootFile,
-            kDsuInstallDirFile,
+            DsuInstallDirFile(dsu_slot),
+            GetCompleteIndication(dsu_slot),
     };
     for (const auto& file : files) {
         std::string message;
-        if (!android::base::RemoveFileIfExists(file, &message)) {
+        if (!RemoveFileIfExists(file, &message)) {
             LOG(ERROR) << message;
             ok = false;
         }
@@ -625,11 +923,63 @@
     return true;
 }
 
+std::string GsiService::GetCompleteIndication(const std::string& dsu_slot) {
+    return DSU_METADATA_PREFIX + dsu_slot + "/complete";
+}
+
+bool GsiService::IsInstallationComplete(const std::string& dsu_slot) {
+    if (access(kDsuInstallStatusFile, F_OK) != 0) {
+        return false;
+    }
+    std::string file = GetCompleteIndication(dsu_slot);
+    std::string content;
+    if (!ReadFileToString(file, &content)) {
+        return false;
+    }
+    return content == "OK";
+}
+
+std::vector<std::string> GsiService::GetInstalledDsuSlots() {
+    std::vector<std::string> dsu_slots;
+    auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(DSU_METADATA_PREFIX), closedir);
+    if (d != nullptr) {
+        struct dirent* de;
+        while ((de = readdir(d.get())) != nullptr) {
+            if (de->d_name[0] == '.') {
+                continue;
+            }
+            auto dsu_slot = std::string(de->d_name);
+            if (access(DsuInstallDirFile(dsu_slot).c_str(), F_OK) != 0) {
+                continue;
+            }
+            dsu_slots.push_back(dsu_slot);
+        }
+    }
+    return dsu_slots;
+}
+
+void GsiService::CleanCorruptedInstallation() {
+    for (auto&& slot : GetInstalledDsuSlots()) {
+        bool is_complete = IsInstallationComplete(slot);
+        if (!is_complete) {
+            LOG(INFO) << "CleanCorruptedInstallation for slot: " << slot;
+            std::string install_dir;
+            if (!android::base::ReadFileToString(DsuInstallDirFile(slot), &install_dir) ||
+                !RemoveGsiFiles(install_dir)) {
+                LOG(ERROR) << "Failed to CleanCorruptedInstallation on " << slot;
+            }
+        }
+    }
+}
+
 void GsiService::RunStartupTasks() {
-    if (!IsGsiInstalled()) {
+    CleanCorruptedInstallation();
+
+    std::string active_dsu;
+    if (!GetActiveDsu(&active_dsu)) {
+        PLOG(INFO) << "no DSU";
         return;
     }
-
     std::string boot_key;
     if (!GetInstallStatus(&boot_key)) {
         PLOG(ERROR) << "read " << kDsuInstallStatusFile;
@@ -639,7 +989,7 @@
     if (!IsGsiRunning()) {
         // Check if a wipe was requested from fastboot or adb-in-gsi.
         if (boot_key == kInstallStatusWipe) {
-            RemoveGsiFiles(GetInstalledImageDir(), true /* wipeUserdata */);
+            RemoveGsiFiles(GetInstalledImageDir());
         }
     } else {
         // NB: When single-boot is enabled, init will write "disabled" into the
@@ -655,5 +1005,47 @@
     }
 }
 
+static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst) {
+    // Read the AVB footer from EOF.
+    int64_t total_size = get_block_device_size(fd);
+    int64_t footer_offset = total_size - AVB_FOOTER_SIZE;
+    std::array<uint8_t, AVB_FOOTER_SIZE> footer_bytes;
+    if (!ReadFullyAtOffset(fd, footer_bytes.data(), AVB_FOOTER_SIZE, footer_offset)) {
+        PLOG(ERROR) << "cannot read AVB footer";
+        return false;
+    }
+    // Validate the AVB footer data and byte swap to native byte order.
+    AvbFooter footer;
+    if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_bytes.data(), &footer)) {
+        LOG(ERROR) << "invalid AVB footer";
+        return false;
+    }
+    // Read the VBMeta image.
+    std::vector<uint8_t> vbmeta_bytes(footer.vbmeta_size);
+    if (!ReadFullyAtOffset(fd, vbmeta_bytes.data(), vbmeta_bytes.size(), footer.vbmeta_offset)) {
+        PLOG(ERROR) << "cannot read VBMeta image";
+        return false;
+    }
+    // Validate the VBMeta image and retrieve AVB public key.
+    // After a successful call to avb_vbmeta_image_verify(), public_key_data
+    // will point to the serialized AVB public key, in the same format generated
+    // by the `avbtool extract_public_key` command.
+    const uint8_t* public_key_data;
+    size_t public_key_size;
+    AvbVBMetaVerifyResult result = avb_vbmeta_image_verify(vbmeta_bytes.data(), vbmeta_bytes.size(),
+                                                           &public_key_data, &public_key_size);
+    if (result != AVB_VBMETA_VERIFY_RESULT_OK) {
+        LOG(ERROR) << "invalid VBMeta image: " << avb_vbmeta_verify_result_to_string(result);
+        return false;
+    }
+    if (public_key_data != nullptr) {
+        dst->bytes.resize(public_key_size);
+        memcpy(dst->bytes.data(), public_key_data, public_key_size);
+        dst->sha1.resize(SHA_DIGEST_LENGTH);
+        SHA1(public_key_data, public_key_size, dst->sha1.data());
+    }
+    return true;
+}
+
 }  // namespace gsi
 }  // namespace android
diff --git a/gsi_service.h b/gsi_service.h
index 1df4aeb..a853e2b 100644
--- a/gsi_service.h
+++ b/gsi_service.h
@@ -30,7 +30,7 @@
 #include <liblp/builder.h>
 #include "libgsi/libgsi.h"
 
-#include "gsi_installer.h"
+#include "partition_installer.h"
 
 namespace android {
 namespace gsi {
@@ -57,7 +57,10 @@
 
     static android::sp<IGsiService> Get(Gsid* parent);
 
-    binder::Status beginGsiInstall(const GsiInstallParams& params, int* _aidl_return) override;
+    binder::Status openInstall(const std::string& install_dir, int* _aidl_return) override;
+    binder::Status closeInstall(int32_t* _aidl_return) override;
+    binder::Status createPartition(const ::std::string& name, int64_t size, bool readOnly,
+                                   int32_t* _aidl_return) override;
     binder::Status commitGsiChunkFromStream(const ::android::os::ParcelFileDescriptor& stream,
                                             int64_t bytes, bool* _aidl_return) override;
     binder::Status getInstallProgress(::android::gsi::GsiProgress* _aidl_return) override;
@@ -65,7 +68,7 @@
                                 bool* _aidl_return) override;
     binder::Status commitGsiChunkFromAshmem(int64_t bytes, bool* _aidl_return) override;
     binder::Status cancelGsiInstall(bool* _aidl_return) override;
-    binder::Status enableGsi(bool oneShot, int* _aidl_return) override;
+    binder::Status enableGsi(bool oneShot, const std::string& dsuSlot, int* _aidl_return) override;
     binder::Status isGsiEnabled(bool* _aidl_return) override;
     binder::Status removeGsi(bool* _aidl_return) override;
     binder::Status disableGsi(bool* _aidl_return) override;
@@ -73,9 +76,13 @@
     binder::Status isGsiRunning(bool* _aidl_return) override;
     binder::Status isGsiInstallInProgress(bool* _aidl_return) override;
     binder::Status getInstalledGsiImageDir(std::string* _aidl_return) override;
-    binder::Status wipeGsiUserdata(int* _aidl_return) override;
+    binder::Status getActiveDsuSlot(std::string* _aidl_return) override;
+    binder::Status getInstalledDsuSlots(std::vector<std::string>* _aidl_return) override;
+    binder::Status zeroPartition(const std::string& name, int* _aidl_return) override;
     binder::Status openImageService(const std::string& prefix,
                                     android::sp<IImageService>* _aidl_return) override;
+    binder::Status dumpDeviceMapperDevices(std::string* _aidl_return) override;
+    binder::Status getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_return) override;
 
     // This is in GsiService, rather than GsiInstaller, since we need to access
     // it outside of the main lock which protects the unique_ptr.
@@ -83,26 +90,37 @@
     void UpdateProgress(int status, int64_t bytes_processed);
 
     // Helper methods for GsiInstaller.
-    static bool RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata);
+    static bool RemoveGsiFiles(const std::string& install_dir);
     bool should_abort() const { return should_abort_; }
     Gsid* parent() const { return parent_.get(); }
 
     static void RunStartupTasks();
     static std::string GetInstalledImageDir();
+    std::string GetActiveDsuSlot();
+    std::string GetActiveInstalledImageDir();
+
+    static std::vector<std::string> GetInstalledDsuSlots();
 
   private:
     GsiService(Gsid* parent);
-    int ValidateInstallParams(GsiInstallParams* params);
+    static int ValidateInstallParams(std::string& install_dir);
     bool DisableGsiInstall();
     int ReenableGsi(bool one_shot);
+    static void CleanCorruptedInstallation();
+    static int SaveInstallation(const std::string&);
+    static bool IsInstallationComplete(const std::string&);
+    static std::string GetCompleteIndication(const std::string&);
 
     enum class AccessLevel { System, SystemOrShell };
     binder::Status CheckUid(AccessLevel level = AccessLevel::System);
+    bool CreateInstallStatusFile();
+    bool SetBootMode(bool one_shot);
 
     static android::wp<GsiService> sInstance;
 
+    std::string install_dir_ = {};
     android::sp<Gsid> parent_;
-    std::unique_ptr<GsiInstaller> installer_;
+    std::unique_ptr<PartitionInstaller> installer_;
 
     // These are initialized or set in StartInstall().
     std::atomic<bool> should_abort_ = false;
diff --git a/gsi_tool.cpp b/gsi_tool.cpp
index e3718c7..a38bf20 100644
--- a/gsi_tool.cpp
+++ b/gsi_tool.cpp
@@ -17,6 +17,7 @@
 #include <getopt.h>
 #include <stdio.h>
 #include <sysexits.h>
+#include <unistd.h>
 
 #include <chrono>
 #include <condition_variable>
@@ -30,6 +31,8 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/gsi/IGsiService.h>
 #include <android/gsi/IGsid.h>
@@ -42,6 +45,8 @@
 using namespace std::chrono_literals;
 
 using android::sp;
+using android::base::Split;
+using android::base::StringPrintf;
 using CommandCallback = std::function<int(sp<IGsiService>, int, char**)>;
 
 static int Disable(sp<IGsiService> gsid, int argc, char** argv);
@@ -187,21 +192,23 @@
 };
 
 static int Install(sp<IGsiService> gsid, int argc, char** argv) {
+    constexpr const char* kDefaultPartition = "system";
     struct option options[] = {
             {"install-dir", required_argument, nullptr, 'i'},
             {"gsi-size", required_argument, nullptr, 's'},
             {"no-reboot", no_argument, nullptr, 'n'},
             {"userdata-size", required_argument, nullptr, 'u'},
+            {"partition-name", required_argument, nullptr, 'p'},
             {"wipe", no_argument, nullptr, 'w'},
             {nullptr, 0, nullptr, 0},
     };
 
-    GsiInstallParams params;
-    params.gsiSize = 0;
-    params.userdataSize = 0;
-    params.wipeUserdata = false;
+    int64_t gsiSize = 0;
+    int64_t userdataSize = 0;
+    bool wipeUserdata = false;
     bool reboot = true;
-
+    std::string installDir = "";
+    std::string partition = kDefaultPartition;
     if (getuid() != 0) {
         std::cerr << "must be root to install a GSI" << std::endl;
         return EX_NOPERM;
@@ -210,24 +217,26 @@
     int rv, index;
     while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
         switch (rv) {
+            case 'p':
+                partition = optarg;
+                break;
             case 's':
-                if (!android::base::ParseInt(optarg, &params.gsiSize) || params.gsiSize <= 0) {
+                if (!android::base::ParseInt(optarg, &gsiSize) || gsiSize <= 0) {
                     std::cerr << "Could not parse image size: " << optarg << std::endl;
                     return EX_USAGE;
                 }
                 break;
             case 'u':
-                if (!android::base::ParseInt(optarg, &params.userdataSize) ||
-                    params.userdataSize < 0) {
+                if (!android::base::ParseInt(optarg, &userdataSize) || userdataSize < 0) {
                     std::cerr << "Could not parse image size: " << optarg << std::endl;
                     return EX_USAGE;
                 }
                 break;
             case 'i':
-                params.installDir = optarg;
+                installDir = optarg;
                 break;
             case 'w':
-                params.wipeUserdata = true;
+                wipeUserdata = true;
                 break;
             case 'n':
                 reboot = false;
@@ -235,7 +244,7 @@
         }
     }
 
-    if (params.gsiSize <= 0) {
+    if (gsiSize <= 0) {
         std::cerr << "Must specify --gsi-size." << std::endl;
         return EX_USAGE;
     }
@@ -253,31 +262,52 @@
         std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
         return EX_SOFTWARE;
     }
-
     // Note: the progress bar needs to be re-started in between each call.
     ProgressBar progress(gsid);
     progress.Display();
-
     int error;
-    auto status = gsid->beginGsiInstall(params, &error);
+    auto status = gsid->openInstall(installDir, &error);
+    if (!status.isOk() || error != IGsiService::INSTALL_OK) {
+        std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error) << "\n";
+        return EX_SOFTWARE;
+    }
+    if (partition == kDefaultPartition) {
+        auto status = gsid->createPartition("userdata", userdataSize, false, &error);
+        if (!status.isOk() || error != IGsiService::INSTALL_OK) {
+            std::cerr << "Could not start live image install: " << ErrorMessage(status, error)
+                      << "\n";
+            return EX_SOFTWARE;
+        }
+    }
+
+    status = gsid->createPartition(partition, gsiSize, true, &error);
     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
         std::cerr << "Could not start live image install: " << ErrorMessage(status, error) << "\n";
         return EX_SOFTWARE;
     }
-
     android::os::ParcelFileDescriptor stream(std::move(input));
 
     bool ok = false;
     progress.Display();
-    status = gsid->commitGsiChunkFromStream(stream, params.gsiSize, &ok);
+    status = gsid->commitGsiChunkFromStream(stream, gsiSize, &ok);
     if (!ok) {
         std::cerr << "Could not commit live image data: " << ErrorMessage(status) << "\n";
         return EX_SOFTWARE;
     }
 
+    status = gsid->closeInstall(&error);
+    if (!status.isOk() || error != IGsiService::INSTALL_OK) {
+        std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error) << "\n";
+        return EX_SOFTWARE;
+    }
     progress.Finish();
-
-    status = gsid->enableGsi(true, &error);
+    std::string dsuSlot;
+    status = gsid->getActiveDsuSlot(&dsuSlot);
+    if (!status.isOk()) {
+        std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
+        return EX_SOFTWARE;
+    }
+    status = gsid->enableGsi(true, dsuSlot, &error);
     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
         std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n";
         return EX_SOFTWARE;
@@ -344,7 +374,7 @@
     }
 
     int error;
-    status = gsid->wipeGsiUserdata(&error);
+    status = gsid->zeroPartition("userdata", &error);
     if (!status.isOk() || error) {
         std::cerr << "Could not wipe GSI userdata: " << ErrorMessage(status, error) << "\n";
         return EX_SOFTWARE;
@@ -386,21 +416,43 @@
     if (getuid() != 0) {
         return 0;
     }
-    sp<IImageService> image_service = nullptr;
-    status = gsid->openImageService("dsu", &image_service);
-    if (!status.isOk()) {
-        std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
-        return EX_SOFTWARE;
-    }
-    std::vector<std::string> images;
-    status = image_service->getAllBackingImages(&images);
-    if (!status.isOk()) {
-        std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
-        return EX_SOFTWARE;
-    }
 
-    for (auto&& image : images) {
-        std::cout << "installed: " << image << std::endl;
+    std::vector<std::string> dsu_slots;
+    status = gsid->getInstalledDsuSlots(&dsu_slots);
+    if (!status.isOk()) {
+        std::cerr << status.exceptionMessage().string() << std::endl;
+        return EX_SOFTWARE;
+    }
+    int n = 0;
+    for (auto&& dsu_slot : dsu_slots) {
+        std::cout << "[" << n++ << "] " << dsu_slot << std::endl;
+        sp<IImageService> image_service = nullptr;
+        status = gsid->openImageService("dsu/" + dsu_slot + "/", &image_service);
+        if (!status.isOk()) {
+            std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
+            return EX_SOFTWARE;
+        }
+        std::vector<std::string> images;
+        status = image_service->getAllBackingImages(&images);
+        if (!status.isOk()) {
+            std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
+            return EX_SOFTWARE;
+        }
+        for (auto&& image : images) {
+            std::cout << "installed: " << image << std::endl;
+            AvbPublicKey public_key;
+            int err = 0;
+            status = image_service->getAvbPublicKey(image, &public_key, &err);
+            std::cout << "AVB public key (sha1): ";
+            if (!public_key.bytes.empty()) {
+                for (auto b : public_key.sha1) {
+                    std::cout << StringPrintf("%02x", b & 255);
+                }
+                std::cout << std::endl;
+            } else {
+                std::cout << "[NONE]" << std::endl;
+            }
+        }
     }
     return 0;
 }
@@ -421,9 +473,10 @@
 
 static int Enable(sp<IGsiService> gsid, int argc, char** argv) {
     bool one_shot = false;
-
+    std::string dsuSlot = {};
     struct option options[] = {
             {"single-boot", no_argument, nullptr, 's'},
+            {"dsuslot", required_argument, nullptr, 'd'},
             {nullptr, 0, nullptr, 0},
     };
     int rv, index;
@@ -432,6 +485,9 @@
             case 's':
                 one_shot = true;
                 break;
+            case 'd':
+                dsuSlot = optarg;
+                break;
             default:
                 std::cerr << "Unrecognized argument to enable\n";
                 return EX_USAGE;
@@ -451,9 +507,15 @@
         std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
         return EX_SOFTWARE;
     }
-
+    if (dsuSlot.empty()) {
+        auto status = gsid->getActiveDsuSlot(&dsuSlot);
+        if (!status.isOk()) {
+            std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
+            return EX_SOFTWARE;
+        }
+    }
     int error;
-    auto status = gsid->enableGsi(one_shot, &error);
+    auto status = gsid->enableGsi(one_shot, dsuSlot, &error);
     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
         std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n";
         return EX_SOFTWARE;
@@ -493,7 +555,8 @@
             "  %s <disable|install|wipe|status> [options]\n"
             "\n"
             "  disable      Disable the currently installed GSI.\n"
-            "  enable [-s, --single-boot]\n"
+            "  enable       [-s, --single-boot]\n"
+            "               [-d, --dsuslot slotname]\n"
             "               Enable a previously disabled GSI.\n"
             "  install      Install a new GSI. Specify the image size with\n"
             "               --gsi-size and the desired userdata size with\n"
diff --git a/gsid.rc b/gsid.rc
index cd69917..1640a73 100644
--- a/gsid.rc
+++ b/gsid.rc
@@ -8,11 +8,13 @@
     mkdir /metadata/gsi 0771 root system
     mkdir /metadata/gsi/dsu 0771 root system
     mkdir /metadata/gsi/ota 0771 root system
+    mkdir /metadata/gsi/remount 0771 root system
 
 on post-fs-data
-    mkdir /data/gsi 0700 root root
+    mkdir /data/gsi 0700 root root encryption=None
     mkdir /data/gsi/dsu 0700 root root
     mkdir /data/gsi/ota 0700 root root
+    mkdir /data/gsi/remount 0700 root root
 
 on boot
     exec_background - root root -- /system/bin/gsid run-startup-tasks
diff --git a/include/libgsi/libgsi.h b/include/libgsi/libgsi.h
index 9b24f47..987797a 100644
--- a/include/libgsi/libgsi.h
+++ b/include/libgsi/libgsi.h
@@ -23,11 +23,25 @@
 
 static constexpr char kGsiServiceName[] = "gsiservice";
 
-static constexpr char kGsiBootedIndicatorFile[] = "/metadata/gsi/dsu/booted";
+#define DSU_METADATA_PREFIX "/metadata/gsi/dsu/"
 
-static constexpr char kGsiLpNamesFile[] = "/metadata/gsi/dsu/lp_names";
+static constexpr char kGsiBootedIndicatorFile[] = DSU_METADATA_PREFIX "booted";
 
-static constexpr char kDsuLpMetadataFile[] = "/metadata/gsi/dsu/lp_metadata";
+static constexpr char kGsiLpNamesFile[] = DSU_METADATA_PREFIX "lp_names";
+
+static constexpr char kDsuActiveFile[] = DSU_METADATA_PREFIX "active";
+
+static inline std::string DsuLpMetadataFile(const std::string& dsu_slot) {
+    return DSU_METADATA_PREFIX + dsu_slot + "/lp_metadata";
+}
+
+static inline std::string DsuInstallDirFile(const std::string& dsu_slot) {
+    return DSU_METADATA_PREFIX + dsu_slot + "/install_dir";
+}
+
+// install_dir "/data/gsi/dsu/dsu" has a slot name "dsu"
+// install_dir "/data/gsi/dsu/dsu2" has a slot name "dsu2"
+std::string GetDsuSlot(const std::string& install_dir);
 
 static constexpr char kGsiBootedProp[] = "ro.gsid.image_running";
 
@@ -35,6 +49,10 @@
 
 static constexpr int kMaxBootAttempts = 1;
 
+// Get the currently active dsu slot
+// Return true on success
+bool GetActiveDsu(std::string* active_dsu);
+
 // Returns true if the currently running system image is a live GSI.
 bool IsGsiRunning();
 
diff --git a/libfiemap/.clang-format b/libfiemap/.clang-format
deleted file mode 120000
index 8b770a1..0000000
--- a/libfiemap/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../../.clang-format-4
\ No newline at end of file
diff --git a/libfiemap/Android.bp b/libfiemap/Android.bp
deleted file mode 100644
index d426e27..0000000
--- a/libfiemap/Android.bp
+++ /dev/null
@@ -1,131 +0,0 @@
-//
-// Copyright (C) 2018 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.
-//
-
-cc_library_headers {
-    name: "libfiemap_headers",
-    recovery_available: true,
-    export_include_dirs: ["include"],
-}
-
-cc_defaults {
-    name: "libfiemap_defaults",
-    defaults: ["fs_mgr_defaults"],
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
-    ],
-
-    srcs: [
-        "fiemap_writer.cpp",
-        "image_manager.cpp",
-        "metadata.cpp",
-        "split_fiemap_writer.cpp",
-        "utility.cpp",
-    ],
-
-    static_libs: [
-        "libdm",
-        "libext2_uuid",
-        "libext4_utils",
-        "liblp",
-        "libfs_mgr",
-    ],
-
-    shared_libs: [
-        "libbase",
-    ],
-
-    header_libs: [
-        "libfiemap_headers",
-        "liblog_headers",
-    ],
-
-    export_shared_lib_headers: [
-        "libbase",
-    ],
-
-    export_header_lib_headers: [
-        "libfiemap_headers",
-    ],
-}
-
-// Open up a binder IImageManager interface.
-cc_library_static {
-    name: "libfiemap_binder",
-    defaults: ["libfiemap_defaults"],
-    srcs: [
-        "binder.cpp",
-    ],
-    whole_static_libs: [
-        "gsi_aidl_interface-cpp",
-        "libgsi",
-    ],
-    shared_libs: [
-        "libbinder",
-    ],
-}
-
-// Open up a passthrough IImageManager interface. Use libfiemap_binder whenever
-// possible. This should only be used when binder is not available.
-cc_library_static {
-    name: "libfiemap_passthrough",
-    defaults: ["libfiemap_defaults"],
-    recovery_available: true,
-    srcs: [
-        "passthrough.cpp",
-    ],
-}
-
-cc_test {
-    name: "fiemap_writer_test",
-    defaults: ["libfiemap_defaults"],
-    static_libs: [
-        "libbase",
-        "libdm",
-        "liblog",
-    ],
-
-    data: [
-        "testdata/unaligned_file",
-        "testdata/file_4k",
-        "testdata/file_32k",
-    ],
-
-    srcs: [
-        "fiemap_writer_test.cpp",
-    ],
-}
-
-cc_test {
-    name: "fiemap_image_test",
-    defaults: ["libfiemap_defaults"],
-    static_libs: [
-        "libdm",
-        "libext4_utils",
-        "libfs_mgr",
-        "liblp",
-    ],
-    shared_libs: [
-        "libcrypto",
-        "libcrypto_utils",
-        "libcutils",
-        "liblog",
-    ],
-    srcs: [
-        "image_test.cpp",
-    ],
-}
diff --git a/libfiemap/Android.mk b/libfiemap/Android.mk
deleted file mode 100644
index 3c07b8e..0000000
--- a/libfiemap/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsFiemapWriterTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/libfiemap/AndroidTest.xml b/libfiemap/AndroidTest.xml
deleted file mode 100644
index 44c80fc..0000000
--- a/libfiemap/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for VTS VtsFiemapWriterTest">
-    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="abort-on-push-failure" value="false"/>
-        <option name="push-group" value="HostDrivenTest.push"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-      <option name="test-module-name" value="VtsFiemapWriterTest"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
-        <option name="binary-test-type" value="gtest"/>
-        <option name="precondition-first-api-level" value="29" />
-        <option name="test-timeout" value="1m"/>
-    </test>
-</configuration>
diff --git a/libfiemap/README.md b/libfiemap/README.md
deleted file mode 100644
index 62d610a..0000000
--- a/libfiemap/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-libfiemap
-=============
-
-`libfiemap` is a library for creating block-devices that are backed by
-storage in read-write partitions. It exists primary for gsid. Generally, the
-library works by using `libfiemap_writer` to allocate large files within
-filesystem, and then tracks their extents.
-
-There are three main uses for `libfiemap`:
- - Creating images that will act as block devices. For example, gsid needs to
-   create a `system_gsi` image to store Dynamic System Updates.
- - Mapping the image as a block device while /data is mounted. This is fairly
-   tricky and is described in more detail below.
- - Mapping the image as a block device during first-stage init. This is simple
-   because it uses the same logic from dynamic partitions.
-
-Image creation is done through `SplitFiemap`. Depending on the file system,
-a large image may have to be split into multiple files. On Ext4 the limit is
-16GiB and on FAT32 it's 4GiB. Images are saved into `/data/gsi/<name>/`
-where `<name>` is chosen by the process requesting the image.
-
-At the same time, a file called `/metadata/gsi/<name>/lp_metadata` is created.
-This is a super partition header that allows first-stage init to create dynamic
-partitions from the image files. It also tracks the canonical size of the image,
-since the file size may be larger due to alignment.
-
-Mapping
--------
-
-It is easy to make block devices out of blocks on `/data` when it is not
-mounted, so first-stage init has no issues mapping dynamic partitions from
-images. After `/data` is mounted however, there are two problems:
- - `/data` is encrypted.
- - `/dev/block/by-name/data` may be marked as in-use.
-
-We break the problem down into three scenarios.
-
-### FDE and Metadata Encrypted Devices
-
-When FDE or metadata encryption is used, `/data` is not mounted from
-`/dev/block/by-name/data`. Instead, it is mounted from an intermediate
-`dm-crypt` or `dm-default-key` device. This means the underlying device is
-not marked in use, and we can create new dm-linear devices on top of it.
-
-On these devices, a block device for an image will consist of a single
-device-mapper device with a `dm-linear` table entry for each extent in the
-backing file.
-
-### Unencrypted and FBE-encrypted Devices
-
-When a device is unencrypted, or is encrypted with FBE but not metadata
-encryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled.
-Since `/data/gsi` has encryption disabled, this means the raw blocks will be
-unencrypted as well.
-
-### Split Images
-
-If an image was too large to store a single file on the underlying filesystem,
-on an FBE/unencrypted device we will have multiple loop devices. In this case,
-we create a device-mapper device as well. For each loop device it will have one
-`dm-linear` table entry spanning the length of the device.
-
-State Tracking
---------------
-
-It's important that we know whether or not an image is currently in-use by a
-block device. It could be catastrophic to write to a dm-linear device if the
-underlying blocks are no longer owned by the original file. Thus, when mapping
-an image, we create a property called `gsid.mapped_image.<name>` and set it to
-the path of the block device.
-
-Additionally, we create a `/metadata/gsi/<subdir>/<name>.status` file. Each
-line in this file denotes a dependency on either a device-mapper node or a loop
-device. When deleting a block device, this file is used to release all
-resources.
diff --git a/libfiemap/binder.cpp b/libfiemap/binder.cpp
deleted file mode 100644
index dcb887a..0000000
--- a/libfiemap/binder.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-//
-// 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.
-//
-
-#if !defined(__ANDROID_RECOVERY__)
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android/gsi/IGsiService.h>
-#include <android/gsi/IGsid.h>
-#include <binder/IServiceManager.h>
-#include <libfiemap/image_manager.h>
-#include <libgsi/libgsi.h>
-
-namespace android {
-namespace fiemap {
-
-using namespace android::gsi;
-using namespace std::chrono_literals;
-
-class ImageManagerBinder final : public IImageManager {
-  public:
-    ImageManagerBinder(android::sp<IGsiService>&& service, android::sp<IImageService>&& manager);
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
-    bool DeleteBackingImage(const std::string& name) override;
-    bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
-                        std::string* path) override;
-    bool UnmapImageDevice(const std::string& name) override;
-    bool BackingImageExists(const std::string& name) override;
-    bool IsImageMapped(const std::string& name) override;
-    bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
-                                  std::string* dev) override;
-    bool ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
-
-    std::vector<std::string> GetAllBackingImages() override;
-
-  private:
-    android::sp<IGsiService> service_;
-    android::sp<IImageService> manager_;
-};
-
-ImageManagerBinder::ImageManagerBinder(android::sp<IGsiService>&& service,
-                                       android::sp<IImageService>&& manager)
-    : service_(std::move(service)), manager_(std::move(manager)) {}
-
-bool ImageManagerBinder::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
-    auto status = manager_->createBackingImage(name, size, flags);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return true;
-}
-
-bool ImageManagerBinder::DeleteBackingImage(const std::string& name) {
-    auto status = manager_->deleteBackingImage(name);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return true;
-}
-
-bool ImageManagerBinder::MapImageDevice(const std::string& name,
-                                        const std::chrono::milliseconds& timeout_ms,
-                                        std::string* path) {
-    int32_t timeout_ms_count =
-            static_cast<int32_t>(std::clamp<typename std::chrono::milliseconds::rep>(
-                    timeout_ms.count(), INT32_MIN, INT32_MAX));
-    MappedImage map;
-    auto status = manager_->mapImageDevice(name, timeout_ms_count, &map);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    *path = map.path;
-    return true;
-}
-
-bool ImageManagerBinder::UnmapImageDevice(const std::string& name) {
-    auto status = manager_->unmapImageDevice(name);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return true;
-}
-
-bool ImageManagerBinder::BackingImageExists(const std::string& name) {
-    bool retval;
-    auto status = manager_->backingImageExists(name, &retval);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return retval;
-}
-
-bool ImageManagerBinder::IsImageMapped(const std::string& name) {
-    bool retval;
-    auto status = manager_->isImageMapped(name, &retval);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return retval;
-}
-
-bool ImageManagerBinder::MapImageWithDeviceMapper(const IPartitionOpener& opener,
-                                                  const std::string& name, std::string* dev) {
-    (void)opener;
-    (void)name;
-    (void)dev;
-    LOG(ERROR) << "MapImageWithDeviceMapper is not available over binder.";
-    return false;
-}
-
-std::vector<std::string> ImageManagerBinder::GetAllBackingImages() {
-    std::vector<std::string> retval;
-    auto status = manager_->getAllBackingImages(&retval);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-    }
-    return retval;
-}
-
-bool ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
-    auto status = manager_->zeroFillNewImage(name, bytes);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return true;
-}
-
-static android::sp<IGsid> AcquireIGsid(const std::chrono::milliseconds& timeout_ms) {
-    if (android::base::GetProperty("init.svc.gsid", "") != "running") {
-        if (!android::base::SetProperty("ctl.start", "gsid") ||
-            !android::base::WaitForProperty("init.svc.gsid", "running", timeout_ms)) {
-            LOG(ERROR) << "Could not start the gsid service";
-            return nullptr;
-        }
-        // Sleep for 250ms to give the service time to register.
-        usleep(250 * 1000);
-    }
-    auto sm = android::defaultServiceManager();
-    auto name = android::String16(kGsiServiceName);
-    auto service = sm->checkService(name);
-    return android::interface_cast<IGsid>(service);
-}
-
-static android::sp<IGsid> GetGsiService(const std::chrono::milliseconds& timeout_ms) {
-    auto start_time = std::chrono::steady_clock::now();
-
-    std::chrono::milliseconds elapsed = std::chrono::milliseconds::zero();
-    do {
-        if (auto gsid = AcquireIGsid(timeout_ms - elapsed); gsid != nullptr) {
-            return gsid;
-        }
-        auto now = std::chrono::steady_clock::now();
-        elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
-    } while (elapsed <= timeout_ms);
-
-    LOG(ERROR) << "Timed out trying to acquire IGsid interface";
-    return nullptr;
-}
-
-std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir,
-                                                   const std::chrono::milliseconds& timeout_ms) {
-    auto gsid = GetGsiService(timeout_ms);
-    if (!gsid) {
-        return nullptr;
-    }
-
-    android::sp<IGsiService> service;
-    auto status = gsid->getClient(&service);
-    if (!status.isOk() || !service) {
-        LOG(ERROR) << "Could not acquire IGsiService";
-        return nullptr;
-    }
-
-    android::sp<IImageService> manager;
-    status = service->openImageService(dir, &manager);
-    if (!status.isOk() || !manager) {
-        LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().string();
-        return nullptr;
-    }
-    return std::make_unique<ImageManagerBinder>(std::move(service), std::move(manager));
-}
-
-}  // namespace fiemap
-}  // namespace android
-
-#endif  // __ANDROID_RECOVERY__
diff --git a/libfiemap/fiemap_writer.cpp b/libfiemap/fiemap_writer.cpp
deleted file mode 100644
index 46fcb0e..0000000
--- a/libfiemap/fiemap_writer.cpp
+++ /dev/null
@@ -1,745 +0,0 @@
-/*
- * Copyright (C) 2018 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 <libfiemap/fiemap_writer.h>
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <stdio.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <limits>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm.h>
-
-namespace android {
-namespace fiemap {
-
-using namespace android::dm;
-
-// We are expecting no more than 512 extents in a fiemap of the file we create.
-// If we find more, then it is treated as error for now.
-static constexpr const uint32_t kMaxExtents = 512;
-
-// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
-static constexpr const uint32_t kUnsupportedExtentFlags =
-        FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
-        FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
-        FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
-
-// Large file support must be enabled.
-static_assert(sizeof(off_t) == sizeof(uint64_t));
-
-static inline void cleanup(const std::string& file_path, bool created) {
-    if (created) {
-        unlink(file_path.c_str());
-    }
-}
-
-static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
-    // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
-    // The directory name in the target corresponds to the name of the block device. We use
-    // that to extract the block device name.
-    // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
-    // follows.
-    //    1:0 -> ../../devices/virtual/block/ram0
-    std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
-    std::string sysfs_bdev;
-
-    if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
-        PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
-        return false;
-    }
-
-    *bdev_name = ::android::base::Basename(sysfs_bdev);
-    // Paranoid sanity check to make sure we just didn't get the
-    // input in return as-is.
-    if (sysfs_bdev == *bdev_name) {
-        LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
-        return false;
-    }
-
-    return true;
-}
-
-static bool ValidateDmTarget(const DeviceMapper::TargetInfo& target) {
-    const auto& entry = target.spec;
-    if (entry.sector_start != 0) {
-        LOG(INFO) << "Stopping at target with non-zero starting sector";
-        return false;
-    }
-
-    auto target_type = DeviceMapper::GetTargetType(entry);
-    if (target_type == "bow" || target_type == "default-key" || target_type == "crypt") {
-        return true;
-    }
-    if (target_type == "linear") {
-        auto pieces = android::base::Split(target.data, " ");
-        if (pieces[1] != "0") {
-            LOG(INFO) << "Stopping at complex linear target with non-zero starting sector: "
-                      << pieces[1];
-            return false;
-        }
-        return true;
-    }
-
-    LOG(INFO) << "Stopping at complex target type " << target_type;
-    return false;
-}
-
-static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
-    *bdev_raw = bdev;
-
-    if (!::android::base::StartsWith(bdev, "dm-")) {
-        // We are at the bottom of the device mapper stack.
-        return true;
-    }
-
-    // Get the device name.
-    auto dm_name_file = "/sys/block/" + bdev + "/dm/name";
-    std::string dm_name;
-    if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
-        PLOG(ERROR) << "Could not read file: " << dm_name_file;
-        return false;
-    }
-    dm_name = android::base::Trim(dm_name);
-
-    auto& dm = DeviceMapper::Instance();
-    std::vector<DeviceMapper::TargetInfo> table;
-    if (!dm.GetTableInfo(dm_name, &table)) {
-        LOG(ERROR) << "Could not read device-mapper table for " << dm_name << " at " << bdev;
-        return false;
-    }
-
-    // The purpose of libfiemap is to provide an extent-based view into
-    // a file. This is difficult if devices are not layered in a 1:1 manner;
-    // we would have to translate and break up extents based on the actual
-    // block mapping. Since this is too complex, we simply stop processing
-    // the device-mapper stack if we encounter a complex case.
-    //
-    // It is up to the caller to decide whether stopping at a virtual block
-    // device is allowable. In most cases it is not, because we want either
-    // "userdata" or an external volume. It is useful for tests however.
-    // Callers can check by comparing the device number to that of userdata,
-    // or by checking whether is a device-mapper node.
-    if (table.size() > 1) {
-        LOG(INFO) << "Stopping at complex table for " << dm_name << " at " << bdev;
-        return true;
-    }
-    if (!ValidateDmTarget(table[0])) {
-        return true;
-    }
-
-    auto dm_leaf_dir = "/sys/block/" + bdev + "/slaves";
-    auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
-    if (d == nullptr) {
-        PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
-        return false;
-    }
-
-    struct dirent* de;
-    uint32_t num_leaves = 0;
-    std::string bdev_next = "";
-    while ((de = readdir(d.get())) != nullptr) {
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-            continue;
-        }
-
-        // We set the first name we find here
-        if (bdev_next.empty()) {
-            bdev_next = de->d_name;
-        }
-        num_leaves++;
-    }
-
-    // if we have more than one leaves, we return immediately. We can't continue to create the
-    // file since we don't know how to write it out using fiemap, so it will be readable via the
-    // underlying block devices later. The reader will also have to construct the same device mapper
-    // target in order read the file out.
-    if (num_leaves > 1) {
-        LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
-                   << bdev;
-        return false;
-    }
-
-    // recursively call with the block device we found in order to pop the device mapper stack.
-    return DeviceMapperStackPop(bdev_next, bdev_raw);
-}
-
-bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
-                                         bool* uses_dm) {
-    struct stat sb;
-    if (stat(file_path.c_str(), &sb)) {
-        PLOG(ERROR) << "Failed to get stat for: " << file_path;
-        return false;
-    }
-
-    std::string bdev;
-    if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
-        LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
-                   << minor(sb.st_dev);
-        return false;
-    }
-
-    std::string bdev_raw;
-    if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
-        LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
-        return false;
-    }
-
-    if (uses_dm) {
-        *uses_dm = (bdev_raw != bdev);
-    }
-
-    LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
-               << bdev << ")";
-
-    *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
-
-    // Make sure we are talking to a block device before calling it a success.
-    if (stat(bdev_path->c_str(), &sb)) {
-        PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
-        return false;
-    }
-
-    if ((sb.st_mode & S_IFMT) != S_IFBLK) {
-        PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
-        return false;
-    }
-
-    return true;
-}
-
-static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
-    uint64_t size_in_bytes = 0;
-    if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
-        PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
-        return false;
-    }
-
-    *bdev_size = size_in_bytes;
-
-    return true;
-}
-
-static uint64_t GetFileSize(const std::string& file_path) {
-    struct stat sb;
-    if (stat(file_path.c_str(), &sb)) {
-        PLOG(ERROR) << "Failed to get size for file: " << file_path;
-        return 0;
-    }
-
-    return sb.st_size;
-}
-
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
-                              uint32_t* fs_type) {
-    struct statfs64 sfs;
-    if (statfs64(file_path.c_str(), &sfs)) {
-        PLOG(ERROR) << "Failed to read file system status at: " << file_path;
-        return false;
-    }
-
-    if (!sfs.f_bsize) {
-        LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
-        return false;
-    }
-
-    // Check if the filesystem is of supported types.
-    // Only ext4, f2fs, and vfat are tested and supported.
-    switch (sfs.f_type) {
-        case EXT4_SUPER_MAGIC:
-        case F2FS_SUPER_MAGIC:
-        case MSDOS_SUPER_MAGIC:
-            break;
-        default:
-            LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
-            return false;
-    }
-
-    uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
-    if (access(file_path.c_str(), F_OK) != 0 && available_bytes <= file_size) {
-        LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
-        return false;
-    }
-
-    *blocksz = sfs.f_bsize;
-    *fs_type = sfs.f_type;
-    return true;
-}
-
-static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
-                              const std::string& file_path,
-                              const std::function<bool(uint64_t, uint64_t)>& on_progress) {
-    // Even though this is much faster than writing zeroes, it is still slow
-    // enough that we need to fire the progress callback periodically. To
-    // easily achieve this, we seek in chunks. We use 1000 chunks since
-    // normally we only fire the callback on 1/1000th increments.
-    uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
-
-    // Seek just to the end of each chunk and write a single byte, causing
-    // the filesystem to allocate blocks.
-    off_t cursor = 0;
-    off_t end = static_cast<off_t>(file_size);
-    while (cursor < end) {
-        cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
-        auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
-        if (rv < 0) {
-            PLOG(ERROR) << "Failed to lseek " << file_path;
-            return false;
-        }
-        if (rv != cursor - 1) {
-            LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
-            return false;
-        }
-        char buffer[] = {0};
-        if (!android::base::WriteFully(file_fd, buffer, 1)) {
-            PLOG(ERROR) << "Write failed: " << file_path;
-            return false;
-        }
-        if (on_progress && !on_progress(cursor, file_size)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
-                         uint64_t file_size, unsigned int fs_type,
-                         std::function<bool(uint64_t, uint64_t)> on_progress) {
-    // Reserve space for the file on the file system and write it out to make sure the extents
-    // don't come back unwritten. Return from this function with the kernel file offset set to 0.
-    // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
-    // aren't moved around.
-    switch (fs_type) {
-        case EXT4_SUPER_MAGIC:
-        case F2FS_SUPER_MAGIC:
-            if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
-                PLOG(ERROR) << "Failed to allocate space for file: " << file_path
-                            << " size: " << file_size;
-                return false;
-            }
-            break;
-        case MSDOS_SUPER_MAGIC:
-            // fallocate() is not supported, and not needed, since VFAT does not support holes.
-            // Instead we can perform a much faster allocation.
-            return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
-        default:
-            LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
-            return false;
-    }
-
-    // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
-    // blocks are actually written to by the file system and thus getting rid of the holes in the
-    // file.
-    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
-    if (buffer == nullptr) {
-        LOG(ERROR) << "failed to allocate memory for writing file";
-        return false;
-    }
-
-    off64_t offset = lseek64(file_fd, 0, SEEK_SET);
-    if (offset < 0) {
-        PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
-        return false;
-    }
-
-    int permille = -1;
-    while (offset < file_size) {
-        if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
-            PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
-                        << " in file " << file_path;
-            return false;
-        }
-
-        offset += blocksz;
-
-        // Don't invoke the callback every iteration - wait until a significant
-        // chunk (here, 1/1000th) of the data has been processed.
-        int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
-        if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
-            if (on_progress && !on_progress(offset, file_size)) {
-                return false;
-            }
-            permille = new_permille;
-        }
-    }
-
-    if (lseek64(file_fd, 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
-        return false;
-    }
-
-    // flush all writes here ..
-    if (fsync(file_fd)) {
-        PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
-        return false;
-    }
-
-    // Send one last progress notification.
-    if (on_progress && !on_progress(file_size, file_size)) {
-        return false;
-    }
-    return true;
-}
-
-static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
-    if (fs_type != F2FS_SUPER_MAGIC) {
-        // No pinning necessary for ext4/msdos. The blocks, once allocated, are
-        // expected to be fixed.
-        return true;
-    }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_SET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
-#endif
-
-    uint32_t pin_status = 1;
-    int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
-    if (error < 0) {
-        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
-            PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
-        } else {
-            PLOG(ERROR) << "Failed to pin file: " << file_path;
-        }
-        return false;
-    }
-
-    return true;
-}
-
-static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
-    if (fs_type != F2FS_SUPER_MAGIC) {
-        // No pinning necessary for ext4 or vfat. The blocks, once allocated,
-        // are expected to be fixed.
-        return true;
-    }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_GET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
-#endif
-
-    // f2fs: export FS_NOCOW_FL flag to user
-    uint32_t flags;
-    int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
-    if (error < 0) {
-        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
-            PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
-        } else {
-            PLOG(ERROR) << "Failed to get flags: " << file_path;
-        }
-        return false;
-    }
-    if (!(flags & FS_NOCOW_FL)) {
-        LOG(ERROR) << "It is not pinned: " << file_path;
-        return false;
-    }
-
-    // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
-    uint32_t moved_blocks_nr;
-    error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
-    if (error < 0) {
-        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
-            PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
-        } else {
-            PLOG(ERROR) << "Failed to get file pin status: " << file_path;
-        }
-        return false;
-    }
-
-    if (moved_blocks_nr) {
-        LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
-    }
-    return moved_blocks_nr == 0;
-}
-
-bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
-    android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));
-    if (fd < 0) {
-        PLOG(ERROR) << "open: " << file_path;
-        return false;
-    }
-
-    struct statfs64 sfs;
-    if (fstatfs64(fd, &sfs)) {
-        PLOG(ERROR) << "fstatfs64: " << file_path;
-        return false;
-    }
-    return IsFilePinned(fd, file_path, sfs.f_type);
-}
-
-static bool ReadFiemap(int file_fd, const std::string& file_path,
-                       std::vector<struct fiemap_extent>* extents) {
-    uint64_t fiemap_size =
-            sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
-    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
-    if (buffer == nullptr) {
-        LOG(ERROR) << "Failed to allocate memory for fiemap";
-        return false;
-    }
-
-    struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
-    fiemap->fm_start = 0;
-    fiemap->fm_length = UINT64_MAX;
-    // make sure file is synced to disk before we read the fiemap
-    fiemap->fm_flags = FIEMAP_FLAG_SYNC;
-    fiemap->fm_extent_count = kMaxExtents;
-
-    if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
-        PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
-        return false;
-    }
-
-    if (fiemap->fm_mapped_extents == 0) {
-        LOG(ERROR) << "File " << file_path << " has zero extents";
-        return false;
-    }
-
-    // Iterate through each extent read and make sure its valid before adding it to the vector
-    bool last_extent_seen = false;
-    struct fiemap_extent* extent = &fiemap->fm_extents[0];
-    for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
-        // LogExtent(i + 1, *extent);
-        if (extent->fe_flags & kUnsupportedExtentFlags) {
-            LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
-                       << " has unsupported flags";
-            extents->clear();
-            return false;
-        }
-
-        if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
-            last_extent_seen = true;
-            if (i != (fiemap->fm_mapped_extents - 1)) {
-                LOG(WARNING) << "Extents are being received out-of-order";
-            }
-        }
-        extents->emplace_back(std::move(*extent));
-    }
-
-    if (!last_extent_seen) {
-        // The file is possibly too fragmented.
-        if (fiemap->fm_mapped_extents == kMaxExtents) {
-            LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
-        }
-        extents->clear();
-    }
-
-    return last_extent_seen;
-}
-
-static bool ReadFibmap(int file_fd, const std::string& file_path,
-                       std::vector<struct fiemap_extent>* extents) {
-    struct stat s;
-    if (fstat(file_fd, &s)) {
-        PLOG(ERROR) << "Failed to stat " << file_path;
-        return false;
-    }
-
-    unsigned int blksize;
-    if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
-        PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
-        return false;
-    }
-    if (!blksize) {
-        LOG(ERROR) << "Invalid filesystem block size: " << blksize;
-        return false;
-    }
-
-    uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
-    if (num_blocks > std::numeric_limits<uint32_t>::max()) {
-        LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
-        return false;
-    }
-
-    for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
-        uint32_t block = block_number;
-        if (ioctl(file_fd, FIBMAP, &block)) {
-            PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
-            return false;
-        }
-        if (!block) {
-            LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
-            return false;
-        }
-
-        if (!extents->empty() && block == last_block + 1) {
-            extents->back().fe_length += blksize;
-        } else {
-            extents->push_back(fiemap_extent{.fe_logical = block_number,
-                                             .fe_physical = static_cast<uint64_t>(block) * blksize,
-                                             .fe_length = static_cast<uint64_t>(blksize),
-                                             .fe_flags = 0});
-        }
-        last_block = block;
-    }
-    return true;
-}
-
-FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
-                                   std::function<bool(uint64_t, uint64_t)> progress) {
-    // if 'create' is false, open an existing file and do not truncate.
-    int open_flags = O_RDWR | O_CLOEXEC;
-    if (create) {
-        if (access(file_path.c_str(), F_OK) == 0) {
-            LOG(WARNING) << "File " << file_path << " already exists, truncating";
-        }
-        open_flags |= O_CREAT | O_TRUNC;
-    }
-    ::android::base::unique_fd file_fd(
-            TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
-    if (file_fd < 0) {
-        PLOG(ERROR) << "Failed to create file at: " << file_path;
-        return nullptr;
-    }
-
-    std::string abs_path;
-    if (!::android::base::Realpath(file_path, &abs_path)) {
-        PLOG(ERROR) << "Invalid file path: " << file_path;
-        cleanup(file_path, create);
-        return nullptr;
-    }
-
-    std::string bdev_path;
-    if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
-        LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
-        cleanup(abs_path, create);
-        return nullptr;
-    }
-
-    ::android::base::unique_fd bdev_fd(
-            TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (bdev_fd < 0) {
-        PLOG(ERROR) << "Failed to open block device: " << bdev_path;
-        cleanup(file_path, create);
-        return nullptr;
-    }
-
-    uint64_t bdevsz;
-    if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
-        LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
-        cleanup(file_path, create);
-        return nullptr;
-    }
-
-    if (!create) {
-        file_size = GetFileSize(abs_path);
-        if (file_size == 0) {
-            LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
-            return nullptr;
-        }
-    }
-
-    uint64_t blocksz;
-    uint32_t fs_type;
-    if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
-        LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
-        cleanup(abs_path, create);
-        return nullptr;
-    }
-
-    // Align up to the nearest block size.
-    if (file_size % blocksz) {
-        file_size += blocksz - (file_size % blocksz);
-    }
-
-    if (create) {
-        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
-            LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
-                       << " bytes";
-            cleanup(abs_path, create);
-            return nullptr;
-        }
-    }
-
-    // f2fs may move the file blocks around.
-    if (!PinFile(file_fd, abs_path, fs_type)) {
-        cleanup(abs_path, create);
-        LOG(ERROR) << "Failed to pin the file in storage";
-        return nullptr;
-    }
-
-    // now allocate the FiemapWriter and start setting it up
-    FiemapUniquePtr fmap(new FiemapWriter());
-    switch (fs_type) {
-        case EXT4_SUPER_MAGIC:
-        case F2FS_SUPER_MAGIC:
-            if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
-                LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
-                cleanup(abs_path, create);
-                return nullptr;
-            }
-            break;
-        case MSDOS_SUPER_MAGIC:
-            if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
-                LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
-                cleanup(abs_path, create);
-                return nullptr;
-            }
-            break;
-    }
-
-    fmap->file_path_ = abs_path;
-    fmap->bdev_path_ = bdev_path;
-    fmap->file_size_ = file_size;
-    fmap->bdev_size_ = bdevsz;
-    fmap->fs_type_ = fs_type;
-    fmap->block_size_ = blocksz;
-
-    LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
-                 << bdev_path;
-    return fmap;
-}
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/fiemap_writer_test.cpp b/libfiemap/fiemap_writer_test.cpp
deleted file mode 100644
index 4ac7161..0000000
--- a/libfiemap/fiemap_writer_test.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Copyright (C) 2018 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 <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <libdm/loop_control.h>
-#include <libfiemap/fiemap_writer.h>
-#include <libfiemap/split_fiemap_writer.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap {
-
-using namespace std;
-using namespace std::string_literals;
-using namespace android::fiemap;
-using unique_fd = android::base::unique_fd;
-using LoopDevice = android::dm::LoopDevice;
-
-std::string gTestDir;
-uint64_t testfile_size = 536870912;  // default of 512MiB
-size_t gBlockSize = 0;
-
-class FiemapWriterTest : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        testfile = gTestDir + "/"s + tinfo->name();
-    }
-
-    void TearDown() override { unlink(testfile.c_str()); }
-
-    // name of the file we use for testing
-    std::string testfile;
-};
-
-class SplitFiemapTest : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        testfile = gTestDir + "/"s + tinfo->name();
-    }
-
-    void TearDown() override {
-        std::string message;
-        if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
-            cerr << "Could not remove all split files: " << message;
-        }
-    }
-
-    // name of the file we use for testing
-    std::string testfile;
-};
-
-TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
-    // Try creating a file of size ~100TB but aligned to
-    // 512 byte to make sure block alignment tests don't
-    // fail.
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
-    EXPECT_EQ(fptr, nullptr);
-    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
-    EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, CreateUnalignedFile) {
-    // Try creating a file of size 4097 bytes which is guaranteed
-    // to be unaligned to all known block sizes.
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
-    ASSERT_NE(fptr, nullptr);
-    ASSERT_EQ(fptr->size(), gBlockSize * 2);
-}
-
-TEST_F(FiemapWriterTest, CheckFilePath) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
-    ASSERT_NE(fptr, nullptr);
-    EXPECT_EQ(fptr->size(), gBlockSize);
-    EXPECT_EQ(fptr->file_path(), testfile);
-    EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSize) {
-    // Create a large-ish file and test that the expected size matches.
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
-    ASSERT_NE(fptr, nullptr);
-
-    struct stat s;
-    ASSERT_EQ(stat(testfile.c_str(), &s), 0);
-    EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
-}
-
-TEST_F(FiemapWriterTest, CheckProgress) {
-    std::vector<uint64_t> expected;
-    size_t invocations = 0;
-    auto callback = [&](uint64_t done, uint64_t total) -> bool {
-        if (invocations >= expected.size()) {
-            return false;
-        }
-        EXPECT_EQ(done, expected[invocations]);
-        EXPECT_EQ(total, gBlockSize);
-        invocations++;
-        return true;
-    };
-
-    expected.push_back(gBlockSize);
-
-    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
-    EXPECT_NE(ptr, nullptr);
-    EXPECT_EQ(invocations, expected.size());
-}
-
-TEST_F(FiemapWriterTest, CheckPinning) {
-    auto ptr = FiemapWriter::Open(testfile, 4096);
-    ASSERT_NE(ptr, nullptr);
-    EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));
-}
-
-TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
-    EXPECT_EQ(fptr->size(), gBlockSize);
-    EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
-    EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
-}
-
-TEST_F(FiemapWriterTest, CheckFileCreated) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
-    ASSERT_NE(fptr, nullptr);
-    unique_fd fd(open(testfile.c_str(), O_RDONLY));
-    EXPECT_GT(fd, -1);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSizeActual) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
-    ASSERT_NE(fptr, nullptr);
-
-    struct stat sb;
-    ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
-    EXPECT_GE(sb.st_size, testfile_size);
-}
-
-TEST_F(FiemapWriterTest, CheckFileExtents) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
-    ASSERT_NE(fptr, nullptr);
-    EXPECT_GT(fptr->extents().size(), 0);
-}
-
-TEST_F(FiemapWriterTest, ExistingFile) {
-    // Create the file.
-    { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
-    // Test that we can still open it.
-    {
-        auto ptr = FiemapWriter::Open(testfile, 0, false);
-        ASSERT_NE(ptr, nullptr);
-        EXPECT_GT(ptr->extents().size(), 0);
-    }
-}
-
-TEST_F(FiemapWriterTest, FileDeletedOnError) {
-    auto callback = [](uint64_t, uint64_t) -> bool { return false; };
-    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
-    EXPECT_EQ(ptr, nullptr);
-    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
-    EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, MaxBlockSize) {
-    ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
-}
-
-TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
-    ASSERT_NE(fptr, nullptr);
-
-    switch (fptr->fs_type()) {
-        case F2FS_SUPER_MAGIC:
-        case EXT4_SUPER_MAGIC:
-            // Skip the test for FIEMAP supported filesystems. This is really
-            // because f2fs/ext4 have caches that seem to defeat reading back
-            // directly from the block device, and writing directly is too
-            // dangerous.
-            std::cout << "Skipping test, filesystem does not use FIBMAP\n";
-            return;
-    }
-
-    bool uses_dm;
-    std::string bdev_path;
-    ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
-
-    if (uses_dm) {
-        // We could use a device-mapper wrapper here to bypass encryption, but
-        // really this test is for FIBMAP correctness on VFAT (where encryption
-        // is never used), so we don't bother.
-        std::cout << "Skipping test, block device is metadata encrypted\n";
-        return;
-    }
-
-    std::string data(fptr->size(), '\0');
-    for (size_t i = 0; i < data.size(); i++) {
-        data[i] = 'A' + static_cast<char>(data.size() % 26);
-    }
-
-    {
-        unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
-        ASSERT_GE(fd, 0);
-        ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
-        ASSERT_EQ(fsync(fd), 0);
-    }
-
-    ASSERT_FALSE(fptr->extents().empty());
-    const auto& first_extent = fptr->extents()[0];
-
-    unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
-    ASSERT_GE(bdev, 0);
-
-    off_t where = first_extent.fe_physical;
-    ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
-
-    // Note: this will fail on encrypted folders.
-    std::string actual(data.size(), '\0');
-    ASSERT_GE(first_extent.fe_length, data.size());
-    ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
-    EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, Create) {
-    auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
-    ASSERT_NE(ptr, nullptr);
-
-    auto extents = ptr->extents();
-
-    // Destroy the fiemap, closing file handles. This should not delete them.
-    ptr = nullptr;
-
-    std::vector<std::string> files;
-    ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
-    for (const auto& path : files) {
-        EXPECT_EQ(access(path.c_str(), F_OK), 0);
-    }
-
-    ASSERT_GE(extents.size(), files.size());
-}
-
-TEST_F(SplitFiemapTest, Open) {
-    {
-        auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
-        ASSERT_NE(ptr, nullptr);
-    }
-
-    auto ptr = SplitFiemap::Open(testfile);
-    ASSERT_NE(ptr, nullptr);
-
-    auto extents = ptr->extents();
-    ASSERT_GE(extents.size(), 24);
-}
-
-TEST_F(SplitFiemapTest, DeleteOnFail) {
-    auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
-    ASSERT_EQ(ptr, nullptr);
-
-    std::string first_file = testfile + ".0001";
-    ASSERT_NE(access(first_file.c_str(), F_OK), 0);
-    ASSERT_EQ(errno, ENOENT);
-    ASSERT_NE(access(testfile.c_str(), F_OK), 0);
-    ASSERT_EQ(errno, ENOENT);
-}
-
-static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
-    std::string result;
-    for (int i = 0; i < num_files; i++) {
-        std::string path = base_path + android::base::StringPrintf(".%04d", i);
-        std::string data;
-        if (!android::base::ReadFileToString(path, &data)) {
-            return {};
-        }
-        result += data;
-    }
-    return result;
-}
-
-TEST_F(SplitFiemapTest, WriteWholeFile) {
-    static constexpr size_t kChunkSize = 32768;
-    static constexpr size_t kSize = kChunkSize * 3;
-    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
-    ASSERT_NE(ptr, nullptr);
-
-    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
-    for (size_t i = 0; i < kSize / sizeof(int); i++) {
-        buffer[i] = i;
-    }
-    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
-
-    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
-    auto actual = ReadSplitFiles(testfile, 3);
-    ASSERT_EQ(expected.size(), actual.size());
-    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks1) {
-    static constexpr size_t kChunkSize = 32768;
-    static constexpr size_t kSize = kChunkSize * 3;
-    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
-    ASSERT_NE(ptr, nullptr);
-
-    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
-    for (size_t i = 0; i < kSize / sizeof(int); i++) {
-        buffer[i] = i;
-    }
-
-    // Write in chunks of 1000 (so some writes straddle the boundary of two
-    // files).
-    size_t bytes_written = 0;
-    while (bytes_written < kSize) {
-        size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
-        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
-        ASSERT_TRUE(ptr->Write(data, to_write));
-        bytes_written += to_write;
-    }
-
-    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
-    auto actual = ReadSplitFiles(testfile, 3);
-    ASSERT_EQ(expected.size(), actual.size());
-    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks2) {
-    static constexpr size_t kChunkSize = 32768;
-    static constexpr size_t kSize = kChunkSize * 3;
-    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
-    ASSERT_NE(ptr, nullptr);
-
-    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
-    for (size_t i = 0; i < kSize / sizeof(int); i++) {
-        buffer[i] = i;
-    }
-
-    // Write in chunks of 32KiB so every write is exactly at the end of the
-    // current file.
-    size_t bytes_written = 0;
-    while (bytes_written < kSize) {
-        size_t to_write = std::min(kSize - bytes_written, kChunkSize);
-        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
-        ASSERT_TRUE(ptr->Write(data, to_write));
-        bytes_written += to_write;
-    }
-
-    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
-    auto actual = ReadSplitFiles(testfile, 3);
-    ASSERT_EQ(expected.size(), actual.size());
-    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WritePastEnd) {
-    static constexpr size_t kChunkSize = 32768;
-    static constexpr size_t kSize = kChunkSize * 3;
-    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
-    ASSERT_NE(ptr, nullptr);
-
-    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
-    for (size_t i = 0; i < kSize / sizeof(int); i++) {
-        buffer[i] = i;
-    }
-    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
-    ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
-}
-
-class VerifyBlockWritesExt4 : public ::testing::Test {
-    // 2GB Filesystem and 4k block size by default
-    static constexpr uint64_t block_size = 4096;
-    static constexpr uint64_t fs_size = 2147483648;
-
-  protected:
-    void SetUp() override {
-        fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
-        uint64_t count = fs_size / block_size;
-        std::string dd_cmd =
-                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
-                                              " count=%" PRIu64 " > /dev/null 2>&1",
-                                              fs_path.c_str(), block_size, count);
-        std::string mkfs_cmd =
-                ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
-        // create mount point
-        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
-        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
-        // create file for the file system
-        int ret = system(dd_cmd.c_str());
-        ASSERT_EQ(ret, 0);
-        // Get and attach a loop device to the filesystem we created
-        LoopDevice loop_dev(fs_path, 10s);
-        ASSERT_TRUE(loop_dev.valid());
-        // create file system
-        ret = system(mkfs_cmd.c_str());
-        ASSERT_EQ(ret, 0);
-
-        // mount the file system
-        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
-    }
-
-    void TearDown() override {
-        umount(mntpoint.c_str());
-        rmdir(mntpoint.c_str());
-        unlink(fs_path.c_str());
-    }
-
-    std::string mntpoint;
-    std::string fs_path;
-};
-
-class VerifyBlockWritesF2fs : public ::testing::Test {
-    // 2GB Filesystem and 4k block size by default
-    static constexpr uint64_t block_size = 4096;
-    static constexpr uint64_t fs_size = 2147483648;
-
-  protected:
-    void SetUp() override {
-        fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
-        uint64_t count = fs_size / block_size;
-        std::string dd_cmd =
-                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
-                                              " count=%" PRIu64 " > /dev/null 2>&1",
-                                              fs_path.c_str(), block_size, count);
-        std::string mkfs_cmd =
-                ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
-        // create mount point
-        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
-        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
-        // create file for the file system
-        int ret = system(dd_cmd.c_str());
-        ASSERT_EQ(ret, 0);
-        // Get and attach a loop device to the filesystem we created
-        LoopDevice loop_dev(fs_path, 10s);
-        ASSERT_TRUE(loop_dev.valid());
-        // create file system
-        ret = system(mkfs_cmd.c_str());
-        ASSERT_EQ(ret, 0);
-
-        // mount the file system
-        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
-    }
-
-    void TearDown() override {
-        umount(mntpoint.c_str());
-        rmdir(mntpoint.c_str());
-        unlink(fs_path.c_str());
-    }
-
-    std::string mntpoint;
-    std::string fs_path;
-};
-
-bool DetermineBlockSize() {
-    struct statfs s;
-    if (statfs(gTestDir.c_str(), &s)) {
-        std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
-        return false;
-    }
-    if (!s.f_bsize) {
-        std::cerr << "Invalid block size: " << s.f_bsize << "\n";
-        return false;
-    }
-
-    gBlockSize = s.f_bsize;
-    return true;
-}
-
-}  // namespace fiemap
-}  // namespace android
-
-using namespace android::fiemap;
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    if (argc > 1 && argv[1] == "-h"s) {
-        cerr << "Usage: [test_dir] [file_size]\n";
-        cerr << "\n";
-        cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
-        exit(EXIT_FAILURE);
-    }
-    ::android::base::InitLogging(argv, ::android::base::StderrLogger);
-
-    std::string root_dir = "/data/local/unencrypted";
-    if (access(root_dir.c_str(), F_OK)) {
-        root_dir = "/data";
-    }
-
-    std::string tempdir = root_dir + "/XXXXXX"s;
-    if (!mkdtemp(tempdir.data())) {
-        cerr << "unable to create tempdir on " << root_dir << "\n";
-        exit(EXIT_FAILURE);
-    }
-    if (!android::base::Realpath(tempdir, &gTestDir)) {
-        cerr << "unable to find realpath for " << tempdir;
-        exit(EXIT_FAILURE);
-    }
-
-    if (argc > 2) {
-        testfile_size = strtoull(argv[2], NULL, 0);
-        if (testfile_size == ULLONG_MAX) {
-            testfile_size = 512 * 1024 * 1024;
-        }
-    }
-
-    if (!DetermineBlockSize()) {
-        exit(EXIT_FAILURE);
-    }
-
-    auto result = RUN_ALL_TESTS();
-
-    std::string cmd = "rm -rf " + gTestDir;
-    system(cmd.c_str());
-
-    return result;
-}
diff --git a/libfiemap/image_manager.cpp b/libfiemap/image_manager.cpp
deleted file mode 100644
index 3e32dcf..0000000
--- a/libfiemap/image_manager.cpp
+++ /dev/null
@@ -1,649 +0,0 @@
-//
-// 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 <libfiemap/image_manager.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr/file_wait.h>
-#include <fs_mgr_dm_linear.h>
-#include <libdm/loop_control.h>
-#include <libfiemap/split_fiemap_writer.h>
-
-#include "metadata.h"
-#include "utility.h"
-
-namespace android {
-namespace fiemap {
-
-using namespace std::literals;
-using android::base::unique_fd;
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::dm::DmTable;
-using android::dm::DmTargetLinear;
-using android::dm::LoopControl;
-using android::fs_mgr::CreateLogicalPartition;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fs_mgr::DestroyLogicalPartition;
-using android::fs_mgr::GetPartitionName;
-
-static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
-
-std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
-    auto metadata_dir = "/metadata/gsi/" + dir_prefix;
-    auto data_dir = "/data/gsi/" + dir_prefix;
-    return Open(metadata_dir, data_dir);
-}
-
-std::unique_ptr<ImageManager> ImageManager::Open(const std::string& metadata_dir,
-                                                 const std::string& data_dir) {
-    return std::unique_ptr<ImageManager>(new ImageManager(metadata_dir, data_dir));
-}
-
-ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir)
-    : metadata_dir_(metadata_dir), data_dir_(data_dir) {
-    partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
-}
-
-std::string ImageManager::GetImageHeaderPath(const std::string& name) {
-    return JoinPaths(data_dir_, name) + ".img";
-}
-
-// The status file has one entry per line, with each entry formatted as one of:
-//   dm:<name>
-//   loop:<path>
-//
-// This simplifies the process of tearing down a mapping, since we can simply
-// unmap each entry in the order it appears.
-std::string ImageManager::GetStatusFilePath(const std::string& image_name) {
-    return JoinPaths(metadata_dir_, image_name) + ".status";
-}
-
-static std::string GetStatusPropertyName(const std::string& image_name) {
-    // Note: we don't prefix |image_name|, because CreateLogicalPartition won't
-    // prefix the name either. There are no plans to change this at the moment,
-    // consumers of the image API must take care to use globally-unique image
-    // names.
-    return "gsid.mapped_image." + image_name;
-}
-
-void ImageManager::set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener) {
-    partition_opener_ = std::move(opener);
-}
-
-bool ImageManager::IsImageMapped(const std::string& image_name) {
-    auto prop_name = GetStatusPropertyName(image_name);
-    if (android::base::GetProperty(prop_name, "").empty()) {
-        // If mapped in first-stage init, the dm-device will exist but not the
-        // property.
-        auto& dm = DeviceMapper::Instance();
-        return dm.GetState(image_name) != DmDeviceState::INVALID;
-    }
-    return true;
-}
-
-std::vector<std::string> ImageManager::GetAllBackingImages() {
-    std::vector<std::string> images;
-    auto metadata = OpenMetadata(metadata_dir_);
-    if (metadata) {
-        for (auto&& partition : metadata->partitions) {
-            images.push_back(partition.name);
-        }
-    }
-    return images;
-}
-
-bool ImageManager::PartitionExists(const std::string& name) {
-    if (!MetadataExists(metadata_dir_)) {
-        return false;
-    }
-    auto metadata = OpenMetadata(metadata_dir_);
-    if (!metadata) {
-        return false;
-    }
-    return !!FindPartition(*metadata.get(), name);
-}
-
-bool ImageManager::BackingImageExists(const std::string& name) {
-    auto header_file = GetImageHeaderPath(name);
-    return access(header_file.c_str(), F_OK) == 0;
-}
-
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
-    return CreateBackingImage(name, size, flags, nullptr);
-}
-
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags,
-                                      std::function<bool(uint64_t, uint64_t)>&& on_progress) {
-    auto data_path = GetImageHeaderPath(name);
-    auto fw = SplitFiemap::Create(data_path, size, 0, on_progress);
-    if (!fw) {
-        return false;
-    }
-
-    // Except for testing, we do not allow persisting metadata that references
-    // device-mapper devices. It just doesn't make sense, because the device
-    // numbering may change on reboot. We allow it for testing since the images
-    // are not meant to survive reboot. Outside of tests, this can only happen
-    // if device-mapper is stacked in some complex way not supported by
-    // FiemapWriter.
-    auto device_path = GetDevicePathForFile(fw.get());
-    if (android::base::StartsWith(device_path, "/dev/block/dm-") &&
-        !android::base::StartsWith(metadata_dir_, kTestImageMetadataDir)) {
-        LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
-
-        fw = {};
-        SplitFiemap::RemoveSplitFiles(data_path);
-        return false;
-    }
-
-    bool readonly = !!(flags & CREATE_IMAGE_READONLY);
-    if (!UpdateMetadata(metadata_dir_, name, fw.get(), size, readonly)) {
-        return false;
-    }
-
-    if (flags & CREATE_IMAGE_ZERO_FILL) {
-        if (!ZeroFillNewImage(name, 0)) {
-            DeleteBackingImage(name);
-            return false;
-        }
-    }
-    return true;
-}
-
-bool ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
-    auto data_path = GetImageHeaderPath(name);
-
-    // See the comment in MapImageDevice() about how this works.
-    std::string block_device;
-    bool can_use_devicemapper;
-    if (!FiemapWriter::GetBlockDeviceForFile(data_path, &block_device, &can_use_devicemapper)) {
-        LOG(ERROR) << "Could not determine block device for " << data_path;
-        return false;
-    }
-
-    if (!can_use_devicemapper) {
-        // We've backed with loop devices, and since we store files in an
-        // unencrypted folder, the initial zeroes we wrote will suffice.
-        return true;
-    }
-
-    // data is dm-crypt, or FBE + dm-default-key. This means the zeroes written
-    // by libfiemap were encrypted, so we need to map the image in and correct
-    // this.
-    auto device = MappedDevice::Open(this, 10s, name);
-    if (!device) {
-        return false;
-    }
-
-    static constexpr size_t kChunkSize = 4096;
-    std::string zeroes(kChunkSize, '\0');
-
-    uint64_t remaining;
-    if (bytes) {
-        remaining = bytes;
-    } else {
-        remaining = get_block_device_size(device->fd());
-        if (!remaining) {
-            PLOG(ERROR) << "Could not get block device size for " << device->path();
-            return false;
-        }
-    }
-    while (remaining) {
-        uint64_t to_write = std::min(static_cast<uint64_t>(zeroes.size()), remaining);
-        if (!android::base::WriteFully(device->fd(), zeroes.data(),
-                                       static_cast<size_t>(to_write))) {
-            PLOG(ERROR) << "write failed: " << device->path();
-            return false;
-        }
-        remaining -= to_write;
-    }
-    return true;
-}
-
-bool ImageManager::DeleteBackingImage(const std::string& name) {
-    // For dm-linear devices sitting on top of /data, we cannot risk deleting
-    // the file. The underlying blocks could be reallocated by the filesystem.
-    if (IsImageMapped(name)) {
-        LOG(ERROR) << "Backing image " << name << " is currently mapped to a block device";
-        return false;
-    }
-
-    std::string message;
-    auto header_file = GetImageHeaderPath(name);
-    if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) {
-        // This is fatal, because we don't want to leave these files dangling.
-        LOG(ERROR) << "Error removing image " << name << ": " << message;
-        return false;
-    }
-
-    auto status_file = GetStatusFilePath(name);
-    if (!android::base::RemoveFileIfExists(status_file)) {
-        LOG(ERROR) << "Error removing " << status_file << ": " << message;
-    }
-    return RemoveImageMetadata(metadata_dir_, name);
-}
-
-// Create a block device for an image file, using its extents in its
-// lp_metadata.
-bool ImageManager::MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
-                                   const std::chrono::milliseconds& timeout_ms, std::string* path) {
-    // :TODO: refresh extents in metadata file until f2fs is fixed.
-    auto metadata = OpenMetadata(metadata_dir_);
-    if (!metadata) {
-        return false;
-    }
-
-    auto super = android::fs_mgr::GetMetadataSuperBlockDevice(*metadata.get());
-    auto block_device = android::fs_mgr::GetBlockDevicePartitionName(*super);
-
-    CreateLogicalPartitionParams params = {
-            .block_device = block_device,
-            .metadata = metadata.get(),
-            .partition_name = name,
-            .force_writable = true,
-            .timeout_ms = timeout_ms,
-            .partition_opener = &opener,
-    };
-    if (!CreateLogicalPartition(params, path)) {
-        LOG(ERROR) << "Error creating device-mapper node for image " << name;
-        return false;
-    }
-
-    auto status_string = "dm:" + name;
-    auto status_file = GetStatusFilePath(name);
-    if (!android::base::WriteStringToFile(status_string, status_file)) {
-        PLOG(ERROR) << "Could not write status file: " << status_file;
-        DestroyLogicalPartition(name);
-        return false;
-    }
-    return true;
-}
-
-// Helper to create a loop device for a file.
-static bool CreateLoopDevice(LoopControl& control, const std::string& file,
-                             const std::chrono::milliseconds& timeout_ms, std::string* path) {
-    static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
-    android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
-    if (file_fd < 0) {
-        PLOG(ERROR) << "Could not open file: " << file;
-        return false;
-    }
-    if (!control.Attach(file_fd, timeout_ms, path)) {
-        LOG(ERROR) << "Could not create loop device for: " << file;
-        return false;
-    }
-    LOG(INFO) << "Created loop device " << *path << " for file " << file;
-    return true;
-}
-
-class AutoDetachLoopDevices final {
-  public:
-    AutoDetachLoopDevices(LoopControl& control, const std::vector<std::string>& devices)
-        : control_(control), devices_(devices), commit_(false) {}
-
-    ~AutoDetachLoopDevices() {
-        if (commit_) return;
-        for (const auto& device : devices_) {
-            control_.Detach(device);
-        }
-    }
-
-    void Commit() { commit_ = true; }
-
-  private:
-    LoopControl& control_;
-    const std::vector<std::string>& devices_;
-    bool commit_;
-};
-
-// If an image is stored across multiple files, this takes a list of loop
-// devices and joins them together using device-mapper.
-bool ImageManager::MapWithLoopDeviceList(const std::vector<std::string>& device_list,
-                                         const std::string& name,
-                                         const std::chrono::milliseconds& timeout_ms,
-                                         std::string* path) {
-    auto metadata = OpenMetadata(metadata_dir_);
-    if (!metadata) {
-        return false;
-    }
-    auto partition = FindPartition(*metadata.get(), name);
-    if (!partition) {
-        LOG(ERROR) << "Could not find image in metadata: " << name;
-        return false;
-    }
-
-    // Since extent lengths are in sector units, the size should be a multiple
-    // of the sector size.
-    uint64_t partition_size = GetPartitionSize(*metadata.get(), *partition);
-    if (partition_size % LP_SECTOR_SIZE != 0) {
-        LOG(ERROR) << "Partition size not sector aligned: " << name << ", " << partition_size
-                   << " bytes";
-        return false;
-    }
-
-    DmTable table;
-
-    uint64_t start_sector = 0;
-    uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;
-    for (const auto& block_device : device_list) {
-        // The final block device must be == partition_size, otherwise we
-        // can't find the AVB footer on verified partitions.
-        static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
-        unique_fd fd(open(block_device.c_str(), kOpenFlags));
-        if (fd < 0) {
-            PLOG(ERROR) << "Open failed: " << block_device;
-            return false;
-        }
-
-        uint64_t file_size = get_block_device_size(fd);
-        uint64_t file_sectors = file_size / LP_SECTOR_SIZE;
-        uint64_t segment_size = std::min(file_sectors, sectors_needed);
-
-        table.Emplace<DmTargetLinear>(start_sector, segment_size, block_device, 0);
-
-        start_sector += segment_size;
-        sectors_needed -= segment_size;
-        if (sectors_needed == 0) {
-            break;
-        }
-    }
-
-    auto& dm = DeviceMapper::Instance();
-    if (!dm.CreateDevice(name, table, path, timeout_ms)) {
-        LOG(ERROR) << "Could not create device-mapper device over loop set";
-        return false;
-    }
-
-    // Build the status file.
-    std::vector<std::string> lines;
-    lines.emplace_back("dm:" + name);
-    for (const auto& block_device : device_list) {
-        lines.emplace_back("loop:" + block_device);
-    }
-    auto status_message = android::base::Join(lines, "\n");
-    auto status_file = GetStatusFilePath(name);
-    if (!android::base::WriteStringToFile(status_message, status_file)) {
-        PLOG(ERROR) << "Write failed: " << status_file;
-        dm.DeleteDevice(name);
-        return false;
-    }
-    return true;
-}
-
-static bool OptimizeLoopDevices(const std::vector<std::string>& device_list) {
-    for (const auto& device : device_list) {
-        unique_fd fd(open(device.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW));
-        if (fd < 0) {
-            PLOG(ERROR) << "Open failed: " << device;
-            return false;
-        }
-        if (!LoopControl::EnableDirectIo(fd)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-// Helper to use one or more loop devices around image files.
-bool ImageManager::MapWithLoopDevice(const std::string& name,
-                                     const std::chrono::milliseconds& timeout_ms,
-                                     std::string* path) {
-    auto image_header = GetImageHeaderPath(name);
-
-    std::vector<std::string> file_list;
-    if (!SplitFiemap::GetSplitFileList(image_header, &file_list)) {
-        LOG(ERROR) << "Could not get image file list";
-        return false;
-    }
-
-    // Map each image file as a loopback device.
-    LoopControl control;
-    std::vector<std::string> loop_devices;
-    AutoDetachLoopDevices auto_detach(control, loop_devices);
-
-    auto start_time = std::chrono::steady_clock::now();
-    for (const auto& file : file_list) {
-        auto now = std::chrono::steady_clock::now();
-        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
-
-        std::string loop_device;
-        if (!CreateLoopDevice(control, file, timeout_ms - elapsed, &loop_device)) {
-            break;
-        }
-        loop_devices.emplace_back(loop_device);
-    }
-    if (loop_devices.size() != file_list.size()) {
-        // The number of devices will mismatch if CreateLoopDevice() failed.
-        return false;
-    }
-
-    // If OptimizeLoopDevices fails, we'd use double the memory.
-    if (!OptimizeLoopDevices(loop_devices)) {
-        return false;
-    }
-
-    // If there's only one loop device (by far the most common case, splits
-    // will normally only happen on sdcards with FAT32), then just return that
-    // as the block device. Otherwise, we need to use dm-linear to stitch
-    // together all the loop devices we just created.
-    if (loop_devices.size() > 1) {
-        if (!MapWithLoopDeviceList(loop_devices, name, timeout_ms, path)) {
-            return false;
-        }
-    }
-
-    auto status_message = "loop:" + loop_devices.back();
-    auto status_file = GetStatusFilePath(name);
-    if (!android::base::WriteStringToFile(status_message, status_file)) {
-        PLOG(ERROR) << "Write failed: " << status_file;
-        return false;
-    }
-
-    auto_detach.Commit();
-
-    *path = loop_devices.back();
-    return true;
-}
-
-bool ImageManager::MapImageDevice(const std::string& name,
-                                  const std::chrono::milliseconds& timeout_ms, std::string* path) {
-    if (IsImageMapped(name)) {
-        LOG(ERROR) << "Backing image " << name << " is already mapped";
-        return false;
-    }
-
-    auto image_header = GetImageHeaderPath(name);
-
-    // If there is a device-mapper node wrapping the block device, then we're
-    // able to create another node around it; the dm layer does not carry the
-    // exclusion lock down the stack when a mount occurs.
-    //
-    // If there is no intermediate device-mapper node, then partitions cannot be
-    // opened writable due to sepolicy and exclusivity of having a mounted
-    // filesystem. This should only happen on devices with no encryption, or
-    // devices with FBE and no metadata encryption. For these cases it suffices
-    // to perform normal file writes to /data/gsi (which is unencrypted).
-    std::string block_device;
-    bool can_use_devicemapper;
-    if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) {
-        LOG(ERROR) << "Could not determine block device for " << image_header;
-        return false;
-    }
-
-    if (can_use_devicemapper) {
-        if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
-            return false;
-        }
-    } else if (!MapWithLoopDevice(name, timeout_ms, path)) {
-        return false;
-    }
-
-    // Set a property so we remember this is mapped.
-    auto prop_name = GetStatusPropertyName(name);
-    if (!android::base::SetProperty(prop_name, *path)) {
-        UnmapImageDevice(name, true);
-        return false;
-    }
-    return true;
-}
-
-bool ImageManager::MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
-                                            std::string* dev) {
-    std::string ignore_path;
-    if (!MapWithDmLinear(opener, name, {}, &ignore_path)) {
-        return false;
-    }
-
-    auto& dm = DeviceMapper::Instance();
-    if (!dm.GetDeviceString(name, dev)) {
-        return false;
-    }
-    return true;
-}
-
-bool ImageManager::UnmapImageDevice(const std::string& name) {
-    return UnmapImageDevice(name, false);
-}
-
-bool ImageManager::UnmapImageDevice(const std::string& name, bool force) {
-    if (!force && !IsImageMapped(name)) {
-        LOG(ERROR) << "Backing image " << name << " is not mapped";
-        return false;
-    }
-    auto& dm = DeviceMapper::Instance();
-    LoopControl loop;
-
-    std::string status;
-    auto status_file = GetStatusFilePath(name);
-    if (!android::base::ReadFileToString(status_file, &status)) {
-        PLOG(ERROR) << "Read failed: " << status_file;
-        return false;
-    }
-
-    auto lines = android::base::Split(status, "\n");
-    for (const auto& line : lines) {
-        auto pieces = android::base::Split(line, ":");
-        if (pieces.size() != 2) {
-            LOG(ERROR) << "Unknown status line";
-            continue;
-        }
-        if (pieces[0] == "dm") {
-            // Failure to remove a dm node is fatal, since we can't safely
-            // remove the file or loop devices.
-            const auto& name = pieces[1];
-            if (!dm.DeleteDeviceIfExists(name)) {
-                return false;
-            }
-        } else if (pieces[0] == "loop") {
-            // Failure to remove a loop device is not fatal, since we can still
-            // remove the backing file if we want.
-            loop.Detach(pieces[1]);
-        } else {
-            LOG(ERROR) << "Unknown status: " << pieces[0];
-        }
-    }
-
-    std::string message;
-    if (!android::base::RemoveFileIfExists(status_file, &message)) {
-        LOG(ERROR) << "Could not remove " << status_file << ": " << message;
-    }
-
-    auto status_prop = GetStatusPropertyName(name);
-    android::base::SetProperty(status_prop, "");
-    return true;
-}
-
-bool ImageManager::RemoveAllImages() {
-    if (!MetadataExists(metadata_dir_)) {
-        return true;
-    }
-    auto metadata = OpenMetadata(metadata_dir_);
-    if (!metadata) {
-        return RemoveAllMetadata(metadata_dir_);
-    }
-
-    bool ok = true;
-    for (const auto& partition : metadata->partitions) {
-        auto partition_name = GetPartitionName(partition);
-        ok &= DeleteBackingImage(partition_name);
-    }
-    return ok && RemoveAllMetadata(metadata_dir_);
-}
-
-bool ImageManager::Validate() {
-    auto metadata = OpenMetadata(metadata_dir_);
-    if (!metadata) {
-        return false;
-    }
-
-    for (const auto& partition : metadata->partitions) {
-        auto name = GetPartitionName(partition);
-        auto image_path = GetImageHeaderPath(name);
-        auto fiemap = SplitFiemap::Open(image_path);
-        if (!fiemap || !fiemap->HasPinnedExtents()) {
-            LOG(ERROR) << "Image is missing or was moved: " << image_path;
-            return false;
-        }
-    }
-    return true;
-}
-
-std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
-                                                 const std::chrono::milliseconds& timeout_ms,
-                                                 const std::string& name) {
-    std::string path;
-    if (!manager->MapImageDevice(name, timeout_ms, &path)) {
-        return nullptr;
-    }
-
-    auto device = std::unique_ptr<MappedDevice>(new MappedDevice(manager, name, path));
-    if (device->fd() < 0) {
-        return nullptr;
-    }
-    return device;
-}
-
-MappedDevice::MappedDevice(IImageManager* manager, const std::string& name, const std::string& path)
-    : manager_(manager), name_(name), path_(path) {
-    // The device is already mapped; try and open it.
-    fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
-}
-
-MappedDevice::~MappedDevice() {
-    fd_ = {};
-    manager_->UnmapImageDevice(name_);
-}
-
-bool IImageManager::UnmapImageIfExists(const std::string& name) {
-    // No lock is needed even though this seems to be vulnerable to TOCTOU. If process A
-    // calls MapImageDevice() while process B calls UnmapImageIfExists(), and MapImageDevice()
-    // happens after process B checks IsImageMapped(), it would be as if MapImageDevice() is called
-    // after process B finishes calling UnmapImageIfExists(), resulting the image to be mapped,
-    // which is a reasonable sequence.
-    if (!IsImageMapped(name)) {
-        return true;
-    }
-    return UnmapImageDevice(name);
-}
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/image_test.cpp b/libfiemap/image_test.cpp
deleted file mode 100644
index f05825c..0000000
--- a/libfiemap/image_test.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-//
-// 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 <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <chrono>
-#include <iostream>
-#include <thread>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr/file_wait.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-
-using namespace android::dm;
-using namespace std::literals;
-using android::base::unique_fd;
-using android::fiemap::ImageManager;
-using android::fs_mgr::BlockDeviceInfo;
-using android::fs_mgr::PartitionOpener;
-using android::fs_mgr::WaitForFile;
-
-static std::string gDataPath;
-static std::string gDataMountPath;
-static constexpr char kMetadataPath[] = "/metadata/gsi/test";
-
-static constexpr uint64_t kTestImageSize = 1024 * 1024;
-
-class TestPartitionOpener final : public PartitionOpener {
-  public:
-    android::base::unique_fd Open(const std::string& partition_name, int flags) const override {
-        return PartitionOpener::Open(GetPathForBlockDeviceName(partition_name), flags);
-    }
-    bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override {
-        return PartitionOpener::GetInfo(GetPathForBlockDeviceName(partition_name), info);
-    }
-    std::string GetDeviceString(const std::string& partition_name) const override {
-        return PartitionOpener::GetDeviceString(GetPathForBlockDeviceName(partition_name));
-    }
-
-  private:
-    static std::string GetPathForBlockDeviceName(const std::string& name) {
-        if (android::base::StartsWith(name, "loop") || android::base::StartsWith(name, "dm-")) {
-            return "/dev/block/"s + name;
-        }
-        return name;
-    }
-};
-
-// This fixture is for tests against the device's native configuration.
-class NativeTest : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        manager_ = ImageManager::Open(kMetadataPath, gDataPath);
-        ASSERT_NE(manager_, nullptr);
-
-        manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
-        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        base_name_ = tinfo->name();
-    }
-
-    void TearDown() override {
-        manager_->UnmapImageDevice(base_name_);
-        manager_->DeleteBackingImage(base_name_);
-    }
-
-    std::string PropertyName() { return "gsid.mapped_image." + base_name_; }
-
-    std::unique_ptr<ImageManager> manager_;
-    std::string base_name_;
-};
-
-TEST_F(NativeTest, CreateAndMap) {
-    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
-
-    std::string path;
-    ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &path));
-    ASSERT_TRUE(manager_->IsImageMapped(base_name_));
-    ASSERT_EQ(android::base::GetProperty(PropertyName(), ""), path);
-
-    {
-        unique_fd fd(open(path.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC));
-        ASSERT_GE(fd, 0);
-        ASSERT_EQ(get_block_device_size(fd), kTestImageSize);
-    }
-
-    ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
-    ASSERT_FALSE(manager_->IsImageMapped(base_name_));
-    ASSERT_EQ(android::base::GetProperty(PropertyName(), ""), "");
-}
-
-// This fixture is for tests against a simulated device environment. Rather
-// than use /data, we create an image and then layer a new filesystem within
-// it. Each test then decides how to mount and create layered images. This
-// allows us to test FBE vs FDE configurations.
-class ImageTest : public ::testing::Test {
-  public:
-    ImageTest() : dm_(DeviceMapper::Instance()) {}
-
-    void SetUp() override {
-        manager_ = ImageManager::Open(kMetadataPath, gDataPath);
-        ASSERT_NE(manager_, nullptr);
-
-        manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
-        submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
-        ASSERT_NE(submanager_, nullptr);
-
-        submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
-        // Ensure that metadata is cleared in between runs.
-        submanager_->RemoveAllImages();
-        manager_->RemoveAllImages();
-
-        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        base_name_ = tinfo->name();
-        test_image_name_ = base_name_ + "-base";
-        wrapper_device_name_ = base_name_ + "-wrapper";
-
-        ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
-        ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
-    }
-
-    void TearDown() override {
-        submanager_->UnmapImageDevice(test_image_name_);
-        umount(gDataMountPath.c_str());
-        dm_.DeleteDeviceIfExists(wrapper_device_name_);
-        manager_->UnmapImageDevice(base_name_);
-        manager_->DeleteBackingImage(base_name_);
-    }
-
-  protected:
-    bool DoFormat(const std::string& device) {
-        // clang-format off
-        std::vector<std::string> mkfs_args = {
-            "/system/bin/mke2fs",
-            "-F",
-            "-b 4096",
-            "-t ext4",
-            "-m 0",
-            "-O has_journal",
-            device,
-            ">/dev/null",
-            "2>/dev/null",
-            "</dev/null",
-        };
-        // clang-format on
-        auto command = android::base::Join(mkfs_args, " ");
-        return system(command.c_str()) == 0;
-    }
-
-    std::unique_ptr<ImageManager> manager_;
-    std::unique_ptr<ImageManager> submanager_;
-
-    DeviceMapper& dm_;
-    std::string base_name_;
-    std::string base_device_;
-    std::string test_image_name_;
-    std::string wrapper_device_name_;
-};
-
-TEST_F(ImageTest, DirectMount) {
-    ASSERT_TRUE(DoFormat(base_device_));
-    ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-    ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
-    std::string path;
-    ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
-    ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
-}
-
-TEST_F(ImageTest, IndirectMount) {
-    // Create a simple wrapper around the base device that we'll mount from
-    // instead. This will simulate the code paths for dm-crypt/default-key/bow
-    // and force us to use device-mapper rather than loop devices.
-    uint64_t device_size = 0;
-    {
-        unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
-        ASSERT_GE(fd, 0);
-        device_size = get_block_device_size(fd);
-        ASSERT_EQ(device_size, kTestImageSize * 16);
-    }
-    uint64_t num_sectors = device_size / 512;
-
-    auto& dm = DeviceMapper::Instance();
-
-    DmTable table;
-    table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
-    ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));
-
-    // Format and mount.
-    std::string wrapper_device;
-    ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
-    ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
-    ASSERT_TRUE(DoFormat(wrapper_device));
-    ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-
-    ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
-    std::string path;
-    ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
-    ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
-}
-
-bool Mkdir(const std::string& path) {
-    if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
-        std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
-        return false;
-    }
-    return true;
-}
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-
-    if (argc >= 2) {
-        gDataPath = argv[1];
-    } else {
-        gDataPath = "/data/gsi/test";
-    }
-    gDataMountPath = gDataPath + "/mnt"s;
-
-    if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(gDataMountPath) ||
-        !Mkdir(kMetadataPath + "/mnt"s)) {
-        return 1;
-    }
-    return RUN_ALL_TESTS();
-}
diff --git a/libfiemap/include/libfiemap/fiemap_writer.h b/libfiemap/include/libfiemap/fiemap_writer.h
deleted file mode 100644
index c692265..0000000
--- a/libfiemap/include/libfiemap/fiemap_writer.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2018 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 <linux/fiemap.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <functional>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace fiemap {
-
-class FiemapWriter;
-using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
-
-class FiemapWriter final {
-  public:
-    // Factory method for FiemapWriter.
-    // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
-    // to the given file directly using raw block i/o. The optional progress callback will be
-    // invoked, if create is true, while the file is being initialized. It receives the bytes
-    // written and the number of total bytes. If the callback returns false, the operation will
-    // fail.
-    //
-    // Note: when create is true, the file size will be aligned up to the nearest file system
-    // block.
-    static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
-                                bool create = true,
-                                std::function<bool(uint64_t, uint64_t)> progress = {});
-
-    // Check that a file still has the same extents since it was last opened with FiemapWriter,
-    // assuming the file was not resized outside of FiemapWriter. Returns false either on error
-    // or if the file was not pinned.
-    //
-    // This will always return true on Ext4. On F2FS, it will return true if either of the
-    // following cases are true:
-    //   - The file was never pinned.
-    //   - The file is pinned and has not been moved by the GC.
-    // Thus, this method should only be called for pinned files (such as those returned by
-    // FiemapWriter::Open).
-    static bool HasPinnedExtents(const std::string& file_path);
-
-    // Returns the underlying block device of a file. This will look past device-mapper layers
-    // as long as each layer would not change block mappings (i.e., dm-crypt, dm-bow, and dm-
-    // default-key tables are okay; dm-linear is not). If a mapping such as dm-linear is found,
-    // it will be returned in place of any physical block device.
-    //
-    // It is the caller's responsibility to check whether the returned block device is acceptable.
-    // Gsid, for example, will only accept /dev/block/by-name/userdata as the bottom device.
-    // Callers can check the device name (dm- or loop prefix), inspect sysfs, or compare the major
-    // number against a boot device.
-    //
-    // If device-mapper nodes were encountered, then |uses_dm| will be set to true.
-    static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
-                                      bool* uses_dm = nullptr);
-
-    ~FiemapWriter() = default;
-
-    const std::string& file_path() const { return file_path_; };
-    uint64_t size() const { return file_size_; };
-    const std::string& bdev_path() const { return bdev_path_; };
-    uint64_t block_size() const { return block_size_; };
-    const std::vector<struct fiemap_extent>& extents() { return extents_; };
-    uint32_t fs_type() const { return fs_type_; }
-
-    // Non-copyable & Non-movable
-    FiemapWriter(const FiemapWriter&) = delete;
-    FiemapWriter& operator=(const FiemapWriter&) = delete;
-    FiemapWriter& operator=(FiemapWriter&&) = delete;
-    FiemapWriter(FiemapWriter&&) = delete;
-
-  private:
-    // Name of the file managed by this class.
-    std::string file_path_;
-    // Block device on which we have created the file.
-    std::string bdev_path_;
-
-    // Size in bytes of the file this class is writing
-    uint64_t file_size_;
-
-    // total size in bytes of the block device
-    uint64_t bdev_size_;
-
-    // Filesystem type where the file is being created.
-    // See: <uapi/linux/magic.h> for filesystem magic numbers
-    uint32_t fs_type_;
-
-    // block size as reported by the kernel of the underlying block device;
-    uint64_t block_size_;
-
-    // This file's fiemap
-    std::vector<struct fiemap_extent> extents_;
-
-    FiemapWriter() = default;
-};
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/include/libfiemap/image_manager.h b/libfiemap/include/libfiemap/image_manager.h
deleted file mode 100644
index efbe9bd..0000000
--- a/libfiemap/include/libfiemap/image_manager.h
+++ /dev/null
@@ -1,184 +0,0 @@
-//
-// 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>
-
-#include <chrono>
-#include <functional>
-#include <memory>
-#include <string>
-
-#include <android-base/unique_fd.h>
-#include <liblp/partition_opener.h>
-
-namespace android {
-namespace fiemap {
-
-class IImageManager {
-  public:
-    using IPartitionOpener = android::fs_mgr::IPartitionOpener;
-
-    virtual ~IImageManager() {}
-
-    // When linking to libfiemap_binder, the Open() call will use binder.
-    // Otherwise, the Open() call will use the ImageManager implementation
-    // below.
-    static std::unique_ptr<IImageManager> Open(const std::string& dir_prefix,
-                                               const std::chrono::milliseconds& timeout_ms);
-
-    // Flags for CreateBackingImage().
-    static constexpr int CREATE_IMAGE_DEFAULT = 0x0;
-    static constexpr int CREATE_IMAGE_READONLY = 0x1;
-    static constexpr int CREATE_IMAGE_ZERO_FILL = 0x2;
-
-    // Create an image that can be mapped as a block-device. If |force_zero_fill|
-    // is true, the image will be zero-filled. Otherwise, the initial content
-    // of the image is undefined. If zero-fill is requested, and the operation
-    // cannot be completed, the image will be deleted and this function will
-    // return false.
-    virtual bool CreateBackingImage(const std::string& name, uint64_t size, int flags) = 0;
-
-    // Delete an image created with CreateBackingImage. Its entry will be
-    // removed from the associated lp_metadata file.
-    virtual bool DeleteBackingImage(const std::string& name) = 0;
-
-    // Create a block device for an image previously created with
-    // CreateBackingImage. This will wait for at most |timeout_ms| milliseconds
-    // for |path| to be available, and will return false if not available in
-    // the requested time. If |timeout_ms| is zero, this is NOT guaranteed to
-    // return true. A timeout of 10s is recommended.
-    //
-    // Note that snapshots created with a readonly flag are always mapped
-    // writable. The flag is persisted in the lp_metadata file however, so if
-    // fs_mgr::CreateLogicalPartition(s) is used, the flag will be respected.
-    virtual bool MapImageDevice(const std::string& name,
-                                const std::chrono::milliseconds& timeout_ms, std::string* path) = 0;
-
-    // Unmap a block device previously mapped with mapBackingImage.
-    virtual bool UnmapImageDevice(const std::string& name) = 0;
-
-    // Returns true whether the named backing image exists.
-    virtual bool BackingImageExists(const std::string& name) = 0;
-
-    // Returns true if the specified image is mapped to a device.
-    virtual bool IsImageMapped(const std::string& name) = 0;
-
-    // Map an image using device-mapper. This is not available over binder, and
-    // is intended only for first-stage init. The returned device is a major:minor
-    // device string.
-    virtual bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
-                                          std::string* dev) = 0;
-
-    // Get all backing image names.
-    virtual std::vector<std::string> GetAllBackingImages() = 0;
-
-    // Writes |bytes| zeros to |name| file. If |bytes| is 0, then the
-    // whole file if filled with zeros.
-    virtual bool ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
-
-    virtual bool UnmapImageIfExists(const std::string& name);
-};
-
-class ImageManager final : public IImageManager {
-  public:
-    // Return an ImageManager for the given metadata and data directories. Both
-    // directories must already exist.
-    static std::unique_ptr<ImageManager> Open(const std::string& metadata_dir,
-                                              const std::string& data_dir);
-
-    // Helper function that derives the metadata and data dirs given a single
-    // prefix.
-    static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix);
-
-    // Methods that must be implemented from IImageManager.
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
-    bool DeleteBackingImage(const std::string& name) override;
-    bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
-                        std::string* path) override;
-    bool UnmapImageDevice(const std::string& name) override;
-    bool BackingImageExists(const std::string& name) override;
-    bool IsImageMapped(const std::string& name) override;
-    bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
-                                  std::string* dev) override;
-
-    std::vector<std::string> GetAllBackingImages();
-    // Same as CreateBackingImage, but provides a progress notification.
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags,
-                            std::function<bool(uint64_t, uint64_t)>&& on_progress);
-
-    // Find and remove all images and metadata for a given image dir.
-    bool RemoveAllImages();
-
-    // Returns true if the named partition exists. This does not check the
-    // consistency of the backing image/data file.
-    bool PartitionExists(const std::string& name);
-
-    // Validates that all images still have pinned extents. This will be removed
-    // once b/134588268 is fixed.
-    bool Validate();
-
-    void set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener);
-
-    // Writes |bytes| zeros at the beginning of the passed image
-    bool ZeroFillNewImage(const std::string& name, uint64_t bytes);
-
-  private:
-    ImageManager(const std::string& metadata_dir, const std::string& data_dir);
-    std::string GetImageHeaderPath(const std::string& name);
-    std::string GetStatusFilePath(const std::string& image_name);
-    bool MapWithLoopDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
-                           std::string* path);
-    bool MapWithLoopDeviceList(const std::vector<std::string>& device_list, const std::string& name,
-                               const std::chrono::milliseconds& timeout_ms, std::string* path);
-    bool MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
-                         const std::chrono::milliseconds& timeout_ms, std::string* path);
-    bool UnmapImageDevice(const std::string& name, bool force);
-
-    ImageManager(const ImageManager&) = delete;
-    ImageManager& operator=(const ImageManager&) = delete;
-    ImageManager& operator=(ImageManager&&) = delete;
-    ImageManager(ImageManager&&) = delete;
-
-    std::string metadata_dir_;
-    std::string data_dir_;
-    std::unique_ptr<IPartitionOpener> partition_opener_;
-};
-
-// RAII helper class for mapping and opening devices with an ImageManager.
-class MappedDevice final {
-  public:
-    static std::unique_ptr<MappedDevice> Open(IImageManager* manager,
-                                              const std::chrono::milliseconds& timeout_ms,
-                                              const std::string& name);
-
-    ~MappedDevice();
-
-    int fd() const { return fd_; }
-    const std::string& path() const { return path_; }
-
-  protected:
-    MappedDevice(IImageManager* manager, const std::string& name, const std::string& path);
-
-    IImageManager* manager_;
-    std::string name_;
-    std::string path_;
-    android::base::unique_fd fd_;
-};
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/include/libfiemap/split_fiemap_writer.h b/libfiemap/include/libfiemap/split_fiemap_writer.h
deleted file mode 100644
index feffb3d..0000000
--- a/libfiemap/include/libfiemap/split_fiemap_writer.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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>
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-#include "fiemap_writer.h"
-
-namespace android {
-namespace fiemap {
-
-// Wrapper around FiemapWriter that is able to split images across files if
-// necessary.
-class SplitFiemap final {
-  public:
-    using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
-
-    // Create a new split fiemap file. If |max_piece_size| is 0, the number of
-    // pieces will be determined automatically by detecting the filesystem.
-    // Otherwise, the file will be split evenly (with the remainder in the
-    // final file).
-    static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
-                                               uint64_t max_piece_size,
-                                               ProgressCallback progress = {});
-
-    // Open an existing split fiemap file.
-    static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
-
-    ~SplitFiemap();
-
-    // Return a list of all files created for a split file.
-    static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
-
-    // Destroy all components of a split file. If the root file does not exist,
-    // this returns true and does not report an error.
-    static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
-
-    // Return whether all components of a split file still have pinned extents.
-    bool HasPinnedExtents() const;
-
-    // Helper method for writing data that spans files. Note there is no seek
-    // method (yet); this starts at 0 and increments the position by |bytes|.
-    bool Write(const void* data, uint64_t bytes);
-
-    // Flush all writes to all split files.
-    bool Flush();
-
-    const std::vector<struct fiemap_extent>& extents();
-    uint32_t block_size() const;
-    uint64_t size() const { return total_size_; }
-    const std::string& bdev_path() const;
-
-    // Non-copyable & Non-movable
-    SplitFiemap(const SplitFiemap&) = delete;
-    SplitFiemap& operator=(const SplitFiemap&) = delete;
-    SplitFiemap& operator=(SplitFiemap&&) = delete;
-    SplitFiemap(SplitFiemap&&) = delete;
-
-  private:
-    SplitFiemap() = default;
-    void AddFile(FiemapUniquePtr&& file);
-
-    bool creating_ = false;
-    std::string list_file_;
-    std::vector<FiemapUniquePtr> files_;
-    std::vector<struct fiemap_extent> extents_;
-    uint64_t total_size_ = 0;
-
-    // Most recently open file and position for Write().
-    size_t cursor_index_ = 0;
-    uint64_t cursor_file_pos_ = 0;
-    android::base::unique_fd cursor_fd_;
-};
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/metadata.cpp b/libfiemap/metadata.cpp
deleted file mode 100644
index 597efe9..0000000
--- a/libfiemap/metadata.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-//
-// 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 "metadata.h"
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <liblp/builder.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap {
-
-using namespace android::fs_mgr;
-
-static constexpr uint32_t kMaxMetadataSize = 256 * 1024;
-
-std::string GetMetadataFile(const std::string& metadata_dir) {
-    return JoinPaths(metadata_dir, "lp_metadata");
-}
-
-bool MetadataExists(const std::string& metadata_dir) {
-    auto metadata_file = GetMetadataFile(metadata_dir);
-    return access(metadata_file.c_str(), F_OK) == 0;
-}
-
-std::unique_ptr<LpMetadata> OpenMetadata(const std::string& metadata_dir) {
-    auto metadata_file = GetMetadataFile(metadata_dir);
-    auto metadata = ReadFromImageFile(metadata_file);
-    if (!metadata) {
-        LOG(ERROR) << "Could not read metadata file " << metadata_file;
-        return nullptr;
-    }
-    return metadata;
-}
-
-// :TODO: overwrite on create if open fails
-std::unique_ptr<MetadataBuilder> OpenOrCreateMetadata(const std::string& metadata_dir,
-                                                      SplitFiemap* file) {
-    auto metadata_file = GetMetadataFile(metadata_dir);
-
-    PartitionOpener opener;
-    std::unique_ptr<MetadataBuilder> builder;
-    if (access(metadata_file.c_str(), R_OK)) {
-        if (errno != ENOENT) {
-            PLOG(ERROR) << "access " << metadata_file << " failed:";
-            return nullptr;
-        }
-
-        auto data_device = GetDevicePathForFile(file);
-
-        BlockDeviceInfo device_info;
-        if (!opener.GetInfo(data_device, &device_info)) {
-            LOG(ERROR) << "Could not read partition: " << data_device;
-            return nullptr;
-        }
-
-        std::vector<BlockDeviceInfo> block_devices = {device_info};
-        auto super_name = android::base::Basename(data_device);
-        builder = MetadataBuilder::New(block_devices, super_name, kMaxMetadataSize, 1);
-    } else {
-        auto metadata = OpenMetadata(metadata_dir);
-        if (!metadata) {
-            return nullptr;
-        }
-        builder = MetadataBuilder::New(*metadata.get(), &opener);
-    }
-
-    if (!builder) {
-        LOG(ERROR) << "Could not create metadata builder";
-        return nullptr;
-    }
-    return builder;
-}
-
-bool SaveMetadata(MetadataBuilder* builder, const std::string& metadata_dir) {
-    auto exported = builder->Export();
-    if (!exported) {
-        LOG(ERROR) << "Unable to export new metadata";
-        return false;
-    }
-
-    // If there are no more partitions in the metadata, just delete the file.
-    auto metadata_file = GetMetadataFile(metadata_dir);
-    if (exported->partitions.empty() && android::base::RemoveFileIfExists(metadata_file)) {
-        return true;
-    }
-    if (!WriteToImageFile(metadata_file, *exported.get())) {
-        LOG(ERROR) << "Unable to save new metadata";
-        return false;
-    }
-    return true;
-}
-
-bool RemoveAllMetadata(const std::string& dir) {
-    auto metadata_file = GetMetadataFile(dir);
-    return android::base::RemoveFileIfExists(metadata_file);
-}
-
-bool FillPartitionExtents(MetadataBuilder* builder, Partition* partition, SplitFiemap* file,
-                          uint64_t partition_size) {
-    auto block_device = android::base::Basename(GetDevicePathForFile(file));
-
-    uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;
-    for (const auto& extent : file->extents()) {
-        if (extent.fe_length % LP_SECTOR_SIZE != 0) {
-            LOG(ERROR) << "Extent is not sector-aligned: " << extent.fe_length;
-            return false;
-        }
-        if (extent.fe_physical % LP_SECTOR_SIZE != 0) {
-            LOG(ERROR) << "Extent physical sector is not sector-aligned: " << extent.fe_physical;
-            return false;
-        }
-
-        uint64_t num_sectors =
-                std::min(static_cast<uint64_t>(extent.fe_length / LP_SECTOR_SIZE), sectors_needed);
-        if (!num_sectors || !sectors_needed) {
-            // This should never happen, but we include it just in case. It would
-            // indicate that the last filesystem block had multiple extents.
-            LOG(WARNING) << "FiemapWriter allocated extra blocks";
-            break;
-        }
-
-        uint64_t physical_sector = extent.fe_physical / LP_SECTOR_SIZE;
-        if (!builder->AddLinearExtent(partition, block_device, num_sectors, physical_sector)) {
-            LOG(ERROR) << "Could not add extent to lp metadata";
-            return false;
-        }
-
-        sectors_needed -= num_sectors;
-    }
-    return true;
-}
-
-bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name) {
-    if (!MetadataExists(metadata_dir)) {
-        return true;
-    }
-    auto metadata = OpenMetadata(metadata_dir);
-    if (!metadata) {
-        return false;
-    }
-
-    PartitionOpener opener;
-    auto builder = MetadataBuilder::New(*metadata.get(), &opener);
-    if (!builder) {
-        return false;
-    }
-    builder->RemovePartition(partition_name);
-    return SaveMetadata(builder.get(), metadata_dir);
-}
-
-bool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,
-                    SplitFiemap* file, uint64_t partition_size, bool readonly) {
-    auto builder = OpenOrCreateMetadata(metadata_dir, file);
-    if (!builder) {
-        return false;
-    }
-    auto partition = builder->FindPartition(partition_name);
-    if (!partition) {
-        int attrs = 0;
-        if (readonly) attrs |= LP_PARTITION_ATTR_READONLY;
-
-        if ((partition = builder->AddPartition(partition_name, attrs)) == nullptr) {
-            LOG(ERROR) << "Could not add partition " << partition_name << " to metadata";
-            return false;
-        }
-    }
-    partition->RemoveExtents();
-
-    if (!FillPartitionExtents(builder.get(), partition, file, partition_size)) {
-        return false;
-    }
-    return SaveMetadata(builder.get(), metadata_dir);
-}
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/metadata.h b/libfiemap/metadata.h
deleted file mode 100644
index f0ce23e..0000000
--- a/libfiemap/metadata.h
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// 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 <stdint.h>
-
-#include <memory>
-#include <string>
-
-#include <libfiemap/split_fiemap_writer.h>
-#include <liblp/liblp.h>
-
-namespace android {
-namespace fiemap {
-
-bool MetadataExists(const std::string& metadata_dir);
-std::unique_ptr<android::fs_mgr::LpMetadata> OpenMetadata(const std::string& metadata_dir);
-bool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,
-                    SplitFiemap* file, uint64_t partition_size, bool readonly);
-bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
-bool RemoveAllMetadata(const std::string& dir);
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/passthrough.cpp b/libfiemap/passthrough.cpp
deleted file mode 100644
index 1ccd9a0..0000000
--- a/libfiemap/passthrough.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-// 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 <libfiemap/image_manager.h>
-
-namespace android {
-namespace fiemap {
-
-std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir_prefix,
-                                                   const std::chrono::milliseconds& timeout_ms) {
-    (void)timeout_ms;
-    return ImageManager::Open(dir_prefix);
-}
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/split_fiemap_writer.cpp b/libfiemap/split_fiemap_writer.cpp
deleted file mode 100644
index cc54f20..0000000
--- a/libfiemap/split_fiemap_writer.cpp
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * 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 <libfiemap/split_fiemap_writer.h>
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap {
-
-using android::base::unique_fd;
-
-// We use a four-digit suffix at the end of filenames.
-static const size_t kMaxFilePieces = 500;
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
-                                                 uint64_t max_piece_size,
-                                                 ProgressCallback progress) {
-    if (!file_size) {
-        LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
-        return nullptr;
-    }
-
-    if (!max_piece_size) {
-        max_piece_size = DetermineMaximumFileSize(file_path);
-        if (!max_piece_size) {
-            LOG(ERROR) << "Could not determine maximum file size for " << file_path;
-            return nullptr;
-        }
-    }
-
-    // Remove any existing file.
-    RemoveSplitFiles(file_path);
-
-    // Call |progress| only when the total percentage would significantly change.
-    int permille = -1;
-    uint64_t total_bytes_written = 0;
-    auto on_progress = [&](uint64_t written, uint64_t) -> bool {
-        uint64_t actual_written = total_bytes_written + written;
-        int new_permille = (actual_written * 1000) / file_size;
-        if (new_permille != permille && actual_written < file_size) {
-            if (progress && !progress(actual_written, file_size)) {
-                return false;
-            }
-            permille = new_permille;
-        }
-        return true;
-    };
-
-    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
-    out->creating_ = true;
-    out->list_file_ = file_path;
-
-    // Create the split files.
-    uint64_t remaining_bytes = file_size;
-    while (remaining_bytes) {
-        if (out->files_.size() >= kMaxFilePieces) {
-            LOG(ERROR) << "Requested size " << file_size << " created too many split files";
-            return nullptr;
-        }
-        std::string chunk_path =
-                android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
-        uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
-        auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
-        if (!writer) {
-            return nullptr;
-        }
-
-        // To make sure the alignment doesn't create too much inconsistency, we
-        // account the *actual* size, not the requested size.
-        total_bytes_written += writer->size();
-
-        // writer->size() is block size aligned and could be bigger than remaining_bytes
-        // If remaining_bytes is bigger, set remaining_bytes to 0 to avoid underflow error.
-        remaining_bytes = remaining_bytes > writer->size() ? (remaining_bytes - writer->size()) : 0;
-
-        out->AddFile(std::move(writer));
-    }
-
-    // Create the split file list.
-    unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
-    if (fd < 0) {
-        PLOG(ERROR) << "Failed to open " << file_path;
-        return nullptr;
-    }
-
-    for (const auto& writer : out->files_) {
-        std::string line = android::base::Basename(writer->file_path()) + "\n";
-        if (!android::base::WriteFully(fd, line.data(), line.size())) {
-            PLOG(ERROR) << "Write failed " << file_path;
-            return nullptr;
-        }
-    }
-
-    // Unset this bit, so we don't unlink on destruction.
-    out->creating_ = false;
-    return out;
-}
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
-    std::vector<std::string> files;
-    if (!GetSplitFileList(file_path, &files)) {
-        return nullptr;
-    }
-
-    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
-    out->list_file_ = file_path;
-
-    for (const auto& file : files) {
-        auto writer = FiemapWriter::Open(file, 0, false);
-        if (!writer) {
-            // Error was logged in Open().
-            return nullptr;
-        }
-        out->AddFile(std::move(writer));
-    }
-    return out;
-}
-
-bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
-    // This is not the most efficient thing, but it is simple and recovering
-    // the fiemap/fibmap is much more expensive.
-    std::string contents;
-    if (!android::base::ReadFileToString(file_path, &contents, true)) {
-        PLOG(ERROR) << "Error reading file: " << file_path;
-        return false;
-    }
-
-    std::vector<std::string> names = android::base::Split(contents, "\n");
-    std::string dir = android::base::Dirname(file_path);
-    for (const auto& name : names) {
-        if (!name.empty()) {
-            list->emplace_back(dir + "/" + name);
-        }
-    }
-    return true;
-}
-
-bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
-    // Early exit if this does not exist, and do not report an error.
-    if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
-        return true;
-    }
-
-    bool ok = true;
-    std::vector<std::string> files;
-    if (GetSplitFileList(file_path, &files)) {
-        for (const auto& file : files) {
-            ok &= android::base::RemoveFileIfExists(file, message);
-        }
-    }
-    ok &= android::base::RemoveFileIfExists(file_path, message);
-    return ok;
-}
-
-bool SplitFiemap::HasPinnedExtents() const {
-    for (const auto& file : files_) {
-        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
-            return false;
-        }
-    }
-    return true;
-}
-
-const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
-    if (extents_.empty()) {
-        for (const auto& file : files_) {
-            const auto& extents = file->extents();
-            extents_.insert(extents_.end(), extents.begin(), extents.end());
-        }
-    }
-    return extents_;
-}
-
-bool SplitFiemap::Write(const void* data, uint64_t bytes) {
-    // Open the current file.
-    FiemapWriter* file = files_[cursor_index_].get();
-
-    const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
-    uint64_t bytes_remaining = bytes;
-    while (bytes_remaining) {
-        // How many bytes can we write into the current file?
-        uint64_t file_bytes_left = file->size() - cursor_file_pos_;
-        if (!file_bytes_left) {
-            if (cursor_index_ == files_.size() - 1) {
-                LOG(ERROR) << "write past end of file requested";
-                return false;
-            }
-
-            // No space left in the current file, but we have more files to
-            // use, so prep the next one.
-            cursor_fd_ = {};
-            cursor_file_pos_ = 0;
-            file = files_[++cursor_index_].get();
-            file_bytes_left = file->size();
-        }
-
-        // Open the current file if it's not open.
-        if (cursor_fd_ < 0) {
-            cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
-            if (cursor_fd_ < 0) {
-                PLOG(ERROR) << "open failed: " << file->file_path();
-                return false;
-            }
-            CHECK(cursor_file_pos_ == 0);
-        }
-
-        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
-            LOG(ERROR) << "file is no longer pinned: " << file->file_path();
-            return false;
-        }
-
-        uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
-        if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
-            PLOG(ERROR) << "write failed: " << file->file_path();
-            return false;
-        }
-        data_ptr += bytes_to_write;
-        bytes_remaining -= bytes_to_write;
-        cursor_file_pos_ += bytes_to_write;
-    }
-
-    // If we've reached the end of the current file, close it for sanity.
-    if (cursor_file_pos_ == file->size()) {
-        cursor_fd_ = {};
-    }
-    return true;
-}
-
-bool SplitFiemap::Flush() {
-    for (const auto& file : files_) {
-        unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
-        if (fd < 0) {
-            PLOG(ERROR) << "open failed: " << file->file_path();
-            return false;
-        }
-        if (fsync(fd)) {
-            PLOG(ERROR) << "fsync failed: " << file->file_path();
-            return false;
-        }
-    }
-    return true;
-}
-
-SplitFiemap::~SplitFiemap() {
-    if (!creating_) {
-        return;
-    }
-
-    // We failed to finish creating, so unlink everything.
-    unlink(list_file_.c_str());
-    for (auto&& file : files_) {
-        std::string path = file->file_path();
-        file = nullptr;
-
-        unlink(path.c_str());
-    }
-}
-
-void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
-    total_size_ += file->size();
-    files_.emplace_back(std::move(file));
-}
-
-uint32_t SplitFiemap::block_size() const {
-    return files_[0]->block_size();
-}
-
-const std::string& SplitFiemap::bdev_path() const {
-    return files_[0]->bdev_path();
-}
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/testdata/file_32k b/libfiemap/testdata/file_32k
deleted file mode 100644
index 12f3be4..0000000
--- a/libfiemap/testdata/file_32k
+++ /dev/null
Binary files differ
diff --git a/libfiemap/testdata/file_4k b/libfiemap/testdata/file_4k
deleted file mode 100644
index 08e7df1..0000000
--- a/libfiemap/testdata/file_4k
+++ /dev/null
Binary files differ
diff --git a/libfiemap/testdata/unaligned_file b/libfiemap/testdata/unaligned_file
deleted file mode 100644
index c107c26..0000000
--- a/libfiemap/testdata/unaligned_file
+++ /dev/null
Binary files differ
diff --git a/libfiemap/utility.cpp b/libfiemap/utility.cpp
deleted file mode 100644
index 1f6378f..0000000
--- a/libfiemap/utility.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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 "utility.h"
-
-#include <stdint.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <libfiemap/fiemap_writer.h>
-
-namespace android {
-namespace fiemap {
-
-static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
-
-uint64_t DetermineMaximumFileSize(const std::string& file_path) {
-    // Create the smallest file possible (one block).
-    auto writer = FiemapWriter::Open(file_path, 1);
-    if (!writer) {
-        return 0;
-    }
-
-    uint64_t result = 0;
-    switch (writer->fs_type()) {
-        case EXT4_SUPER_MAGIC:
-            // The minimum is 16GiB, so just report that. If we wanted we could parse the
-            // superblock and figure out if 64-bit support is enabled.
-            result = 17179869184ULL;
-            break;
-        case F2FS_SUPER_MAGIC:
-            // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
-            // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
-            result = 4329690886144ULL;
-            break;
-        case MSDOS_SUPER_MAGIC:
-            // 4GB-1, which we want aligned to the block size.
-            result = 4294967295;
-            result -= (result % writer->block_size());
-            break;
-        default:
-            LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
-            break;
-    }
-
-    // Close and delete the temporary file.
-    writer = nullptr;
-    unlink(file_path.c_str());
-
-    return result;
-}
-
-// Given a SplitFiemap, this returns a device path that will work during first-
-// stage init (i.e., its path can be found by InitRequiredDevices).
-std::string GetDevicePathForFile(SplitFiemap* file) {
-    auto bdev_path = file->bdev_path();
-
-    struct stat userdata, given;
-    if (!stat(bdev_path.c_str(), &given) && !stat(kUserdataDevice, &userdata)) {
-        if (S_ISBLK(given.st_mode) && S_ISBLK(userdata.st_mode) &&
-            given.st_rdev == userdata.st_rdev) {
-            return kUserdataDevice;
-        }
-    }
-    return bdev_path;
-}
-
-std::string JoinPaths(const std::string& dir, const std::string& file) {
-    if (android::base::EndsWith(dir, "/")) {
-        return dir + file;
-    }
-    return dir + "/" + file;
-}
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libfiemap/utility.h b/libfiemap/utility.h
deleted file mode 100644
index 19b632d..0000000
--- a/libfiemap/utility.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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>
-
-#include <string>
-
-#include <libfiemap/split_fiemap_writer.h>
-
-namespace android {
-namespace fiemap {
-
-// Given a file that will be created, determine the maximum size its containing
-// filesystem allows. Note this is a theoretical maximum size; free space is
-// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
-
-// Given a SplitFiemap, this returns a device path that will work during first-
-// stage init (i.e., its path can be found by InitRequiredDevices).
-std::string GetDevicePathForFile(android::fiemap::SplitFiemap* file);
-
-// Combine two path components into a single path.
-std::string JoinPaths(const std::string& dir, const std::string& file);
-
-}  // namespace fiemap
-}  // namespace android
diff --git a/libgsi.cpp b/libgsi.cpp
index 2c8deb0..b342cc5 100644
--- a/libgsi.cpp
+++ b/libgsi.cpp
@@ -23,6 +23,7 @@
 
 #include <android-base/file.h>
 #include <android-base/parseint.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
 #include "file_paths.h"
@@ -32,8 +33,14 @@
 namespace gsi {
 
 using namespace std::literals;
+using android::base::ReadFileToString;
+using android::base::Split;
 using android::base::unique_fd;
 
+bool GetActiveDsu(std::string* active_dsu) {
+    return android::base::ReadFileToString(kDsuActiveFile, active_dsu);
+}
+
 bool IsGsiRunning() {
     return !access(kGsiBootedIndicatorFile, F_OK);
 }
@@ -53,6 +60,10 @@
     return fsync(fd) == 0;
 }
 
+std::string GetDsuSlot(const std::string& install_dir) {
+    return android::base::Basename(install_dir);
+}
+
 bool CanBootIntoGsi(std::string* error) {
     // Always delete this as a safety precaution, so we can return to the
     // original system image. If we're confident GSI will boot, this will
diff --git a/partition_installer.cpp b/partition_installer.cpp
new file mode 100644
index 0000000..2ac313f
--- /dev/null
+++ b/partition_installer.cpp
@@ -0,0 +1,359 @@
+/*
+ * 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 "partition_installer.h"
+
+#include <sys/statvfs.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_dm_linear.h>
+#include <libdm/dm.h>
+#include <libgsi/libgsi.h>
+
+#include "file_paths.h"
+#include "gsi_service.h"
+#include "libgsi_private.h"
+
+namespace android {
+namespace gsi {
+
+using namespace std::literals;
+using namespace android::dm;
+using namespace android::fiemap;
+using namespace android::fs_mgr;
+using android::base::unique_fd;
+
+// The default size of userdata.img for GSI.
+// We are looking for /data to have atleast 40% free space
+static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
+
+PartitionInstaller::PartitionInstaller(GsiService* service, const std::string& install_dir,
+                                       const std::string& name, const std::string& active_dsu,
+                                       int64_t size, bool read_only)
+    : service_(service),
+      install_dir_(install_dir),
+      name_(name),
+      active_dsu_(active_dsu),
+      size_(size),
+      readOnly_(read_only) {
+    images_ = ImageManager::Open(MetadataDir(active_dsu), install_dir_);
+}
+
+PartitionInstaller::~PartitionInstaller() {
+    Finish();
+    if (!succeeded_) {
+        // Close open handles before we remove files.
+        system_device_ = nullptr;
+        PostInstallCleanup(images_.get());
+    }
+    if (IsAshmemMapped()) {
+        UnmapAshmem();
+    }
+}
+
+void PartitionInstaller::PostInstallCleanup() {
+    auto manager = ImageManager::Open(MetadataDir(active_dsu_), install_dir_);
+    if (!manager) {
+        LOG(ERROR) << "Could not open image manager";
+        return;
+    }
+    return PostInstallCleanup(manager.get());
+}
+
+void PartitionInstaller::PostInstallCleanup(ImageManager* manager) {
+    std::string file = GetBackingFile(name_);
+    if (manager->IsImageMapped(file)) {
+        LOG(ERROR) << "unmap " << file;
+        manager->UnmapImageDevice(file);
+    }
+    manager->DeleteBackingImage(file);
+}
+
+int PartitionInstaller::StartInstall() {
+    if (int status = PerformSanityChecks()) {
+        return status;
+    }
+    if (int status = Preallocate()) {
+        return status;
+    }
+    if (!readOnly_) {
+        if (!Format()) {
+            return IGsiService::INSTALL_ERROR_GENERIC;
+        }
+        succeeded_ = true;
+    } else {
+        // Map ${name}_gsi so we can write to it.
+        system_device_ = OpenPartition(GetBackingFile(name_));
+        if (!system_device_) {
+            return IGsiService::INSTALL_ERROR_GENERIC;
+        }
+
+        // Clear the progress indicator.
+        service_->UpdateProgress(IGsiService::STATUS_NO_OPERATION, 0);
+    }
+    return IGsiService::INSTALL_OK;
+}
+
+int PartitionInstaller::PerformSanityChecks() {
+    if (!images_) {
+        LOG(ERROR) << "unable to create image manager";
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+    if (size_ < 0) {
+        LOG(ERROR) << "image size " << size_ << " is negative";
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+    if (android::gsi::IsGsiRunning()) {
+        LOG(ERROR) << "cannot install gsi inside a live gsi";
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+
+    struct statvfs sb;
+    if (statvfs(install_dir_.c_str(), &sb)) {
+        PLOG(ERROR) << "failed to read file system stats";
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+
+    // This is the same as android::vold::GetFreebytes() but we also
+    // need the total file system size so we open code it here.
+    uint64_t free_space = 1ULL * sb.f_bavail * sb.f_frsize;
+    uint64_t fs_size = sb.f_blocks * sb.f_frsize;
+    if (free_space <= (size_)) {
+        LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
+        return IGsiService::INSTALL_ERROR_NO_SPACE;
+    }
+    // We are asking for 40% of the /data to be empty.
+    // TODO: may be not hard code it like this
+    double free_space_percent = ((1.0 * free_space) / fs_size) * 100;
+    if (free_space_percent < kMinimumFreeSpaceThreshold) {
+        LOG(ERROR) << "free space " << static_cast<uint64_t>(free_space_percent)
+                   << "% is below the minimum threshold of " << kMinimumFreeSpaceThreshold << "%";
+        return IGsiService::INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
+    }
+    return IGsiService::INSTALL_OK;
+}
+
+int PartitionInstaller::Preallocate() {
+    std::string file = GetBackingFile(name_);
+    if (!images_->UnmapImageIfExists(file)) {
+        LOG(ERROR) << "failed to UnmapImageIfExists " << file;
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+    // always delete the old one when it presents in case there might a partition
+    // with same name but different size.
+    if (images_->BackingImageExists(file)) {
+        if (!images_->DeleteBackingImage(file)) {
+            LOG(ERROR) << "failed to DeleteBackingImage " << file;
+            return IGsiService::INSTALL_ERROR_GENERIC;
+        }
+    }
+    service_->StartAsyncOperation("create " + name_, size_);
+    if (!CreateImage(file, size_)) {
+        LOG(ERROR) << "Could not create userdata image";
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+    service_->UpdateProgress(IGsiService::STATUS_COMPLETE, 0);
+    return IGsiService::INSTALL_OK;
+}
+
+bool PartitionInstaller::CreateImage(const std::string& name, uint64_t size) {
+    auto progress = [this](uint64_t bytes, uint64_t /* total */) -> bool {
+        service_->UpdateProgress(IGsiService::STATUS_WORKING, bytes);
+        if (service_->should_abort()) return false;
+        return true;
+    };
+    int flags = ImageManager::CREATE_IMAGE_DEFAULT;
+    if (readOnly_) {
+        flags |= ImageManager::CREATE_IMAGE_READONLY;
+    }
+    return images_->CreateBackingImage(name, size, flags, std::move(progress));
+}
+
+std::unique_ptr<MappedDevice> PartitionInstaller::OpenPartition(const std::string& name) {
+    return MappedDevice::Open(images_.get(), 10s, name);
+}
+
+bool PartitionInstaller::CommitGsiChunk(int stream_fd, int64_t bytes) {
+    service_->StartAsyncOperation("write " + name_, size_);
+
+    if (bytes < 0) {
+        LOG(ERROR) << "chunk size " << bytes << " is negative";
+        return false;
+    }
+
+    static const size_t kBlockSize = 4096;
+    auto buffer = std::make_unique<char[]>(kBlockSize);
+
+    int progress = -1;
+    uint64_t remaining = bytes;
+    while (remaining) {
+        size_t max_to_read = std::min(static_cast<uint64_t>(kBlockSize), remaining);
+        ssize_t rv = TEMP_FAILURE_RETRY(read(stream_fd, buffer.get(), max_to_read));
+        if (rv < 0) {
+            PLOG(ERROR) << "read gsi chunk";
+            return false;
+        }
+        if (rv == 0) {
+            LOG(ERROR) << "no bytes left in stream";
+            return false;
+        }
+        if (!CommitGsiChunk(buffer.get(), rv)) {
+            return false;
+        }
+        CHECK(static_cast<uint64_t>(rv) <= remaining);
+        remaining -= rv;
+
+        // Only update the progress when the % (or permille, in this case)
+        // significantly changes.
+        int new_progress = ((size_ - remaining) * 1000) / size_;
+        if (new_progress != progress) {
+            service_->UpdateProgress(IGsiService::STATUS_WORKING, size_ - remaining);
+        }
+    }
+
+    service_->UpdateProgress(IGsiService::STATUS_COMPLETE, size_);
+    return true;
+}
+
+bool PartitionInstaller::IsFinishedWriting() {
+    return gsi_bytes_written_ == size_;
+}
+
+bool PartitionInstaller::IsAshmemMapped() {
+    return ashmem_data_ != MAP_FAILED;
+}
+
+bool PartitionInstaller::CommitGsiChunk(const void* data, size_t bytes) {
+    if (static_cast<uint64_t>(bytes) > size_ - gsi_bytes_written_) {
+        // We cannot write past the end of the image file.
+        LOG(ERROR) << "chunk size " << bytes << " exceeds remaining image size (" << size_
+                   << " expected, " << gsi_bytes_written_ << " written)";
+        return false;
+    }
+    if (service_->should_abort()) {
+        return false;
+    }
+    if (!android::base::WriteFully(system_device_->fd(), data, bytes)) {
+        PLOG(ERROR) << "write failed";
+        return false;
+    }
+    gsi_bytes_written_ += bytes;
+    return true;
+}
+
+int PartitionInstaller::GetPartitionFd() {
+    return system_device_->fd();
+}
+
+bool PartitionInstaller::MapAshmem(int fd, size_t size) {
+    ashmem_size_ = size;
+    ashmem_data_ = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    return ashmem_data_ != MAP_FAILED;
+}
+
+void PartitionInstaller::UnmapAshmem() {
+    if (munmap(ashmem_data_, ashmem_size_) != 0) {
+        PLOG(ERROR) << "cannot munmap";
+        return;
+    }
+    ashmem_data_ = MAP_FAILED;
+    ashmem_size_ = -1;
+}
+
+bool PartitionInstaller::CommitGsiChunk(size_t bytes) {
+    if (!IsAshmemMapped()) {
+        PLOG(ERROR) << "ashmem is not mapped";
+        return false;
+    }
+    bool success = CommitGsiChunk(ashmem_data_, bytes);
+    if (success && IsFinishedWriting()) {
+        UnmapAshmem();
+    }
+    return success;
+}
+
+const std::string PartitionInstaller::GetBackingFile(std::string name) {
+    return name + "_gsi";
+}
+
+bool PartitionInstaller::Format() {
+    auto file = GetBackingFile(name_);
+    auto device = OpenPartition(file);
+    if (!device) {
+        return false;
+    }
+
+    // libcutils checks the first 4K, no matter the block size.
+    std::string zeroes(4096, 0);
+    if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
+        PLOG(ERROR) << "write " << file;
+        return false;
+    }
+    return true;
+}
+
+int PartitionInstaller::Finish() {
+    if (readOnly_ && gsi_bytes_written_ != size_) {
+        // We cannot boot if the image is incomplete.
+        LOG(ERROR) << "image incomplete; expected " << size_ << " bytes, waiting for "
+                   << (size_ - gsi_bytes_written_) << " bytes";
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+    if (system_device_ != nullptr && fsync(system_device_->fd())) {
+        PLOG(ERROR) << "fsync failed for " << name_ << "_gsi";
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+    system_device_ = {};
+
+    // If files moved (are no longer pinned), the metadata file will be invalid.
+    // This check can be removed once b/133967059 is fixed.
+    if (!images_->Validate()) {
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+
+    succeeded_ = true;
+    return IGsiService::INSTALL_OK;
+}
+
+int PartitionInstaller::WipeWritable(const std::string& active_dsu, const std::string& install_dir,
+                                     const std::string& name) {
+    auto image = ImageManager::Open(MetadataDir(active_dsu), install_dir);
+    // The device object has to be destroyed before the image object
+    auto device = MappedDevice::Open(image.get(), 10s, name);
+    if (!device) {
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+
+    // Wipe the first 1MiB of the device, ensuring both the first block and
+    // the superblock are destroyed.
+    static constexpr uint64_t kEraseSize = 1024 * 1024;
+
+    std::string zeroes(4096, 0);
+    uint64_t erase_size = std::min(kEraseSize, get_block_device_size(device->fd()));
+    for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
+        if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
+            PLOG(ERROR) << "write userdata_gsi";
+            return IGsiService::INSTALL_ERROR_GENERIC;
+        }
+    }
+    return IGsiService::INSTALL_OK;
+}
+
+}  // namespace gsi
+}  // namespace android
diff --git a/gsi_installer.h b/partition_installer.h
similarity index 68%
rename from gsi_installer.h
rename to partition_installer.h
index 33bebfb..1503648 100644
--- a/gsi_installer.h
+++ b/partition_installer.h
@@ -32,16 +32,15 @@
 
 class GsiService;
 
-class GsiInstaller final {
+class PartitionInstaller final {
     using ImageManager = android::fiemap::ImageManager;
     using MappedDevice = android::fiemap::MappedDevice;
 
   public:
     // Constructor for a new GSI installation.
-    GsiInstaller(GsiService* service, const GsiInstallParams& params);
-    // Constructor for re-enabling a previous GSI installation.
-    GsiInstaller(GsiService* service, const std::string& install_dir);
-    ~GsiInstaller();
+    PartitionInstaller(GsiService* service, const std::string& installDir, const std::string& name,
+                       const std::string& active_dsu, int64_t size, bool read_only);
+    ~PartitionInstaller();
 
     // Methods for a clean GSI install.
     int StartInstall();
@@ -49,30 +48,26 @@
     bool CommitGsiChunk(const void* data, size_t bytes);
     bool MapAshmem(int fd, size_t size);
     bool CommitGsiChunk(size_t bytes);
-    int SetGsiBootable(bool one_shot);
+    int GetPartitionFd();
 
-    // Methods for interacting with an existing install.
-    int ReenableGsi(bool one_shot);
-    int WipeUserdata();
+    static int WipeWritable(const std::string& active_dsu, const std::string& install_dir,
+                            const std::string& name);
 
     // Clean up install state if gsid crashed and restarted.
-    static void PostInstallCleanup();
-    static void PostInstallCleanup(ImageManager* manager);
+    void PostInstallCleanup();
+    void PostInstallCleanup(ImageManager* manager);
 
     const std::string& install_dir() const { return install_dir_; }
-    uint64_t userdata_size() const { return userdata_size_; }
 
   private:
+    int Finish();
     int PerformSanityChecks();
-    int PreallocateFiles();
-    int PreallocateUserdata();
-    int PreallocateSystem();
-    bool FormatUserdata();
-    bool CreateImage(const std::string& name, uint64_t size, bool readonly);
+    int Preallocate();
+    bool Format();
+    bool CreateImage(const std::string& name, uint64_t size);
     std::unique_ptr<MappedDevice> OpenPartition(const std::string& name);
     int CheckInstallState();
-    bool CreateInstallStatusFile();
-    bool SetBootMode(bool one_shot);
+    static const std::string GetBackingFile(std::string name);
     bool IsFinishedWriting();
     bool IsAshmemMapped();
     void UnmapAshmem();
@@ -80,11 +75,11 @@
     GsiService* service_;
 
     std::string install_dir_;
+    std::string name_;
+    std::string active_dsu_;
     std::unique_ptr<ImageManager> images_;
-    uint64_t gsi_size_ = 0;
-    uint64_t userdata_size_ = 0;
-    bool wipe_userdata_ = false;
-    bool wipe_userdata_on_failure_ = false;
+    uint64_t size_ = 0;
+    bool readOnly_;
     // Remaining data we're waiting to receive for the GSI image.
     uint64_t gsi_bytes_written_ = 0;
     bool succeeded_ = false;
diff --git a/tests/Android.bp b/tests/Android.bp
index e162dfb..6718eb9 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -42,9 +42,13 @@
 cc_test {
     name: "vts_gsi_boot_test",
     defaults: ["gsi_boot_defaults"],
-    test_suites: ["vts-core"],
+    test_suites: [
+        "general-tests",
+        "vts-core",
+    ],
     auto_gen_config: true,
     test_min_api_level: 29,
+    require_root: true,
 }
 
 java_test_host {
diff --git a/tests/DSUEndtoEndTest.java b/tests/DSUEndtoEndTest.java
index 175ca54..69d40ad 100644
--- a/tests/DSUEndtoEndTest.java
+++ b/tests/DSUEndtoEndTest.java
@@ -34,6 +34,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.lang.Process;
@@ -51,6 +52,7 @@
     private static final String UI_AUTOMATOR_INSTRUMENTATION_RUNNER =
         "androidx.test.uiautomator.UiAutomatorInstrumentationTestRunner";
     private static final String CLASS = "LockScreenAutomation";
+    private static final String LPUNPACK_PATH = "bin/lpunpack";
     private static final String SIMG2IMG_PATH = "bin/simg2img";
 
     // Example: atest -v DSUEndtoEndTest -- --test-arg \
@@ -62,6 +64,12 @@
             importance=Importance.ALWAYS)
     private String mSystemImagePath;
 
+    @Option(name="userdata_size",
+            shortName='u',
+            description="size in bytes of the new userdata partition",
+            importance=Importance.ALWAYS)
+    private long mUserdataSize = kDefaultUserdataSize;
+
     private File mUnsparseSystemImage;
 
     @After
@@ -77,11 +85,32 @@
         String simg2imgPath = "simg2img";
         if (mSystemImagePath == null) {
             IBuildInfo buildInfo = getBuild();
-            File system = ((IDeviceBuildInfo) buildInfo).getDeviceImageFile();
-            Assert.assertNotEquals("Failed to fetch system image. See system_image_path parameter", null, system);
-            mSystemImagePath = ZipUtil2.extractFileFromZip(new ZipFile(system), "system.img").getAbsolutePath();
+            File imgs = ((IDeviceBuildInfo) buildInfo).getDeviceImageFile();
+            Assert.assertNotEquals("Failed to fetch system image. See system_image_path parameter", null, imgs);
             File otaTools = buildInfo.getFile("otatools.zip");
             File tempdir = ZipUtil2.extractZipToTemp(otaTools, "otatools");
+            File system = ZipUtil2.extractFileFromZip(new ZipFile(imgs), "system.img");
+            if (system == null) {
+                File superImg = ZipUtil2.extractFileFromZip(new ZipFile(imgs), "super.img");
+                String lpunpackPath = new File(tempdir, LPUNPACK_PATH).getAbsolutePath();
+                String outputDir = superImg.getParentFile().getAbsolutePath();
+                String[] cmd = {lpunpackPath, "-p", "system_a", superImg.getAbsolutePath(), outputDir};
+                Process p = Runtime.getRuntime().exec(cmd);
+                p.waitFor();
+                if (p.exitValue() == 0) {
+                    mSystemImagePath = new File(outputDir, "system_a.img").getAbsolutePath();
+                } else {
+                    ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+                    int len;
+                    byte[] buf = new byte[1024];
+                    while ((len = p.getErrorStream().read(buf)) != -1) {
+                          stderr.write(buf, 0, len);
+                    }
+                    Assert.assertEquals("non-zero exit value (" + stderr.toString("UTF-8") + ")", 0, p.exitValue());
+                }
+            } else {
+                mSystemImagePath = system.getAbsolutePath();
+            }
             simg2imgPath = new File(tempdir, SIMG2IMG_PATH).getAbsolutePath();
         }
         File gsi = new File(mSystemImagePath);
@@ -109,7 +138,7 @@
 
         // Sleep after installing to allow time for gsi_tool to reboot. This prevents a race between
         // the device rebooting and waitForDeviceAvailable() returning.
-        getDevice().executeShellV2Command("gsi_tool install --userdata-size " + kDefaultUserdataSize +
+        getDevice().executeShellV2Command("gsi_tool install --userdata-size " + mUserdataSize +
             " --gsi-size " + gsi.length() + " && sleep 10000000", gsi, null, 10, TimeUnit.MINUTES, 1);
         getDevice().waitForDeviceAvailable();
         getDevice().enableAdbRoot();