DO NOT MERGE -  Mark RQ2A.210105.001 as merged.

Bug: 180401296
Merged-In: Ic37985f98e6cbfe4fa38b981d3332c4dfc40c5b8
Change-Id: Ic82b58f8975ae7b5410d87536342f83e827a7893
diff --git a/Android.bp b/Android.bp
index b033647..84809a3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "vold_default_flags",
 
@@ -42,9 +46,6 @@
         "libvold_binder",
     ],
     shared_libs: [
-        "android.hardware.keymaster@3.0",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@4.1",
         "android.hardware.boot@1.0",
         "libbase",
         "libbinder",
@@ -54,12 +55,11 @@
         "libdiskconfig",
         "libext4_utils",
         "libf2fs_sparseblock",
+        "libgsi",
         "libhardware",
         "libhardware_legacy",
         "libincfs",
         "libhidlbase",
-        "libkeymaster4support",
-        "libkeymaster4_1support",
         "libkeyutils",
         "liblog",
         "liblogwrap",
@@ -91,6 +91,9 @@
     whole_static_libs: [
         "libincremental_aidl-cpp",
     ],
+    export_shared_lib_headers: [
+        "libbinder",
+    ],
 }
 
 cc_library_headers {
@@ -110,7 +113,6 @@
     srcs: [
         "AppFuseUtil.cpp",
         "Benchmark.cpp",
-        "CheckEncryption.cpp",
         "Checkpoint.cpp",
         "CryptoType.cpp",
         "Devmapper.cpp",
@@ -151,11 +153,10 @@
     product_variables: {
         arc: {
             exclude_srcs: [
-                "model/ObbVolume.cpp",
+                "model/StubVolume.cpp",
             ],
             static_libs: [
-                "arc_services_aidl",
-                "libarcobbvolume",
+                "libarcvolume",
             ],
         },
         debuggable: {
@@ -164,6 +165,11 @@
     },
     shared_libs: [
         "android.hardware.health.storage@1.0",
+        "android.hardware.health.storage-V1-ndk_platform",
+        "android.system.keystore2-V1-ndk_platform",
+        "android.security.maintenance-ndk_platform",
+        "libbinder_ndk",
+        "libkeymint_support",
     ],
     whole_static_libs: [
         "com.android.sysprop.apex",
@@ -180,14 +186,6 @@
 
     srcs: ["main.cpp"],
     static_libs: ["libvold"],
-    product_variables: {
-        arc: {
-            static_libs: [
-                "arc_services_aidl",
-                "libarcobbvolume",
-            ],
-        },
-    },
     init_rc: [
         "vold.rc",
         "wait_for_keymaster.rc",
@@ -201,7 +199,23 @@
 
     shared_libs: [
         "android.hardware.health.storage@1.0",
+        "android.hardware.health.storage-V1-ndk_platform",
+        "android.system.keystore2-V1-ndk_platform",
+        "android.security.maintenance-ndk_platform",
+        "libbinder_ndk",
+        "libkeymint_support",
     ],
+
+    product_variables: {
+        arc: {
+            exclude_srcs: [
+                "model/StubVolume.cpp",
+            ],
+            static_libs: [
+                "libarcvolume",
+            ],
+        },
+	},
 }
 
 cc_binary {
@@ -232,15 +246,14 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libbinder_ndk",
 
-        "android.hardware.keymaster@3.0",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@4.1",
+        "android.system.keystore2-V1-ndk_platform",
+        "android.security.maintenance-ndk_platform",
         "libhardware",
         "libhardware_legacy",
         "libhidlbase",
-        "libkeymaster4support",
-        "libkeymaster4_1support",
+        "libkeymint_support",
         "libutils",
     ],
 }
diff --git a/Benchmark.cpp b/Benchmark.cpp
index 0770da7..e81cd61 100644
--- a/Benchmark.cpp
+++ b/Benchmark.cpp
@@ -181,7 +181,10 @@
 void Benchmark(const std::string& path,
                const android::sp<android::os::IVoldTaskListener>& listener) {
     std::lock_guard<std::mutex> lock(kBenchmarkLock);
-    android::wakelock::WakeLock wl{kWakeLock};
+    auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
+    if (!wl.has_value()) {
+        return;
+    }
 
     PerformanceBoost boost;
     android::os::PersistableBundle extras;
diff --git a/CheckEncryption.cpp b/CheckEncryption.cpp
deleted file mode 100644
index ffa3698..0000000
--- a/CheckEncryption.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2017 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 "CheckEncryption.h"
-#include "FileDeviceUtils.h"
-#include "Utils.h"
-#include "VolumeManager.h"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <cutils/iosched_policy.h>
-#include <private/android_filesystem_config.h>
-
-#include <sstream>
-
-#include <sys/resource.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include <assert.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-using android::base::unique_fd;
-
-using android::base::ReadFileToString;
-using android::base::WriteStringToFile;
-
-namespace android {
-namespace vold {
-
-constexpr uint32_t max_extents = 32;
-constexpr size_t bytecount = 8;
-constexpr int repeats = 256;
-
-bool check_file(const std::string& needle) {
-    LOG(DEBUG) << "checkEncryption check_file: " << needle;
-    auto haystack = android::vold::BlockDeviceForPath(needle);
-    if (haystack.empty()) {
-        LOG(ERROR) << "Failed to find device for path: " << needle;
-        return false;
-    }
-
-    std::string randombytes;
-    if (ReadRandomBytes(bytecount, randombytes) != 0) {
-        LOG(ERROR) << "Failed to read random bytes";
-        return false;
-    }
-    std::string randomhex;
-    StrToHex(randombytes, randomhex);
-    std::ostringstream os;
-    for (int i = 0; i < repeats; i++) os << randomhex;
-    auto towrite = os.str();
-
-    if (access(needle.c_str(), F_OK) == 0) {
-        if (unlink(needle.c_str()) != 0) {
-            PLOG(ERROR) << "Failed to unlink " << needle;
-            return false;
-        }
-    }
-    LOG(DEBUG) << "Writing to " << needle;
-    if (!WriteStringToFile(towrite, needle)) {
-        PLOG(ERROR) << "Failed to write " << needle;
-        return false;
-    }
-    sync();
-
-    unique_fd haystack_fd(open(haystack.c_str(), O_RDONLY | O_CLOEXEC));
-    if (haystack_fd.get() == -1) {
-        PLOG(ERROR) << "Failed to open " << haystack;
-        return false;
-    }
-
-    auto fiemap = PathFiemap(needle, max_extents);
-
-    std::string area;
-    for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++) {
-        auto xt = &(fiemap->fm_extents[i]);
-        LOG(DEBUG) << "Extent " << i << " at " << xt->fe_physical << " length " << xt->fe_length;
-        if (lseek64(haystack_fd.get(), xt->fe_physical, SEEK_SET) == -1) {
-            PLOG(ERROR) << "Failed lseek";
-            return false;
-        }
-        auto toread = xt->fe_length;
-        while (toread > 0) {
-            char buf[BUFSIZ];
-            size_t wlen =
-                static_cast<size_t>(std::min(static_cast<typeof(toread)>(sizeof(buf)), toread));
-            auto l = read(haystack_fd.get(), buf, wlen);
-            if (l < 1) {
-                PLOG(ERROR) << "Failed read";
-                if (errno != EINTR) {
-                    return false;
-                }
-            }
-            area.append(buf, l);
-            toread -= l;
-        }
-    }
-
-    LOG(DEBUG) << "Searching " << area.size() << " bytes of " << needle;
-    LOG(DEBUG) << "First position of blob: " << area.find(randomhex);
-    return true;
-}
-
-int CheckEncryption(const std::string& path) {
-    auto deNeedle(path);
-    deNeedle += "/misc";
-    if (android::vold::PrepareDir(deNeedle, 01771, AID_SYSTEM, AID_MISC)) {
-        return -1;
-    }
-    deNeedle += "/vold";
-    if (android::vold::PrepareDir(deNeedle, 0700, AID_ROOT, AID_ROOT)) {
-        return -1;
-    }
-    deNeedle += "/checkEncryption";
-
-    auto neNeedle(path);
-    neNeedle += "/unencrypted/checkEncryption";
-
-    check_file(deNeedle);
-    check_file(neNeedle);
-
-    return 0;
-}
-
-}  // namespace vold
-}  // namespace android
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 61035e5..755f0e3 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -47,7 +47,6 @@
 using android::base::SetProperty;
 using android::binder::Status;
 using android::fs_mgr::Fstab;
-using android::fs_mgr::ReadDefaultFstab;
 using android::fs_mgr::ReadFstabFromFile;
 using android::hardware::hidl_string;
 using android::hardware::boot::V1_0::BoolResult;
diff --git a/EncryptInplace.cpp b/EncryptInplace.cpp
index b1bd11d..057b3ef 100644
--- a/EncryptInplace.cpp
+++ b/EncryptInplace.cpp
@@ -20,618 +20,361 @@
 #include <ext4_utils/ext4_utils.h>
 #include <f2fs_sparseblock.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/types.h>
 #include <time.h>
 
 #include <algorithm>
+#include <vector>
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/unique_fd.h>
 
-// HORRIBLE HACK, FIXME
-#include "cryptfs.h"
-
-// FIXME horrible cut-and-paste code
-static inline int unix_read(int fd, void* buff, int len) {
-    return TEMP_FAILURE_RETRY(read(fd, buff, len));
-}
-
-static inline int unix_write(int fd, const void* buff, int len) {
-    return TEMP_FAILURE_RETRY(write(fd, buff, len));
-}
-
-#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE)
-
-/* aligned 32K writes tends to make flash happy.
- * SD card association recommends it.
- */
-#ifndef CONFIG_HW_DISK_ENCRYPTION
-#define BLOCKS_AT_A_TIME 8
-#else
-#define BLOCKS_AT_A_TIME 1024
-#endif
-
-struct encryptGroupsData {
-    int realfd;
-    int cryptofd;
-    off64_t numblocks;
-    off64_t one_pct, cur_pct, new_pct;
-    off64_t blocks_already_done, tot_numblocks;
-    off64_t used_blocks_already_done, tot_used_blocks;
-    const char* real_blkdev;
-    const char* crypto_blkdev;
-    int count;
-    off64_t offset;
-    char* buffer;
-    off64_t last_written_sector;
-    int completed;
-    time_t time_started;
-    int remaining_time;
-    bool set_progress_properties;
+enum EncryptInPlaceError {
+    kSuccess,
+    kFailed,
+    kFilesystemNotFound,
 };
 
-static void update_progress(struct encryptGroupsData* data, int is_used) {
-    data->blocks_already_done++;
+static uint64_t round_up(uint64_t val, size_t amount) {
+    if (val % amount) val += amount - (val % amount);
+    return val;
+}
 
-    if (is_used) {
-        data->used_blocks_already_done++;
-    }
-    if (data->tot_used_blocks) {
-        data->new_pct = data->used_blocks_already_done / data->one_pct;
+class InPlaceEncrypter {
+  public:
+    bool EncryptInPlace(const std::string& crypto_blkdev, const std::string& real_blkdev,
+                        uint64_t nr_sec, bool set_progress_properties);
+    bool ProcessUsedBlock(uint64_t block_num);
+
+  private:
+    // aligned 32K writes tends to make flash happy.
+    // SD card association recommends it.
+    static const size_t kIOBufferSize = 32768;
+
+    // Avoid spamming the logs.  Print the "Encrypting blocks" log message once
+    // every 10000 blocks (which is usually every 40 MB or so), and once at the end.
+    static const int kLogInterval = 10000;
+
+    std::string DescribeFilesystem();
+    void InitFs(const std::string& fs_type, uint64_t blocks_to_encrypt, uint64_t total_blocks,
+                unsigned int block_size);
+    void UpdateProgress(size_t blocks, bool done);
+    bool EncryptPendingData();
+    bool DoEncryptInPlace();
+
+    // ext4 methods
+    bool ReadExt4BlockBitmap(uint32_t group, uint8_t* buf);
+    uint64_t FirstBlockInGroup(uint32_t group);
+    uint32_t NumBlocksInGroup(uint32_t group);
+    uint32_t NumBaseMetaBlocksInGroup(uint64_t group);
+    EncryptInPlaceError EncryptInPlaceExt4();
+
+    // f2fs methods
+    EncryptInPlaceError EncryptInPlaceF2fs();
+
+    std::string real_blkdev_;
+    std::string crypto_blkdev_;
+    uint64_t nr_sec_;
+    bool set_progress_properties_;
+
+    android::base::unique_fd realfd_;
+    android::base::unique_fd cryptofd_;
+
+    time_t time_started_;
+    int remaining_time_;
+
+    std::string fs_type_;
+    uint64_t blocks_done_;
+    uint64_t blocks_to_encrypt_;
+    unsigned int block_size_;
+    unsigned int cur_pct_;
+
+    std::vector<uint8_t> io_buffer_;
+    uint64_t first_pending_block_;
+    size_t blocks_pending_;
+};
+
+std::string InPlaceEncrypter::DescribeFilesystem() {
+    if (fs_type_.empty())
+        return "full block device " + real_blkdev_;
+    else
+        return fs_type_ + " filesystem on " + real_blkdev_;
+}
+
+// Finishes initializing the encrypter, now that the filesystem details are known.
+void InPlaceEncrypter::InitFs(const std::string& fs_type, uint64_t blocks_to_encrypt,
+                              uint64_t total_blocks, unsigned int block_size) {
+    fs_type_ = fs_type;
+    blocks_done_ = 0;
+    blocks_to_encrypt_ = blocks_to_encrypt;
+    block_size_ = block_size;
+    cur_pct_ = 0;
+
+    // Allocate the I/O buffer.  kIOBufferSize should always be a multiple of
+    // the filesystem block size, but round it up just in case.
+    io_buffer_.resize(round_up(kIOBufferSize, block_size));
+    first_pending_block_ = 0;
+    blocks_pending_ = 0;
+
+    LOG(INFO) << "Encrypting " << DescribeFilesystem() << " in-place via " << crypto_blkdev_;
+    LOG(INFO) << blocks_to_encrypt << " blocks (" << (blocks_to_encrypt * block_size) / 1000000
+              << " MB) of " << total_blocks << " blocks are in-use";
+}
+
+void InPlaceEncrypter::UpdateProgress(size_t blocks, bool done) {
+    // A log message already got printed for blocks_done_ if one was due, so the
+    // next message will be due at the *next* block rounded up to kLogInterval.
+    uint64_t blocks_next_msg = round_up(blocks_done_ + 1, kLogInterval);
+
+    blocks_done_ += blocks;
+
+    // Ensure that a log message gets printed at the end, but not if one was
+    // already printed due to the block count being a multiple of kLogInterval.
+    // E.g. we want to show "50000 of 50327" and then "50327 of "50327", but not
+    // "50000 of 50000" and then redundantly "50000 of 50000" again.
+    if (done && blocks_done_ % kLogInterval != 0) blocks_next_msg = blocks_done_;
+
+    if (blocks_done_ >= blocks_next_msg)
+        LOG(DEBUG) << "Encrypted " << blocks_next_msg << " of " << blocks_to_encrypt_ << " blocks";
+
+    if (!set_progress_properties_) return;
+
+    uint64_t new_pct;
+    if (done) {
+        new_pct = 100;
     } else {
-        data->new_pct = data->blocks_already_done / data->one_pct;
+        new_pct = (blocks_done_ * 100) / std::max<uint64_t>(blocks_to_encrypt_, 1);
+        new_pct = std::min<uint64_t>(new_pct, 99);
+    }
+    if (new_pct > cur_pct_) {
+        cur_pct_ = new_pct;
+        android::base::SetProperty("vold.encrypt_progress", std::to_string(new_pct));
     }
 
-    if (!data->set_progress_properties) return;
-
-    if (data->new_pct > data->cur_pct) {
-        char buf[8];
-        data->cur_pct = data->new_pct;
-        snprintf(buf, sizeof(buf), "%" PRId64, data->cur_pct);
-        android::base::SetProperty("vold.encrypt_progress", buf);
-    }
-
-    if (data->cur_pct >= 5) {
+    if (cur_pct_ >= 5) {
         struct timespec time_now;
         if (clock_gettime(CLOCK_MONOTONIC, &time_now)) {
-            LOG(WARNING) << "Error getting time";
+            PLOG(WARNING) << "Error getting time while updating encryption progress";
         } else {
-            double elapsed_time = difftime(time_now.tv_sec, data->time_started);
-            off64_t remaining_blocks = data->tot_used_blocks - data->used_blocks_already_done;
-            int remaining_time =
-                (int)(elapsed_time * remaining_blocks / data->used_blocks_already_done);
+            double elapsed_time = difftime(time_now.tv_sec, time_started_);
+
+            uint64_t remaining_blocks = 0;
+            if (blocks_done_ < blocks_to_encrypt_)
+                remaining_blocks = blocks_to_encrypt_ - blocks_done_;
+
+            int remaining_time = 0;
+            if (blocks_done_ != 0)
+                remaining_time = (int)(elapsed_time * remaining_blocks / blocks_done_);
 
             // Change time only if not yet set, lower, or a lot higher for
             // best user experience
-            if (data->remaining_time == -1 || remaining_time < data->remaining_time ||
-                remaining_time > data->remaining_time + 60) {
-                char buf[8];
-                snprintf(buf, sizeof(buf), "%d", remaining_time);
-                android::base::SetProperty("vold.encrypt_time_remaining", buf);
-                data->remaining_time = remaining_time;
+            if (remaining_time_ == -1 || remaining_time < remaining_time_ ||
+                remaining_time > remaining_time_ + 60) {
+                remaining_time_ = remaining_time;
+                android::base::SetProperty("vold.encrypt_time_remaining",
+                                           std::to_string(remaining_time));
             }
         }
     }
 }
 
-static void log_progress(struct encryptGroupsData const* data, bool completed) {
-    // Precondition - if completed data = 0 else data != 0
+bool InPlaceEncrypter::EncryptPendingData() {
+    if (blocks_pending_ == 0) return true;
 
-    // Track progress so we can skip logging blocks
-    static off64_t offset = -1;
+    ssize_t bytes = blocks_pending_ * block_size_;
+    uint64_t offset = first_pending_block_ * block_size_;
 
-    // Need to close existing 'Encrypting from' log?
-    if (completed || (offset != -1 && data->offset != offset)) {
-        LOG(INFO) << "Encrypted to sector " << offset / info.block_size * CRYPT_SECTOR_SIZE;
-        offset = -1;
+    if (pread64(realfd_, &io_buffer_[0], bytes, offset) != bytes) {
+        PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev_ << " for inplace encrypt";
+        return false;
     }
 
-    // Need to start new 'Encrypting from' log?
-    if (!completed && offset != data->offset) {
-        LOG(INFO) << "Encrypting from sector " << data->offset / info.block_size * CRYPT_SECTOR_SIZE;
+    if (pwrite64(cryptofd_, &io_buffer_[0], bytes, offset) != bytes) {
+        PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev_ << " for inplace encrypt";
+        return false;
     }
 
-    // Update offset
-    if (!completed) {
-        offset = data->offset + (off64_t)data->count * info.block_size;
-    }
+    UpdateProgress(blocks_pending_, false);
+
+    blocks_pending_ = 0;
+    return true;
 }
 
-static int flush_outstanding_data(struct encryptGroupsData* data) {
-    if (data->count == 0) {
-        return 0;
+bool InPlaceEncrypter::ProcessUsedBlock(uint64_t block_num) {
+    // Flush if the amount of pending data has reached the I/O buffer size, if
+    // there's a gap between the pending blocks and the next block (due to
+    // block(s) not being used by the filesystem and thus not needing
+    // encryption), or if the next block will be aligned to the I/O buffer size.
+    if (blocks_pending_ * block_size_ == io_buffer_.size() ||
+        block_num != first_pending_block_ + blocks_pending_ ||
+        (block_num * block_size_) % io_buffer_.size() == 0) {
+        if (!EncryptPendingData()) return false;
+        first_pending_block_ = block_num;
+    }
+    blocks_pending_++;
+    return true;
+}
+
+// Reads the block bitmap for block group |group| into |buf|.
+bool InPlaceEncrypter::ReadExt4BlockBitmap(uint32_t group, uint8_t* buf) {
+    uint64_t offset = (uint64_t)aux_info.bg_desc[group].bg_block_bitmap * info.block_size;
+    if (pread64(realfd_, buf, info.block_size, offset) != (ssize_t)info.block_size) {
+        PLOG(ERROR) << "Failed to read block bitmap for block group " << group;
+        return false;
+    }
+    return true;
+}
+
+uint64_t InPlaceEncrypter::FirstBlockInGroup(uint32_t group) {
+    return aux_info.first_data_block + (group * (uint64_t)info.blocks_per_group);
+}
+
+uint32_t InPlaceEncrypter::NumBlocksInGroup(uint32_t group) {
+    uint64_t remaining = aux_info.len_blocks - FirstBlockInGroup(group);
+    return std::min<uint64_t>(info.blocks_per_group, remaining);
+}
+
+// In block groups with an uninitialized block bitmap, we only need to encrypt
+// the backup superblock and the block group descriptors (if they are present).
+uint32_t InPlaceEncrypter::NumBaseMetaBlocksInGroup(uint64_t group) {
+    if (!ext4_bg_has_super_block(group)) return 0;
+    return 1 + aux_info.bg_desc_blocks;
+}
+
+EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceExt4() {
+    if (setjmp(setjmp_env))  // NOLINT
+        return kFilesystemNotFound;
+
+    if (read_ext(realfd_, 0) != 0) return kFilesystemNotFound;
+
+    LOG(DEBUG) << "ext4 filesystem has " << aux_info.groups << " block groups";
+
+    uint64_t blocks_to_encrypt = 0;
+    for (uint32_t group = 0; group < aux_info.groups; group++) {
+        if (aux_info.bg_desc[group].bg_flags & EXT4_BG_BLOCK_UNINIT)
+            blocks_to_encrypt += NumBaseMetaBlocksInGroup(group);
+        else
+            blocks_to_encrypt +=
+                    (NumBlocksInGroup(group) - aux_info.bg_desc[group].bg_free_blocks_count);
     }
 
-    LOG(DEBUG) << "Copying " << data->count << " blocks at offset " << data->offset;
+    InitFs("ext4", blocks_to_encrypt, aux_info.len_blocks, info.block_size);
 
-    if (pread64(data->realfd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
-        LOG(ERROR) << "Error reading real_blkdev " << data->real_blkdev << " for inplace encrypt";
-        return -1;
+    // Encrypt each block group.
+    std::vector<uint8_t> block_bitmap(info.block_size);
+    for (uint32_t group = 0; group < aux_info.groups; group++) {
+        if (!ReadExt4BlockBitmap(group, &block_bitmap[0])) return kFailed;
+
+        uint64_t first_block_num = FirstBlockInGroup(group);
+        bool uninit = (aux_info.bg_desc[group].bg_flags & EXT4_BG_BLOCK_UNINIT);
+        uint32_t block_count = uninit ? NumBaseMetaBlocksInGroup(group) : NumBlocksInGroup(group);
+
+        // Encrypt each used block in the block group.
+        for (uint32_t i = 0; i < block_count; i++) {
+            if (uninit || bitmap_get_bit(&block_bitmap[0], i))
+                ProcessUsedBlock(first_block_num + i);
+        }
     }
+    return kSuccess;
+}
 
-    if (pwrite64(data->cryptofd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
-        LOG(ERROR) << "Error writing crypto_blkdev " << data->crypto_blkdev
-                   << " for inplace encrypt";
-        return -1;
-    } else {
-        log_progress(data, false);
-    }
-
-    data->count = 0;
-    data->last_written_sector =
-        (data->offset + data->count) / info.block_size * CRYPT_SECTOR_SIZE - 1;
+static int encrypt_f2fs_block(uint64_t block_num, void* _encrypter) {
+    InPlaceEncrypter* encrypter = reinterpret_cast<InPlaceEncrypter*>(_encrypter);
+    if (!encrypter->ProcessUsedBlock(block_num)) return -1;
     return 0;
 }
 
-static int encrypt_groups(struct encryptGroupsData* data) {
-    unsigned int i;
-    u8* block_bitmap = 0;
-    unsigned int block;
-    off64_t ret;
-    int rc = -1;
+EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceF2fs() {
+    std::unique_ptr<struct f2fs_info, void (*)(struct f2fs_info*)> fs_info(
+            generate_f2fs_info(realfd_), free_f2fs_info);
+    if (!fs_info) return kFilesystemNotFound;
 
-    data->buffer = (char*)malloc(info.block_size * BLOCKS_AT_A_TIME);
-    if (!data->buffer) {
-        LOG(ERROR) << "Failed to allocate crypto buffer";
-        goto errout;
-    }
-
-    block_bitmap = (u8*)malloc(info.block_size);
-    if (!block_bitmap) {
-        LOG(ERROR) << "failed to allocate block bitmap";
-        goto errout;
-    }
-
-    for (i = 0; i < aux_info.groups; ++i) {
-        LOG(INFO) << "Encrypting group " << i;
-
-        u32 first_block = aux_info.first_data_block + i * info.blocks_per_group;
-        u32 block_count = std::min(info.blocks_per_group, (u32)(aux_info.len_blocks - first_block));
-
-        off64_t offset = (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap;
-
-        ret = pread64(data->realfd, block_bitmap, info.block_size, offset);
-        if (ret != (int)info.block_size) {
-            LOG(ERROR) << "failed to read all of block group bitmap " << i;
-            goto errout;
-        }
-
-        offset = (u64)info.block_size * first_block;
-
-        data->count = 0;
-
-        for (block = 0; block < block_count; block++) {
-            int used;
-
-            if (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT) {
-                // In block groups with an uninitialized block bitmap, we only
-                // need to encrypt the backup superblock (if one is present).
-                used = (ext4_bg_has_super_block(i) && block < 1 + aux_info.bg_desc_blocks);
-            } else {
-                used = bitmap_get_bit(block_bitmap, block);
-            }
-
-            update_progress(data, used);
-            if (used) {
-                if (data->count == 0) {
-                    data->offset = offset;
-                }
-                data->count++;
-            } else {
-                if (flush_outstanding_data(data)) {
-                    goto errout;
-                }
-            }
-
-            offset += info.block_size;
-
-            /* Write data if we are aligned or buffer size reached */
-            if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0 ||
-                data->count == BLOCKS_AT_A_TIME) {
-                if (flush_outstanding_data(data)) {
-                    goto errout;
-                }
-            }
-        }
-        if (flush_outstanding_data(data)) {
-            goto errout;
-        }
-    }
-
-    data->completed = 1;
-    rc = 0;
-
-errout:
-    log_progress(0, true);
-    free(data->buffer);
-    free(block_bitmap);
-    return rc;
+    InitFs("f2fs", get_num_blocks_used(fs_info.get()), fs_info->total_blocks, fs_info->block_size);
+    if (run_on_used_blocks(0, fs_info.get(), encrypt_f2fs_block, this) != 0) return kFailed;
+    return kSuccess;
 }
 
-static int cryptfs_enable_inplace_ext4(const char* crypto_blkdev, const char* real_blkdev,
-                                       off64_t size, off64_t* size_already_done, off64_t tot_size,
-                                       off64_t previously_encrypted_upto,
-                                       bool set_progress_properties) {
-    u32 i;
-    struct encryptGroupsData data;
-    int rc;  // Can't initialize without causing warning -Wclobbered
-    int retries = RETRY_MOUNT_ATTEMPTS;
+bool InPlaceEncrypter::DoEncryptInPlace() {
+    EncryptInPlaceError rc;
+
+    rc = EncryptInPlaceExt4();
+    if (rc != kFilesystemNotFound) return rc == kSuccess;
+
+    rc = EncryptInPlaceF2fs();
+    if (rc != kFilesystemNotFound) return rc == kSuccess;
+
+    LOG(WARNING) << "No recognized filesystem found on " << real_blkdev_
+                 << ".  Falling back to encrypting the full block device.";
+    InitFs("", nr_sec_, nr_sec_, 512);
+    for (uint64_t i = 0; i < nr_sec_; i++) {
+        if (!ProcessUsedBlock(i)) return false;
+    }
+    return true;
+}
+
+bool InPlaceEncrypter::EncryptInPlace(const std::string& crypto_blkdev,
+                                      const std::string& real_blkdev, uint64_t nr_sec,
+                                      bool set_progress_properties) {
     struct timespec time_started = {0};
 
-    if (previously_encrypted_upto > *size_already_done) {
-        LOG(DEBUG) << "Not fast encrypting since resuming part way through";
-        return -1;
-    }
+    real_blkdev_ = real_blkdev;
+    crypto_blkdev_ = crypto_blkdev;
+    nr_sec_ = nr_sec;
+    set_progress_properties_ = set_progress_properties;
 
-    memset(&data, 0, sizeof(data));
-    data.real_blkdev = real_blkdev;
-    data.crypto_blkdev = crypto_blkdev;
-    data.set_progress_properties = set_progress_properties;
-
-    LOG(DEBUG) << "Opening" << real_blkdev;
-    if ((data.realfd = open(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
+    realfd_.reset(open64(real_blkdev.c_str(), O_RDONLY | O_CLOEXEC));
+    if (realfd_ < 0) {
         PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
-        rc = -1;
-        goto errout;
+        return false;
     }
 
-    LOG(DEBUG) << "Opening" << crypto_blkdev;
-    // Wait until the block device appears.  Re-use the mount retry values since it is reasonable.
-    while ((data.cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
-        if (--retries) {
-            PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
-                        << " for ext4 inplace encrypt, retrying";
-            sleep(RETRY_MOUNT_DELAY_SECONDS);
-        } else {
-            PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
-                        << " for ext4 inplace encrypt";
-            rc = ENABLE_INPLACE_ERR_DEV;
-            goto errout;
-        }
-    }
-
-    if (setjmp(setjmp_env)) {  // NOLINT
-        LOG(ERROR) << "Reading ext4 extent caused an exception";
-        rc = -1;
-        goto errout;
-    }
-
-    if (read_ext(data.realfd, 0) != 0) {
-        LOG(ERROR) << "Failed to read ext4 extent";
-        rc = -1;
-        goto errout;
-    }
-
-    data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
-    data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
-    data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
-
-    LOG(INFO) << "Encrypting ext4 filesystem in place...";
-
-    data.tot_used_blocks = data.numblocks;
-    for (i = 0; i < aux_info.groups; ++i) {
-        data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count;
-    }
-
-    data.one_pct = data.tot_used_blocks / 100;
-    data.cur_pct = 0;
-
-    if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
-        LOG(WARNING) << "Error getting time at start";
-        // Note - continue anyway - we'll run with 0
-    }
-    data.time_started = time_started.tv_sec;
-    data.remaining_time = -1;
-
-    rc = encrypt_groups(&data);
-    if (rc) {
-        LOG(ERROR) << "Error encrypting groups";
-        goto errout;
-    }
-
-    *size_already_done += data.completed ? size : data.last_written_sector;
-    rc = 0;
-
-errout:
-    close(data.realfd);
-    close(data.cryptofd);
-
-    return rc;
-}
-
-static void log_progress_f2fs(u64 block, bool completed) {
-    // Precondition - if completed data = 0 else data != 0
-
-    // Track progress so we can skip logging blocks
-    static u64 last_block = (u64)-1;
-
-    // Need to close existing 'Encrypting from' log?
-    if (completed || (last_block != (u64)-1 && block != last_block + 1)) {
-        LOG(INFO) << "Encrypted to block " << last_block;
-        last_block = -1;
-    }
-
-    // Need to start new 'Encrypting from' log?
-    if (!completed && (last_block == (u64)-1 || block != last_block + 1)) {
-        LOG(INFO) << "Encrypting from block " << block;
-    }
-
-    // Update offset
-    if (!completed) {
-        last_block = block;
-    }
-}
-
-static int encrypt_one_block_f2fs(u64 pos, void* data) {
-    struct encryptGroupsData* priv_dat = (struct encryptGroupsData*)data;
-
-    priv_dat->blocks_already_done = pos - 1;
-    update_progress(priv_dat, 1);
-
-    off64_t offset = pos * CRYPT_INPLACE_BUFSIZE;
-
-    if (pread64(priv_dat->realfd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
-        LOG(ERROR) << "Error reading real_blkdev " << priv_dat->crypto_blkdev
-                   << " for f2fs inplace encrypt";
-        return -1;
-    }
-
-    if (pwrite64(priv_dat->cryptofd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
-        LOG(ERROR) << "Error writing crypto_blkdev " << priv_dat->crypto_blkdev
-                   << " for f2fs inplace encrypt";
-        return -1;
-    } else {
-        log_progress_f2fs(pos, false);
-    }
-
-    return 0;
-}
-
-static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* real_blkdev,
-                                       off64_t size, off64_t* size_already_done, off64_t tot_size,
-                                       off64_t previously_encrypted_upto,
-                                       bool set_progress_properties) {
-    struct encryptGroupsData data;
-    struct f2fs_info* f2fs_info = NULL;
-    int rc = ENABLE_INPLACE_ERR_OTHER;
-    struct timespec time_started = {0};
-
-    if (previously_encrypted_upto > *size_already_done) {
-        LOG(DEBUG) << "Not fast encrypting since resuming part way through";
-        return ENABLE_INPLACE_ERR_OTHER;
-    }
-    memset(&data, 0, sizeof(data));
-    data.real_blkdev = real_blkdev;
-    data.crypto_blkdev = crypto_blkdev;
-    data.set_progress_properties = set_progress_properties;
-    data.realfd = -1;
-    data.cryptofd = -1;
-    if ((data.realfd = open64(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
-        PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for f2fs inplace encrypt";
-        goto errout;
-    }
-    if ((data.cryptofd = open64(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
-        PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
-                    << " for f2fs inplace encrypt";
-        rc = ENABLE_INPLACE_ERR_DEV;
-        goto errout;
-    }
-
-    f2fs_info = generate_f2fs_info(data.realfd);
-    if (!f2fs_info) goto errout;
-
-    data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
-    data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
-    data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
-
-    data.tot_used_blocks = get_num_blocks_used(f2fs_info);
-
-    data.one_pct = data.tot_used_blocks / 100;
-    data.cur_pct = 0;
-    if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
-        LOG(WARNING) << "Error getting time at start";
-        // Note - continue anyway - we'll run with 0
-    }
-    data.time_started = time_started.tv_sec;
-    data.remaining_time = -1;
-
-
-    data.buffer = (char*)malloc(f2fs_info->block_size);
-    if (!data.buffer) {
-        LOG(ERROR) << "Failed to allocate crypto buffer";
-        goto errout;
-    }
-
-    data.count = 0;
-
-    /* Currently, this either runs to completion, or hits a nonrecoverable error */
-    rc = run_on_used_blocks(data.blocks_already_done, f2fs_info, &encrypt_one_block_f2fs, &data);
-
-    if (rc) {
-        LOG(ERROR) << "Error in running over f2fs blocks";
-        rc = ENABLE_INPLACE_ERR_OTHER;
-        goto errout;
-    }
-
-    *size_already_done += size;
-    rc = 0;
-
-errout:
-    if (rc) LOG(ERROR) << "Failed to encrypt f2fs filesystem on " << real_blkdev;
-
-    log_progress_f2fs(0, true);
-    free(f2fs_info);
-    free(data.buffer);
-    close(data.realfd);
-    close(data.cryptofd);
-
-    return rc;
-}
-
-static int cryptfs_enable_inplace_full(const char* crypto_blkdev, const char* real_blkdev,
-                                       off64_t size, off64_t* size_already_done, off64_t tot_size,
-                                       off64_t previously_encrypted_upto,
-                                       bool set_progress_properties) {
-    int realfd, cryptofd;
-    char* buf[CRYPT_INPLACE_BUFSIZE];
-    int rc = ENABLE_INPLACE_ERR_OTHER;
-    off64_t numblocks, i, remainder;
-    off64_t one_pct, cur_pct, new_pct;
-    off64_t blocks_already_done, tot_numblocks;
-
-    if ((realfd = open(real_blkdev, O_RDONLY | O_CLOEXEC)) < 0) {
-        PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
-        return ENABLE_INPLACE_ERR_OTHER;
-    }
-
-    if ((cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+    cryptofd_.reset(open64(crypto_blkdev.c_str(), O_WRONLY | O_CLOEXEC));
+    if (cryptofd_ < 0) {
         PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
-        close(realfd);
-        return ENABLE_INPLACE_ERR_DEV;
+        return false;
     }
 
-    /* This is pretty much a simple loop of reading 4K, and writing 4K.
-     * The size passed in is the number of 512 byte sectors in the filesystem.
-     * So compute the number of whole 4K blocks we should read/write,
-     * and the remainder.
-     */
-    numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
-    remainder = size % CRYPT_SECTORS_PER_BUFSIZE;
-    tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
-    blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+    if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
+        PLOG(WARNING) << "Error getting time at start of in-place encryption";
+        // Note - continue anyway - we'll run with 0
+    }
+    time_started_ = time_started.tv_sec;
+    remaining_time_ = -1;
 
-    LOG(ERROR) << "Encrypting filesystem in place...";
+    bool success = DoEncryptInPlace();
 
-    i = previously_encrypted_upto + 1 - *size_already_done;
+    if (success) success &= EncryptPendingData();
 
-    if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
-        PLOG(ERROR) << "Cannot seek to previously encrypted point on " << real_blkdev;
-        goto errout;
+    if (success && fsync(cryptofd_) != 0) {
+        PLOG(ERROR) << "Error syncing " << crypto_blkdev_;
+        success = false;
     }
 
-    if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
-        PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev;
-        goto errout;
+    if (!success) {
+        LOG(ERROR) << "In-place encryption of " << DescribeFilesystem() << " failed";
+        return false;
     }
-
-    for (; i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) {
-        if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
-            PLOG(ERROR) << "Error reading initial sectors from real_blkdev " << real_blkdev
-                        << " for inplace encrypt";
-            goto errout;
-        }
-        if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
-            PLOG(ERROR) << "Error writing initial sectors to crypto_blkdev " << crypto_blkdev
-                        << " for inplace encrypt";
-            goto errout;
-        } else {
-            LOG(INFO) << "Encrypted 1 block at " << i;
-        }
+    if (blocks_done_ != blocks_to_encrypt_) {
+        LOG(WARNING) << "blocks_to_encrypt (" << blocks_to_encrypt_
+                     << ") was incorrect; we actually encrypted " << blocks_done_
+                     << " blocks.  Encryption progress was inaccurate";
     }
-
-    one_pct = tot_numblocks / 100;
-    cur_pct = 0;
-    /* process the majority of the filesystem in blocks */
-    for (i /= CRYPT_SECTORS_PER_BUFSIZE; i < numblocks; i++) {
-        new_pct = (i + blocks_already_done) / one_pct;
-        if (set_progress_properties && new_pct > cur_pct) {
-            char property_buf[8];
-
-            cur_pct = new_pct;
-            snprintf(property_buf, sizeof(property_buf), "%" PRId64, cur_pct);
-            android::base::SetProperty("vold.encrypt_progress", property_buf);
-        }
-        if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
-            PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev << " for inplace encrypt";
-            goto errout;
-        }
-        if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
-            PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
-            goto errout;
-        } else {
-            LOG(DEBUG) << "Encrypted " << CRYPT_SECTORS_PER_BUFSIZE << " block at "
-                       << i * CRYPT_SECTORS_PER_BUFSIZE;
-        }
-    }
-
-    /* Do any remaining sectors */
-    for (i = 0; i < remainder; i++) {
-        if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
-            LOG(ERROR) << "Error reading final sectors from real_blkdev " << real_blkdev
-                       << " for inplace encrypt";
-            goto errout;
-        }
-        if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
-            LOG(ERROR) << "Error writing final sectors to crypto_blkdev " << crypto_blkdev
-                       << " for inplace encrypt";
-            goto errout;
-        } else {
-            LOG(INFO) << "Encrypted 1 block at next location";
-        }
-    }
-
-    *size_already_done += size;
-    rc = 0;
-
-errout:
-    close(realfd);
-    close(cryptofd);
-
-    return rc;
+    // Make sure vold.encrypt_progress gets set to 100.
+    UpdateProgress(0, true);
+    LOG(INFO) << "Successfully encrypted " << DescribeFilesystem();
+    return true;
 }
 
-/* returns on of the ENABLE_INPLACE_* return codes */
-int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
-                           off64_t* size_already_done, off64_t tot_size,
-                           off64_t previously_encrypted_upto, bool set_progress_properties) {
-    int rc_ext4, rc_f2fs, rc_full;
-    LOG(DEBUG) << "cryptfs_enable_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << size
-               << ", " << size_already_done << ", " << tot_size << ", " << previously_encrypted_upto
-               << ", " << set_progress_properties << ")";
-    if (previously_encrypted_upto) {
-        LOG(DEBUG) << "Continuing encryption from " << previously_encrypted_upto;
-    }
+// Encrypts |real_blkdev| in-place by reading the data from |real_blkdev| and
+// writing it to |crypto_blkdev|, which should be a dm-crypt or dm-default-key
+// device backed by |real_blkdev|.  The size to encrypt is |nr_sec| 512-byte
+// sectors; however, if a filesystem is detected, then its size will be used
+// instead, and only the in-use blocks of the filesystem will be encrypted.
+bool encrypt_inplace(const std::string& crypto_blkdev, const std::string& real_blkdev,
+                     uint64_t nr_sec, bool set_progress_properties) {
+    LOG(DEBUG) << "encrypt_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << nr_sec
+               << ", " << (set_progress_properties ? "true" : "false") << ")";
 
-    if (*size_already_done + size < previously_encrypted_upto) {
-        LOG(DEBUG) << "cryptfs_enable_inplace already done";
-        *size_already_done += size;
-        return 0;
-    }
-
-    /* TODO: identify filesystem type.
-     * As is, cryptfs_enable_inplace_ext4 will fail on an f2fs partition, and
-     * then we will drop down to cryptfs_enable_inplace_f2fs.
-     * */
-    if ((rc_ext4 = cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev, size, size_already_done,
-                                               tot_size, previously_encrypted_upto,
-                                               set_progress_properties)) == 0) {
-        LOG(DEBUG) << "cryptfs_enable_inplace_ext4 success";
-        return 0;
-    }
-    LOG(DEBUG) << "cryptfs_enable_inplace_ext4()=" << rc_ext4;
-
-    if ((rc_f2fs = cryptfs_enable_inplace_f2fs(crypto_blkdev, real_blkdev, size, size_already_done,
-                                               tot_size, previously_encrypted_upto,
-                                               set_progress_properties)) == 0) {
-        LOG(DEBUG) << "cryptfs_enable_inplace_f2fs success";
-        return 0;
-    }
-    LOG(DEBUG) << "cryptfs_enable_inplace_f2fs()=" << rc_f2fs;
-
-    rc_full =
-        cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev, size, size_already_done, tot_size,
-                                    previously_encrypted_upto, set_progress_properties);
-    LOG(DEBUG) << "cryptfs_enable_inplace_full()=" << rc_full;
-
-    /* Hack for b/17898962, the following is the symptom... */
-    if (rc_ext4 == ENABLE_INPLACE_ERR_DEV && rc_f2fs == ENABLE_INPLACE_ERR_DEV &&
-        rc_full == ENABLE_INPLACE_ERR_DEV) {
-        LOG(DEBUG) << "ENABLE_INPLACE_ERR_DEV";
-        return ENABLE_INPLACE_ERR_DEV;
-    }
-    return rc_full;
+    InPlaceEncrypter encrypter;
+    return encrypter.EncryptInPlace(crypto_blkdev, real_blkdev, nr_sec, set_progress_properties);
 }
diff --git a/EncryptInplace.h b/EncryptInplace.h
index a2b46cf..480a47c 100644
--- a/EncryptInplace.h
+++ b/EncryptInplace.h
@@ -17,20 +17,10 @@
 #ifndef _ENCRYPT_INPLACE_H
 #define _ENCRYPT_INPLACE_H
 
-#include <sys/types.h>
+#include <stdint.h>
+#include <string>
 
-#define CRYPT_INPLACE_BUFSIZE 4096
-#define CRYPT_SECTOR_SIZE 512
-#define RETRY_MOUNT_ATTEMPTS 10
-#define RETRY_MOUNT_DELAY_SECONDS 1
-
-/* Return values for cryptfs_enable_inplace() */
-#define ENABLE_INPLACE_OK 0
-#define ENABLE_INPLACE_ERR_OTHER (-1)
-#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
-
-int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
-                           off64_t* size_already_done, off64_t tot_size,
-                           off64_t previously_encrypted_upto, bool set_progress_properties);
+bool encrypt_inplace(const std::string& crypto_blkdev, const std::string& real_blkdev,
+                     uint64_t nr_sec, bool set_progress_properties);
 
 #endif
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index 061ee7c..04def5c 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -67,12 +67,14 @@
 using android::base::StringPrintf;
 using android::fs_mgr::GetEntryForMountPoint;
 using android::vold::BuildDataPath;
+using android::vold::IsDotOrDotDot;
 using android::vold::IsFilesystemSupported;
 using android::vold::kEmptyAuthentication;
 using android::vold::KeyBuffer;
 using android::vold::KeyGeneration;
 using android::vold::retrieveKey;
 using android::vold::retrieveOrGenerateKey;
+using android::vold::SetDefaultAcl;
 using android::vold::SetQuotaInherit;
 using android::vold::SetQuotaProjectId;
 using android::vold::writeStringToFile;
@@ -140,6 +142,7 @@
             }
             break;
         }
+        if (IsDotOrDotDot(*entry)) continue;
         if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
             LOG(DEBUG) << "Skipping non-key " << entry->d_name;
             continue;
@@ -198,7 +201,7 @@
     auto const paths = get_ce_key_paths(directory_path);
     for (auto const ce_key_path : paths) {
         LOG(DEBUG) << "Trying user CE key " << ce_key_path;
-        if (retrieveKey(ce_key_path, auth, ce_key, false)) {
+        if (retrieveKey(ce_key_path, auth, ce_key)) {
             LOG(DEBUG) << "Successfully retrieved key";
             fixate_user_ce_key(directory_path, ce_key_path, paths);
             return true;
@@ -269,10 +272,9 @@
     // HEH as default was always a mistake. Use the libfscrypt default (CTS)
     // for devices launching on versions above Android 10.
     auto first_api_level = GetFirstApiLevel();
-    constexpr uint64_t pre_gki_level = 29;
     auto filenames_mode =
             android::base::GetProperty("ro.crypto.volume.filenames_mode",
-                                       first_api_level > pre_gki_level ? "" : "aes-256-heh");
+                                       first_api_level > __ANDROID_API_Q__ ? "" : "aes-256-heh");
     auto options_string = android::base::GetProperty("ro.crypto.volume.options",
                                                      contents_mode + ":" + filenames_mode);
     if (!ParseOptionsForApiLevel(first_api_level, options_string, options)) {
@@ -392,6 +394,7 @@
             }
             break;
         }
+        if (IsDotOrDotDot(*entry)) continue;
         if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
             LOG(DEBUG) << "Skipping non-de-key " << entry->d_name;
             continue;
@@ -399,7 +402,7 @@
         userid_t user_id = std::stoi(entry->d_name);
         auto key_path = de_dir + "/" + entry->d_name;
         KeyBuffer de_key;
-        if (!retrieveKey(key_path, kEmptyAuthentication, &de_key, false)) return false;
+        if (!retrieveKey(key_path, kEmptyAuthentication, &de_key)) return false;
         EncryptionPolicy de_policy;
         if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false;
         auto ret = s_de_policies.insert({user_id, de_policy});
@@ -433,7 +436,7 @@
 
     KeyBuffer device_key;
     if (!retrieveOrGenerateKey(device_key_path, device_key_temp, kEmptyAuthentication,
-                               makeGen(options), &device_key, false))
+                               makeGen(options), &device_key))
         return false;
 
     EncryptionPolicy device_policy;
@@ -460,7 +463,6 @@
         return false;
     LOG(INFO) << "Wrote per boot key reference to:" << per_boot_ref_filename;
 
-    if (!android::vold::FsyncDirectory(device_key_dir)) return false;
     return true;
 }
 
@@ -488,7 +490,7 @@
     // If this is a non-FBE device that recently left an emulated mode,
     // restore user data directories to known-good state.
     if (!fscrypt_is_native() && !fscrypt_is_emulated()) {
-        fscrypt_unlock_user_key(0, 0, "!", "!");
+        fscrypt_unlock_user_key(0, 0, "!");
     }
 
     // In some scenarios (e.g. userspace reboot) we might unmount userdata
@@ -623,14 +625,13 @@
 }
 
 static std::optional<android::vold::KeyAuthentication> authentication_from_hex(
-        const std::string& token_hex, const std::string& secret_hex) {
-    std::string token, secret;
-    if (!parse_hex(token_hex, &token)) return std::optional<android::vold::KeyAuthentication>();
+        const std::string& secret_hex) {
+    std::string secret;
     if (!parse_hex(secret_hex, &secret)) return std::optional<android::vold::KeyAuthentication>();
     if (secret.empty()) {
         return kEmptyAuthentication;
     } else {
-        return android::vold::KeyAuthentication(token, secret);
+        return android::vold::KeyAuthentication(secret);
     }
 }
 
@@ -650,24 +651,18 @@
         if (!android::vold::readSecdiscardable(secdiscardable_path, &secdiscardable_hash))
             return false;
     } else {
-        if (fs_mkdirs(secdiscardable_path.c_str(), 0700) != 0) {
-            PLOG(ERROR) << "Creating directories for: " << secdiscardable_path;
-            return false;
-        }
+        if (!android::vold::MkdirsSync(secdiscardable_path, 0700)) return false;
         if (!android::vold::createSecdiscardable(secdiscardable_path, &secdiscardable_hash))
             return false;
     }
     auto key_path = volkey_path(misc_path, volume_uuid);
-    if (fs_mkdirs(key_path.c_str(), 0700) != 0) {
-        PLOG(ERROR) << "Creating directories for: " << key_path;
-        return false;
-    }
-    android::vold::KeyAuthentication auth("", secdiscardable_hash);
+    if (!android::vold::MkdirsSync(key_path, 0700)) return false;
+    android::vold::KeyAuthentication auth(secdiscardable_hash);
 
     EncryptionOptions options;
     if (!get_volume_file_encryption_options(&options)) return false;
     KeyBuffer key;
-    if (!retrieveOrGenerateKey(key_path, key_path + "_tmp", auth, makeGen(options), &key, false))
+    if (!retrieveOrGenerateKey(key_path, key_path + "_tmp", auth, makeGen(options), &key))
         return false;
     if (!install_storage_key(BuildDataPath(volume_uuid), options, key, policy)) return false;
     return true;
@@ -686,12 +681,12 @@
     auto const directory_path = get_ce_key_directory_path(user_id);
     KeyBuffer ce_key;
     std::string ce_key_current_path = get_ce_key_current_path(directory_path);
-    if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key, false)) {
+    if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key)) {
         LOG(DEBUG) << "Successfully retrieved key";
         // TODO(147732812): Remove this once Locksettingservice is fixed.
         // Currently it calls fscrypt_clear_user_key_auth with a secret when lockscreen is
         // changed from swipe to none or vice-versa
-    } else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key, false)) {
+    } else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key)) {
         LOG(DEBUG) << "Successfully retrieved key with empty auth";
     } else {
         LOG(ERROR) << "Failed to retrieve key for user " << user_id;
@@ -702,26 +697,21 @@
     if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
     if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, store_auth, ce_key))
         return false;
-    if (!android::vold::FsyncDirectory(directory_path)) return false;
     return true;
 }
 
-bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
-                               const std::string& secret_hex) {
-    LOG(DEBUG) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial
-               << " token_present=" << (token_hex != "!");
+bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& secret_hex) {
+    LOG(DEBUG) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial;
     if (!fscrypt_is_native()) return true;
-    auto auth = authentication_from_hex(token_hex, secret_hex);
+    auto auth = authentication_from_hex(secret_hex);
     if (!auth) return false;
     return fscrypt_rewrap_user_key(user_id, serial, kEmptyAuthentication, *auth);
 }
 
-bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
-                                 const std::string& secret_hex) {
-    LOG(DEBUG) << "fscrypt_clear_user_key_auth " << user_id << " serial=" << serial
-               << " token_present=" << (token_hex != "!");
+bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& secret_hex) {
+    LOG(DEBUG) << "fscrypt_clear_user_key_auth " << user_id << " serial=" << serial;
     if (!fscrypt_is_native()) return true;
-    auto auth = authentication_from_hex(token_hex, secret_hex);
+    auto auth = authentication_from_hex(secret_hex);
     if (!auth) return false;
     return fscrypt_rewrap_user_key(user_id, serial, *auth, kEmptyAuthentication);
 }
@@ -740,17 +730,23 @@
     return true;
 }
 
+std::vector<int> fscrypt_get_unlocked_users() {
+    std::vector<int> user_ids;
+    for (const auto& it : s_ce_policies) {
+        user_ids.push_back(it.first);
+    }
+    return user_ids;
+}
+
 // TODO: rename to 'install' for consistency, and take flags to know which keys to install
-bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex,
-                             const std::string& secret_hex) {
-    LOG(DEBUG) << "fscrypt_unlock_user_key " << user_id << " serial=" << serial
-               << " token_present=" << (token_hex != "!");
+bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& secret_hex) {
+    LOG(DEBUG) << "fscrypt_unlock_user_key " << user_id << " serial=" << serial;
     if (fscrypt_is_native()) {
         if (s_ce_policies.count(user_id) != 0) {
             LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
             return true;
         }
-        auto auth = authentication_from_hex(token_hex, secret_hex);
+        auto auth = authentication_from_hex(secret_hex);
         if (!auth) return false;
         if (!read_and_install_user_ce_key(user_id, *auth)) {
             LOG(ERROR) << "Couldn't read key for " << user_id;
@@ -860,7 +856,15 @@
             if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
             if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false;
         }
-        if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
+        if (!prepare_dir(media_ce_path, 02770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
+        // On devices without sdcardfs (kernel 5.4+), the path permissions aren't fixed
+        // up automatically; therefore, use a default ACL, to ensure apps with MEDIA_RW
+        // can keep reading external storage; in particular, this allows app cloning
+        // scenarios to work correctly on such devices.
+        int ret = SetDefaultAcl(media_ce_path, 02770, AID_MEDIA_RW, AID_MEDIA_RW, {AID_MEDIA_RW});
+        if (ret != android::OK) {
+            return false;
+        }
 
         if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
 
@@ -969,6 +973,7 @@
             }
             break;
         }
+        if (IsDotOrDotDot(*entry)) continue;
         if (entry->d_type != DT_DIR || entry->d_name[0] == '.') {
             LOG(DEBUG) << "Skipping non-user " << entry->d_name;
             continue;
diff --git a/FsCrypt.h b/FsCrypt.h
index 641991a..2946be5 100644
--- a/FsCrypt.h
+++ b/FsCrypt.h
@@ -15,6 +15,7 @@
  */
 
 #include <string>
+#include <vector>
 
 #include <cutils/multiuser.h>
 
@@ -23,14 +24,12 @@
 bool fscrypt_init_user0();
 bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
 bool fscrypt_destroy_user_key(userid_t user_id);
-bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
-                               const std::string& secret);
-bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& token,
-                                 const std::string& secret);
+bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& secret);
+bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& secret);
 bool fscrypt_fixate_newest_user_key_auth(userid_t user_id);
 
-bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
-                             const std::string& secret);
+std::vector<int> fscrypt_get_unlocked_users();
+bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& secret);
 bool fscrypt_lock_user_key(userid_t user_id);
 
 bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
diff --git a/IdleMaint.cpp b/IdleMaint.cpp
index 2b5a8f1..8005cf4 100644
--- a/IdleMaint.cpp
+++ b/IdleMaint.cpp
@@ -17,16 +17,21 @@
 #include "IdleMaint.h"
 #include "FileDeviceUtils.h"
 #include "Utils.h"
+#include "VoldUtil.h"
 #include "VolumeManager.h"
 #include "model/PrivateVolume.h"
 
 #include <thread>
+#include <utility>
 
+#include <aidl/android/hardware/health/storage/BnGarbageCollectCallback.h>
+#include <aidl/android/hardware/health/storage/IStorage.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android/binder_manager.h>
 #include <android/hardware/health/storage/1.0/IStorage.h>
 #include <fs_mgr.h>
 #include <private/android_filesystem_config.h>
@@ -45,13 +50,16 @@
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::WriteStringToFile;
-using android::fs_mgr::Fstab;
-using android::fs_mgr::ReadDefaultFstab;
 using android::hardware::Return;
 using android::hardware::Void;
-using android::hardware::health::storage::V1_0::IStorage;
-using android::hardware::health::storage::V1_0::IGarbageCollectCallback;
-using android::hardware::health::storage::V1_0::Result;
+using AStorage = aidl::android::hardware::health::storage::IStorage;
+using ABnGarbageCollectCallback =
+        aidl::android::hardware::health::storage::BnGarbageCollectCallback;
+using AResult = aidl::android::hardware::health::storage::Result;
+using HStorage = android::hardware::health::storage::V1_0::IStorage;
+using HGarbageCollectCallback = android::hardware::health::storage::V1_0::IGarbageCollectCallback;
+using HResult = android::hardware::health::storage::V1_0::Result;
+using std::string_literals::operator""s;
 
 namespace android {
 namespace vold {
@@ -104,17 +112,18 @@
 }
 
 static void addFromFstab(std::list<std::string>* paths, PathTypes path_type) {
-    Fstab fstab;
-    ReadDefaultFstab(&fstab);
-
     std::string previous_mount_point;
-    for (const auto& entry : fstab) {
-        // Skip raw partitions.
-        if (entry.fs_type == "emmc" || entry.fs_type == "mtd") {
+    for (const auto& entry : fstab_default) {
+        // Skip raw partitions and swap space.
+        if (entry.fs_type == "emmc" || entry.fs_type == "mtd" || entry.fs_type == "swap") {
             continue;
         }
-        // Skip read-only filesystems
-        if (entry.flags & MS_RDONLY) {
+        // Skip read-only filesystems and bind mounts.
+        if (entry.flags & (MS_RDONLY | MS_BIND)) {
+            continue;
+        }
+        // Skip anything without an underlying block device, e.g. virtiofs.
+        if (entry.blk_device[0] != '/') {
             continue;
         }
         if (entry.fs_mgr_flags.vold_managed) {
@@ -145,7 +154,10 @@
 }
 
 void Trim(const android::sp<android::os::IVoldTaskListener>& listener) {
-    android::wakelock::WakeLock wl{kWakeLock};
+    auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
+    if (!wl.has_value()) {
+        return;
+    }
 
     // Collect both fstab and vold volumes
     std::list<std::string> paths;
@@ -253,11 +265,8 @@
 }
 
 static void runDevGcFstab(void) {
-    Fstab fstab;
-    ReadDefaultFstab(&fstab);
-
     std::string path;
-    for (const auto& entry : fstab) {
+    for (const auto& entry : fstab_default) {
         if (!entry.sysfs_path.empty()) {
             path = entry.sysfs_path;
             break;
@@ -303,26 +312,33 @@
     return;
 }
 
-class GcCallback : public IGarbageCollectCallback {
-  public:
-    Return<void> onFinish(Result result) override {
+enum class IDL { HIDL, AIDL };
+std::ostream& operator<<(std::ostream& os, IDL idl) {
+    return os << (idl == IDL::HIDL ? "HIDL" : "AIDL");
+}
+
+template <IDL idl, typename Result>
+class GcCallbackImpl {
+  protected:
+    void onFinishInternal(Result result) {
         std::unique_lock<std::mutex> lock(mMutex);
         mFinished = true;
         mResult = result;
         lock.unlock();
         mCv.notify_all();
-        return Void();
     }
+
+  public:
     void wait(uint64_t seconds) {
         std::unique_lock<std::mutex> lock(mMutex);
         mCv.wait_for(lock, std::chrono::seconds(seconds), [this] { return mFinished; });
 
         if (!mFinished) {
-            LOG(WARNING) << "Dev GC on HAL timeout";
+            LOG(WARNING) << "Dev GC on " << idl << " HAL timeout";
         } else if (mResult != Result::SUCCESS) {
-            LOG(WARNING) << "Dev GC on HAL failed with " << toString(mResult);
+            LOG(WARNING) << "Dev GC on " << idl << " HAL failed with " << toString(mResult);
         } else {
-            LOG(INFO) << "Dev GC on HAL successful";
+            LOG(INFO) << "Dev GC on " << idl << " HAL successful";
         }
     }
 
@@ -333,25 +349,57 @@
     Result mResult{Result::UNKNOWN_ERROR};
 };
 
-static void runDevGcOnHal(sp<IStorage> service) {
-    LOG(DEBUG) << "Start Dev GC on HAL";
-    sp<GcCallback> cb = new GcCallback();
+class AGcCallbackImpl : public ABnGarbageCollectCallback,
+                        public GcCallbackImpl<IDL::AIDL, AResult> {
+    ndk::ScopedAStatus onFinish(AResult result) override {
+        onFinishInternal(result);
+        return ndk::ScopedAStatus::ok();
+    }
+};
+
+class HGcCallbackImpl : public HGarbageCollectCallback, public GcCallbackImpl<IDL::HIDL, HResult> {
+    Return<void> onFinish(HResult result) override {
+        onFinishInternal(result);
+        return Void();
+    }
+};
+
+template <IDL idl, typename Service, typename GcCallbackImpl, typename GetDescription>
+static void runDevGcOnHal(Service service, GcCallbackImpl cb, GetDescription get_description) {
+    LOG(DEBUG) << "Start Dev GC on " << idl << " HAL";
     auto ret = service->garbageCollect(DEVGC_TIMEOUT_SEC, cb);
     if (!ret.isOk()) {
-        LOG(WARNING) << "Cannot start Dev GC on HAL: " << ret.description();
+        LOG(WARNING) << "Cannot start Dev GC on " << idl
+                     << " HAL: " << std::invoke(get_description, ret);
         return;
     }
     cb->wait(DEVGC_TIMEOUT_SEC);
 }
 
 static void runDevGc(void) {
-    auto service = IStorage::getService();
-    if (service != nullptr) {
-        runDevGcOnHal(service);
-    } else {
-        // fallback to legacy code path
-        runDevGcFstab();
+    auto aidl_service_name = AStorage::descriptor + "/default"s;
+    if (AServiceManager_isDeclared(aidl_service_name.c_str())) {
+        ndk::SpAIBinder binder(AServiceManager_waitForService(aidl_service_name.c_str()));
+        if (binder.get() != nullptr) {
+            std::shared_ptr<AStorage> aidl_service = AStorage::fromBinder(binder);
+            if (aidl_service != nullptr) {
+                runDevGcOnHal<IDL::AIDL>(aidl_service, ndk::SharedRefBase::make<AGcCallbackImpl>(),
+                                         &ndk::ScopedAStatus::getDescription);
+                return;
+            }
+        }
+        LOG(WARNING) << "Device declares " << aidl_service_name
+                     << " but it is not running, skip dev GC on AIDL HAL";
+        return;
     }
+    auto hidl_service = HStorage::getService();
+    if (hidl_service != nullptr) {
+        runDevGcOnHal<IDL::HIDL>(hidl_service, sp<HGcCallbackImpl>(new HGcCallbackImpl()),
+                                 &Return<void>::description);
+        return;
+    }
+    // fallback to legacy code path
+    runDevGcFstab();
 }
 
 int RunIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener) {
@@ -369,7 +417,10 @@
 
     LOG(DEBUG) << "idle maintenance started";
 
-    android::wakelock::WakeLock wl{kWakeLock};
+    auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
+    if (!wl.has_value()) {
+        return android::UNEXPECTED_NULL;
+    }
 
     std::list<std::string> paths;
     addFromFstab(&paths, PathTypes::kBlkDevice);
@@ -403,7 +454,10 @@
 }
 
 int AbortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener) {
-    android::wakelock::WakeLock wl{kWakeLock};
+    auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
+    if (!wl.has_value()) {
+        return android::UNEXPECTED_NULL;
+    }
 
     std::unique_lock<std::mutex> lk(cv_m);
     if (idle_maint_stat != IdleMaintStats::kStopped) {
diff --git a/KeyStorage.cpp b/KeyStorage.cpp
index 951536b..8d518de 100644
--- a/KeyStorage.cpp
+++ b/KeyStorage.cpp
@@ -21,6 +21,9 @@
 #include "ScryptParameters.h"
 #include "Utils.h"
 
+#include <algorithm>
+#include <memory>
+#include <mutex>
 #include <thread>
 #include <vector>
 
@@ -43,8 +46,6 @@
 #include <cutils/properties.h>
 
 #include <hardware/hw_auth_token.h>
-#include <keymasterV4_1/authorization_set.h>
-#include <keymasterV4_1/keymaster_utils.h>
 
 extern "C" {
 
@@ -54,7 +55,7 @@
 namespace android {
 namespace vold {
 
-const KeyAuthentication kEmptyAuthentication{"", ""};
+const KeyAuthentication kEmptyAuthentication{""};
 
 static constexpr size_t AES_KEY_BYTES = 32;
 static constexpr size_t GCM_NONCE_BYTES = 12;
@@ -81,6 +82,31 @@
 static const char* kFn_stretching = "stretching";
 static const char* kFn_version = "version";
 
+namespace {
+
+// Storage binding info for ensuring key encryption keys include a
+// platform-provided seed in their derivation.
+struct StorageBindingInfo {
+    enum class State {
+        UNINITIALIZED,
+        IN_USE,    // key storage keys are bound to seed
+        NOT_USED,  // key storage keys are NOT bound to seed
+    };
+
+    // Binding seed mixed into all key storage keys.
+    std::vector<uint8_t> seed;
+
+    // State tracker for the key storage key binding.
+    State state = State::UNINITIALIZED;
+
+    std::mutex guard;
+};
+
+// Never freed as the dtor is non-trivial.
+StorageBindingInfo& storage_binding_info = *new StorageBindingInfo;
+
+}  // namespace
+
 static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
     if (actual != expected) {
         LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
@@ -105,35 +131,31 @@
     SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
 }
 
-static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
-                                 const std::string& appId, std::string* key) {
-    auto paramBuilder = km::AuthorizationSetBuilder()
-                            .AesEncryptionKey(AES_KEY_BYTES * 8)
-                            .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
-                            .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
-    if (auth.token.empty()) {
-        LOG(DEBUG) << "Creating key that doesn't need auth token";
-        paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED);
-    } else {
-        LOG(DEBUG) << "Auth token required for key";
-        if (auth.token.size() != sizeof(hw_auth_token_t)) {
-            LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
-                       << auth.token.size() << " bytes";
-            return false;
-        }
-        const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
-        auto user_id = at->user_id;  // Make a copy because at->user_id is unaligned.
-        paramBuilder.Authorization(km::TAG_USER_SECURE_ID, user_id);
-        paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
-        paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
-    }
-
+// Generates a keymaster key, using rollback resistance if supported.
+static bool generateKeymasterKey(Keymaster& keymaster,
+                                 const km::AuthorizationSetBuilder& paramBuilder,
+                                 std::string* key) {
     auto paramsWithRollback = paramBuilder;
     paramsWithRollback.Authorization(km::TAG_ROLLBACK_RESISTANCE);
 
-    // Generate rollback-resistant key if possible.
-    return keymaster.generateKey(paramsWithRollback, key) ||
-           keymaster.generateKey(paramBuilder, key);
+    if (!keymaster.generateKey(paramsWithRollback, key)) {
+        LOG(WARNING) << "Failed to generate rollback-resistant key.  This is expected if keymaster "
+                        "doesn't support rollback resistance.  Falling back to "
+                        "non-rollback-resistant key.";
+        if (!keymaster.generateKey(paramBuilder, key)) return false;
+    }
+    return true;
+}
+
+static bool generateKeyStorageKey(Keymaster& keymaster, const std::string& appId,
+                                  std::string* key) {
+    auto paramBuilder = km::AuthorizationSetBuilder()
+                                .AesEncryptionKey(AES_KEY_BYTES * 8)
+                                .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
+                                .Authorization(km::TAG_APPLICATION_ID, appId)
+                                .Authorization(km::TAG_NO_AUTH_REQUIRED);
+    LOG(DEBUG) << "Generating \"key storage\" key that doesn't need auth token";
+    return generateKeymasterKey(keymaster, paramBuilder, key);
 }
 
 bool generateWrappedStorageKey(KeyBuffer* key) {
@@ -141,9 +163,8 @@
     if (!keymaster) return false;
     std::string key_temp;
     auto paramBuilder = km::AuthorizationSetBuilder().AesEncryptionKey(AES_KEY_BYTES * 8);
-    paramBuilder.Authorization(km::TAG_ROLLBACK_RESISTANCE);
     paramBuilder.Authorization(km::TAG_STORAGE_KEY);
-    if (!keymaster.generateKey(paramBuilder, &key_temp)) return false;
+    if (!generateKeymasterKey(keymaster, paramBuilder, &key_temp)) return false;
     *key = KeyBuffer(key_temp.size());
     memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
     return true;
@@ -160,17 +181,10 @@
     return true;
 }
 
-static std::pair<km::AuthorizationSet, km::HardwareAuthToken> beginParams(
-    const KeyAuthentication& auth, const std::string& appId) {
-    auto paramBuilder = km::AuthorizationSetBuilder()
-                            .GcmModeMacLen(GCM_MAC_BYTES * 8)
-                            .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
-    km::HardwareAuthToken authToken;
-    if (!auth.token.empty()) {
-        LOG(DEBUG) << "Supplying auth token to Keymaster";
-        authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token));
-    }
-    return {paramBuilder, authToken};
+static km::AuthorizationSet beginParams(const std::string& appId) {
+    return km::AuthorizationSetBuilder()
+            .GcmModeMacLen(GCM_MAC_BYTES * 8)
+            .Authorization(km::TAG_APPLICATION_ID, appId);
 }
 
 static bool readFileToString(const std::string& filename, std::string* result) {
@@ -205,84 +219,167 @@
     return true;
 }
 
-static void deferedKmDeleteKey(const std::string& kmkey) {
-    while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
-        LOG(ERROR) << "Wait for boot timed out";
+static std::mutex key_upgrade_lock;
+
+// List of key directories that have had their Keymaster key upgraded during
+// this boot and written to "keymaster_key_blob_upgraded", but replacing the old
+// key was delayed due to an active checkpoint.  Protected by key_upgrade_lock.
+static std::vector<std::string> key_dirs_to_commit;
+
+// Replaces |dir|/keymaster_key_blob with |dir|/keymaster_key_blob_upgraded and
+// deletes the old key from Keymaster.
+static bool CommitUpgradedKey(Keymaster& keymaster, const std::string& dir) {
+    auto blob_file = dir + "/" + kFn_keymaster_key_blob;
+    auto upgraded_blob_file = dir + "/" + kFn_keymaster_key_blob_upgraded;
+
+    std::string blob;
+    if (!readFileToString(blob_file, &blob)) return false;
+
+    if (rename(upgraded_blob_file.c_str(), blob_file.c_str()) != 0) {
+        PLOG(ERROR) << "Failed to rename " << upgraded_blob_file << " to " << blob_file;
+        return false;
     }
+    // Ensure that the rename is persisted before deleting the Keymaster key.
+    if (!FsyncDirectory(dir)) return false;
+
+    if (!keymaster || !keymaster.deleteKey(blob)) {
+        LOG(WARNING) << "Failed to delete old key " << blob_file
+                     << " from Keymaster; continuing anyway";
+        // Continue on, but the space in Keymaster used by the old key won't be freed.
+    }
+    return true;
+}
+
+static void DeferredCommitKeys() {
+    android::base::WaitForProperty("vold.checkpoint_committed", "1");
+    LOG(INFO) << "Committing upgraded keys";
     Keymaster keymaster;
-    if (!keymaster || !keymaster.deleteKey(kmkey)) {
-        LOG(ERROR) << "Defered Key deletion failed during upgrade";
+    if (!keymaster) {
+        LOG(ERROR) << "Failed to open Keymaster; old keys won't be deleted from Keymaster";
+        // Continue on, but the space in Keymaster used by the old keys won't be freed.
+    }
+    std::lock_guard<std::mutex> lock(key_upgrade_lock);
+    for (auto& dir : key_dirs_to_commit) {
+        LOG(INFO) << "Committing upgraded key " << dir;
+        CommitUpgradedKey(keymaster, dir);
+    }
+    key_dirs_to_commit.clear();
+}
+
+// Returns true if the Keymaster key in |dir| has already been upgraded and is
+// pending being committed.  Assumes that key_upgrade_lock is held.
+static bool IsKeyCommitPending(const std::string& dir) {
+    for (const auto& dir_to_commit : key_dirs_to_commit) {
+        if (IsSameFile(dir, dir_to_commit)) return true;
+    }
+    return false;
+}
+
+// Schedules the upgraded Keymaster key in |dir| to be committed later.
+// Assumes that key_upgrade_lock is held.
+static void ScheduleKeyCommit(const std::string& dir) {
+    if (key_dirs_to_commit.empty()) std::thread(DeferredCommitKeys).detach();
+    key_dirs_to_commit.push_back(dir);
+}
+
+static void CancelPendingKeyCommit(const std::string& dir) {
+    std::lock_guard<std::mutex> lock(key_upgrade_lock);
+    for (auto it = key_dirs_to_commit.begin(); it != key_dirs_to_commit.end(); it++) {
+        if (IsSameFile(*it, dir)) {
+            LOG(DEBUG) << "Cancelling pending commit of upgraded key " << dir
+                       << " because it is being destroyed";
+            key_dirs_to_commit.erase(it);
+            break;
+        }
     }
 }
 
-bool kmDeleteKey(Keymaster& keymaster, const std::string& kmKey) {
-    bool needs_cp = cp_needsCheckpoint();
-
-    if (needs_cp) {
-        std::thread(deferedKmDeleteKey, kmKey).detach();
-        LOG(INFO) << "Deferring Key deletion during upgrade";
-        return true;
-    } else {
-        return keymaster.deleteKey(kmKey);
+// Deletes a leftover upgraded key, if present.  An upgraded key can be left
+// over if an update failed, or if we rebooted before committing the key in a
+// freak accident.  Either way, we can re-upgrade the key if we need to.
+static void DeleteUpgradedKey(Keymaster& keymaster, const std::string& path) {
+    if (pathExists(path)) {
+        LOG(DEBUG) << "Deleting leftover upgraded key " << path;
+        std::string blob;
+        if (!android::base::ReadFileToString(path, &blob)) {
+            LOG(WARNING) << "Failed to read leftover upgraded key " << path
+                         << "; continuing anyway";
+        } else if (!keymaster.deleteKey(blob)) {
+            LOG(WARNING) << "Failed to delete leftover upgraded key " << path
+                         << " from Keymaster; continuing anyway";
+        }
+        if (unlink(path.c_str()) != 0) {
+            LOG(WARNING) << "Failed to unlink leftover upgraded key " << path
+                         << "; continuing anyway";
+        }
     }
 }
 
-static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
-                                km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
-                                const km::AuthorizationSet& opParams,
-                                const km::HardwareAuthToken& authToken,
-                                km::AuthorizationSet* outParams, bool keepOld) {
-    auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
-    std::string kmKey;
-    if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+// Begins a Keymaster operation using the key stored in |dir|.
+static KeymasterOperation BeginKeymasterOp(Keymaster& keymaster, const std::string& dir,
+                                           const km::AuthorizationSet& keyParams,
+                                           const km::AuthorizationSet& opParams,
+                                           km::AuthorizationSet* outParams) {
     km::AuthorizationSet inParams(keyParams);
     inParams.append(opParams.begin(), opParams.end());
-    for (;;) {
-        auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams);
-        if (opHandle) {
-            return opHandle;
-        }
-        if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
-        LOG(DEBUG) << "Upgrading key: " << dir;
-        std::string newKey;
-        if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
-        auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
-        if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
-        if (!keepOld) {
-            if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
-                PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
-                return KeymasterOperation();
-            }
-            if (!android::vold::FsyncDirectory(dir)) {
-                LOG(ERROR) << "Key dir sync failed: " << dir;
-                return KeymasterOperation();
-            }
-            if (!kmDeleteKey(keymaster, kmKey)) {
-                LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
-            }
-        }
-        kmKey = newKey;
-        LOG(INFO) << "Key upgraded: " << dir;
+
+    auto blob_file = dir + "/" + kFn_keymaster_key_blob;
+    auto upgraded_blob_file = dir + "/" + kFn_keymaster_key_blob_upgraded;
+
+    std::lock_guard<std::mutex> lock(key_upgrade_lock);
+
+    std::string blob;
+    bool already_upgraded = IsKeyCommitPending(dir);
+    if (already_upgraded) {
+        LOG(DEBUG)
+                << blob_file
+                << " was already upgraded and is waiting to be committed; using the upgraded blob";
+        if (!readFileToString(upgraded_blob_file, &blob)) return KeymasterOperation();
+    } else {
+        DeleteUpgradedKey(keymaster, upgraded_blob_file);
+        if (!readFileToString(blob_file, &blob)) return KeymasterOperation();
     }
+
+    auto opHandle = keymaster.begin(blob, inParams, outParams);
+    if (!opHandle) return opHandle;
+
+    // If key blob wasn't upgraded, nothing left to do.
+    if (!opHandle.getUpgradedBlob()) return opHandle;
+
+    if (already_upgraded) {
+        LOG(ERROR) << "Unexpected case; already-upgraded key " << upgraded_blob_file
+                   << " still requires upgrade";
+        return KeymasterOperation();
+    }
+    LOG(INFO) << "Upgrading key: " << blob_file;
+    if (!writeStringToFile(*opHandle.getUpgradedBlob(), upgraded_blob_file))
+        return KeymasterOperation();
+    if (cp_needsCheckpoint()) {
+        LOG(INFO) << "Wrote upgraded key to " << upgraded_blob_file
+                  << "; delaying commit due to checkpoint";
+        ScheduleKeyCommit(dir);
+    } else {
+        if (!CommitUpgradedKey(keymaster, dir)) return KeymasterOperation();
+        LOG(INFO) << "Key upgraded: " << blob_file;
+    }
+    return opHandle;
 }
 
 static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
                                     const km::AuthorizationSet& keyParams,
-                                    const km::HardwareAuthToken& authToken, const KeyBuffer& message,
-                                    std::string* ciphertext, bool keepOld) {
-    km::AuthorizationSet opParams;
+                                    const KeyBuffer& message, std::string* ciphertext) {
+    km::AuthorizationSet opParams =
+            km::AuthorizationSetBuilder().Authorization(km::TAG_PURPOSE, km::KeyPurpose::ENCRYPT);
     km::AuthorizationSet outParams;
-    auto opHandle = begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken,
-                          &outParams, keepOld);
+    auto opHandle = BeginKeymasterOp(keymaster, dir, keyParams, opParams, &outParams);
     if (!opHandle) return false;
     auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
-    if (!nonceBlob.isOk()) {
+    if (!nonceBlob) {
         LOG(ERROR) << "GCM encryption but no nonce generated";
         return false;
     }
     // nonceBlob here is just a pointer into existing data, must not be freed
-    std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]),
-                      nonceBlob.value().size());
+    std::string nonce(nonceBlob.value().get().begin(), nonceBlob.value().get().end());
     if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
     std::string body;
     if (!opHandle.updateCompletely(message, &body)) return false;
@@ -296,15 +393,13 @@
 
 static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
                                     const km::AuthorizationSet& keyParams,
-                                    const km::HardwareAuthToken& authToken,
-                                    const std::string& ciphertext, KeyBuffer* message,
-                                    bool keepOld) {
-    auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+                                    const std::string& ciphertext, KeyBuffer* message) {
+    const std::string nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
     auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
-    auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
-                                                                km::support::blob2hidlVec(nonce));
-    auto opHandle = begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken,
-                          nullptr, keepOld);
+    auto opParams = km::AuthorizationSetBuilder()
+                            .Authorization(km::TAG_NONCE, nonce)
+                            .Authorization(km::TAG_PURPOSE, km::KeyPurpose::DECRYPT);
+    auto opHandle = BeginKeymasterOp(keymaster, dir, keyParams, opParams, nullptr);
     if (!opHandle) return false;
     if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
     if (!opHandle.finish(nullptr)) return false;
@@ -312,22 +407,13 @@
 }
 
 static std::string getStretching(const KeyAuthentication& auth) {
-    if (!auth.usesKeymaster()) {
-        return kStretch_none;
-    } else if (auth.secret.empty()) {
+    if (auth.usesKeymaster()) {
         return kStretch_nopassword;
     } else {
-        char paramstr[PROPERTY_VALUE_MAX];
-
-        property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
-        return std::string() + kStretchPrefix_scrypt + paramstr;
+        return kStretch_none;
     }
 }
 
-static bool stretchingNeedsSalt(const std::string& stretching) {
-    return stretching != kStretch_nopassword && stretching != kStretch_none;
-}
-
 static bool stretchSecret(const std::string& stretching, const std::string& secret,
                           const std::string& salt, std::string* stretched) {
     if (stretching == kStretch_nopassword) {
@@ -338,22 +424,6 @@
         stretched->clear();
     } else if (stretching == kStretch_none) {
         *stretched = secret;
-    } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
-                          stretching.begin())) {
-        int Nf, rf, pf;
-        if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
-                                     &rf, &pf)) {
-            LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
-            return false;
-        }
-        stretched->assign(STRETCHED_BYTES, '\0');
-        if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
-                          reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), 1 << Nf,
-                          1 << rf, 1 << pf, reinterpret_cast<uint8_t*>(&(*stretched)[0]),
-                          stretched->size()) != 0) {
-            LOG(ERROR) << "scrypt failed with params: " << stretching;
-            return false;
-        }
     } else {
         LOG(ERROR) << "Unknown stretching type: " << stretching;
         return false;
@@ -367,6 +437,20 @@
     std::string stretched;
     if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
     *appId = secdiscardable_hash + stretched;
+
+    const std::lock_guard<std::mutex> scope_lock(storage_binding_info.guard);
+    switch (storage_binding_info.state) {
+        case StorageBindingInfo::State::UNINITIALIZED:
+            storage_binding_info.state = StorageBindingInfo::State::NOT_USED;
+            break;
+        case StorageBindingInfo::State::IN_USE:
+            appId->append(storage_binding_info.seed.begin(), storage_binding_info.seed.end());
+            break;
+        case StorageBindingInfo::State::NOT_USED:
+            // noop
+            break;
+    }
+
     return true;
 }
 
@@ -476,10 +560,6 @@
     return true;
 }
 
-bool pathExists(const std::string& path) {
-    return access(path.c_str(), F_OK) == 0;
-}
-
 bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
     if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
         PLOG(ERROR) << "key mkdir " << dir;
@@ -491,13 +571,6 @@
     std::string stretching = getStretching(auth);
     if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
     std::string salt;
-    if (stretchingNeedsSalt(stretching)) {
-        if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
-            LOG(ERROR) << "Random read failed";
-            return false;
-        }
-        if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
-    }
     std::string appId;
     if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
     std::string encryptedKey;
@@ -505,14 +578,10 @@
         Keymaster keymaster;
         if (!keymaster) return false;
         std::string kmKey;
-        if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
+        if (!generateKeyStorageKey(keymaster, appId, &kmKey)) return false;
         if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
-        km::AuthorizationSet keyParams;
-        km::HardwareAuthToken authToken;
-        std::tie(keyParams, authToken) = beginParams(auth, appId);
-        if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey,
-                                     false))
-            return false;
+        km::AuthorizationSet keyParams = beginParams(appId);
+        if (!encryptWithKeymasterKey(keymaster, dir, keyParams, key, &encryptedKey)) return false;
     } else {
         if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
     }
@@ -536,12 +605,12 @@
         PLOG(ERROR) << "Unable to move new key to location: " << key_path;
         return false;
     }
+    if (!FsyncParentDirectory(key_path)) return false;
     LOG(DEBUG) << "Created key: " << key_path;
     return true;
 }
 
-bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
-                 bool keepOld) {
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) {
     std::string version;
     if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
     if (version != kCurrentVersion) {
@@ -553,9 +622,6 @@
     std::string stretching;
     if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
     std::string salt;
-    if (stretchingNeedsSalt(stretching)) {
-        if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
-    }
     std::string appId;
     if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
     std::string encryptedMessage;
@@ -563,11 +629,8 @@
     if (auth.usesKeymaster()) {
         Keymaster keymaster;
         if (!keymaster) return false;
-        km::AuthorizationSet keyParams;
-        km::HardwareAuthToken authToken;
-        std::tie(keyParams, authToken) = beginParams(auth, appId);
-        if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key,
-                                     keepOld))
+        km::AuthorizationSet keyParams = beginParams(appId);
+        if (!decryptWithKeymasterKey(keymaster, dir, keyParams, encryptedMessage, key))
             return false;
     } else {
         if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
@@ -575,12 +638,13 @@
     return true;
 }
 
-static bool deleteKey(const std::string& dir) {
-    std::string kmKey;
-    if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+static bool DeleteKeymasterKey(const std::string& blob_file) {
+    std::string blob;
+    if (!readFileToString(blob_file, &blob)) return false;
     Keymaster keymaster;
     if (!keymaster) return false;
-    if (!keymaster.deleteKey(kmKey)) return false;
+    LOG(DEBUG) << "Deleting key " << blob_file << " from Keymaster";
+    if (!keymaster.deleteKey(blob)) return false;
     return true;
 }
 
@@ -602,19 +666,23 @@
 
 bool destroyKey(const std::string& dir) {
     bool success = true;
-    // Try each thing, even if previous things failed.
-    bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob);
-    if (uses_km) {
-        success &= deleteKey(dir);
-    }
+
+    CancelPendingKeyCommit(dir);
+
     auto secdiscard_cmd = std::vector<std::string>{
         kSecdiscardPath,
         "--",
         dir + "/" + kFn_encrypted_key,
         dir + "/" + kFn_secdiscardable,
     };
-    if (uses_km) {
-        secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob);
+    // Try each thing, even if previous things failed.
+
+    for (auto& fn : {kFn_keymaster_key_blob, kFn_keymaster_key_blob_upgraded}) {
+        auto blob_file = dir + "/" + fn;
+        if (pathExists(blob_file)) {
+            success &= DeleteKeymasterKey(blob_file);
+            secdiscard_cmd.push_back(blob_file);
+        }
     }
     if (ForkExecvp(secdiscard_cmd) != 0) {
         LOG(ERROR) << "secdiscard failed";
@@ -624,5 +692,22 @@
     return success;
 }
 
+bool setKeyStorageBindingSeed(const std::vector<uint8_t>& seed) {
+    const std::lock_guard<std::mutex> scope_lock(storage_binding_info.guard);
+    switch (storage_binding_info.state) {
+        case StorageBindingInfo::State::UNINITIALIZED:
+            storage_binding_info.state = StorageBindingInfo::State::IN_USE;
+            storage_binding_info.seed = seed;
+            return true;
+        case StorageBindingInfo::State::IN_USE:
+            LOG(ERROR) << "key storage binding seed already set";
+            return false;
+        case StorageBindingInfo::State::NOT_USED:
+            LOG(ERROR) << "key storage already in use without binding";
+            return false;
+    }
+    return false;
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/KeyStorage.h b/KeyStorage.h
index 5228f08..09d0aac 100644
--- a/KeyStorage.h
+++ b/KeyStorage.h
@@ -19,7 +19,9 @@
 
 #include "KeyBuffer.h"
 
+#include <cstdint>
 #include <string>
+#include <vector>
 
 namespace android {
 namespace vold {
@@ -31,19 +33,15 @@
 // If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
 class KeyAuthentication {
   public:
-    KeyAuthentication(const std::string& t, const std::string& s) : token{t}, secret{s} {};
+    KeyAuthentication(const std::string& s) : secret{s} {};
 
-    bool usesKeymaster() const { return !token.empty() || secret.empty(); };
+    bool usesKeymaster() const { return secret.empty(); };
 
-    const std::string token;
     const std::string secret;
 };
 
 extern const KeyAuthentication kEmptyAuthentication;
 
-// Checks if path "path" exists.
-bool pathExists(const std::string& path);
-
 bool createSecdiscardable(const std::string& path, std::string* hash);
 bool readSecdiscardable(const std::string& path, std::string* hash);
 
@@ -56,25 +54,13 @@
 // Create a directory at the named path, and store "key" in it as storeKey
 // This version creates the key in "tmp_path" then atomically renames "tmp_path"
 // to "key_path" thereby ensuring that the key is either stored entirely or
-// not at all.
+// not at all.  All the needed files and directories are also fsync'ed to ensure
+// that the key is actually persisted to disk.
 bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
                         const KeyAuthentication& auth, const KeyBuffer& key);
 
 // Retrieve the key from the named directory.
-//
-// If the key is wrapped by a Keymaster key that requires an upgrade, then that
-// Keymaster key is upgraded.  If |keepOld| is false, then the upgraded
-// Keymaster key replaces the original one.  As part of this, the original is
-// deleted from Keymaster; however, if a user data checkpoint is active, this
-// part is delayed until the checkpoint is committed.
-//
-// If instead |keepOld| is true, then the upgraded key doesn't actually replace
-// the original one.  This is needed *only* if |dir| isn't located in /data and
-// a user data checkpoint is active.  In this case the caller must handle
-// replacing the original key if the checkpoint is committed, and deleting the
-// upgraded key if the checkpoint is rolled back.
-bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
-                 bool keepOld);
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key);
 
 // Securely destroy the key stored in the named directory and delete the directory.
 bool destroyKey(const std::string& dir);
@@ -85,6 +71,9 @@
 bool generateWrappedStorageKey(KeyBuffer* key);
 // Export the per-boot boot wrapped storage key using keymaster.
 bool exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key);
+
+// Set a seed to be mixed into all key storage encryption keys.
+bool setKeyStorageBindingSeed(const std::vector<uint8_t>& seed);
 }  // namespace vold
 }  // namespace android
 
diff --git a/KeyUtil.cpp b/KeyUtil.cpp
index acc42db..886054e 100644
--- a/KeyUtil.cpp
+++ b/KeyUtil.cpp
@@ -30,7 +30,6 @@
 #include <android-base/properties.h>
 #include <keyutils.h>
 
-#include <fscrypt_uapi.h>
 #include "KeyStorage.h"
 #include "Utils.h"
 
@@ -58,46 +57,45 @@
             LOG(ERROR) << "Cannot generate a wrapped key " << gen.keysize << " bytes long";
             return false;
         }
+        LOG(DEBUG) << "Generating wrapped storage key";
         return generateWrappedStorageKey(key);
     } else {
+        LOG(DEBUG) << "Generating standard storage key";
         return randomKey(gen.keysize, key);
     }
 }
 
+static bool isFsKeyringSupportedImpl() {
+    android::base::unique_fd fd(open("/data", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+
+    // FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY if
+    // the ioctl isn't supported.  Otherwise it will fail with another error
+    // code such as EFAULT.
+    //
+    // Note that there's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY,
+    // since it's guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is.
+    // There's also no need to check for support on external volumes separately
+    // from /data, since either the kernel supports the ioctls on all
+    // fscrypt-capable filesystems or it doesn't.
+    errno = 0;
+    (void)ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, NULL);
+    if (errno == ENOTTY) {
+        LOG(INFO) << "Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY.  Falling back to "
+                     "session keyring";
+        return false;
+    }
+    if (errno != EFAULT) {
+        PLOG(WARNING) << "Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY";
+    }
+    LOG(DEBUG) << "Detected support for FS_IOC_ADD_ENCRYPTION_KEY";
+    android::base::SetProperty("ro.crypto.uses_fs_ioc_add_encryption_key", "true");
+    return true;
+}
+
 // Return true if the kernel supports the ioctls to add/remove fscrypt keys
 // directly to/from the filesystem.
 bool isFsKeyringSupported(void) {
-    static bool initialized = false;
-    static bool supported;
-
-    if (!initialized) {
-        android::base::unique_fd fd(open("/data", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
-
-        // FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY
-        // if the ioctl isn't supported.  Otherwise it will fail with another
-        // error code such as EFAULT.
-        errno = 0;
-        (void)ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, NULL);
-        if (errno == ENOTTY) {
-            LOG(INFO) << "Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY.  Falling back to "
-                         "session keyring";
-            supported = false;
-        } else {
-            if (errno != EFAULT) {
-                PLOG(WARNING) << "Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY";
-            }
-            LOG(DEBUG) << "Detected support for FS_IOC_ADD_ENCRYPTION_KEY";
-            supported = true;
-            android::base::SetProperty("ro.crypto.uses_fs_ioc_add_encryption_key", "true");
-        }
-        // There's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY, since it's
-        // guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is.  There's
-        // also no need to check for support on external volumes separately from
-        // /data, since either the kernel supports the ioctls on all
-        // fscrypt-capable filesystems or it doesn't.
-
-        initialized = true;
-    }
+    static bool supported = isFsKeyringSupportedImpl();
     return supported;
 }
 
@@ -245,7 +243,7 @@
 // https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html#fs-ioc-add-encryption-key
 static bool installFsKeyringKey(const std::string& mountpoint, const EncryptionOptions& options,
                                 fscrypt_add_key_arg* arg) {
-    if (options.use_hw_wrapped_key) arg->flags |= FSCRYPT_ADD_KEY_FLAG_WRAPPED;
+    if (options.use_hw_wrapped_key) arg->__flags |= __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED;
 
     android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
     if (fd == -1) {
@@ -396,10 +394,10 @@
 
 bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
                            const KeyAuthentication& key_authentication, const KeyGeneration& gen,
-                           KeyBuffer* key, bool keepOld) {
+                           KeyBuffer* key) {
     if (pathExists(key_path)) {
         LOG(DEBUG) << "Key exists, using: " << key_path;
-        if (!retrieveKey(key_path, key_authentication, key, keepOld)) return false;
+        if (!retrieveKey(key_path, key_authentication, key)) return false;
     } else {
         if (!gen.allow_gen) {
             LOG(ERROR) << "No key found in " << key_path;
diff --git a/KeyUtil.h b/KeyUtil.h
index 0f5bc93..73255a3 100644
--- a/KeyUtil.h
+++ b/KeyUtil.h
@@ -75,10 +75,10 @@
 bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy);
 
 // Retrieves the key from the named directory, or generates it if it doesn't
-// exist.  In most cases |keepOld| must be false; see retrieveKey() for details.
+// exist.
 bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
                            const KeyAuthentication& key_authentication, const KeyGeneration& gen,
-                           KeyBuffer* key, bool keepOld);
+                           KeyBuffer* key);
 
 // Re-installs a file-based encryption key of fscrypt-provisioning type from the
 // global session keyring back into fs keyring of the mountpoint.
diff --git a/Keymaster.cpp b/Keymaster.cpp
index 786cdb5..1d69dde 100644
--- a/Keymaster.cpp
+++ b/Keymaster.cpp
@@ -17,368 +17,218 @@
 #include "Keymaster.h"
 
 #include <android-base/logging.h>
-#include <keymasterV4_1/authorization_set.h>
-#include <keymasterV4_1/keymaster_utils.h>
+
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+#include <aidl/android/security/maintenance/IKeystoreMaintenance.h>
+#include <aidl/android/system/keystore2/Domain.h>
+#include <aidl/android/system/keystore2/EphemeralStorageKeyResponse.h>
+#include <aidl/android/system/keystore2/KeyDescriptor.h>
+
+// Keep these in sync with system/security/keystore2/src/keystore2_main.rs
+static constexpr const char keystore2_service_name[] =
+        "android.system.keystore2.IKeystoreService/default";
+static constexpr const char maintenance_service_name[] = "android.security.maintenance";
+
+/*
+ * Keep this in sync with the description for update() in
+ * system/hardware/interfaces/keystore2/aidl/android/system/keystore2/IKeystoreOperation.aidl
+ */
+static constexpr const size_t UPDATE_INPUT_MAX_SIZE = 32 * 1024;  // 32 KiB
+
+// Keep this in sync with system/sepolicy/private/keystore2_key_contexts
+static constexpr const int VOLD_NAMESPACE = 100;
 
 namespace android {
 namespace vold {
 
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::keymaster::V4_0::SecurityLevel;
+namespace ks2_maint = ::aidl::android::security::maintenance;
 
 KeymasterOperation::~KeymasterOperation() {
-    if (mDevice) mDevice->abort(mOpHandle);
+    if (ks2Operation) ks2Operation->abort();
+}
+
+static void zeroize_vector(std::vector<uint8_t>& vec) {
+    memset_s(vec.data(), 0, vec.size());
+}
+
+static bool logKeystore2ExceptionIfPresent(::ndk::ScopedAStatus& rc, const std::string& func_name) {
+    if (rc.isOk()) return false;
+
+    auto exception_code = rc.getExceptionCode();
+    if (exception_code == EX_SERVICE_SPECIFIC) {
+        LOG(ERROR) << "keystore2 Keystore " << func_name
+                   << " returned service specific error: " << rc.getServiceSpecificError();
+    } else {
+        LOG(ERROR) << "keystore2 Communication with Keystore " << func_name
+                   << " failed error: " << exception_code;
+    }
+    return true;
 }
 
 bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen,
                                           const std::function<void(const char*, size_t)> consumer) {
-    uint32_t inputConsumed = 0;
+    if (!ks2Operation) return false;
 
-    km::ErrorCode km_error;
-    auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta,
-                      const hidl_vec<km::KeyParameter>& /*ignored*/,
-                      const hidl_vec<uint8_t>& _output) {
-        km_error = ret;
-        if (km_error != km::ErrorCode::OK) return;
-        inputConsumed += inputConsumedDelta;
-        consumer(reinterpret_cast<const char*>(&_output[0]), _output.size());
-    };
+    while (inputLen != 0) {
+        size_t currLen = std::min(inputLen, UPDATE_INPUT_MAX_SIZE);
+        std::vector<uint8_t> input_vec(input, input + currLen);
+        inputLen -= currLen;
+        input += currLen;
 
-    while (inputConsumed != inputLen) {
-        size_t toRead = static_cast<size_t>(inputLen - inputConsumed);
-        auto inputBlob = km::support::blob2hidlVec(
-            reinterpret_cast<const uint8_t*>(&input[inputConsumed]), toRead);
-        auto error = mDevice->update(mOpHandle, hidl_vec<km::KeyParameter>(), inputBlob,
-                                     km::HardwareAuthToken(), km::VerificationToken(), hidlCB);
-        if (!error.isOk()) {
-            LOG(ERROR) << "update failed: " << error.description();
-            mDevice = nullptr;
+        std::optional<std::vector<uint8_t>> output;
+        auto rc = ks2Operation->update(input_vec, &output);
+        zeroize_vector(input_vec);
+        if (logKeystore2ExceptionIfPresent(rc, "update")) {
+            ks2Operation = nullptr;
             return false;
         }
-        if (km_error != km::ErrorCode::OK) {
-            LOG(ERROR) << "update failed, code " << int32_t(km_error);
-            mDevice = nullptr;
-            return false;
-        }
-        if (inputConsumed > inputLen) {
-            LOG(ERROR) << "update reported too much input consumed";
-            mDevice = nullptr;
-            return false;
-        }
+        if (output) consumer((const char*)output->data(), output->size());
     }
     return true;
 }
 
 bool KeymasterOperation::finish(std::string* output) {
-    km::ErrorCode km_error;
-    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& /*ignored*/,
-                      const hidl_vec<uint8_t>& _output) {
-        km_error = ret;
-        if (km_error != km::ErrorCode::OK) return;
-        if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
-    };
-    auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
-                                 hidl_vec<uint8_t>(), km::HardwareAuthToken(),
-                                 km::VerificationToken(), hidlCb);
-    mDevice = nullptr;
-    if (!error.isOk()) {
-        LOG(ERROR) << "finish failed: " << error.description();
+    std::optional<std::vector<uint8_t>> out_vec;
+
+    if (!ks2Operation) return false;
+
+    auto rc = ks2Operation->finish(std::nullopt, std::nullopt, &out_vec);
+    if (logKeystore2ExceptionIfPresent(rc, "finish")) {
+        ks2Operation = nullptr;
         return false;
     }
-    if (km_error != km::ErrorCode::OK) {
-        LOG(ERROR) << "finish failed, code " << int32_t(km_error);
-        return false;
-    }
+
+    if (output) *output = std::string(out_vec->begin(), out_vec->end());
+
     return true;
 }
 
-/* static */ bool Keymaster::hmacKeyGenerated = false;
-
 Keymaster::Keymaster() {
-    auto devices = KmDevice::enumerateAvailableDevices();
-    if (!hmacKeyGenerated) {
-        KmDevice::performHmacKeyAgreement(devices);
-        hmacKeyGenerated = true;
+    ::ndk::SpAIBinder binder(AServiceManager_getService(keystore2_service_name));
+    auto keystore2Service = ks2::IKeystoreService::fromBinder(binder);
+
+    if (!keystore2Service) {
+        LOG(ERROR) << "Vold unable to connect to keystore2.";
+        return;
     }
-    for (auto& dev : devices) {
-        // Do not use StrongBox for device encryption / credential encryption.  If a security chip
-        // is present it will have Weaver, which already strengthens CE.  We get no additional
-        // benefit from using StrongBox here, so skip it.
-        if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) {
-            mDevice = std::move(dev);
-            break;
-        }
-    }
-    if (!mDevice) return;
-    auto& version = mDevice->halVersion();
-    LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName
-              << " for encryption.  Security level: " << toString(version.securityLevel)
-              << ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName();
+
+    /*
+     * There are only two options available to vold for the SecurityLevel: TRUSTED_ENVIRONMENT (TEE)
+     * and STRONGBOX. We don't use STRONGBOX because if a TEE is present it will have Weaver, which
+     * already strengthens CE, so there's no additional benefit from using StrongBox.
+     *
+     * The picture is slightly more complicated because Keystore2 reports a SOFTWARE instance as
+     * a TEE instance when there isn't a TEE instance available, but in that case, a STRONGBOX
+     * instance won't be available either, so we'll still be doing the best we can.
+     */
+    auto rc = keystore2Service->getSecurityLevel(km::SecurityLevel::TRUSTED_ENVIRONMENT,
+                                                 &securityLevel);
+    if (logKeystore2ExceptionIfPresent(rc, "getSecurityLevel"))
+        LOG(ERROR) << "Vold unable to get security level from keystore2.";
 }
 
 bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) {
-    km::ErrorCode km_error;
-    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
-                      const km::KeyCharacteristics& /*ignored*/) {
-        km_error = ret;
-        if (km_error != km::ErrorCode::OK) return;
-        if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
+    ks2::KeyDescriptor in_key = {
+            .domain = ks2::Domain::BLOB,
+            .alias = std::nullopt,
+            .nspace = VOLD_NAMESPACE,
+            .blob = std::nullopt,
     };
+    ks2::KeyMetadata keyMetadata;
+    auto rc = securityLevel->generateKey(in_key, std::nullopt, inParams.vector_data(), 0, {},
+                                         &keyMetadata);
 
-    auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
-    if (!error.isOk()) {
-        LOG(ERROR) << "generate_key failed: " << error.description();
+    if (logKeystore2ExceptionIfPresent(rc, "generateKey")) return false;
+
+    if (keyMetadata.key.blob == std::nullopt) {
+        LOG(ERROR) << "keystore2 generated key blob was null";
         return false;
     }
-    if (km_error != km::ErrorCode::OK) {
-        LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
-        return false;
-    }
+    if (key) *key = std::string(keyMetadata.key.blob->begin(), keyMetadata.key.blob->end());
+
+    zeroize_vector(keyMetadata.key.blob.value());
     return true;
 }
 
 bool Keymaster::exportKey(const KeyBuffer& kmKey, std::string* key) {
-    auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size()));
-    km::ErrorCode km_error;
-    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
-        km_error = ret;
-        if (km_error != km::ErrorCode::OK) return;
-        if (key)
-            key->assign(reinterpret_cast<const char*>(&exportedKeyBlob[0]), exportedKeyBlob.size());
+    bool ret = false;
+    ks2::KeyDescriptor storageKey = {
+            .domain = ks2::Domain::BLOB,
+            .alias = std::nullopt,
+            .nspace = VOLD_NAMESPACE,
     };
-    auto error = mDevice->exportKey(km::KeyFormat::RAW, kmKeyBlob, {}, {}, hidlCb);
-    if (!error.isOk()) {
-        LOG(ERROR) << "export_key failed: " << error.description();
-        return false;
-    }
-    if (km_error != km::ErrorCode::OK) {
-        LOG(ERROR) << "export_key failed, code " << int32_t(km_error);
-        return false;
-    }
-    return true;
+    storageKey.blob = std::make_optional<std::vector<uint8_t>>(kmKey.begin(), kmKey.end());
+    ks2::EphemeralStorageKeyResponse ephemeral_key_response;
+    auto rc = securityLevel->convertStorageKeyToEphemeral(storageKey, &ephemeral_key_response);
+
+    if (logKeystore2ExceptionIfPresent(rc, "exportKey")) goto out;
+    if (key)
+        *key = std::string(ephemeral_key_response.ephemeralKey.begin(),
+                           ephemeral_key_response.ephemeralKey.end());
+
+    // TODO b/185811713 store the upgraded key blob if provided and delete the old key blob.
+
+    ret = true;
+out:
+    zeroize_vector(ephemeral_key_response.ephemeralKey);
+    zeroize_vector(storageKey.blob.value());
+    return ret;
 }
 
 bool Keymaster::deleteKey(const std::string& key) {
-    auto keyBlob = km::support::blob2hidlVec(key);
-    auto error = mDevice->deleteKey(keyBlob);
-    if (!error.isOk()) {
-        LOG(ERROR) << "delete_key failed: " << error.description();
-        return false;
-    }
-    if (error != km::ErrorCode::OK) {
-        LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
-        return false;
-    }
-    return true;
-}
-
-bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
-                           std::string* newKey) {
-    auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
-    km::ErrorCode km_error;
-    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
-        km_error = ret;
-        if (km_error != km::ErrorCode::OK) return;
-        if (newKey)
-            newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
-                           upgradedKeyBlob.size());
+    ks2::KeyDescriptor keyDesc = {
+            .domain = ks2::Domain::BLOB,
+            .alias = std::nullopt,
+            .nspace = VOLD_NAMESPACE,
     };
-    auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
-    if (!error.isOk()) {
-        LOG(ERROR) << "upgrade_key failed: " << error.description();
-        return false;
-    }
-    if (km_error != km::ErrorCode::OK) {
-        LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
-        return false;
-    }
-    return true;
+    keyDesc.blob =
+            std::optional<std::vector<uint8_t>>(std::vector<uint8_t>(key.begin(), key.end()));
+
+    auto rc = securityLevel->deleteKey(keyDesc);
+    return !logKeystore2ExceptionIfPresent(rc, "deleteKey");
 }
 
-KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key,
-                                    const km::AuthorizationSet& inParams,
-                                    const km::HardwareAuthToken& authToken,
+KeymasterOperation Keymaster::begin(const std::string& key, const km::AuthorizationSet& inParams,
                                     km::AuthorizationSet* outParams) {
-    auto keyBlob = km::support::blob2hidlVec(key);
-    uint64_t mOpHandle;
-    km::ErrorCode km_error;
-
-    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& _outParams,
-                      uint64_t operationHandle) {
-        km_error = ret;
-        if (km_error != km::ErrorCode::OK) return;
-        if (outParams) *outParams = _outParams;
-        mOpHandle = operationHandle;
+    ks2::KeyDescriptor keyDesc = {
+            .domain = ks2::Domain::BLOB,
+            .alias = std::nullopt,
+            .nspace = VOLD_NAMESPACE,
     };
+    keyDesc.blob =
+            std::optional<std::vector<uint8_t>>(std::vector<uint8_t>(key.begin(), key.end()));
 
-    auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb);
-    if (!error.isOk()) {
-        LOG(ERROR) << "begin failed: " << error.description();
-        return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
+    ks2::CreateOperationResponse cor;
+    auto rc = securityLevel->createOperation(keyDesc, inParams.vector_data(), true, &cor);
+    if (logKeystore2ExceptionIfPresent(rc, "createOperation")) {
+        if (rc.getExceptionCode() == EX_SERVICE_SPECIFIC)
+            return KeymasterOperation((km::ErrorCode)rc.getServiceSpecificError());
+        else
+            return KeymasterOperation();
     }
-    if (km_error != km::ErrorCode::OK) {
-        LOG(ERROR) << "begin failed, code " << int32_t(km_error);
-        return KeymasterOperation(km_error);
-    }
-    return KeymasterOperation(mDevice.get(), mOpHandle);
-}
 
-bool Keymaster::isSecure() {
-    return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
+    if (!cor.iOperation) {
+        LOG(ERROR) << "keystore2 createOperation didn't return an operation";
+        return KeymasterOperation();
+    }
+
+    if (outParams && cor.parameters) *outParams = cor.parameters->keyParameter;
+
+    return KeymasterOperation(cor.iOperation, cor.upgradedBlob);
 }
 
 void Keymaster::earlyBootEnded() {
-    auto devices = KmDevice::enumerateAvailableDevices();
-    for (auto& dev : devices) {
-        auto error = dev->earlyBootEnded();
-        if (!error.isOk()) {
-            LOG(ERROR) << "earlyBootEnded call failed: " << error.description() << " for "
-                       << dev->halVersion().keymasterName;
-        }
-        km::V4_1_ErrorCode km_error = error;
-        if (km_error != km::V4_1_ErrorCode::OK && km_error != km::V4_1_ErrorCode::UNIMPLEMENTED) {
-            LOG(ERROR) << "Error reporting early boot ending to keymaster: "
-                       << static_cast<int32_t>(km_error) << " for "
-                       << dev->halVersion().keymasterName;
-        }
+    ::ndk::SpAIBinder binder(AServiceManager_getService(maintenance_service_name));
+    auto maint_service = ks2_maint::IKeystoreMaintenance::fromBinder(binder);
+
+    if (!maint_service) {
+        LOG(ERROR) << "Unable to connect to keystore2 maintenance service for earlyBootEnded";
+        return;
     }
+
+    auto rc = maint_service->earlyBootEnded();
+    logKeystore2ExceptionIfPresent(rc, "earlyBootEnded");
 }
 
 }  // namespace vold
 }  // namespace android
-
-using namespace ::android::vold;
-
-int keymaster_compatibility_cryptfs_scrypt() {
-    Keymaster dev;
-    if (!dev) {
-        LOG(ERROR) << "Failed to initiate keymaster session";
-        return -1;
-    }
-    return dev.isSecure();
-}
-
-static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
-                                uint32_t* out_size) {
-    if (!buffer || !out_size) {
-        LOG(ERROR) << "Missing target pointers";
-        return false;
-    }
-    *out_size = towrite.size();
-    if (buffer_size < towrite.size()) {
-        LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size();
-        return false;
-    }
-    memset(buffer, '\0', buffer_size);
-    std::copy(towrite.begin(), towrite.end(), buffer);
-    return true;
-}
-
-static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent,
-                                      uint32_t ratelimit) {
-    return km::AuthorizationSetBuilder()
-        .RsaSigningKey(rsa_key_size, rsa_exponent)
-        .NoDigestOrPadding()
-        .Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE)
-        .Authorization(km::TAG_NO_AUTH_REQUIRED)
-        .Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
-}
-
-int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
-                                            uint32_t ratelimit, uint8_t* key_buffer,
-                                            uint32_t key_buffer_size, uint32_t* key_out_size) {
-    if (key_out_size) {
-        *key_out_size = 0;
-    }
-    Keymaster dev;
-    if (!dev) {
-        LOG(ERROR) << "Failed to initiate keymaster session";
-        return -1;
-    }
-    std::string key;
-    if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1;
-    if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
-    return 0;
-}
-
-int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
-                                             uint32_t ratelimit, const uint8_t* key_blob,
-                                             size_t key_blob_size, uint8_t* key_buffer,
-                                             uint32_t key_buffer_size, uint32_t* key_out_size) {
-    if (key_out_size) {
-        *key_out_size = 0;
-    }
-    Keymaster dev;
-    if (!dev) {
-        LOG(ERROR) << "Failed to initiate keymaster session";
-        return -1;
-    }
-    std::string old_key(reinterpret_cast<const char*>(key_blob), key_blob_size);
-    std::string new_key;
-    if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key))
-        return -1;
-    if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1;
-    return 0;
-}
-
-KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
-    const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
-    const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) {
-    Keymaster dev;
-    if (!dev) {
-        LOG(ERROR) << "Failed to initiate keymaster session";
-        return KeymasterSignResult::error;
-    }
-    if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
-        LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
-        return KeymasterSignResult::error;
-    }
-
-    km::AuthorizationSet outParams;
-    std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
-    std::string input(reinterpret_cast<const char*>(object), object_size);
-    std::string output;
-    KeymasterOperation op;
-
-    auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding();
-    while (true) {
-        op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams);
-        if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
-            sleep(ratelimit);
-            continue;
-        } else
-            break;
-    }
-
-    if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
-        LOG(ERROR) << "Keymaster key requires upgrade";
-        return KeymasterSignResult::upgrade;
-    }
-
-    if (op.errorCode() != km::ErrorCode::OK) {
-        LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
-        return KeymasterSignResult::error;
-    }
-
-    if (!op.updateCompletely(input, &output)) {
-        LOG(ERROR) << "Error sending data to keymaster signature transaction: "
-                   << uint32_t(op.errorCode());
-        return KeymasterSignResult::error;
-    }
-
-    if (!op.finish(&output)) {
-        LOG(ERROR) << "Error finalizing keymaster signature transaction: "
-                   << int32_t(op.errorCode());
-        return KeymasterSignResult::error;
-    }
-
-    *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
-    if (*signature_buffer == nullptr) {
-        LOG(ERROR) << "Error allocation buffer for keymaster signature";
-        return KeymasterSignResult::error;
-    }
-    *signature_buffer_size = output.size();
-    std::copy(output.data(), output.data() + output.size(), *signature_buffer);
-    return KeymasterSignResult::ok;
-}
diff --git a/Keymaster.h b/Keymaster.h
index d9ced91..1100840 100644
--- a/Keymaster.h
+++ b/Keymaster.h
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+// TODO: Maybe "Keymaster" should be replaced with Keystore2 everywhere?
 #ifndef ANDROID_VOLD_KEYMASTER_H
 #define ANDROID_VOLD_KEYMASTER_H
 
@@ -24,33 +24,25 @@
 #include <utility>
 
 #include <android-base/macros.h>
-#include <keymasterV4_1/Keymaster.h>
-#include <keymasterV4_1/authorization_set.h>
+#include <keymint_support/authorization_set.h>
+#include <keymint_support/keymint_tags.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <android/binder_manager.h>
 
 namespace android {
 namespace vold {
 
-namespace km {
+namespace ks2 = ::aidl::android::system::keystore2;
+namespace km = ::aidl::android::hardware::security::keymint;
 
-using namespace ::android::hardware::keymaster::V4_1;
-
-// Surprisingly -- to me, at least -- this is totally fine.  You can re-define symbols that were
-// brought in via a using directive (the "using namespace") above.  In general this seems like a
-// dangerous thing to rely on, but in this case its implications are simple and straightforward:
-// km::ErrorCode refers to the 4.0 ErrorCode, though we pull everything else from 4.1.
-using ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
-using V4_1_ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode;
-
-}  // namespace km
-
-using KmDevice = km::support::Keymaster;
-
-// C++ wrappers to the Keymaster hidl interface.
+// C++ wrappers to the Keystore2 AIDL interface.
 // This is tailored to the needs of KeyStorage, but could be extended to be
 // a more general interface.
 
-// Wrapper for a Keymaster operation handle representing an
-// ongoing Keymaster operation.  Aborts the operation
+// Wrapper for a Keystore2 operation handle representing an
+// ongoing Keystore2 operation.  Aborts the operation
 // in the destructor if it is unfinished. Methods log failures
 // to LOG(ERROR).
 class KeymasterOperation {
@@ -58,8 +50,9 @@
     ~KeymasterOperation();
     // Is this instance valid? This is false if creation fails, and becomes
     // false on finish or if an update fails.
-    explicit operator bool() const { return mError == km::ErrorCode::OK; }
-    km::ErrorCode errorCode() const { return mError; }
+    explicit operator bool() const { return (bool)ks2Operation; }
+    km::ErrorCode getErrorCode() const { return errorCode; }
+    std::optional<std::string> getUpgradedBlob() const { return upgradedBlob; }
     // Call "update" repeatedly until all of the input is consumed, and
     // concatenate the output. Return true on success.
     template <class TI, class TO>
@@ -75,103 +68,71 @@
     // Move constructor
     KeymasterOperation(KeymasterOperation&& rhs) { *this = std::move(rhs); }
     // Construct an object in an error state for error returns
-    KeymasterOperation() : mDevice{nullptr}, mOpHandle{0}, mError{km::ErrorCode::UNKNOWN_ERROR} {}
+    KeymasterOperation() { errorCode = km::ErrorCode::UNKNOWN_ERROR; }
     // Move Assignment
     KeymasterOperation& operator=(KeymasterOperation&& rhs) {
-        mDevice = rhs.mDevice;
-        rhs.mDevice = nullptr;
+        ks2Operation = rhs.ks2Operation;
+        rhs.ks2Operation = nullptr;
 
-        mOpHandle = rhs.mOpHandle;
-        rhs.mOpHandle = 0;
+        upgradedBlob = rhs.upgradedBlob;
+        rhs.upgradedBlob = std::nullopt;
 
-        mError = rhs.mError;
-        rhs.mError = km::ErrorCode::UNKNOWN_ERROR;
+        errorCode = rhs.errorCode;
+        rhs.errorCode = km::ErrorCode::UNKNOWN_ERROR;
 
         return *this;
     }
 
   private:
-    KeymasterOperation(KmDevice* d, uint64_t h)
-        : mDevice{d}, mOpHandle{h}, mError{km::ErrorCode::OK} {}
-    KeymasterOperation(km::ErrorCode error) : mDevice{nullptr}, mOpHandle{0}, mError{error} {}
+    KeymasterOperation(std::shared_ptr<ks2::IKeystoreOperation> ks2Op,
+                       std::optional<std::vector<uint8_t>> blob)
+        : ks2Operation{ks2Op}, errorCode{km::ErrorCode::OK} {
+        if (blob)
+            upgradedBlob = std::optional(std::string(blob->begin(), blob->end()));
+        else
+            upgradedBlob = std::nullopt;
+    }
+
+    KeymasterOperation(km::ErrorCode errCode) : errorCode{errCode} {}
 
     bool updateCompletely(const char* input, size_t inputLen,
                           const std::function<void(const char*, size_t)> consumer);
 
-    KmDevice* mDevice;
-    uint64_t mOpHandle;
-    km::ErrorCode mError;
+    std::shared_ptr<ks2::IKeystoreOperation> ks2Operation;
+    std::optional<std::string> upgradedBlob;
+    km::ErrorCode errorCode;
     DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
     friend class Keymaster;
 };
 
-// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
-// part of one.
+// Wrapper for keystore2 methods that vold uses.
 class Keymaster {
   public:
     Keymaster();
-    // false if we failed to open the keymaster device.
-    explicit operator bool() { return mDevice.get() != nullptr; }
-    // Generate a key in the keymaster from the given params.
+    // false if we failed to get a keystore2 security level.
+    explicit operator bool() { return (bool)securityLevel; }
+    // Generate a key using keystore2 from the given params.
     bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
-    // Exports a keymaster key with STORAGE_KEY tag wrapped with a per-boot ephemeral key
+    // Exports a keystore2 key with STORAGE_KEY tag wrapped with a per-boot ephemeral key
     bool exportKey(const KeyBuffer& kmKey, std::string* key);
-    // If the keymaster supports it, permanently delete a key.
+    // If supported, permanently delete a key from the keymint device it belongs to.
     bool deleteKey(const std::string& key);
-    // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
-    bool upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
-                    std::string* newKey);
     // Begin a new cryptographic operation, collecting output parameters if pointer is non-null
-    KeymasterOperation begin(km::KeyPurpose purpose, const std::string& key,
-                             const km::AuthorizationSet& inParams,
-                             const km::HardwareAuthToken& authToken,
+    // If the key was upgraded as a result of a call to this method, the returned KeymasterOperation
+    // also stores the upgraded key blob.
+    KeymasterOperation begin(const std::string& key, const km::AuthorizationSet& inParams,
                              km::AuthorizationSet* outParams);
-    bool isSecure();
 
-    // Tell all Keymaster instances that early boot has ended and early boot-only keys can no longer
+    // Tell all Keymint devices that early boot has ended and early boot-only keys can no longer
     // be created or used.
     static void earlyBootEnded();
 
   private:
-    sp<KmDevice> mDevice;
+    std::shared_ptr<ks2::IKeystoreSecurityLevel> securityLevel;
     DISALLOW_COPY_AND_ASSIGN(Keymaster);
-    static bool hmacKeyGenerated;
 };
 
 }  // namespace vold
 }  // namespace android
 
-// FIXME no longer needed now cryptfs is in C++.
-
-/*
- * The following functions provide C bindings to keymaster services
- * needed by cryptfs scrypt. The compatibility check checks whether
- * the keymaster implementation is considered secure, i.e., TEE backed.
- * The create_key function generates an RSA key for signing.
- * The sign_object function signes an object with the given keymaster
- * key.
- */
-
-/* Return values for keymaster_sign_object_for_cryptfs_scrypt */
-
-enum class KeymasterSignResult {
-    ok = 0,
-    error = -1,
-    upgrade = -2,
-};
-
-int keymaster_compatibility_cryptfs_scrypt();
-int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
-                                            uint32_t ratelimit, uint8_t* key_buffer,
-                                            uint32_t key_buffer_size, uint32_t* key_out_size);
-
-int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
-                                             uint32_t ratelimit, const uint8_t* key_blob,
-                                             size_t key_blob_size, uint8_t* key_buffer,
-                                             uint32_t key_buffer_size, uint32_t* key_out_size);
-
-KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
-    const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
-    const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size);
-
 #endif
diff --git a/Loop.cpp b/Loop.cpp
index 9fa876c..87f105d 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -150,7 +150,9 @@
 
         struct loop_info64 li;
         if (ioctl(fd.get(), LOOP_GET_STATUS64, &li) < 0) {
-            PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
+            if (errno != ENXIO) {
+                PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
+            }
             continue;
         }
 
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index ca2813d..dc50679 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -17,17 +17,13 @@
 #include "MetadataCrypt.h"
 #include "KeyBuffer.h"
 
-#include <algorithm>
 #include <string>
-#include <thread>
-#include <vector>
 
 #include <fcntl.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
@@ -35,6 +31,7 @@
 #include <cutils/fs.h>
 #include <fs_mgr.h>
 #include <libdm/dm.h>
+#include <libgsi/libgsi.h>
 
 #include "Checkpoint.h"
 #include "CryptoType.h"
@@ -44,8 +41,8 @@
 #include "Keymaster.h"
 #include "Utils.h"
 #include "VoldUtil.h"
-
-#define TABLE_LOAD_RETRIES 10
+#include "fs/Ext4.h"
+#include "fs/F2fs.h"
 
 namespace android {
 namespace vold {
@@ -65,9 +62,6 @@
 
 static const std::string kDmNameUserdata = "userdata";
 
-static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
-static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
-
 // The first entry in this table is the default crypto type.
 constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum};
 
@@ -87,10 +81,6 @@
 }
 
 static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
-    // We're about to mount data not verified by verified boot.  Tell Keymaster instances that early
-    // boot has ended.
-    ::android::vold::Keymaster::earlyBootEnded();
-
     // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
     // partitions in the fsck domain.
     if (setexeccon(android::vold::sFsckContext)) {
@@ -112,31 +102,6 @@
     return true;
 }
 
-// Note: It is possible to orphan a key if it is removed before deleting
-// Update this once keymaster APIs change, and we have a proper commit.
-static void commit_key(const std::string& dir) {
-    while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
-        LOG(ERROR) << "Wait for boot timed out";
-    }
-    Keymaster keymaster;
-    auto keyPath = dir + "/" + kFn_keymaster_key_blob;
-    auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
-    std::string key;
-
-    if (!android::base::ReadFileToString(keyPath, &key)) {
-        LOG(ERROR) << "Failed to read old key: " << dir;
-        return;
-    }
-    if (rename(newKeyPath.c_str(), keyPath.c_str()) != 0) {
-        PLOG(ERROR) << "Unable to move upgraded key to location: " << keyPath;
-        return;
-    }
-    if (!keymaster.deleteKey(key)) {
-        LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
-    }
-    LOG(INFO) << "Old Key deleted: " << dir;
-}
-
 static bool read_key(const std::string& metadata_key_dir, const KeyGeneration& gen,
                      KeyBuffer* key) {
     if (metadata_key_dir.empty()) {
@@ -146,30 +111,9 @@
     std::string sKey;
     auto dir = metadata_key_dir + "/key";
     LOG(DEBUG) << "metadata_key_dir/key: " << dir;
-    if (fs_mkdirs(dir.c_str(), 0700)) {
-        PLOG(ERROR) << "Creating directories: " << dir;
-        return false;
-    }
+    if (!MkdirsSync(dir, 0700)) return false;
     auto temp = metadata_key_dir + "/tmp";
-    auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
-    /* If we have a leftover upgraded key, delete it.
-     * We either failed an update and must return to the old key,
-     * or we rebooted before commiting the keys in a freak accident.
-     * Either way, we can re-upgrade the key if we need to.
-     */
-    Keymaster keymaster;
-    if (pathExists(newKeyPath)) {
-        if (!android::base::ReadFileToString(newKeyPath, &sKey))
-            LOG(ERROR) << "Failed to read incomplete key: " << dir;
-        else if (!keymaster.deleteKey(sKey))
-            LOG(ERROR) << "Incomplete key deletion failed, continuing anyway: " << dir;
-        else
-            unlink(newKeyPath.c_str());
-    }
-    bool needs_cp = cp_needsCheckpoint();
-    if (!retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key, needs_cp)) return false;
-    if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
-    return true;
+    return retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key);
 }
 
 static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_sec) {
@@ -215,20 +159,8 @@
     table.AddTarget(std::move(target));
 
     auto& dm = DeviceMapper::Instance();
-    for (int i = 0;; i++) {
-        if (dm.CreateDevice(dm_name, table)) {
-            break;
-        }
-        if (i + 1 >= TABLE_LOAD_RETRIES) {
-            PLOG(ERROR) << "Could not create default-key device " << dm_name;
-            return false;
-        }
-        PLOG(INFO) << "Could not create default-key device, retrying";
-        usleep(500000);
-    }
-
-    if (!dm.GetDmDevicePathByName(dm_name, crypto_blkdev)) {
-        LOG(ERROR) << "Cannot retrieve default-key device status " << dm_name;
+    if (!dm.CreateDevice(dm_name, table, crypto_blkdev, std::chrono::seconds(5))) {
+        PLOG(ERROR) << "Could not create default-key device " << dm_name;
         return false;
     }
     return true;
@@ -269,8 +201,11 @@
 }
 
 bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point,
-                                      bool needs_encrypt) {
-    LOG(DEBUG) << "fscrypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt;
+                                      bool needs_encrypt, bool should_format,
+                                      const std::string& fs_type) {
+    LOG(DEBUG) << "fscrypt_mount_metadata_encrypted: " << mount_point
+               << " encrypt: " << needs_encrypt << " format: " << should_format << " with "
+               << fs_type;
     auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
     if (encrypted_state != "" && encrypted_state != "encrypted") {
         LOG(DEBUG) << "fscrypt_enable_crypto got unexpected starting state: " << encrypted_state;
@@ -283,10 +218,9 @@
         return false;
     }
 
-    constexpr unsigned int pre_gki_level = 29;
     unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
             "ro.crypto.dm_default_key.options_format.version",
-            (GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
+            (GetFirstApiLevel() <= __ANDROID_API_Q__ ? 1 : 2));
 
     CryptoOptions options;
     if (options_format_version == 1) {
@@ -318,21 +252,23 @@
     if (!create_crypto_blk_dev(kDmNameUserdata, blk_device, key, options, &crypto_blkdev, &nr_sec))
         return false;
 
-    // FIXME handle the corrupt case
     if (needs_encrypt) {
-        LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
-        off64_t size_already_done = 0;
-        auto rc = cryptfs_enable_inplace(crypto_blkdev.data(), blk_device.data(), nr_sec,
-                                         &size_already_done, nr_sec, 0, false);
-        if (rc != 0) {
-            LOG(ERROR) << "Inplace crypto failed with code: " << rc;
-            return false;
+        if (should_format) {
+            status_t error;
+
+            if (fs_type == "ext4") {
+                error = ext4::Format(crypto_blkdev, 0, mount_point);
+            } else if (fs_type == "f2fs") {
+                error = f2fs::Format(crypto_blkdev);
+            } else {
+                LOG(ERROR) << "Unknown filesystem type: " << fs_type;
+                return false;
+            }
+            LOG(DEBUG) << "Format (err=" << error << ") " << crypto_blkdev << " on " << mount_point;
+            if (error != 0) return false;
+        } else {
+            if (!encrypt_inplace(crypto_blkdev, blk_device, nr_sec, false)) return false;
         }
-        if (static_cast<uint64_t>(size_already_done) != nr_sec) {
-            LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
-            return false;
-        }
-        LOG(INFO) << "Inplace encryption complete";
     }
 
     LOG(DEBUG) << "Mounting metadata-encrypted filesystem:" << mount_point;
@@ -367,5 +303,44 @@
     return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec);
 }
 
+bool destroy_dsu_metadata_key(const std::string& dsu_slot) {
+    LOG(DEBUG) << "destroy_dsu_metadata_key: " << dsu_slot;
+
+    const auto dsu_metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
+    if (!pathExists(dsu_metadata_key_dir)) {
+        LOG(DEBUG) << "DSU metadata_key_dir doesn't exist, nothing to remove: "
+                   << dsu_metadata_key_dir;
+        return true;
+    }
+
+    // Ensure that the DSU key directory is different from the host OS'.
+    // Under normal circumstances, this should never happen, but handle it just in case.
+    if (auto data_rec = GetEntryForMountPoint(&fstab_default, "/data")) {
+        if (dsu_metadata_key_dir == data_rec->metadata_key_dir) {
+            LOG(ERROR) << "DSU metadata_key_dir is same as host OS: " << dsu_metadata_key_dir;
+            return false;
+        }
+    }
+
+    bool ok = true;
+    for (auto suffix : {"/key", "/tmp"}) {
+        const auto key_path = dsu_metadata_key_dir + suffix;
+        if (pathExists(key_path)) {
+            LOG(DEBUG) << "Destroy key: " << key_path;
+            if (!android::vold::destroyKey(key_path)) {
+                LOG(ERROR) << "Failed to destroyKey(): " << key_path;
+                ok = false;
+            }
+        }
+    }
+    if (!ok) {
+        return false;
+    }
+
+    LOG(DEBUG) << "Remove DSU metadata_key_dir: " << dsu_metadata_key_dir;
+    // DeleteDirContentsAndDir() already logged any error, so don't log repeatedly.
+    return android::vold::DeleteDirContentsAndDir(dsu_metadata_key_dir) == android::OK;
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/MetadataCrypt.h b/MetadataCrypt.h
index dc68e7c..e482765 100644
--- a/MetadataCrypt.h
+++ b/MetadataCrypt.h
@@ -26,7 +26,8 @@
 namespace vold {
 
 bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
-                                      const std::string& mount_point, bool needs_encrypt);
+                                      const std::string& mount_point, bool needs_encrypt,
+                                      bool should_format, const std::string& fs_type);
 
 bool defaultkey_volume_keygen(KeyGeneration* gen);
 
@@ -34,6 +35,8 @@
                                  const android::vold::KeyBuffer& key,
                                  std::string* out_crypto_blkdev);
 
+bool destroy_dsu_metadata_key(const std::string& dsu_slot);
+
 }  // namespace vold
 }  // namespace android
 #endif
diff --git a/MoveStorage.cpp b/MoveStorage.cpp
index 2447cce..54e28a9 100644
--- a/MoveStorage.cpp
+++ b/MoveStorage.cpp
@@ -70,9 +70,7 @@
     bool found = false;
     struct dirent* ent;
     while ((ent = readdir(dirp.get())) != NULL) {
-        if ((!strcmp(ent->d_name, ".")) || (!strcmp(ent->d_name, ".."))) {
-            continue;
-        }
+        if (IsDotOrDotDot(*ent)) continue;
         auto subdir = path + "/" + ent->d_name;
         found |= pushBackContents(subdir, cmd, searchLevels - 1);
     }
@@ -258,7 +256,10 @@
 
 void MoveStorage(const std::shared_ptr<VolumeBase>& from, const std::shared_ptr<VolumeBase>& to,
                  const android::sp<android::os::IVoldTaskListener>& listener) {
-    android::wakelock::WakeLock wl{kWakeLock};
+    auto wl = android::wakelock::WakeLock::tryGet(kWakeLock);
+    if (!wl.has_value()) {
+        return;
+    }
 
     android::os::PersistableBundle extras;
     status_t res = moveStorageInternal(from, to, listener);
diff --git a/NetlinkHandler.cpp b/NetlinkHandler.cpp
index d180a95..2231cd1 100644
--- a/NetlinkHandler.cpp
+++ b/NetlinkHandler.cpp
@@ -33,10 +33,6 @@
     return this->startListener();
 }
 
-int NetlinkHandler::stop() {
-    return this->stopListener();
-}
-
 void NetlinkHandler::onEvent(NetlinkEvent* evt) {
     VolumeManager* vm = VolumeManager::Instance();
     const char* subsys = evt->getSubsystem();
diff --git a/NetlinkHandler.h b/NetlinkHandler.h
index 8af7575..d779eae 100644
--- a/NetlinkHandler.h
+++ b/NetlinkHandler.h
@@ -25,7 +25,6 @@
     virtual ~NetlinkHandler();
 
     int start(void);
-    int stop(void);
 
   protected:
     virtual void onEvent(NetlinkEvent* evt);
diff --git a/NetlinkManager.cpp b/NetlinkManager.cpp
index aacf4b9..56d9df6 100644
--- a/NetlinkManager.cpp
+++ b/NetlinkManager.cpp
@@ -90,19 +90,3 @@
     close(mSock);
     return -1;
 }
-
-int NetlinkManager::stop() {
-    int status = 0;
-
-    if (mHandler->stop()) {
-        PLOG(ERROR) << "Unable to stop NetlinkHandler";
-        status = -1;
-    }
-    delete mHandler;
-    mHandler = NULL;
-
-    close(mSock);
-    mSock = -1;
-
-    return status;
-}
diff --git a/NetlinkManager.h b/NetlinkManager.h
index e31fc2e..c4f3ab5 100644
--- a/NetlinkManager.h
+++ b/NetlinkManager.h
@@ -35,7 +35,6 @@
     virtual ~NetlinkManager();
 
     int start();
-    int stop();
 
     void setBroadcaster(SocketListener* sl) { mBroadcaster = sl; }
     SocketListener* getBroadcaster() { return mBroadcaster; }
diff --git a/OWNERS b/OWNERS
index bab0ef6..6d8d89f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,9 @@
+alanstokes@google.com
+drosen@google.com
+ebiggers@google.com
+jeffv@google.com
 jsharkey@android.com
+maco@google.com
 paulcrowley@google.com
 paullawrence@google.com
-ebiggers@google.com
-drosen@google.com
 zezeozue@google.com
diff --git a/Process.cpp b/Process.cpp
index 277d6a3..79fe15d 100644
--- a/Process.cpp
+++ b/Process.cpp
@@ -39,6 +39,7 @@
 #include <android-base/strings.h>
 
 #include "Process.h"
+#include "Utils.h"
 
 using android::base::StringPrintf;
 
@@ -83,7 +84,7 @@
 }
 
 // TODO: Refactor the code with KillProcessesWithOpenFiles().
-int KillProcessesWithMounts(const std::string& prefix, int signal) {
+int KillProcessesWithTmpfsMounts(const std::string& prefix, int signal) {
     std::unordered_set<pid_t> pids;
 
     auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
@@ -111,7 +112,8 @@
         // Check if obb directory is mounted, and get all packages of mounted app data directory.
         mntent* mentry;
         while ((mentry = getmntent(fp.get())) != nullptr) {
-            if (android::base::StartsWith(mentry->mnt_dir, prefix)) {
+            if (mentry->mnt_fsname != nullptr && strncmp(mentry->mnt_fsname, "tmpfs", 5) == 0
+                    && android::base::StartsWith(mentry->mnt_dir, prefix)) {
                 pids.insert(pid);
                 break;
             }
@@ -127,7 +129,7 @@
     return pids.size();
 }
 
-int KillProcessesWithOpenFiles(const std::string& prefix, int signal) {
+int KillProcessesWithOpenFiles(const std::string& prefix, int signal, bool killFuseDaemon) {
     std::unordered_set<pid_t> pids;
 
     auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
@@ -164,7 +166,11 @@
         }
 
         if (found) {
-            pids.insert(pid);
+            if (!IsFuseDaemon(pid) || killFuseDaemon) {
+                pids.insert(pid);
+            } else {
+                LOG(WARNING) << "Found FUSE daemon with open file. Skipping...";
+            }
         }
     }
     if (signal != 0) {
diff --git a/Process.h b/Process.h
index 1c59812..f3728b5 100644
--- a/Process.h
+++ b/Process.h
@@ -20,8 +20,8 @@
 namespace android {
 namespace vold {
 
-int KillProcessesWithOpenFiles(const std::string& path, int signal);
-int KillProcessesWithMounts(const std::string& path, int signal);
+int KillProcessesWithOpenFiles(const std::string& path, int signal, bool killFuseDaemon = true);
+int KillProcessesWithTmpfsMounts(const std::string& path, int signal);
 
 }  // namespace vold
 }  // namespace android
diff --git a/Utils.cpp b/Utils.cpp
index 17921e8..4635975 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -136,8 +136,8 @@
 }
 
 // Sets a default ACL on the directory.
-int SetDefaultAcl(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
-                  std::vector<gid_t> additionalGids) {
+status_t SetDefaultAcl(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
+                       std::vector<gid_t> additionalGids) {
     if (IsSdcardfsUsed()) {
         // sdcardfs magically takes care of this
         return OK;
@@ -199,7 +199,7 @@
 }
 
 int SetQuotaInherit(const std::string& path) {
-    unsigned long flags;
+    unsigned int flags;
 
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd == -1) {
@@ -240,7 +240,12 @@
     }
 
     fsx.fsx_projid = projectId;
-    return ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
+    ret = ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
+    if (ret == -1) {
+        PLOG(ERROR) << "Failed to set project id on " << path;
+        return ret;
+    }
+    return 0;
 }
 
 int PrepareDirWithProjectId(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
@@ -417,7 +422,7 @@
 }
 
 int SetAttrs(const std::string& path, unsigned int attrs) {
-    unsigned long flags;
+    unsigned int flags;
     android::base::unique_fd fd(
             TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
 
@@ -426,14 +431,14 @@
         return -1;
     }
 
-    if (ioctl(fd, FS_IOC_GETFLAGS, (void*)&flags)) {
+    if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
         PLOG(ERROR) << "Failed to get flags for " << path;
         return -1;
     }
 
     if ((flags & attrs) == attrs) return 0;
     flags |= attrs;
-    if (ioctl(fd, FS_IOC_SETFLAGS, (void*)&flags)) {
+    if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) {
         PLOG(ERROR) << "Failed to set flags for " << path << "(0x" << std::hex << attrs << ")";
         return -1;
     }
@@ -499,25 +504,25 @@
     return -errno;
 }
 
-status_t KillProcessesWithMountPrefix(const std::string& path) {
-    if (KillProcessesWithMounts(path, SIGINT) == 0) {
+status_t KillProcessesWithTmpfsMountPrefix(const std::string& path) {
+    if (KillProcessesWithTmpfsMounts(path, SIGINT) == 0) {
         return OK;
     }
     if (sSleepOnUnmount) sleep(5);
 
-    if (KillProcessesWithMounts(path, SIGTERM) == 0) {
+    if (KillProcessesWithTmpfsMounts(path, SIGTERM) == 0) {
         return OK;
     }
     if (sSleepOnUnmount) sleep(5);
 
-    if (KillProcessesWithMounts(path, SIGKILL) == 0) {
+    if (KillProcessesWithTmpfsMounts(path, SIGKILL) == 0) {
         return OK;
     }
     if (sSleepOnUnmount) sleep(5);
 
     // Send SIGKILL a second time to determine if we've
     // actually killed everyone mount
-    if (KillProcessesWithMounts(path, SIGKILL) == 0) {
+    if (KillProcessesWithTmpfsMounts(path, SIGKILL) == 0) {
         return OK;
     }
     PLOG(ERROR) << "Failed to kill processes using " << path;
@@ -525,24 +530,25 @@
 }
 
 status_t KillProcessesUsingPath(const std::string& path) {
-    if (KillProcessesWithOpenFiles(path, SIGINT) == 0) {
+    if (KillProcessesWithOpenFiles(path, SIGINT, false /* killFuseDaemon */) == 0) {
         return OK;
     }
     if (sSleepOnUnmount) sleep(5);
 
-    if (KillProcessesWithOpenFiles(path, SIGTERM) == 0) {
+    if (KillProcessesWithOpenFiles(path, SIGTERM, false /* killFuseDaemon */) == 0) {
         return OK;
     }
     if (sSleepOnUnmount) sleep(5);
 
-    if (KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
+    if (KillProcessesWithOpenFiles(path, SIGKILL, false /* killFuseDaemon */) == 0) {
         return OK;
     }
     if (sSleepOnUnmount) sleep(5);
 
     // Send SIGKILL a second time to determine if we've
     // actually killed everyone with open files
-    if (KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
+    // This time, we also kill the FUSE daemon if found
+    if (KillProcessesWithOpenFiles(path, SIGKILL, true /* killFuseDaemon */) == 0) {
         return OK;
     }
     PLOG(ERROR) << "Failed to kill processes using " << path;
@@ -956,10 +962,7 @@
             int subfd;
 
             /* always skip "." and ".." */
-            if (name[0] == '.') {
-                if (name[1] == 0) continue;
-                if ((name[1] == '.') && (name[2] == 0)) continue;
-            }
+            if (IsDotOrDotDot(*de)) continue;
 
             subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
             if (subfd >= 0) {
@@ -1132,6 +1135,13 @@
     }
 }
 
+// Returns true if |path1| names the same existing file or directory as |path2|.
+bool IsSameFile(const std::string& path1, const std::string& path2) {
+    struct stat stbuf1, stbuf2;
+    if (stat(path1.c_str(), &stbuf1) != 0 || stat(path2.c_str(), &stbuf2) != 0) return false;
+    return stbuf1.st_ino == stbuf2.st_ino && stbuf1.st_dev == stbuf2.st_dev;
+}
+
 status_t RestoreconRecursive(const std::string& path) {
     LOG(DEBUG) << "Starting restorecon of " << path;
 
@@ -1256,6 +1266,10 @@
     return OK;
 }
 
+bool IsDotOrDotDot(const struct dirent& ent) {
+    return strcmp(ent.d_name, ".") == 0 || strcmp(ent.d_name, "..") == 0;
+}
+
 static status_t delete_dir_contents(DIR* dir) {
     // Shamelessly borrowed from android::installd
     int dfd = dirfd(dir);
@@ -1269,10 +1283,7 @@
         const char* name = de->d_name;
         if (de->d_type == DT_DIR) {
             /* always skip "." and ".." */
-            if (name[0] == '.') {
-                if (name[1] == 0) continue;
-                if ((name[1] == '.') && (name[2] == 0)) continue;
-            }
+            if (IsDotOrDotDot(*de)) continue;
 
             android::base::unique_fd subfd(
                 openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC));
@@ -1344,6 +1355,10 @@
     return -1;
 }
 
+bool pathExists(const std::string& path) {
+    return access(path.c_str(), F_OK) == 0;
+}
+
 bool FsyncDirectory(const std::string& dirname) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd == -1) {
@@ -1362,6 +1377,40 @@
     return true;
 }
 
+bool FsyncParentDirectory(const std::string& path) {
+    return FsyncDirectory(android::base::Dirname(path));
+}
+
+// Creates all parent directories of |path| that don't already exist.  Assigns
+// the specified |mode| to any new directories, and also fsync()s their parent
+// directories so that the new directories get written to disk right away.
+bool MkdirsSync(const std::string& path, mode_t mode) {
+    if (path[0] != '/') {
+        LOG(ERROR) << "MkdirsSync() needs an absolute path, but got " << path;
+        return false;
+    }
+    std::vector<std::string> components = android::base::Split(android::base::Dirname(path), "/");
+
+    std::string current_dir = "/";
+    for (const std::string& component : components) {
+        if (component.empty()) continue;
+
+        std::string parent_dir = current_dir;
+        if (current_dir != "/") current_dir += "/";
+        current_dir += component;
+
+        if (!pathExists(current_dir)) {
+            if (mkdir(current_dir.c_str(), mode) != 0) {
+                PLOG(ERROR) << "Failed to create " << current_dir;
+                return false;
+            }
+            if (!FsyncDirectory(parent_dir)) return false;
+            LOG(DEBUG) << "Created directory " << current_dir;
+        }
+    }
+    return true;
+}
+
 bool writeStringToFile(const std::string& payload, const std::string& filename) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
         open(filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0666)));
@@ -1583,18 +1632,8 @@
     std::string pass_through_path(
             StringPrintf("/mnt/pass_through/%d/%s", user_id, relative_upper_path.c_str()));
 
-    // Best effort unmount pass_through path
-    sSleepOnUnmount = false;
-    LOG(INFO) << "Unmounting pass_through_path " << pass_through_path;
-    auto status = ForceUnmount(pass_through_path);
-    if (status != android::OK) {
-        LOG(ERROR) << "Failed to unmount " << pass_through_path;
-    }
-    rmdir(pass_through_path.c_str());
-
     LOG(INFO) << "Unmounting fuse path " << fuse_path;
     android::status_t result = ForceUnmount(fuse_path);
-    sSleepOnUnmount = true;
     if (result != android::OK) {
         // TODO(b/135341433): MNT_DETACH is needed for fuse because umount2 can fail with EBUSY.
         // Figure out why we get EBUSY and remove this special casing if possible.
@@ -1608,6 +1647,13 @@
     }
     rmdir(fuse_path.c_str());
 
+    LOG(INFO) << "Unmounting pass_through_path " << pass_through_path;
+    auto status = ForceUnmount(pass_through_path);
+    if (status != android::OK) {
+        LOG(ERROR) << "Failed to unmount " << pass_through_path;
+    }
+    rmdir(pass_through_path.c_str());
+
     return result;
 }
 
diff --git a/Utils.h b/Utils.h
index 5351450..a3316c3 100644
--- a/Utils.h
+++ b/Utils.h
@@ -34,7 +34,6 @@
 namespace android {
 namespace vold {
 
-static const char* kPropFuse = "persist.sys.fuse";
 static const char* kVoldAppDataIsolationEnabled = "persist.sys.vold_app_data_isolation_enabled";
 static const char* kExternalStorageSdcardfs = "external_storage.sdcardfs.enabled";
 
@@ -52,6 +51,9 @@
 status_t CreateDeviceNode(const std::string& path, dev_t dev);
 status_t DestroyDeviceNode(const std::string& path);
 
+status_t SetDefaultAcl(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
+                       std::vector<gid_t> additionalGids);
+
 status_t AbortFuseConnections();
 
 int SetQuotaInherit(const std::string& path);
@@ -76,8 +78,8 @@
 /* Kills any processes using given path */
 status_t KillProcessesUsingPath(const std::string& path);
 
-/* Kills any processes using given mount prifix */
-status_t KillProcessesWithMountPrefix(const std::string& path);
+/* Kills any processes using given tmpfs mount prifix */
+status_t KillProcessesWithTmpfsMountPrefix(const std::string& path);
 
 /* Creates bind mount from source to target */
 status_t BindMount(const std::string& source, const std::string& target);
@@ -155,6 +157,8 @@
 
 dev_t GetDevice(const std::string& path);
 
+bool IsSameFile(const std::string& path1, const std::string& path2);
+
 status_t EnsureDirExists(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
 
 status_t RestoreconRecursive(const std::string& path);
@@ -168,13 +172,21 @@
 status_t UnmountTreeWithPrefix(const std::string& prefix);
 status_t UnmountTree(const std::string& mountPoint);
 
+bool IsDotOrDotDot(const struct dirent& ent);
+
 status_t DeleteDirContentsAndDir(const std::string& pathname);
 status_t DeleteDirContents(const std::string& pathname);
 
 status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout);
 
+bool pathExists(const std::string& path);
+
 bool FsyncDirectory(const std::string& dirname);
 
+bool FsyncParentDirectory(const std::string& path);
+
+bool MkdirsSync(const std::string& path, mode_t mode);
+
 bool writeStringToFile(const std::string& payload, const std::string& filename);
 
 void ConfigureMaxDirtyRatioForFuse(const std::string& fuse_mount, unsigned int max_ratio);
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 0cb86ce..5ea72bb 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -30,10 +30,11 @@
 #include <thread>
 
 #include "Benchmark.h"
-#include "CheckEncryption.h"
 #include "Checkpoint.h"
 #include "FsCrypt.h"
 #include "IdleMaint.h"
+#include "KeyStorage.h"
+#include "Keymaster.h"
 #include "MetadataCrypt.h"
 #include "MoveStorage.h"
 #include "Process.h"
@@ -177,7 +178,9 @@
 
 binder::Status VoldNativeService::abortFuse() {
     ENFORCE_SYSTEM_OR_ROOT;
-    ACQUIRE_LOCK;
+    // if acquire lock, maybe lead to a deadlock if lock is held by a
+    // thread that is blocked on a FUSE operation.
+    // abort fuse doesn't need to access any state, so do not acquire lock
 
     return translate(VolumeManager::Instance()->abortFuse());
 }
@@ -282,12 +285,6 @@
         return translate(res);
     }
 
-    if ((mountFlags & MOUNT_FLAG_PRIMARY) != 0) {
-        res = VolumeManager::Instance()->setPrimary(vol);
-        if (res != OK) {
-            return translate(res);
-        }
-    }
     return translate(OK);
 }
 
@@ -351,17 +348,6 @@
     return Ok();
 }
 
-binder::Status VoldNativeService::checkEncryption(const std::string& volId) {
-    ENFORCE_SYSTEM_OR_ROOT;
-    CHECK_ARGUMENT_ID(volId);
-    ACQUIRE_LOCK;
-
-    std::string path;
-    auto status = pathForVolId(volId, &path);
-    if (!status.isOk()) return status;
-    return translate(android::vold::CheckEncryption(path));
-}
-
 binder::Status VoldNativeService::moveStorage(
         const std::string& fromVolId, const std::string& toVolId,
         const android::sp<android::os::IVoldTaskListener>& listener) {
@@ -394,7 +380,17 @@
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
-    return translate(VolumeManager::Instance()->remountAppStorageDirs(uid, pid, packageNames));
+    return translate(VolumeManager::Instance()->handleAppStorageDirs(uid, pid,
+            false /* doUnmount */, packageNames));
+}
+
+binder::Status VoldNativeService::unmountAppStorageDirs(int uid, int pid,
+        const std::vector<std::string>& packageNames) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->handleAppStorageDirs(uid, pid,
+            true /* doUnmount */, packageNames));
 }
 
 binder::Status VoldNativeService::setupAppDir(const std::string& path, int32_t appUid) {
@@ -405,6 +401,14 @@
     return translate(VolumeManager::Instance()->setupAppDir(path, appUid));
 }
 
+binder::Status VoldNativeService::ensureAppDirsCreated(const std::vector<std::string>& paths,
+        int32_t appUid) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->ensureAppDirsCreated(paths, appUid));
+}
+
 binder::Status VoldNativeService::fixupAppDir(const std::string& path, int32_t appUid) {
     ENFORCE_SYSTEM_OR_ROOT;
     CHECK_ARGUMENT_PATH(path);
@@ -684,15 +688,25 @@
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
-    return translateBool(fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, false));
+    return translateBool(
+            fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, false, false, "null"));
 }
 
 binder::Status VoldNativeService::encryptFstab(const std::string& blkDevice,
-                                               const std::string& mountPoint) {
+                                               const std::string& mountPoint, bool shouldFormat,
+                                               const std::string& fsType) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
-    return translateBool(fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, true));
+    return translateBool(
+            fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, true, shouldFormat, fsType));
+}
+
+binder::Status VoldNativeService::setStorageBindingSeed(const std::vector<uint8_t>& seed) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(setKeyStorageBindingSeed(seed));
 }
 
 binder::Status VoldNativeService::createUserKey(int32_t userId, int32_t userSerial,
@@ -710,13 +724,22 @@
     return translateBool(fscrypt_destroy_user_key(userId));
 }
 
+static bool token_empty(const std::string& token) {
+    return token.size() == 0 || token == "!";
+}
+
 binder::Status VoldNativeService::addUserKeyAuth(int32_t userId, int32_t userSerial,
                                                  const std::string& token,
                                                  const std::string& secret) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_CRYPT_LOCK;
 
-    return translateBool(fscrypt_add_user_key_auth(userId, userSerial, token, secret));
+    if (!token_empty(token)) {
+        LOG(ERROR) << "Vold doesn't use auth tokens, but non-empty token passed to addUserKeyAuth.";
+        return binder::Status::fromServiceSpecificError(-EINVAL);
+    }
+
+    return translateBool(fscrypt_add_user_key_auth(userId, userSerial, secret));
 }
 
 binder::Status VoldNativeService::clearUserKeyAuth(int32_t userId, int32_t userSerial,
@@ -725,7 +748,13 @@
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_CRYPT_LOCK;
 
-    return translateBool(fscrypt_clear_user_key_auth(userId, userSerial, token, secret));
+    if (!token_empty(token)) {
+        LOG(ERROR)
+                << "Vold doesn't use auth tokens, but non-empty token passed to clearUserKeyAuth.";
+        return binder::Status::fromServiceSpecificError(-EINVAL);
+    }
+
+    return translateBool(fscrypt_clear_user_key_auth(userId, userSerial, secret));
 }
 
 binder::Status VoldNativeService::fixateNewestUserKeyAuth(int32_t userId) {
@@ -735,13 +764,26 @@
     return translateBool(fscrypt_fixate_newest_user_key_auth(userId));
 }
 
+binder::Status VoldNativeService::getUnlockedUsers(std::vector<int>* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_CRYPT_LOCK;
+
+    *_aidl_return = fscrypt_get_unlocked_users();
+    return Ok();
+}
+
 binder::Status VoldNativeService::unlockUserKey(int32_t userId, int32_t userSerial,
                                                 const std::string& token,
                                                 const std::string& secret) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_CRYPT_LOCK;
 
-    return translateBool(fscrypt_unlock_user_key(userId, userSerial, token, secret));
+    if (!token_empty(token)) {
+        LOG(ERROR) << "Vold doesn't use auth tokens, but non-empty token passed to unlockUserKey.";
+        return binder::Status::fromServiceSpecificError(-EINVAL);
+    }
+
+    return translateBool(fscrypt_unlock_user_key(userId, userSerial, secret));
 }
 
 binder::Status VoldNativeService::lockUserKey(int32_t userId) {
@@ -751,7 +793,7 @@
     return translateBool(fscrypt_lock_user_key(userId));
 }
 
-binder::Status VoldNativeService::prepareUserStorage(const std::unique_ptr<std::string>& uuid,
+binder::Status VoldNativeService::prepareUserStorage(const std::optional<std::string>& uuid,
                                                      int32_t userId, int32_t userSerial,
                                                      int32_t flags) {
     ENFORCE_SYSTEM_OR_ROOT;
@@ -763,7 +805,7 @@
     return translateBool(fscrypt_prepare_user_storage(uuid_, userId, userSerial, flags));
 }
 
-binder::Status VoldNativeService::destroyUserStorage(const std::unique_ptr<std::string>& uuid,
+binder::Status VoldNativeService::destroyUserStorage(const std::optional<std::string>& uuid,
                                                      int32_t userId, int32_t flags) {
     ENFORCE_SYSTEM_OR_ROOT;
     std::string empty_string = "";
@@ -891,6 +933,20 @@
     return Ok();
 }
 
+static void initializeIncFs() {
+    // Obtaining IncFS features triggers initialization of IncFS.
+    incfs::features();
+}
+
+binder::Status VoldNativeService::earlyBootEnded() {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    initializeIncFs();
+    Keymaster::earlyBootEnded();
+    return Ok();
+}
+
 binder::Status VoldNativeService::incFsEnabled(bool* _aidl_return) {
     ENFORCE_SYSTEM_OR_ROOT;
 
@@ -962,5 +1018,12 @@
     return translate(incfs::bindMount(sourceDir, targetDir));
 }
 
+binder::Status VoldNativeService::destroyDsuMetadataKey(const std::string& dsuSlot) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    return translateBool(destroy_dsu_metadata_key(dsuSlot));
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 013d1c2..33d0f3a 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -59,7 +59,6 @@
     binder::Status format(const std::string& volId, const std::string& fsType);
     binder::Status benchmark(const std::string& volId,
                              const android::sp<android::os::IVoldTaskListener>& listener);
-    binder::Status checkEncryption(const std::string& volId);
 
     binder::Status moveStorage(const std::string& fromVolId, const std::string& toVolId,
                                const android::sp<android::os::IVoldTaskListener>& listener);
@@ -67,7 +66,10 @@
     binder::Status remountUid(int32_t uid, int32_t remountMode);
     binder::Status remountAppStorageDirs(int uid, int pid,
                                const std::vector<std::string>& packageNames);
+    binder::Status unmountAppStorageDirs(int uid, int pid,
+                               const std::vector<std::string>& packageNames);
 
+    binder::Status ensureAppDirsCreated(const std::vector<std::string>& paths, int32_t appUid);
     binder::Status setupAppDir(const std::string& path, int32_t appUid);
     binder::Status fixupAppDir(const std::string& path, int32_t appUid);
 
@@ -111,7 +113,10 @@
     binder::Status initUser0();
     binder::Status isConvertibleToFbe(bool* _aidl_return);
     binder::Status mountFstab(const std::string& blkDevice, const std::string& mountPoint);
-    binder::Status encryptFstab(const std::string& blkDevice, const std::string& mountPoint);
+    binder::Status encryptFstab(const std::string& blkDevice, const std::string& mountPoint,
+                                bool shouldFormat, const std::string& fsType);
+
+    binder::Status setStorageBindingSeed(const std::vector<uint8_t>& seed);
 
     binder::Status createUserKey(int32_t userId, int32_t userSerial, bool ephemeral);
     binder::Status destroyUserKey(int32_t userId);
@@ -122,13 +127,14 @@
                                     const std::string& secret);
     binder::Status fixateNewestUserKeyAuth(int32_t userId);
 
+    binder::Status getUnlockedUsers(std::vector<int>* _aidl_return);
     binder::Status unlockUserKey(int32_t userId, int32_t userSerial, const std::string& token,
                                  const std::string& secret);
     binder::Status lockUserKey(int32_t userId);
 
-    binder::Status prepareUserStorage(const std::unique_ptr<std::string>& uuid, int32_t userId,
+    binder::Status prepareUserStorage(const std::optional<std::string>& uuid, int32_t userId,
                                       int32_t userSerial, int32_t flags);
-    binder::Status destroyUserStorage(const std::unique_ptr<std::string>& uuid, int32_t userId,
+    binder::Status destroyUserStorage(const std::optional<std::string>& uuid, int32_t userId,
                                       int32_t flags);
 
     binder::Status prepareSandboxForApp(const std::string& packageName, int32_t appId,
@@ -151,6 +157,8 @@
     binder::Status supportsFileCheckpoint(bool* _aidl_return);
     binder::Status resetCheckpoint();
 
+    binder::Status earlyBootEnded();
+
     binder::Status incFsEnabled(bool* _aidl_return) override;
     binder::Status mountIncFs(
             const std::string& backingPath, const std::string& targetDir, int32_t flags,
@@ -160,6 +168,8 @@
             const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
             bool enableReadLogs) override;
     binder::Status bindMount(const std::string& sourceDir, const std::string& targetDir) override;
+
+    binder::Status destroyDsuMetadataKey(const std::string& dsuSlot) override;
 };
 
 }  // namespace vold
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 585d2d5..2697f67 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -198,16 +198,6 @@
     return 0;
 }
 
-int VolumeManager::stop() {
-    CHECK(!mInternalEmulatedVolumes.empty());
-    for (const auto& vol : mInternalEmulatedVolumes) {
-        vol->destroy();
-    }
-    mInternalEmulatedVolumes.clear();
-
-    return 0;
-}
-
 void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
     std::lock_guard<std::mutex> lock(mLock);
 
@@ -379,21 +369,6 @@
     return success ? 0 : -1;
 }
 
-int VolumeManager::linkPrimary(userid_t userId) {
-    if (!GetBoolProperty(android::vold::kPropFuse, false)) {
-        std::string source(mPrimary->getPath());
-        if (mPrimary->isEmulated()) {
-            source = StringPrintf("%s/%d", source.c_str(), userId);
-            fs_prepare_dir(source.c_str(), 0755, AID_ROOT, AID_ROOT);
-        }
-
-        std::string target(StringPrintf("/mnt/user/%d/primary", userId));
-        LOG(DEBUG) << "Linking " << source << " to " << target;
-        Symlink(source, target);
-    }
-    return 0;
-}
-
 void VolumeManager::destroyEmulatedVolumesForUser(userid_t userId) {
     // Destroy and remove all unstacked EmulatedVolumes for the user
     auto i = mInternalEmulatedVolumes.begin();
@@ -474,18 +449,6 @@
         createEmulatedVolumesForUser(userId);
     }
 
-    if (!GetBoolProperty(android::vold::kPropFuse, false)) {
-        // Note that sometimes the system will spin up processes from Zygote
-        // before actually starting the user, so we're okay if Zygote
-        // already created this directory.
-        std::string path(StringPrintf("%s/%d", kPathUserMount, userId));
-        fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT);
-
-        if (mPrimary) {
-            linkPrimary(userId);
-        }
-    }
-
     mStartedUsers.insert(userId);
 
     createPendingDisksIfNeeded();
@@ -522,14 +485,6 @@
     return 0;
 }
 
-int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol) {
-    mPrimary = vol;
-    for (userid_t userId : mStartedUsers) {
-        linkPrimary(userId);
-    }
-    return 0;
-}
-
 // This code is executed after a fork so it's very important that the set of
 // methods we call here is strictly limited.
 //
@@ -588,17 +543,10 @@
         case VoldNativeService::REMOUNT_MODE_DEFAULT:
             storageSource = "/mnt/runtime/default";
             break;
-        case VoldNativeService::REMOUNT_MODE_READ:
-            storageSource = "/mnt/runtime/read";
-            break;
-        case VoldNativeService::REMOUNT_MODE_WRITE:
-        case VoldNativeService::REMOUNT_MODE_LEGACY:
+        case VoldNativeService::REMOUNT_MODE_ANDROID_WRITABLE:
         case VoldNativeService::REMOUNT_MODE_INSTALLER:
             storageSource = "/mnt/runtime/write";
             break;
-        case VoldNativeService::REMOUNT_MODE_FULL:
-            storageSource = "/mnt/runtime/full";
-            break;
         case VoldNativeService::REMOUNT_MODE_PASS_THROUGH:
             return true;
         default:
@@ -728,15 +676,42 @@
     return true;
 }
 
-int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
-    if (GetBoolProperty(android::vold::kPropFuse, false)) {
-        // TODO(135341433): Implement fuse specific logic.
-        return 0;
+// In each app's namespace, unmount obb and data dirs
+static bool umountStorageDirs(int nsFd, const char* android_data_dir, const char* android_obb_dir,
+        int uid, const char* targets[], int size) {
+    // This code is executed after a fork so it's very important that the set of
+    // methods we call here is strictly limited.
+    if (setns(nsFd, CLONE_NEWNS) != 0) {
+        async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns %s", strerror(errno));
+        return false;
     }
-    return scanProcProcesses(uid, static_cast<userid_t>(-1),
-            forkAndRemountChild, &mountMode) ? 0 : -1;
-}
 
+    // Unmount of Android/data/foo needs to be done before Android/data below.
+    bool result = true;
+    for (int i = 0; i < size; i++) {
+        if (TEMP_FAILURE_RETRY(umount2(targets[i], MNT_DETACH)) < 0 && errno != EINVAL &&
+                errno != ENOENT) {
+            async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s: %s",
+                                  targets[i], strerror(errno));
+            result = false;
+        }
+    }
+
+    // Mount tmpfs on Android/data and Android/obb
+    if (TEMP_FAILURE_RETRY(umount2(android_data_dir, MNT_DETACH)) < 0 && errno != EINVAL &&
+                errno != ENOENT) {
+        async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s :%s",
+                        android_data_dir, strerror(errno));
+        result = false;
+    }
+    if (TEMP_FAILURE_RETRY(umount2(android_obb_dir, MNT_DETACH)) < 0 && errno != EINVAL &&
+                errno != ENOENT) {
+        async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s :%s",
+                android_obb_dir, strerror(errno));
+        result = false;
+    }
+    return result;
+}
 
 // In each app's namespace, mount tmpfs on obb and data dir, and bind mount obb and data
 // package dirs.
@@ -796,8 +771,8 @@
             userId, dirName.c_str(), packageName.c_str());
 }
 
-// Fork the process and remount storage
-bool VolumeManager::forkAndRemountStorage(int uid, int pid,
+// Fork the process and remount / unmount app data and obb dirs
+bool VolumeManager::forkAndRemountStorage(int uid, int pid, bool doUnmount,
                                           const std::vector<std::string>& packageNames) {
     userid_t userId = multiuser_get_user_id(uid);
     std::string mnt_path = StringPrintf("/proc/%d/ns/mnt", pid);
@@ -831,17 +806,18 @@
     }
 
     for (int i = 0; i < size; i++) {
-        auto status = EnsureDirExists(sources_cstr[i], 0771, AID_MEDIA_RW, AID_MEDIA_RW);
-        if (status != OK) {
-            PLOG(ERROR) << "Failed to create dir: " << sources_cstr[i];
-            return false;
-        }
         // Make sure /storage/emulated/... paths are setup correctly
-        status = setupAppDir(targets_cstr[i], uid, false /* fixupExistingOnly */);
+        // This needs to be done before EnsureDirExists to ensure Android/ is created.
+        auto status = setupAppDir(targets_cstr[i], uid, false /* fixupExistingOnly */);
         if (status != OK) {
             PLOG(ERROR) << "Failed to create dir: " << targets_cstr[i];
             return false;
         }
+        status = EnsureDirExists(sources_cstr[i], 0771, AID_MEDIA_RW, AID_MEDIA_RW);
+        if (status != OK) {
+            PLOG(ERROR) << "Failed to create dir: " << sources_cstr[i];
+            return false;
+        }
     }
 
     char android_data_dir[PATH_MAX];
@@ -853,11 +829,20 @@
     // Fork a child to mount Android/obb android Android/data dirs, as we don't want it to affect
     // original vold process mount namespace.
     if (!(child = fork())) {
-        if (remountStorageDirs(nsFd, android_data_dir, android_obb_dir, uid,
-                sources_cstr, targets_cstr, size)) {
-            _exit(0);
+        if (doUnmount) {
+            if (umountStorageDirs(nsFd, android_data_dir, android_obb_dir, uid,
+                    targets_cstr, size)) {
+                _exit(0);
+            } else {
+                _exit(1);
+            }
         } else {
-            _exit(1);
+            if (remountStorageDirs(nsFd, android_data_dir, android_obb_dir, uid,
+                    sources_cstr, targets_cstr, size)) {
+                _exit(0);
+            } else {
+                _exit(1);
+            }
         }
     }
 
@@ -882,11 +867,8 @@
     return true;
 }
 
-int VolumeManager::remountAppStorageDirs(int uid, int pid,
-        const std::vector<std::string>& packageNames) {
-    if (!GetBoolProperty(android::vold::kPropFuse, false)) {
-        return 0;
-    }
+int VolumeManager::handleAppStorageDirs(int uid, int pid,
+        bool doUnmount, const std::vector<std::string>& packageNames) {
     // Only run the remount if fuse is mounted for that user.
     userid_t userId = multiuser_get_user_id(uid);
     bool fuseMounted = false;
@@ -900,7 +882,7 @@
         }
     }
     if (fuseMounted) {
-        forkAndRemountStorage(uid, pid, packageNames);
+        forkAndRemountStorage(uid, pid, doUnmount, packageNames);
     }
     return 0;
 }
@@ -994,7 +976,20 @@
     return 0;
 }
 
-int VolumeManager::setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly) {
+int VolumeManager::ensureAppDirsCreated(const std::vector<std::string>& paths, int32_t appUid) {
+    int size = paths.size();
+    for (int i = 0; i < size; i++) {
+        int result = setupAppDir(paths[i], appUid, false /* fixupExistingOnly */,
+                true /* skipIfDirExists */);
+        if (result != OK) {
+            return result;
+        }
+    }
+    return OK;
+}
+
+int VolumeManager::setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly,
+        bool skipIfDirExists) {
     // Only offer to create directories for paths managed by vold
     if (!StartsWith(path, "/storage/")) {
         LOG(ERROR) << "Failed to find mounted volume for " << path;
@@ -1039,11 +1034,18 @@
 
     const std::string volumeRoot = volume->getRootPath();  // eg /data/media/0
 
-    if (fixupExistingOnly && (access(lowerPath.c_str(), F_OK) != 0)) {
+    const int access_result = access(lowerPath.c_str(), F_OK);
+    if (fixupExistingOnly && access_result != 0) {
         // Nothing to fixup
         return OK;
     }
 
+    if (skipIfDirExists && access_result == 0) {
+        // It's safe to assume it's ok as it will be used for zygote to bind mount dir only,
+        // which the dir doesn't need to have correct permission for now yet.
+        return OK;
+    }
+
     if (volume->getType() == VolumeBase::Type::kPublic) {
         // On public volumes, we don't need to setup permissions, as everything goes through
         // FUSE; just create the dirs and be done with it.
@@ -1130,9 +1132,14 @@
     auto vol = std::shared_ptr<android::vold::StubVolume>(
             new android::vold::StubVolume(stubId, sourcePath, mountPath, fsType, fsUuid, fsLabel));
 
-    int32_t passedFlags = android::vold::Disk::Flags::kStub;
+    int32_t passedFlags = 0;
     passedFlags |= (flags & android::vold::Disk::Flags::kUsb);
     passedFlags |= (flags & android::vold::Disk::Flags::kSd);
+    if (flags & android::vold::Disk::Flags::kStubVisible) {
+        passedFlags |= (flags & android::vold::Disk::Flags::kStubVisible);
+    } else {
+        passedFlags |= (flags & android::vold::Disk::Flags::kStubInvisible);
+    }
     // StubDisk doesn't have device node corresponds to it. So, a fake device
     // number is used.
     auto disk = std::shared_ptr<android::vold::Disk>(
diff --git a/VolumeManager.h b/VolumeManager.h
index 3277f75..3573b1a 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -56,7 +56,6 @@
     android::sp<android::os::IVoldListener> getListener() const { return mListener; }
 
     int start();
-    int stop();
 
     void handleBlockEvent(NetlinkEvent* evt);
 
@@ -115,10 +114,9 @@
     void createPendingDisksIfNeeded();
     int onSecureKeyguardStateChanged(bool isShowing);
 
-    int setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol);
-
-    int remountUid(uid_t uid, int32_t remountMode);
-    int remountAppStorageDirs(int uid, int pid, const std::vector<std::string>& packageNames);
+    int remountUid(uid_t uid, int32_t remountMode) { return 0; }
+    int handleAppStorageDirs(int uid, int pid,
+            bool doUnmount, const std::vector<std::string>& packageNames);
 
     /* Aborts all FUSE filesystems, in case the FUSE daemon is no longer up. */
     int abortFuse();
@@ -132,7 +130,8 @@
     int updateVirtualDisk();
     int setDebug(bool enable);
 
-    bool forkAndRemountStorage(int uid, int pid, const std::vector<std::string>& packageNames);
+    bool forkAndRemountStorage(int uid, int pid, bool doUnmount,
+        const std::vector<std::string>& packageNames);
 
     static VolumeManager* Instance();
 
@@ -166,12 +165,16 @@
      * files in the passed in path, but only if that path exists; if it doesn't
      * exist, this function doesn't create them.
      *
+     * If skipIfDirExists is set, we will not fix any existing dirs, we will
+     * only create app dirs if it doesn't exist.
+     *
      * Validates that given paths are absolute and that they contain no relative
      * "." or ".." paths or symlinks.  Last path segment is treated as filename
      * and ignored, unless the path ends with "/".  Also ensures that path
      * belongs to a volume managed by vold.
      */
-    int setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly = false);
+    int setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly = false,
+            bool skipIfDirExists = false);
 
     /**
      * Fixes up an existing application directory, as if it was created with
@@ -180,6 +183,9 @@
      */
     int fixupAppDir(const std::string& path, int32_t appUid);
 
+    // Called before zygote starts to ensure dir exists so zygote can bind mount them.
+    int ensureAppDirsCreated(const std::vector<std::string>& paths, int32_t appUid);
+
     int createObb(const std::string& path, const std::string& key, int32_t ownerGid,
                   std::string* outVolId);
     int destroyObb(const std::string& volId);
diff --git a/CheckEncryption.h b/bench/inodeop_bench/Android.bp
similarity index 60%
rename from CheckEncryption.h
rename to bench/inodeop_bench/Android.bp
index 158d886..3d1769e 100644
--- a/CheckEncryption.h
+++ b/bench/inodeop_bench/Android.bp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,19 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
 
-#ifndef ANDROID_VOLD_CHECK_ENCRYPTION_H
-#define ANDROID_VOLD_CHECK_ENCRYPTION_H
-
-#include <string>
-
-namespace android {
-namespace vold {
-
-/* Check encryption of private volume mounted at the given path */
-int CheckEncryption(const std::string& path);
-
-}  // namespace vold
-}  // namespace android
-
-#endif
+cc_binary {
+    name: "inodeop_bench",
+    srcs: ["inodeop_bench.cpp"],
+}
diff --git a/bench/inodeop_bench/OWNERS b/bench/inodeop_bench/OWNERS
new file mode 100644
index 0000000..3ced4a1
--- /dev/null
+++ b/bench/inodeop_bench/OWNERS
@@ -0,0 +1,3 @@
+balsini@google.com
+stefanoduo@google.com
+zezeozue@google.com
diff --git a/bench/inodeop_bench/inodeop_bench.cpp b/bench/inodeop_bench/inodeop_bench.cpp
new file mode 100644
index 0000000..cf38e4c
--- /dev/null
+++ b/bench/inodeop_bench/inodeop_bench.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2020 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 <chrono>
+#include <functional>
+#include <iostream>
+#include <ratio>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static constexpr char VERSION[] = "0";
+
+// Self-contained class for collecting and reporting benchmark metrics
+// (currently only execution time).
+class Collector {
+    using time_point = std::chrono::time_point<std::chrono::steady_clock>;
+    using time_unit = std::chrono::duration<double, std::milli>;
+
+    struct Metric {
+        std::string workload;
+        time_unit exec_time;
+        Metric(const std::string& workload, const time_unit& exec_time)
+            : workload(workload), exec_time(exec_time) {}
+    };
+
+    static constexpr char TIME_UNIT[] = "ms";
+    static constexpr char VERSION[] = "0";
+    std::vector<Metric> metrics;
+    time_point reset_time;
+
+  public:
+    Collector() { reset(); }
+
+    void reset() { reset_time = std::chrono::steady_clock::now(); }
+
+    void collect_metric(const std::string& workload) {
+        auto elapsed = std::chrono::steady_clock::now() - reset_time;
+        metrics.emplace_back(workload, std::chrono::duration_cast<time_unit>(elapsed));
+    }
+
+    void report_metrics() {
+        for (const Metric& metric : metrics)
+            std::cout << VERSION << ";" << metric.workload << ";" << metric.exec_time.count() << ";"
+                      << TIME_UNIT << std::endl;
+    }
+};
+
+struct Command {
+    static constexpr char CREATE[] = "create";
+    static constexpr char DELETE[] = "delete";
+    static constexpr char MOVE[] = "move";
+    static constexpr char HARDLINK[] = "hardlink";
+    static constexpr char SYMLINK[] = "symlink";
+    static constexpr char READDIR[] = "readdir";
+    std::string workload;
+    std::string from_dir;
+    std::string from_basename;
+    std::string to_dir;
+    std::string to_basename;
+    bool drop_state;
+    int n_file;
+
+    Command() { reset(); }
+
+    std::string to_string() const {
+        std::stringstream string_repr;
+        string_repr << "Command {\n";
+        string_repr << "\t.workload = " << workload << ",\n";
+        string_repr << "\t.from_dir = " << from_dir << ",\n";
+        string_repr << "\t.from_basename = " << from_basename << ",\n";
+        string_repr << "\t.to_dir = " << to_dir << ",\n";
+        string_repr << "\t.to_basename = " << to_basename << ",\n";
+        string_repr << "\t.drop_state = " << drop_state << ",\n";
+        string_repr << "\t.n_file = " << n_file << "\n";
+        string_repr << "}\n";
+        return string_repr.str();
+    }
+
+    void reset() {
+        workload = "";
+        from_dir = to_dir = "./";
+        from_basename = "from_file";
+        to_basename = "to_file";
+        drop_state = true;
+        n_file = 0;
+    }
+};
+
+void print_version() {
+    std::cout << VERSION << std::endl;
+}
+
+void print_commands(const std::vector<Command>& commands) {
+    for (const Command& command : commands) std::cout << command.to_string();
+}
+
+void usage(std::ostream& ostr, const std::string& program_name) {
+    Command command;
+
+    ostr << "Usage: " << program_name << " [global_options] {[workload_options] -w WORKLOAD_T}\n";
+    ostr << "WORKLOAD_T = {" << Command::CREATE << ", " << Command::DELETE << ", " << Command::MOVE
+         << ", " << Command::HARDLINK << ", " << Command::SYMLINK << "}\n";
+    ostr << "Global options\n";
+    ostr << "\t-v: Print version.\n";
+    ostr << "\t-p: Print parsed workloads and exit.\n";
+    ostr << "Workload options\n";
+    ostr << "\t-d DIR\t\t: Work directory for " << Command::CREATE << "/" << Command::DELETE
+         << " (default '" << command.from_dir << "').\n";
+    ostr << "\t-f FROM-DIR\t: Source directory for " << Command::MOVE << "/" << Command::SYMLINK
+         << "/" << Command::HARDLINK << " (default '" << command.from_dir << "').\n";
+    ostr << "\t-t TO-DIR\t: Destination directory for " << Command::MOVE << "/" << Command::SYMLINK
+         << "/" << Command::HARDLINK << " (default '" << command.to_dir << "').\n";
+    ostr << "\t-n N_FILES\t: Number of files to create/delete etc. (default " << command.n_file
+         << ").\n";
+    ostr << "\t-s\t\t: Do not drop state (caches) before running the workload (default "
+         << !command.drop_state << ").\n";
+    ostr << "NOTE: -w WORKLOAD_T defines a new command and must come after its workload_options."
+         << std::endl;
+}
+
+void drop_state() {
+    // Drop inode/dentry/page caches.
+    std::system("sync; echo 3 > /proc/sys/vm/drop_caches");
+}
+
+static constexpr int OPEN_DIR_FLAGS = O_RDONLY | O_DIRECTORY | O_PATH | O_CLOEXEC;
+
+bool delete_files(const std::string& dir, int n_file, const std::string& basename) {
+    int dir_fd = open(dir.c_str(), OPEN_DIR_FLAGS);
+    if (dir_fd == -1) {
+        int error = errno;
+        std::cerr << "Failed to open work directory '" << dir << "', error '" << strerror(error)
+                  << "'." << std::endl;
+        return false;
+    }
+
+    bool ret = true;
+    for (int i = 0; i < n_file; i++) {
+        std::string filename = basename + std::to_string(i);
+        ret = ret && (unlinkat(dir_fd, filename.c_str(), 0) == 0);
+    }
+
+    if (!ret) std::cerr << "Failed to delete at least one of the files" << std::endl;
+    close(dir_fd);
+    return ret;
+}
+
+bool create_files(const std::string& dir, int n_file, const std::string& basename) {
+    int dir_fd = open(dir.c_str(), OPEN_DIR_FLAGS);
+    if (dir_fd == -1) {
+        int error = errno;
+        std::cerr << "Failed to open work directory '" << dir << "', error '" << strerror(error)
+                  << "'." << std::endl;
+        return false;
+    }
+
+    bool ret = true;
+    for (int i = 0; i < n_file; i++) {
+        std::string filename = basename + std::to_string(i);
+        int fd = openat(dir_fd, filename.c_str(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0777);
+        ret = ret && fd != -1;
+        close(fd);
+    }
+
+    close(dir_fd);
+    if (!ret) {
+        std::cerr << "Failed to open at least one of the files" << std::endl;
+        delete_files(dir, n_file, basename);
+    }
+    return ret;
+}
+
+bool move_files(const std::string& from_dir, const std::string& to_dir, int n_file,
+                const std::string& from_basename, const std::string& to_basename) {
+    int from_dir_fd = open(from_dir.c_str(), OPEN_DIR_FLAGS);
+    if (from_dir_fd == -1) {
+        int error = errno;
+        std::cerr << "Failed to open source directory '" << from_dir << "', error '"
+                  << strerror(error) << "'." << std::endl;
+        return false;
+    }
+    int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
+    if (to_dir_fd == -1) {
+        int error = errno;
+        std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
+                  << strerror(error) << "'." << std::endl;
+        close(from_dir_fd);
+        return false;
+    }
+
+    bool ret = true;
+    for (int i = 0; i < n_file; i++) {
+        std::string from_filename = from_basename + std::to_string(i);
+        std::string to_filename = to_basename + std::to_string(i);
+        ret = ret &&
+              (renameat(from_dir_fd, from_filename.c_str(), to_dir_fd, to_filename.c_str()) == 0);
+    }
+
+    if (!ret) std::cerr << "Failed to move at least one of the files" << std::endl;
+    close(from_dir_fd);
+    close(from_dir_fd);
+    return ret;
+}
+
+bool hardlink_files(const std::string& from_dir, const std::string& to_dir, int n_file,
+                    const std::string& from_basename, const std::string& to_basename) {
+    int from_dir_fd = open(from_dir.c_str(), OPEN_DIR_FLAGS);
+    if (from_dir_fd == -1) {
+        int error = errno;
+        std::cerr << "Failed to open source directory '" << from_dir << "', error '"
+                  << strerror(error) << "'." << std::endl;
+        return false;
+    }
+    int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
+    if (to_dir_fd == -1) {
+        int error = errno;
+        std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
+                  << strerror(error) << "'." << std::endl;
+        close(from_dir_fd);
+        return false;
+    }
+
+    bool ret = true;
+    for (int i = 0; i < n_file; i++) {
+        std::string from_filename = from_basename + std::to_string(i);
+        std::string to_filename = to_basename + std::to_string(i);
+        ret = ret &&
+              (linkat(from_dir_fd, from_filename.c_str(), to_dir_fd, to_filename.c_str(), 0) == 0);
+    }
+
+    if (!ret) std::cerr << "Failed to hardlink at least one of the files" << std::endl;
+    close(from_dir_fd);
+    close(to_dir_fd);
+    return ret;
+}
+
+bool symlink_files(std::string from_dir, const std::string& to_dir, int n_file,
+                   const std::string& from_basename, const std::string& to_basename) {
+    if (from_dir.back() != '/') from_dir.push_back('/');
+    int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
+    if (to_dir_fd == -1) {
+        int error = errno;
+        std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
+                  << strerror(error) << "'." << std::endl;
+        return false;
+    }
+
+    bool ret = true;
+    for (int i = 0; i < n_file; i++) {
+        std::string from_filepath = from_dir + from_basename + std::to_string(i);
+        std::string to_filename = to_basename + std::to_string(i);
+        ret = ret && (symlinkat(from_filepath.c_str(), to_dir_fd, to_filename.c_str()) == 0);
+    }
+
+    if (!ret) std::cerr << "Failed to symlink at least one of the files" << std::endl;
+    close(to_dir_fd);
+    return ret;
+}
+
+bool exhaustive_readdir(const std::string& from_dir) {
+    DIR* dir = opendir(from_dir.c_str());
+    if (dir == nullptr) {
+        int error = errno;
+        std::cerr << "Failed to open working directory '" << from_dir << "', error '"
+                  << strerror(error) << "'." << std::endl;
+        return false;
+    }
+
+    errno = 0;
+    while (readdir(dir) != nullptr)
+        ;
+    // In case of failure readdir returns nullptr and sets errno accordingly (to
+    // something != 0).
+    // In case of success readdir != nullptr and errno is not changed.
+    // Source: man 3 readdir.
+    bool ret = errno == 0;
+    closedir(dir);
+    return ret;
+}
+
+void create_workload(Collector* collector, const Command& command) {
+    if (command.drop_state) drop_state();
+    collector->reset();
+    if (create_files(command.from_dir, command.n_file, command.from_basename))
+        collector->collect_metric(command.workload);
+
+    delete_files(command.from_dir, command.n_file, command.from_basename);
+}
+
+void delete_workload(Collector* collector, const Command& command) {
+    if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+    if (command.drop_state) drop_state();
+    collector->reset();
+    if (delete_files(command.from_dir, command.n_file, command.from_basename))
+        collector->collect_metric(command.workload);
+}
+
+void move_workload(Collector* collector, const Command& command) {
+    if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+    if (command.drop_state) drop_state();
+    collector->reset();
+    if (move_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
+                   command.to_basename))
+        collector->collect_metric(command.workload);
+
+    delete_files(command.to_dir, command.n_file, command.to_basename);
+}
+
+void hardlink_workload(Collector* collector, const Command& command) {
+    if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+    if (command.drop_state) drop_state();
+    collector->reset();
+    if (hardlink_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
+                       command.to_basename))
+        collector->collect_metric(command.workload);
+
+    delete_files(command.from_dir, command.n_file, command.from_basename);
+    delete_files(command.to_dir, command.n_file, command.to_basename);
+}
+
+void symlink_workload(Collector* collector, const Command& command) {
+    if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+    if (command.drop_state) drop_state();
+    collector->reset();
+    if (symlink_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
+                      command.to_basename))
+        collector->collect_metric(command.workload);
+
+    delete_files(command.to_dir, command.n_file, command.to_basename);
+    delete_files(command.from_dir, command.n_file, command.from_basename);
+}
+
+void readdir_workload(Collector* collector, const Command& command) {
+    if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+    if (command.drop_state) drop_state();
+    collector->reset();
+    if (exhaustive_readdir(command.from_dir)) collector->collect_metric(command.workload);
+
+    delete_files(command.from_dir, command.n_file, command.from_basename);
+}
+
+using workload_executor_t = std::function<void(Collector*, const Command&)>;
+
+std::unordered_map<std::string, workload_executor_t> executors = {
+        {Command::CREATE, create_workload},   {Command::DELETE, delete_workload},
+        {Command::MOVE, move_workload},       {Command::HARDLINK, hardlink_workload},
+        {Command::SYMLINK, symlink_workload}, {Command::READDIR, readdir_workload}};
+
+int main(int argc, char** argv) {
+    std::vector<Command> commands;
+    Command command;
+    int opt;
+
+    while ((opt = getopt(argc, argv, "hvpsw:d:f:t:n:")) != -1) {
+        switch (opt) {
+            case 'h':
+                usage(std::cout, argv[0]);
+                return EXIT_SUCCESS;
+            case 'v':
+                print_version();
+                return EXIT_SUCCESS;
+            case 'p':
+                print_commands(commands);
+                return EXIT_SUCCESS;
+            case 's':
+                command.drop_state = false;
+                break;
+            case 'w':
+                command.workload = optarg;
+                commands.push_back(command);
+                command.reset();
+                break;
+            case 'd':
+            case 'f':
+                command.from_dir = optarg;
+                break;
+            case 't':
+                command.to_dir = optarg;
+                break;
+            case 'n':
+                command.n_file = std::stoi(optarg);
+                break;
+            default:
+                usage(std::cerr, argv[0]);
+                return EXIT_FAILURE;
+        }
+    }
+
+    Collector collector;
+    for (const Command& command : commands) {
+        auto executor = executors.find(command.workload);
+        if (executor == executors.end()) continue;
+        executor->second(&collector, command);
+    }
+    collector.report_metrics();
+
+    return EXIT_SUCCESS;
+}
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 54b86d0..62685e5 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -48,16 +48,17 @@
     void unmount(@utf8InCpp String volId);
     void format(@utf8InCpp String volId, @utf8InCpp String fsType);
     void benchmark(@utf8InCpp String volId, IVoldTaskListener listener);
-    void checkEncryption(@utf8InCpp String volId);
 
     void moveStorage(@utf8InCpp String fromVolId, @utf8InCpp String toVolId,
                      IVoldTaskListener listener);
 
     void remountUid(int uid, int remountMode);
     void remountAppStorageDirs(int uid, int pid, in @utf8InCpp String[] packageNames);
+    void unmountAppStorageDirs(int uid, int pid, in @utf8InCpp String[] packageNames);
 
     void setupAppDir(@utf8InCpp String path, int appUid);
     void fixupAppDir(@utf8InCpp String path, int appUid);
+    void ensureAppDirsCreated(in @utf8InCpp String[] paths, int appUid);
 
     @utf8InCpp String createObb(@utf8InCpp String sourcePath, @utf8InCpp String sourceKey,
                                 int ownerGid);
@@ -88,7 +89,9 @@
     void initUser0();
     boolean isConvertibleToFbe();
     void mountFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint);
-    void encryptFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint);
+    void encryptFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint, boolean shouldFormat, @utf8InCpp String fsType);
+
+    void setStorageBindingSeed(in byte[] seed);
 
     void createUserKey(int userId, int userSerial, boolean ephemeral);
     void destroyUserKey(int userId);
@@ -99,6 +102,7 @@
                         @utf8InCpp String secret);
     void fixateNewestUserKeyAuth(int userId);
 
+    int[] getUnlockedUsers();
     void unlockUserKey(int userId, int userSerial, @utf8InCpp String token,
                        @utf8InCpp String secret);
     void lockUserKey(int userId);
@@ -127,6 +131,7 @@
     boolean supportsFileCheckpoint();
     void resetCheckpoint();
 
+    void earlyBootEnded();
     @utf8InCpp String createStubVolume(@utf8InCpp String sourcePath,
             @utf8InCpp String mountPath, @utf8InCpp String fsType,
             @utf8InCpp String fsUuid, @utf8InCpp String fsLabel, int flags);
@@ -140,6 +145,8 @@
     void setIncFsMountOptions(in IncrementalFileSystemControlParcel control, boolean enableReadLogs);
     void bindMount(@utf8InCpp String sourceDir, @utf8InCpp String targetDir);
 
+    void destroyDsuMetadataKey(@utf8InCpp String dsuSlot);
+
     const int ENCRYPTION_FLAG_NO_UI = 4;
 
     const int ENCRYPTION_STATE_NONE = 1;
@@ -168,13 +175,9 @@
 
     const int REMOUNT_MODE_NONE = 0;
     const int REMOUNT_MODE_DEFAULT = 1;
-    const int REMOUNT_MODE_READ = 2;
-    const int REMOUNT_MODE_WRITE = 3;
-    const int REMOUNT_MODE_LEGACY = 4;
-    const int REMOUNT_MODE_INSTALLER = 5;
-    const int REMOUNT_MODE_FULL = 6;
-    const int REMOUNT_MODE_PASS_THROUGH = 7;
-    const int REMOUNT_MODE_ANDROID_WRITABLE = 8;
+    const int REMOUNT_MODE_INSTALLER = 2;
+    const int REMOUNT_MODE_PASS_THROUGH = 3;
+    const int REMOUNT_MODE_ANDROID_WRITABLE = 4;
 
     const int VOLUME_STATE_UNMOUNTED = 0;
     const int VOLUME_STATE_CHECKING = 1;
diff --git a/cryptfs.cpp b/cryptfs.cpp
index 8b7ac0a..5764b5d 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -29,6 +29,7 @@
 #include "VoldUtil.h"
 #include "VolumeManager.h"
 
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -80,6 +81,7 @@
 using android::vold::CryptoType;
 using android::vold::KeyBuffer;
 using android::vold::KeyGeneration;
+using namespace android::vold;
 using namespace android::dm;
 using namespace std::chrono_literals;
 
@@ -90,6 +92,8 @@
 #define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
 #define CRYPT_PERSIST_DATA_SIZE 0x1000
 
+#define CRYPT_SECTOR_SIZE 512
+
 #define MAX_CRYPTO_TYPE_NAME_LEN 64
 
 #define MAX_KEY_LEN 48
@@ -98,9 +102,7 @@
 
 /* definitions of flags in the structure below */
 #define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
-#define CRYPT_ENCRYPTION_IN_PROGRESS       \
-    0x2 /* Encryption partially completed, \
-           encrypted_upto valid*/
+#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* no longer used */
 #define CRYPT_INCONSISTENT_STATE                    \
     0x4 /* Set when starting encryption, clear when \
            exit cleanly, either through success or  \
@@ -195,12 +197,8 @@
     __le8 N_factor;        /* (1 << N) */
     __le8 r_factor;        /* (1 << r) */
     __le8 p_factor;        /* (1 << p) */
-    __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
-                              we have to stop (e.g. power low) this is the last
-                              encrypted 512 byte sector.*/
-    __le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
-                                                     set, hash of first block, used
-                                                     to validate before continuing*/
+    __le64 encrypted_upto; /* no longer used */
+    __le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* no longer used */
 
     /* key_master key, used to sign the derived key which is then used to generate
      * the intermediate key
@@ -330,9 +328,43 @@
     return KeyGeneration{get_crypto_type().get_keysize(), true, false};
 }
 
-/* Should we use keymaster? */
-static int keymaster_check_compatibility() {
-    return keymaster_compatibility_cryptfs_scrypt();
+static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
+                                uint32_t* out_size) {
+    if (!buffer || !out_size) {
+        LOG(ERROR) << "Missing target pointers";
+        return false;
+    }
+    *out_size = towrite.size();
+    if (buffer_size < towrite.size()) {
+        LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size();
+        return false;
+    }
+    memset(buffer, '\0', buffer_size);
+    std::copy(towrite.begin(), towrite.end(), buffer);
+    return true;
+}
+
+static int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                                   uint32_t ratelimit, uint8_t* key_buffer,
+                                                   uint32_t key_buffer_size,
+                                                   uint32_t* key_out_size) {
+    if (key_out_size) {
+        *key_out_size = 0;
+    }
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return -1;
+    }
+    auto keyParams = km::AuthorizationSetBuilder()
+                             .RsaSigningKey(rsa_key_size, rsa_exponent)
+                             .NoDigestOrPadding()
+                             .Authorization(km::TAG_NO_AUTH_REQUIRED)
+                             .Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
+    std::string key;
+    if (!dev.generateKey(keyParams, &key)) return -1;
+    if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
+    return 0;
 }
 
 /* Create a new keymaster key and store it in this footer */
@@ -356,6 +388,79 @@
     return 0;
 }
 
+static int keymaster_sign_object_for_cryptfs_scrypt(struct crypt_mnt_ftr* ftr, uint32_t ratelimit,
+                                                    const uint8_t* object, const size_t object_size,
+                                                    uint8_t** signature_buffer,
+                                                    size_t* signature_buffer_size) {
+    if (!object || !signature_buffer || !signature_buffer_size) {
+        LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+        return -1;
+    }
+
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return -1;
+    }
+
+    km::AuthorizationSet outParams;
+    std::string key(reinterpret_cast<const char*>(ftr->keymaster_blob), ftr->keymaster_blob_size);
+    std::string input(reinterpret_cast<const char*>(object), object_size);
+    std::string output;
+    KeymasterOperation op;
+
+    auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding().Authorization(
+            km::TAG_PURPOSE, km::KeyPurpose::SIGN);
+    while (true) {
+        op = dev.begin(key, paramBuilder, &outParams);
+        if (op.getErrorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
+            sleep(ratelimit);
+            continue;
+        } else
+            break;
+    }
+
+    if (!op) {
+        LOG(ERROR) << "Error starting keymaster signature transaction: "
+                   << int32_t(op.getErrorCode());
+        return -1;
+    }
+
+    if (op.getUpgradedBlob()) {
+        write_string_to_buf(*op.getUpgradedBlob(), ftr->keymaster_blob, KEYMASTER_BLOB_SIZE,
+                            &ftr->keymaster_blob_size);
+
+        SLOGD("Upgrading key");
+        if (put_crypt_ftr_and_key(ftr) != 0) {
+            SLOGE("Failed to write upgraded key to disk");
+            return -1;
+        }
+        SLOGD("Key upgraded successfully");
+    }
+
+    if (!op.updateCompletely(input, &output)) {
+        LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+                   << int32_t(op.getErrorCode());
+        return -1;
+    }
+
+    if (!op.finish(&output)) {
+        LOG(ERROR) << "Error finalizing keymaster signature transaction: "
+                   << int32_t(op.getErrorCode());
+        return -1;
+    }
+
+    *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+    if (*signature_buffer == nullptr) {
+        LOG(ERROR) << "Error allocation buffer for keymaster signature";
+        return -1;
+    }
+    *signature_buffer_size = output.size();
+    std::copy(output.data(), output.data() + output.size(), *signature_buffer);
+
+    return 0;
+}
+
 /* This signs the given object using the keymaster key. */
 static int keymaster_sign_object(struct crypt_mnt_ftr* ftr, const unsigned char* object,
                                  const size_t object_size, unsigned char** signature,
@@ -393,31 +498,8 @@
             SLOGE("Unknown KDF type %d", ftr->kdf_type);
             return -1;
     }
-    for (;;) {
-        auto result = keymaster_sign_object_for_cryptfs_scrypt(
-            ftr->keymaster_blob, ftr->keymaster_blob_size, KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign,
-            to_sign_size, signature, signature_size);
-        switch (result) {
-            case KeymasterSignResult::ok:
-                return 0;
-            case KeymasterSignResult::upgrade:
-                break;
-            default:
-                return -1;
-        }
-        SLOGD("Upgrading key");
-        if (keymaster_upgrade_key_for_cryptfs_scrypt(
-                RSA_KEY_SIZE, RSA_EXPONENT, KEYMASTER_CRYPTFS_RATE_LIMIT, ftr->keymaster_blob,
-                ftr->keymaster_blob_size, ftr->keymaster_blob, KEYMASTER_BLOB_SIZE,
-                &ftr->keymaster_blob_size) != 0) {
-            SLOGE("Failed to upgrade key");
-            return -1;
-        }
-        if (put_crypt_ftr_and_key(ftr) != 0) {
-            SLOGE("Failed to write upgraded key to disk");
-        }
-        SLOGD("Key upgraded successfully");
-    }
+    return keymaster_sign_object_for_cryptfs_scrypt(ftr, KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign,
+                                                    to_sign_size, signature, signature_size);
 }
 
 /* Store password when userdata is successfully decrypted and mounted.
@@ -1747,7 +1829,6 @@
     char tmp_mount_point[64];
     unsigned int orig_failed_decrypt_count;
     int rc;
-    int use_keymaster = 0;
     int upgrade = 0;
     unsigned char* intermediate_key = 0;
     size_t intermediate_key_size = 0;
@@ -1829,15 +1910,9 @@
         rc = 0;
 
         // Upgrade if we're not using the latest KDF.
-        use_keymaster = keymaster_check_compatibility();
-        if (crypt_ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
-            // Don't allow downgrade
-        } else if (use_keymaster == 1 && crypt_ftr->kdf_type != KDF_SCRYPT_KEYMASTER) {
+        if (crypt_ftr->kdf_type != KDF_SCRYPT_KEYMASTER) {
             crypt_ftr->kdf_type = KDF_SCRYPT_KEYMASTER;
             upgrade = 1;
-        } else if (use_keymaster == 0 && crypt_ftr->kdf_type != KDF_SCRYPT) {
-            crypt_ftr->kdf_type = KDF_SCRYPT;
-            upgrade = 1;
         }
 
         if (upgrade) {
@@ -2041,20 +2116,7 @@
     ftr->minor_version = CURRENT_MINOR_VERSION;
     ftr->ftr_size = sizeof(struct crypt_mnt_ftr);
     ftr->keysize = get_crypto_type().get_keysize();
-
-    switch (keymaster_check_compatibility()) {
-        case 1:
-            ftr->kdf_type = KDF_SCRYPT_KEYMASTER;
-            break;
-
-        case 0:
-            ftr->kdf_type = KDF_SCRYPT;
-            break;
-
-        default:
-            SLOGE("keymaster_check_compatibility failed");
-            return -1;
-    }
+    ftr->kdf_type = KDF_SCRYPT_KEYMASTER;
 
     get_device_scrypt_params(ftr);
 
@@ -2069,61 +2131,6 @@
 
 #define FRAMEWORK_BOOT_WAIT 60
 
-static int cryptfs_SHA256_fileblock(const char* filename, __le8* buf) {
-    int fd = open(filename, O_RDONLY | O_CLOEXEC);
-    if (fd == -1) {
-        SLOGE("Error opening file %s", filename);
-        return -1;
-    }
-
-    char block[CRYPT_INPLACE_BUFSIZE];
-    memset(block, 0, sizeof(block));
-    if (unix_read(fd, block, sizeof(block)) < 0) {
-        SLOGE("Error reading file %s", filename);
-        close(fd);
-        return -1;
-    }
-
-    close(fd);
-
-    SHA256_CTX c;
-    SHA256_Init(&c);
-    SHA256_Update(&c, block, sizeof(block));
-    SHA256_Final(buf, &c);
-
-    return 0;
-}
-
-static int cryptfs_enable_all_volumes(struct crypt_mnt_ftr* crypt_ftr, const char* crypto_blkdev,
-                                      const char* real_blkdev, int previously_encrypted_upto) {
-    off64_t cur_encryption_done = 0, tot_encryption_size = 0;
-    int rc = -1;
-
-    /* The size of the userdata partition, and add in the vold volumes below */
-    tot_encryption_size = crypt_ftr->fs_size;
-
-    rc = cryptfs_enable_inplace(crypto_blkdev, real_blkdev, crypt_ftr->fs_size, &cur_encryption_done,
-                                tot_encryption_size, previously_encrypted_upto, true);
-
-    if (rc == ENABLE_INPLACE_ERR_DEV) {
-        /* Hack for b/17898962 */
-        SLOGE("cryptfs_enable: crypto block dev failure. Must reboot...\n");
-        cryptfs_reboot(RebootType::reboot);
-    }
-
-    if (!rc) {
-        crypt_ftr->encrypted_upto = cur_encryption_done;
-    }
-
-    if (!rc && crypt_ftr->encrypted_upto == crypt_ftr->fs_size) {
-        /* The inplace routine never actually sets the progress to 100% due
-         * to the round down nature of integer division, so set it here */
-        property_set("vold.encrypt_progress", "100");
-    }
-
-    return rc;
-}
-
 static int vold_unmountAll(void) {
     VolumeManager* vm = VolumeManager::Instance();
     return vm->unmountAll();
@@ -2140,26 +2147,21 @@
     char lockid[32] = {0};
     std::string key_loc;
     int num_vols;
-    off64_t previously_encrypted_upto = 0;
     bool rebootEncryption = false;
     bool onlyCreateHeader = false;
-    std::unique_ptr<android::wakelock::WakeLock> wakeLock = nullptr;
+
+    /* Get a wakelock as this may take a while, and we don't want the
+     * device to sleep on us.  We'll grab a partial wakelock, and if the UI
+     * wants to keep the screen on, it can grab a full wakelock.
+     */
+    snprintf(lockid, sizeof(lockid), "enablecrypto%d", (int)getpid());
+    auto wl = android::wakelock::WakeLock::tryGet(lockid);
+    if (!wl.has_value()) {
+        return android::UNEXPECTED_NULL;
+    }
 
     if (get_crypt_ftr_and_key(&crypt_ftr) == 0) {
-        if (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS) {
-            /* An encryption was underway and was interrupted */
-            previously_encrypted_upto = crypt_ftr.encrypted_upto;
-            crypt_ftr.encrypted_upto = 0;
-            crypt_ftr.flags &= ~CRYPT_ENCRYPTION_IN_PROGRESS;
-
-            /* At this point, we are in an inconsistent state. Until we successfully
-               complete encryption, a reboot will leave us broken. So mark the
-               encryption failed in case that happens.
-               On successfully completing encryption, remove this flag */
-            crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
-
-            put_crypt_ftr_and_key(&crypt_ftr);
-        } else if (crypt_ftr.flags & CRYPT_FORCE_ENCRYPTION) {
+        if (crypt_ftr.flags & CRYPT_FORCE_ENCRYPTION) {
             if (!check_ftr_sha(&crypt_ftr)) {
                 memset(&crypt_ftr, 0, sizeof(crypt_ftr));
                 put_crypt_ftr_and_key(&crypt_ftr);
@@ -2177,7 +2179,7 @@
     }
 
     property_get("ro.crypto.state", encrypted_state, "");
-    if (!strcmp(encrypted_state, "encrypted") && !previously_encrypted_upto) {
+    if (!strcmp(encrypted_state, "encrypted")) {
         SLOGE("Device is already running encrypted, aborting");
         goto error_unencrypted;
     }
@@ -2205,13 +2207,6 @@
         }
     }
 
-    /* Get a wakelock as this may take a while, and we don't want the
-     * device to sleep on us.  We'll grab a partial wakelock, and if the UI
-     * wants to keep the screen on, it can grab a full wakelock.
-     */
-    snprintf(lockid, sizeof(lockid), "enablecrypto%d", (int)getpid());
-    wakeLock = std::make_unique<android::wakelock::WakeLock>(lockid);
-
     /* The init files are setup to stop the class main and late start when
      * vold sets trigger_shutdown_framework.
      */
@@ -2264,7 +2259,7 @@
 
     /* Start the actual work of making an encrypted filesystem */
     /* Initialize a crypt_mnt_ftr for the partition */
-    if (previously_encrypted_upto == 0 && !rebootEncryption) {
+    if (!rebootEncryption) {
         if (cryptfs_init_crypt_mnt_ftr(&crypt_ftr)) {
             goto error_shutting_down;
         }
@@ -2339,77 +2334,46 @@
     }
 
     decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
-    create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev.c_str(), &crypto_blkdev,
-                          CRYPTO_BLOCK_DEVICE, 0);
-
-    /* If we are continuing, check checksums match */
-    rc = 0;
-    if (previously_encrypted_upto) {
-        __le8 hash_first_block[SHA256_DIGEST_LENGTH];
-        rc = cryptfs_SHA256_fileblock(crypto_blkdev.c_str(), hash_first_block);
-
-        if (!rc &&
-            memcmp(hash_first_block, crypt_ftr.hash_first_block, sizeof(hash_first_block)) != 0) {
-            SLOGE("Checksums do not match - trigger wipe");
-            rc = -1;
-        }
-    }
-
+    rc = create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev.c_str(),
+                               &crypto_blkdev, CRYPTO_BLOCK_DEVICE, 0);
     if (!rc) {
-        rc = cryptfs_enable_all_volumes(&crypt_ftr, crypto_blkdev.c_str(), real_blkdev.data(),
-                                        previously_encrypted_upto);
-    }
-
-    /* Calculate checksum if we are not finished */
-    if (!rc && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {
-        rc = cryptfs_SHA256_fileblock(crypto_blkdev.c_str(), crypt_ftr.hash_first_block);
-        if (rc) {
-            SLOGE("Error calculating checksum for continuing encryption");
+        if (encrypt_inplace(crypto_blkdev, real_blkdev, crypt_ftr.fs_size, true)) {
+            crypt_ftr.encrypted_upto = crypt_ftr.fs_size;
+            rc = 0;
+        } else {
             rc = -1;
         }
+        /* Undo the dm-crypt mapping whether we succeed or not */
+        delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
     }
 
-    /* Undo the dm-crypt mapping whether we succeed or not */
-    delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
-
     if (!rc) {
         /* Success */
         crypt_ftr.flags &= ~CRYPT_INCONSISTENT_STATE;
 
-        if (crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {
-            SLOGD("Encrypted up to sector %lld - will continue after reboot",
-                  crypt_ftr.encrypted_upto);
-            crypt_ftr.flags |= CRYPT_ENCRYPTION_IN_PROGRESS;
-        }
-
         put_crypt_ftr_and_key(&crypt_ftr);
 
-        if (crypt_ftr.encrypted_upto == crypt_ftr.fs_size) {
-            char value[PROPERTY_VALUE_MAX];
-            property_get("ro.crypto.state", value, "");
-            if (!strcmp(value, "")) {
-                /* default encryption - continue first boot sequence */
-                property_set("ro.crypto.state", "encrypted");
-                property_set("ro.crypto.type", "block");
-                wakeLock.reset(nullptr);
-                if (rebootEncryption && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
-                    // Bring up cryptkeeper that will check the password and set it
-                    property_set("vold.decrypt", "trigger_shutdown_framework");
-                    sleep(2);
-                    property_set("vold.encrypt_progress", "");
-                    cryptfs_trigger_restart_min_framework();
-                } else {
-                    cryptfs_check_passwd(DEFAULT_PASSWORD);
-                    cryptfs_restart_internal(1);
-                }
-                return 0;
+        char value[PROPERTY_VALUE_MAX];
+        property_get("ro.crypto.state", value, "");
+        if (!strcmp(value, "")) {
+            /* default encryption - continue first boot sequence */
+            property_set("ro.crypto.state", "encrypted");
+            property_set("ro.crypto.type", "block");
+            wl.reset();
+            if (rebootEncryption && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
+                // Bring up cryptkeeper that will check the password and set it
+                property_set("vold.decrypt", "trigger_shutdown_framework");
+                sleep(2);
+                property_set("vold.encrypt_progress", "");
+                cryptfs_trigger_restart_min_framework();
             } else {
-                sleep(2); /* Give the UI a chance to show 100% progress */
-                cryptfs_reboot(RebootType::reboot);
+                cryptfs_check_passwd(DEFAULT_PASSWORD);
+                cryptfs_restart_internal(1);
             }
+            return 0;
         } else {
-            sleep(2); /* Partially encrypted, ensure writes flushed to ssd */
-            cryptfs_reboot(RebootType::shutdown);
+            sleep(2); /* Give the UI a chance to show 100% progress */
+            cryptfs_reboot(RebootType::reboot);
         }
     } else {
         char value[PROPERTY_VALUE_MAX];
diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp
index 34f1024..7782dd3 100644
--- a/fs/Exfat.cpp
+++ b/fs/Exfat.cpp
@@ -41,7 +41,7 @@
 status_t Check(const std::string& source) {
     std::vector<std::string> cmd;
     cmd.push_back(kFsckPath);
-    cmd.push_back("-a");
+    cmd.push_back("-y");
     cmd.push_back(source);
 
     int rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext);
diff --git a/fs/F2fs.cpp b/fs/F2fs.cpp
index 9b8d2c4..f4a81ee 100644
--- a/fs/F2fs.cpp
+++ b/fs/F2fs.cpp
@@ -20,6 +20,7 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <logwrap/logwrap.h>
 #include <fscrypt/fscrypt.h>
 
 #include <string>
@@ -71,40 +72,46 @@
 }
 
 status_t Format(const std::string& source) {
-    std::vector<std::string> cmd;
-    cmd.push_back(kMkfsPath);
+    std::vector<char const*> cmd;
+    cmd.emplace_back(kMkfsPath);
 
-    cmd.push_back("-f");
-    cmd.push_back("-d1");
+    cmd.emplace_back("-f");
+    cmd.emplace_back("-d1");
 
     if (android::base::GetBoolProperty("vold.has_quota", false)) {
-        cmd.push_back("-O");
-        cmd.push_back("quota");
+        cmd.emplace_back("-O");
+        cmd.emplace_back("quota");
     }
     if (fscrypt_is_native()) {
-        cmd.push_back("-O");
-        cmd.push_back("encrypt");
+        cmd.emplace_back("-O");
+        cmd.emplace_back("encrypt");
     }
-
-    cmd.push_back("-O");
-    cmd.push_back("verity");
+    if (android::base::GetBoolProperty("vold.has_compress", false)) {
+        cmd.emplace_back("-O");
+        cmd.emplace_back("compression");
+        cmd.emplace_back("-O");
+        cmd.emplace_back("extra_attr");
+    }
+    cmd.emplace_back("-O");
+    cmd.emplace_back("verity");
 
     const bool needs_casefold =
             android::base::GetBoolProperty("external_storage.casefold.enabled", false);
     const bool needs_projid =
             android::base::GetBoolProperty("external_storage.projid.enabled", false);
     if (needs_projid) {
-        cmd.push_back("-O");
-        cmd.push_back("project_quota,extra_attr");
+        cmd.emplace_back("-O");
+        cmd.emplace_back("project_quota,extra_attr");
     }
     if (needs_casefold) {
-        cmd.push_back("-O");
-        cmd.push_back("casefold");
-        cmd.push_back("-C");
-        cmd.push_back("utf8");
+        cmd.emplace_back("-O");
+        cmd.emplace_back("casefold");
+        cmd.emplace_back("-C");
+        cmd.emplace_back("utf8");
     }
-    cmd.push_back(source);
-    return ForkExecvp(cmd);
+    cmd.emplace_back(source.c_str());
+    return logwrap_fork_execvp(cmd.size(), cmd.data(), nullptr, false, LOG_KLOG,
+                             false, nullptr);
 }
 
 }  // namespace f2fs
diff --git a/fscrypt_uapi.h b/fscrypt_uapi.h
deleted file mode 100644
index 3cda96e..0000000
--- a/fscrypt_uapi.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef _UAPI_LINUX_FSCRYPT_VOLD_H
-#define _UAPI_LINUX_FSCRYPT_VOLD_H
-
-#include <linux/fscrypt.h>
-#include <linux/types.h>
-
-#define FSCRYPT_ADD_KEY_FLAG_WRAPPED 0x01
-
-struct sys_fscrypt_add_key_arg {
-    struct fscrypt_key_specifier key_spec;
-    __u32 raw_size;
-    __u32 key_id;
-    __u32 __reserved[7];
-    __u32 flags;
-    __u8 raw[];
-};
-
-struct sys_fscrypt_provisioning_key_payload {
-    __u32 type;
-    __u32 __reserved;
-    __u8 raw[];
-};
-
-#define fscrypt_add_key_arg sys_fscrypt_add_key_arg
-#define fscrypt_provisioning_key_payload sys_fscrypt_provisioning_key_payload
-
-#endif  //_UAPI_LINUX_FSCRYPT_VOLD_H
diff --git a/main.cpp b/main.cpp
index ebe5510..1f85fb5 100644
--- a/main.cpp
+++ b/main.cpp
@@ -41,8 +41,14 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-static int process_config(VolumeManager* vm, bool* has_adoptable, bool* has_quota,
-                          bool* has_reserved);
+typedef struct vold_configs {
+    bool has_adoptable : 1;
+    bool has_quota : 1;
+    bool has_reserved : 1;
+    bool has_compress : 1;
+} VoldConfigs;
+
+static int process_config(VolumeManager* vm, VoldConfigs* configs);
 static void coldboot(const char* path);
 static void parse_args(int argc, char** argv);
 
@@ -100,11 +106,8 @@
         exit(1);
     }
 
-    bool has_adoptable;
-    bool has_quota;
-    bool has_reserved;
-
-    if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {
+    VoldConfigs configs = {};
+    if (process_config(vm, &configs)) {
         PLOG(ERROR) << "Error reading configuration... continuing anyways";
     }
 
@@ -128,9 +131,10 @@
 
     // This call should go after listeners are started to avoid
     // a deadlock between vold and init (see b/34278978 for details)
-    android::base::SetProperty("vold.has_adoptable", has_adoptable ? "1" : "0");
-    android::base::SetProperty("vold.has_quota", has_quota ? "1" : "0");
-    android::base::SetProperty("vold.has_reserved", has_reserved ? "1" : "0");
+    android::base::SetProperty("vold.has_adoptable", configs.has_adoptable ? "1" : "0");
+    android::base::SetProperty("vold.has_quota", configs.has_quota ? "1" : "0");
+    android::base::SetProperty("vold.has_reserved", configs.has_reserved ? "1" : "0");
+    android::base::SetProperty("vold.has_compress", configs.has_compress ? "1" : "0");
 
     // Do coldboot here so it won't block booting,
     // also the cold boot is needed in case we have flash drive
@@ -213,8 +217,7 @@
     }
 }
 
-static int process_config(VolumeManager* vm, bool* has_adoptable, bool* has_quota,
-                          bool* has_reserved) {
+static int process_config(VolumeManager* vm, VoldConfigs* configs) {
     ATRACE_NAME("process_config");
 
     if (!ReadDefaultFstab(&fstab_default)) {
@@ -223,19 +226,24 @@
     }
 
     /* Loop through entries looking for ones that vold manages */
-    *has_adoptable = false;
-    *has_quota = false;
-    *has_reserved = false;
+    configs->has_adoptable = false;
+    configs->has_quota = false;
+    configs->has_reserved = false;
+    configs->has_compress = false;
     for (auto& entry : fstab_default) {
         if (entry.fs_mgr_flags.quota) {
-            *has_quota = true;
+            configs->has_quota = true;
         }
         if (entry.reserved_size > 0) {
-            *has_reserved = true;
+            configs->has_reserved = true;
+        }
+        if (entry.fs_mgr_flags.fs_compress) {
+            configs->has_compress = true;
         }
 
         /* Make sure logical partitions have an updated blk_device. */
-        if (entry.fs_mgr_flags.logical && !fs_mgr_update_logical_partition(&entry)) {
+        if (entry.fs_mgr_flags.logical && !fs_mgr_update_logical_partition(&entry) &&
+            !entry.fs_mgr_flags.no_fail) {
             PLOG(FATAL) << "could not find logical partition " << entry.blk_device;
         }
 
@@ -251,7 +259,7 @@
 
             if (entry.is_encryptable()) {
                 flags |= android::vold::Disk::Flags::kAdoptable;
-                *has_adoptable = true;
+                configs->has_adoptable = true;
             }
             if (entry.fs_mgr_flags.no_emulated_sd ||
                 android::base::GetBoolProperty("vold.debug.default_primary", false)) {
diff --git a/model/Disk.h b/model/Disk.h
index 99c98fc..16476dc 100644
--- a/model/Disk.h
+++ b/model/Disk.h
@@ -53,9 +53,12 @@
         kUsb = 1 << 3,
         /* Flag that disk is EMMC internal */
         kEmmc = 1 << 4,
-        /* Flag that disk is Stub disk, i.e., disk that is managed from outside
-         * Android (e.g., ARC++). */
-        kStub = 1 << 5,
+        /* Flag that disk is an invisible Stub disk, i.e., disk that is managed from outside
+         * Android (e.g., ARC++) and invisible to apps. */
+        kStubInvisible = 1 << 5,
+        /* Flag that disk is a visible Stub disk, i.e., disk that is managed from outside
+         * Android (e.g., ARC++) and visible to apps. */
+        kStubVisible = 1 << 6,
     };
 
     const std::string& getId() const { return mId; }
@@ -120,7 +123,7 @@
 
     int getMaxMinors();
 
-    bool isStub() { return mFlags & kStub; }
+    bool isStub() { return (mFlags & kStubInvisible) || (mFlags & kStubVisible); }
 
     DISALLOW_COPY_AND_ASSIGN(Disk);
 };
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index db93bc2..6f21ff8 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -116,24 +116,22 @@
     }
 
     status_t status = OK;
-    // When app data isolation is enabled, obb/ will be mounted per app, otherwise we should
-    // bind mount the whole Android/ to speed up reading.
-    if (!mAppDataIsolationEnabled) {
-        std::string androidDataSource = StringPrintf("%s/data", androidSource.c_str());
-        std::string androidDataTarget(
-                StringPrintf("/mnt/user/%d/%s/%d/Android/data", userId, label.c_str(), userId));
-        status = doFuseBindMount(androidDataSource, androidDataTarget, pathsToUnmount);
-        if (status != OK) {
-            return status;
-        }
+    // Zygote will unmount these dirs if app data isolation is enabled, so apps
+    // cannot access these dirs directly.
+    std::string androidDataSource = StringPrintf("%s/data", androidSource.c_str());
+    std::string androidDataTarget(
+            StringPrintf("/mnt/user/%d/%s/%d/Android/data", userId, label.c_str(), userId));
+    status = doFuseBindMount(androidDataSource, androidDataTarget, pathsToUnmount);
+    if (status != OK) {
+        return status;
+    }
 
-        std::string androidObbSource = StringPrintf("%s/obb", androidSource.c_str());
-        std::string androidObbTarget(
-                StringPrintf("/mnt/user/%d/%s/%d/Android/obb", userId, label.c_str(), userId));
-        status = doFuseBindMount(androidObbSource, androidObbTarget, pathsToUnmount);
-        if (status != OK) {
-            return status;
-        }
+    std::string androidObbSource = StringPrintf("%s/obb", androidSource.c_str());
+    std::string androidObbTarget(
+            StringPrintf("/mnt/user/%d/%s/%d/Android/obb", userId, label.c_str(), userId));
+    status = doFuseBindMount(androidObbSource, androidObbTarget, pathsToUnmount);
+    if (status != OK) {
+        return status;
     }
 
     // Installers get the same view as all other apps, with the sole exception that the
@@ -150,44 +148,8 @@
         if (status != OK) {
             return status;
         }
-    } else if (mAppDataIsolationEnabled) {
-        std::string obbSource(StringPrintf("%s/obb", androidSource.c_str()));
-        std::string obbInstallerTarget(StringPrintf("/mnt/installer/%d/%s/%d/Android/obb",
-                userId, label.c_str(), userId));
-
-        status = doFuseBindMount(obbSource, obbInstallerTarget, pathsToUnmount);
-        if (status != OK) {
-            return status;
-        }
     }
 
-    // /mnt/androidwriteable is similar to /mnt/installer, but it's for
-    // MOUNT_EXTERNAL_ANDROID_WRITABLE apps and it can also access DATA (Android/data) dirs.
-    if (mAppDataIsolationEnabled) {
-        std::string obbSource = mUseSdcardFs ?
-            StringPrintf("/mnt/runtime/write/%s/%d/Android/obb", label.c_str(), userId)
-            : StringPrintf("%s/obb", androidSource.c_str());
-
-        std::string obbAndroidWritableTarget(
-                StringPrintf("/mnt/androidwritable/%d/%s/%d/Android/obb",
-                userId, label.c_str(), userId));
-
-        status = doFuseBindMount(obbSource, obbAndroidWritableTarget, pathsToUnmount);
-        if (status != OK) {
-            return status;
-        }
-
-        std::string dataSource = mUseSdcardFs ?
-                StringPrintf("/mnt/runtime/write/%s/%d/Android/data", label.c_str(), userId)
-                : StringPrintf("%s/data", androidSource.c_str());
-        std::string dataTarget(StringPrintf("/mnt/androidwritable/%d/%s/%d/Android/data",
-                userId, label.c_str(), userId));
-
-        status = doFuseBindMount(dataSource, dataTarget, pathsToUnmount);
-        if (status != OK) {
-            return status;
-        }
-    }
     unmount_guard.Disable();
     return OK;
 }
@@ -229,28 +191,31 @@
     // umount the whole Android/ dir.
     if (mAppDataIsolationEnabled) {
         std::string appObbDir(StringPrintf("%s/%d/Android/obb", getPath().c_str(), userId));
-        KillProcessesWithMountPrefix(appObbDir);
-    } else {
-        std::string androidDataTarget(
-                StringPrintf("/mnt/user/%d/%s/%d/Android/data", userId, label.c_str(), userId));
-
-        LOG(INFO) << "Unmounting " << androidDataTarget;
-        auto status = UnmountTree(androidDataTarget);
-        if (status != OK) {
-            return status;
-        }
-        LOG(INFO) << "Unmounted " << androidDataTarget;
-
-        std::string androidObbTarget(
-                StringPrintf("/mnt/user/%d/%s/%d/Android/obb", userId, label.c_str(), userId));
-
-        LOG(INFO) << "Unmounting " << androidObbTarget;
-        status = UnmountTree(androidObbTarget);
-        if (status != OK) {
-            return status;
-        }
-        LOG(INFO) << "Unmounted " << androidObbTarget;
+        // Here we assume obb/data dirs is mounted as tmpfs, then it must be caused by
+        // app data isolation.
+        KillProcessesWithTmpfsMountPrefix(appObbDir);
     }
+
+    // Always unmount data and obb dirs as they are mounted to lowerfs for speeding up access.
+    std::string androidDataTarget(
+            StringPrintf("/mnt/user/%d/%s/%d/Android/data", userId, label.c_str(), userId));
+
+    LOG(INFO) << "Unmounting " << androidDataTarget;
+    auto status = UnmountTree(androidDataTarget);
+    if (status != OK) {
+        return status;
+    }
+    LOG(INFO) << "Unmounted " << androidDataTarget;
+
+    std::string androidObbTarget(
+            StringPrintf("/mnt/user/%d/%s/%d/Android/obb", userId, label.c_str(), userId));
+
+    LOG(INFO) << "Unmounting " << androidObbTarget;
+    status = UnmountTree(androidObbTarget);
+    if (status != OK) {
+        return status;
+    }
+    LOG(INFO) << "Unmounted " << androidObbTarget;
     return OK;
 }
 
@@ -301,8 +266,6 @@
 
     dev_t before = GetDevice(mSdcardFsFull);
 
-    bool isFuse = base::GetBoolProperty(kPropFuse, false);
-
     // Mount sdcardfs regardless of FUSE, since we need it to bind-mount on top of the
     // FUSE volume for various reasons.
     if (mUseSdcardFs && getMountUserId() == 0) {
@@ -350,7 +313,7 @@
         sdcardFsPid = 0;
     }
 
-    if (isFuse && isVisible) {
+    if (isVisible) {
         // Make sure we unmount sdcardfs if we bail out with an error below
         auto sdcardfs_unmounter = [&]() {
             LOG(INFO) << "sdcardfs_unmounter scope_guard running";
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index d40e3e3..12e31ff 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -227,39 +227,36 @@
         TEMP_FAILURE_RETRY(waitpid(sdcardFsPid, nullptr, 0));
     }
 
-    bool isFuse = base::GetBoolProperty(kPropFuse, false);
-    if (isFuse) {
-        // We need to mount FUSE *after* sdcardfs, since the FUSE daemon may depend
-        // on sdcardfs being up.
-        LOG(INFO) << "Mounting public fuse volume";
-        android::base::unique_fd fd;
-        int user_id = getMountUserId();
-        int result = MountUserFuse(user_id, getInternalPath(), stableName, &fd);
+    // We need to mount FUSE *after* sdcardfs, since the FUSE daemon may depend
+    // on sdcardfs being up.
+    LOG(INFO) << "Mounting public fuse volume";
+    android::base::unique_fd fd;
+    int user_id = getMountUserId();
+    int result = MountUserFuse(user_id, getInternalPath(), stableName, &fd);
 
-        if (result != 0) {
-            LOG(ERROR) << "Failed to mount public fuse volume";
-            doUnmount();
-            return -result;
-        }
-
-        mFuseMounted = true;
-        auto callback = getMountCallback();
-        if (callback) {
-            bool is_ready = false;
-            callback->onVolumeChecking(std::move(fd), getPath(), getInternalPath(), &is_ready);
-            if (!is_ready) {
-                LOG(ERROR) << "Failed to complete public volume mount";
-                doUnmount();
-                return -EIO;
-            }
-        }
-
-        ConfigureReadAheadForFuse(GetFuseMountPathForUser(user_id, stableName), 256u);
-
-        // See comment in model/EmulatedVolume.cpp
-        ConfigureMaxDirtyRatioForFuse(GetFuseMountPathForUser(user_id, stableName), 40u);
+    if (result != 0) {
+        LOG(ERROR) << "Failed to mount public fuse volume";
+        doUnmount();
+        return -result;
     }
 
+    mFuseMounted = true;
+    auto callback = getMountCallback();
+    if (callback) {
+        bool is_ready = false;
+        callback->onVolumeChecking(std::move(fd), getPath(), getInternalPath(), &is_ready);
+        if (!is_ready) {
+            LOG(ERROR) << "Failed to complete public volume mount";
+            doUnmount();
+            return -EIO;
+        }
+    }
+
+    ConfigureReadAheadForFuse(GetFuseMountPathForUser(user_id, stableName), 256u);
+
+    // See comment in model/EmulatedVolume.cpp
+    ConfigureMaxDirtyRatioForFuse(GetFuseMountPathForUser(user_id, stableName), 40u);
+
     return OK;
 }
 
diff --git a/model/VolumeEncryption.cpp b/model/VolumeEncryption.cpp
index 5b0e73d..e6a55a9 100644
--- a/model/VolumeEncryption.cpp
+++ b/model/VolumeEncryption.cpp
@@ -32,16 +32,16 @@
 enum class VolumeMethod { kFailed, kCrypt, kDefaultKey };
 
 static VolumeMethod lookup_volume_method() {
-    constexpr uint64_t pre_gki_level = 29;
     auto first_api_level =
             android::base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
     auto method = android::base::GetProperty("ro.crypto.volume.metadata.method", "default");
     if (method == "default") {
-        return first_api_level > pre_gki_level ? VolumeMethod::kDefaultKey : VolumeMethod::kCrypt;
+        return first_api_level > __ANDROID_API_Q__ ? VolumeMethod::kDefaultKey
+                                                   : VolumeMethod::kCrypt;
     } else if (method == "dm-default-key") {
         return VolumeMethod::kDefaultKey;
     } else if (method == "dm-crypt") {
-        if (first_api_level > pre_gki_level) {
+        if (first_api_level > __ANDROID_API_Q__) {
             LOG(ERROR) << "volume encryption method dm-crypt cannot be used, "
                           "ro.product.first_api_level = "
                        << first_api_level;
diff --git a/secdiscard.cpp b/secdiscard.cpp
index 4659eed..b91f321 100644
--- a/secdiscard.cpp
+++ b/secdiscard.cpp
@@ -50,6 +50,27 @@
 
 }  // namespace
 
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v5.9-rc1.
+//   9af846486d78 ("f2fs: add F2FS_IOC_SEC_TRIM_FILE ioctl")
+// In android12-5.4,
+//   7fc27297c44d ("Merge remote-tracking branch 'aosp/upstream-f2fs-stable-linux-5.4.y'
+//   into android12-5.4")
+#ifndef F2FS_IOC_SEC_TRIM_FILE
+struct f2fs_sectrim_range {
+    __u64 start;
+    __u64 len;
+    __u64 flags;
+};
+#define F2FS_IOC_SEC_TRIM_FILE _IOW(F2FS_IOCTL_MAGIC, 20, struct f2fs_sectrim_range)
+#define F2FS_TRIM_FILE_DISCARD 0x1
+#define F2FS_TRIM_FILE_ZEROOUT 0x2
+#endif
+
 int main(int argc, const char* const argv[]) {
     android::base::InitLogging(const_cast<char**>(argv));
     Options options;
@@ -69,9 +90,6 @@
 // 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)
 #define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
 #endif
@@ -85,8 +103,31 @@
         ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
 
         LOG(DEBUG) << "Securely discarding '" << target << "' unlink=" << options.unlink;
-        if (!secdiscard_path(target)) {
-            LOG(ERROR) << "Secure discard failed for: " << target;
+        struct f2fs_sectrim_range secRange;
+        secRange.start = 0;
+        secRange.len = -1;  // until end of file
+        secRange.flags = F2FS_TRIM_FILE_DISCARD | F2FS_TRIM_FILE_ZEROOUT;
+        /*
+         * F2FS_IOC_SEC_TRIM_FILE is only supported by F2FS.
+         * 1. If device supports secure discard, it sends secure discard command on the file.
+         * 2. Otherwise, it sends discard command on the file.
+         * 3. Lastly, it overwrites zero data on it.
+         */
+        int ret = ioctl(fd, F2FS_IOC_SEC_TRIM_FILE, &secRange);
+        if (ret != 0) {
+            if (errno == EOPNOTSUPP) {
+                // If device doesn't support any type of discard, just overwrite zero data.
+                secRange.flags = F2FS_TRIM_FILE_ZEROOUT;
+                ret = ioctl(fd, F2FS_IOC_SEC_TRIM_FILE, &secRange);
+            }
+            if (ret != 0 && errno != ENOTTY) {
+                PLOG(WARNING) << "F2FS_IOC_SEC_TRIM_FILE failed on " << target;
+            }
+        }
+        if (ret != 0) {
+            if (!secdiscard_path(target)) {
+                LOG(ERROR) << "Secure discard failed for: " << target;
+            }
         }
         if (options.unlink) {
             if (unlink(target.c_str()) != 0 && errno != ENOENT) {
diff --git a/tests/Android.bp b/tests/Android.bp
index b90de3a..cad96fd 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_test {
     name: "vold_tests",
     defaults: [
diff --git a/tests/Utils_test.cpp b/tests/Utils_test.cpp
index d18dc67..35b40cd 100644
--- a/tests/Utils_test.cpp
+++ b/tests/Utils_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android-base/file.h>
 #include <gtest/gtest.h>
 
 #include "../Utils.h"
@@ -43,5 +44,23 @@
     ASSERT_EQ("QUUX", tmp);
 }
 
+TEST_F(UtilsTest, MkdirsSyncTest) {
+    TemporaryDir temp_dir;
+    std::string temp_dir_path;
+
+    ASSERT_TRUE(android::base::Realpath(temp_dir.path, &temp_dir_path));
+
+    ASSERT_FALSE(pathExists(temp_dir_path + "/a"));
+    ASSERT_TRUE(MkdirsSync(temp_dir_path + "/a/b/c", 0700));
+    ASSERT_TRUE(pathExists(temp_dir_path + "/a"));
+    ASSERT_TRUE(pathExists(temp_dir_path + "/a/b"));
+    // The final component of the path should not be created; only the previous
+    // components should be.
+    ASSERT_FALSE(pathExists(temp_dir_path + "/a/b/c"));
+
+    // Currently, MkdirsSync() only supports absolute paths.
+    ASSERT_FALSE(MkdirsSync("foo", 0700));
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/vdc.cpp b/vdc.cpp
index c0b798d..47d98de 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -31,9 +31,10 @@
 #include "android/os/IVold.h"
 
 #include <android-base/logging.h>
+#include <android-base/parsebool.h>
 #include <android-base/parseint.h>
-#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <binder/IServiceManager.h>
 #include <binder/Status.h>
 
@@ -105,12 +106,14 @@
         checkStatus(args, vold->shutdown());
     } else if (args[0] == "volume" && args[1] == "reset") {
         checkStatus(args, vold->reset());
-    } else if (args[0] == "cryptfs" && args[1] == "checkEncryption" && args.size() == 3) {
-        checkStatus(args, vold->checkEncryption(args[2]));
     } else if (args[0] == "cryptfs" && args[1] == "mountFstab" && args.size() == 4) {
         checkStatus(args, vold->mountFstab(args[2], args[3]));
-    } else if (args[0] == "cryptfs" && args[1] == "encryptFstab" && args.size() == 4) {
-        checkStatus(args, vold->encryptFstab(args[2], args[3]));
+    } else if (args[0] == "cryptfs" && args[1] == "encryptFstab" && args.size() == 6) {
+        auto shouldFormat = android::base::ParseBool(args[4]);
+        if (shouldFormat == android::base::ParseBoolResult::kError) exit(EINVAL);
+        checkStatus(args, vold->encryptFstab(args[2], args[3],
+                                             shouldFormat == android::base::ParseBoolResult::kTrue,
+                                             args[5]));
     } else if (args[0] == "checkpoint" && args[1] == "supportsCheckpoint" && args.size() == 2) {
         bool supported = false;
         checkStatus(args, vold->supportsCheckpoint(&supported));
@@ -154,6 +157,8 @@
         checkStatus(args, vold->abortChanges(args[2], retry != 0));
     } else if (args[0] == "checkpoint" && args[1] == "resetCheckpoint") {
         checkStatus(args, vold->resetCheckpoint());
+    } else if (args[0] == "keymaster" && args[1] == "earlyBootEnded") {
+        checkStatus(args, vold->earlyBootEnded());
     } else {
         LOG(ERROR) << "Raw commands are no longer supported";
         exit(EINVAL);
diff --git a/vold_prepare_subdirs.cpp b/vold_prepare_subdirs.cpp
index d624d73..e2afb81 100644
--- a/vold_prepare_subdirs.cpp
+++ b/vold_prepare_subdirs.cpp
@@ -54,20 +54,33 @@
     return s.size() < 40 && s.find_first_not_of("0123456789abcdefABCDEF-_") == std::string::npos;
 }
 
-static bool prepare_dir(struct selabel_handle* sehandle, mode_t mode, uid_t uid, gid_t gid,
-                        const std::string& path) {
+static bool prepare_dir_for_user(struct selabel_handle* sehandle, mode_t mode, uid_t uid, gid_t gid,
+                                 const std::string& path, uid_t user_id) {
     auto clearfscreatecon = android::base::make_scope_guard([] { setfscreatecon(nullptr); });
     auto secontext = std::unique_ptr<char, void (*)(char*)>(nullptr, freecon);
-    char* tmp_secontext;
-    if (sehandle && selabel_lookup(sehandle, &tmp_secontext, path.c_str(), S_IFDIR) == 0) {
-        secontext.reset(tmp_secontext);
+    if (sehandle) {
+        char* tmp_secontext;
+
+        if (selabel_lookup(sehandle, &tmp_secontext, path.c_str(), S_IFDIR) == 0) {
+            secontext.reset(tmp_secontext);
+
+            if (user_id != (uid_t)-1) {
+                if (selinux_android_context_with_level(secontext.get(), &tmp_secontext, user_id,
+                                                       (uid_t)-1) != 0) {
+                    PLOG(ERROR) << "Unable to create context with level for: " << path;
+                    return false;
+                }
+                secontext.reset(tmp_secontext);  // Free the context
+            }
+        }
     }
+
     LOG(DEBUG) << "Setting up mode " << std::oct << mode << std::dec << " uid " << uid << " gid "
                << gid << " context " << (secontext ? secontext.get() : "null")
                << " on path: " << path;
     if (secontext) {
         if (setfscreatecon(secontext.get()) != 0) {
-            PLOG(ERROR) << "Unable to read setfscreatecon for: " << path;
+            PLOG(ERROR) << "Unable to setfscreatecon for: " << path;
             return false;
         }
     }
@@ -93,6 +106,11 @@
     return true;
 }
 
+static bool prepare_dir(struct selabel_handle* sehandle, mode_t mode, uid_t uid, gid_t gid,
+                        const std::string& path) {
+    return prepare_dir_for_user(sehandle, mode, uid, gid, path, (uid_t)-1);
+}
+
 static bool rmrf_contents(const std::string& path) {
     auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(path.c_str()), closedir);
     if (!dirp) {
@@ -148,8 +166,13 @@
 static bool prepare_subdirs(const std::string& volume_uuid, int user_id, int flags) {
     struct selabel_handle* sehandle = selinux_android_file_context_handle();
 
-    if (volume_uuid.empty()) {
-        if (flags & android::os::IVold::STORAGE_FLAG_DE) {
+    if (flags & android::os::IVold::STORAGE_FLAG_DE) {
+        auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
+        if (!prepare_dir_for_user(sehandle, 0771, AID_SYSTEM, AID_SYSTEM, user_de_path, user_id)) {
+            return false;
+        }
+
+        if (volume_uuid.empty()) {
             auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/vold")) return false;
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/storaged")) return false;
@@ -158,6 +181,12 @@
             prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/apexrollback");
             prepare_apex_subdirs(sehandle, misc_de_path);
 
+            auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);
+            if (!prepare_dir_for_user(sehandle, 0771, AID_SYSTEM, AID_SYSTEM, profiles_de_path,
+                                      user_id)) {
+                return false;
+            }
+
             auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id);
             if (!prepare_dir(sehandle, 0700, AID_SYSTEM, AID_SYSTEM, vendor_de_path + "/fpdata")) {
                 return false;
@@ -167,11 +196,19 @@
                 return false;
             }
         }
-        if (flags & android::os::IVold::STORAGE_FLAG_CE) {
+    }
+    if (flags & android::os::IVold::STORAGE_FLAG_CE) {
+        auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
+        if (!prepare_dir_for_user(sehandle, 0771, AID_SYSTEM, AID_SYSTEM, user_ce_path, user_id)) {
+            return false;
+        }
+
+        if (volume_uuid.empty()) {
             auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/vold")) return false;
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/storaged")) return false;
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/rollback")) return false;
+
             // TODO: Return false if this returns false once sure this should succeed.
             prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/apexrollback");
             prepare_apex_subdirs(sehandle, misc_ce_path);