Merge "libutils: RefBase: extra check for double own"
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 79c3ac7..46190f2 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1683,10 +1683,9 @@
     return size;
 }
 
-static void fb_perform_format(
-                              const std::string& partition, int skip_if_not_supported,
+static void fb_perform_format(const std::string& partition, int skip_if_not_supported,
                               const std::string& type_override, const std::string& size_override,
-                              const std::string& initial_dir, const unsigned fs_options) {
+                              const unsigned fs_options) {
     std::string partition_type, partition_size;
 
     struct fastboot_buffer buf;
@@ -1748,8 +1747,7 @@
     eraseBlkSize = fb_get_flash_block_size("erase-block-size");
     logicalBlkSize = fb_get_flash_block_size("logical-block-size");
 
-    if (fs_generator_generate(gen, output.path, size, initial_dir,
-            eraseBlkSize, logicalBlkSize, fs_options)) {
+    if (fs_generator_generate(gen, output.path, size, eraseBlkSize, logicalBlkSize, fs_options)) {
         die("Cannot generate image for %s", partition.c_str());
     }
 
@@ -2091,7 +2089,7 @@
             std::string partition = next_arg(&args);
 
             auto format = [&](const std::string& partition) {
-                fb_perform_format(partition, 0, type_override, size_override, "", fs_options);
+                fb_perform_format(partition, 0, type_override, size_override, fs_options);
             };
             do_for_partitions(partition, slot_override, format, true);
         } else if (command == "signature") {
@@ -2282,7 +2280,7 @@
             }
             if (partition_type.empty()) continue;
             fb->Erase(partition);
-            fb_perform_format(partition, 1, partition_type, "", "", fs_options);
+            fb_perform_format(partition, 1, partition_type, "", fs_options);
         }
     }
     if (wants_set_active) {
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index d268a50..c8d1b59 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -111,8 +111,7 @@
 }
 #endif
 
-static int generate_ext4_image(const char* fileName, long long partSize,
-                               const std::string& initial_dir, unsigned eraseBlkSize,
+static int generate_ext4_image(const char* fileName, long long partSize, unsigned eraseBlkSize,
                                unsigned logicalBlkSize, const unsigned fsOptions) {
     static constexpr int block_size = 4096;
     const std::string exec_dir = android::base::GetExecutableDirectory();
@@ -163,16 +162,7 @@
     if (ret != 0) {
         return -1;
     }
-
-    if (initial_dir.empty()) {
-        return 0;
-    }
-
-    const std::string e2fsdroid_path = exec_dir + "/e2fsdroid";
-    std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
-                                               fileName, nullptr};
-
-    return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
+    return 0;
 }
 
 enum {
@@ -188,8 +178,7 @@
     // clang-format on
 };
 
-static int generate_f2fs_image(const char* fileName, long long partSize,
-                               const std::string& initial_dir, unsigned /* unused */,
+static int generate_f2fs_image(const char* fileName, long long partSize, unsigned /* unused */,
                                unsigned /* unused */, const unsigned fsOptions) {
     const std::string exec_dir = android::base::GetExecutableDirectory();
     const std::string mkf2fs_path = exec_dir + "/make_f2fs";
@@ -227,19 +216,6 @@
     if (ret != 0) {
         return -1;
     }
-
-    if (initial_dir.empty()) {
-        return 0;
-    }
-
-    const std::string sload_path = exec_dir + "/sload_f2fs";
-    std::vector<const char*> sload_args = {sload_path.c_str(), "-S",
-                                       "-f", initial_dir.c_str(), fileName, nullptr};
-
-    ret = exec_cmd(sload_args[0], sload_args.data(), nullptr);
-    if (ret != 0 && ret != FSCK_ERROR_CORRECTED) {
-        return -1;
-    }
     return 0;
 }
 
@@ -247,8 +223,8 @@
     const char* fs_type;  //must match what fastboot reports for partition type
 
     //returns 0 or error value
-    int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
-                    unsigned eraseBlkSize, unsigned logicalBlkSize, const unsigned fsOptions);
+    int (*generate)(const char* fileName, long long partSize, unsigned eraseBlkSize,
+                    unsigned logicalBlkSize, const unsigned fsOptions);
 
 } generators[] = {
     { "ext4", generate_ext4_image},
@@ -265,7 +241,7 @@
 }
 
 int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
-                          const std::string& initial_dir, unsigned eraseBlkSize,
-                          unsigned logicalBlkSize, const unsigned fsOptions) {
-    return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize, fsOptions);
+                          unsigned eraseBlkSize, unsigned logicalBlkSize,
+                          const unsigned fsOptions) {
+    return gen->generate(fileName, partSize, eraseBlkSize, logicalBlkSize, fsOptions);
 }
diff --git a/fastboot/fs.h b/fastboot/fs.h
index f832938..5ae473b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -13,5 +13,5 @@
 
 const struct fs_generator* fs_get_generator(const std::string& fs_type);
 int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
-                          const std::string& initial_dir, unsigned eraseBlkSize = 0,
-                          unsigned logicalBlkSize = 0, unsigned fsOptions = 0);
+                          unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0,
+                          unsigned fsOptions = 0);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f1071b0..43961da 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -439,34 +439,6 @@
     return fstab_result;
 }
 
-// Return the path to the fstab file.  There may be multiple fstab files; the
-// one that is returned will be the first that exists of fstab.<fstab_suffix>,
-// fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for
-// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
-// the first stage ramdisk during early boot.  Previously, the first stage
-// ramdisk's copy of the fstab had to be located in the root directory, but now
-// the system/etc directory is supported too and is the preferred location.
-std::string GetFstabPath() {
-    for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
-        std::string suffix;
-
-        if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
-
-        for (const char* prefix : {// late-boot/post-boot locations
-                                   "/odm/etc/fstab.", "/vendor/etc/fstab.",
-                                   // early boot locations
-                                   "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
-                                   "/fstab.", "/first_stage_ramdisk/fstab."}) {
-            std::string fstab_path = prefix + suffix;
-            if (access(fstab_path.c_str(), F_OK) == 0) {
-                return fstab_path;
-            }
-        }
-    }
-
-    return "";
-}
-
 /* Extracts <device>s from the by-name symlinks specified in a fstab:
  *   /dev/block/<type>/<device>/by-name/<partition>
  *
@@ -526,6 +498,34 @@
 
 }  // namespace
 
+// Return the path to the fstab file.  There may be multiple fstab files; the
+// one that is returned will be the first that exists of fstab.<fstab_suffix>,
+// fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot.  Previously, the first stage
+// ramdisk's copy of the fstab had to be located in the root directory, but now
+// the system/etc directory is supported too and is the preferred location.
+std::string GetFstabPath() {
+    for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+        std::string suffix;
+
+        if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
+
+        for (const char* prefix : {// late-boot/post-boot locations
+                                   "/odm/etc/fstab.", "/vendor/etc/fstab.",
+                                   // early boot locations
+                                   "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+                                   "/fstab.", "/first_stage_ramdisk/fstab."}) {
+            std::string fstab_path = prefix + suffix;
+            if (access(fstab_path.c_str(), F_OK) == 0) {
+                return fstab_path;
+            }
+        }
+    }
+
+    return "";
+}
+
 bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) {
     const int expected_fields = proc_mounts ? 4 : 5;
 
@@ -804,7 +804,7 @@
 
     std::string default_fstab_path;
     // Use different fstab paths for normal boot and recovery boot, respectively
-    if (access("/system/bin/recovery", F_OK) == 0) {
+    if ((access("/sbin/recovery", F_OK) == 0) || (access("/system/bin/recovery", F_OK) == 0)) {
         default_fstab_path = "/etc/recovery.fstab";
     } else {  // normal boot
         default_fstab_path = GetFstabPath();
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 82b5275..07eaf58 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -642,6 +642,10 @@
     if (ret) {
         PERROR << "__mount(target=" << mount_point
                << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
+        // If "/system" doesn't look like a mountpoint, retry with "/".
+        if (errno == EINVAL && mount_point == "/system") {
+            return fs_mgr_overlayfs_set_shared_mount("/", shared_flag);
+        }
         return false;
     }
     return true;
@@ -1140,7 +1144,13 @@
         return 0;
     }
 
-    return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+    auto ideal_size = std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+
+    // Align up to the filesystem block size.
+    if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {
+        ideal_size += s.f_bsize - remainder;
+    }
+    return ideal_size;
 }
 
 static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 8f200a8..689d18b 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -95,6 +95,8 @@
 
 // Exported for testability. Regular users should use ReadFstabFromFile().
 bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
+// Exported for testability. Regular users should use ReadDefaultFstab().
+std::string GetFstabPath();
 
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
 bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 7472949..fb104b7 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -14,11 +14,13 @@
 // limitations under the License.
 //
 
+#include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/vfs.h>
 
 #include <chrono>
 #include <iostream>
@@ -26,12 +28,14 @@
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr/file_wait.h>
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
+#include <libdm/loop_control.h>
 #include <libfiemap/image_manager.h>
 
 #include "utility.h"
@@ -46,7 +50,7 @@
 using android::fs_mgr::WaitForFile;
 
 static std::string gDataPath;
-static std::string gDataMountPath;
+static std::string gTestDir;
 static constexpr char kMetadataPath[] = "/metadata/gsi/test";
 
 static constexpr uint64_t kTestImageSize = 1024 * 1024;
@@ -178,6 +182,119 @@
 
 INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
 
+// This allows test cases for filesystems with larger than 4KiB alignment.
+// It creates a loop device, formats it with a FAT filesystem, and then
+// creates an ImageManager so backing images can be created on that filesystem.
+class VfatTest : public ::testing::Test {
+  protected:
+    // 64MB Filesystem and 32k block size by default
+    static constexpr uint64_t kBlockSize = 32768;
+    static constexpr uint64_t kFilesystemSize = 64 * 1024 * 1024;
+
+    void SetUp() override {
+        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+        base_name_ = tinfo->name();
+
+        fs_path_ = gTestDir + "/vfat.img";
+        uint64_t count = kFilesystemSize / kBlockSize;
+        std::string dd_cmd =
+                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+                                              " count=%" PRIu64 " > /dev/null 2>&1",
+                                              fs_path_.c_str(), kBlockSize, count);
+        // create mount point
+        mntpoint_ = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+        if (mkdir(mntpoint_.c_str(), S_IRWXU) < 0) {
+            ASSERT_EQ(errno, EEXIST) << strerror(errno);
+        }
+
+        // create file for the file system
+        int ret = system(dd_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // Get and attach a loop device to the filesystem we created
+        loop_device_.emplace(fs_path_, 10s);
+        ASSERT_TRUE(loop_device_->valid());
+
+        // create file system
+        uint64_t sectors = kFilesystemSize / 512;
+        std::string mkfs_cmd =
+                ::android::base::StringPrintf("/system/bin/newfs_msdos -A -O Android -s %" PRIu64
+                                              " -b %" PRIu64 " %s > /dev/null 2>&1",
+                                              sectors, kBlockSize, loop_device_->device().c_str());
+        ret = system(mkfs_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // Create a wrapping DM device to prevent gsid taking the loopback path.
+        auto& dm = DeviceMapper::Instance();
+        DmTable table;
+        table.Emplace<DmTargetLinear>(0, kFilesystemSize / 512, loop_device_->device(), 0);
+
+        dm_name_ = android::base::Basename(loop_device_->device()) + "-wrapper";
+        ASSERT_TRUE(dm.CreateDevice(dm_name_, table, &dm_path_, 10s));
+
+        // mount the file system
+        ASSERT_EQ(mount(dm_path_.c_str(), mntpoint_.c_str(), "vfat", 0, nullptr), 0)
+                << strerror(errno);
+    }
+
+    void TearDown() override {
+        // Clear up anything backed on the temporary FS.
+        if (manager_) {
+            manager_->UnmapImageIfExists(base_name_);
+            manager_->DeleteBackingImage(base_name_);
+        }
+
+        // Unmount temporary FS.
+        if (umount(mntpoint_.c_str()) < 0) {
+            ASSERT_EQ(errno, EINVAL) << strerror(errno);
+        }
+
+        // Destroy the dm wrapper.
+        auto& dm = DeviceMapper::Instance();
+        ASSERT_TRUE(dm.DeleteDeviceIfExists(dm_name_));
+
+        // Destroy the loop device.
+        loop_device_ = {};
+
+        // Destroy the temporary FS.
+        if (rmdir(mntpoint_.c_str()) < 0) {
+            ASSERT_EQ(errno, ENOENT) << strerror(errno);
+        }
+        if (unlink(fs_path_.c_str()) < 0) {
+            ASSERT_EQ(errno, ENOENT) << strerror(errno);
+        }
+    }
+
+    std::string base_name_;
+    std::string mntpoint_;
+    std::string fs_path_;
+    std::optional<LoopDevice> loop_device_;
+    std::string dm_name_;
+    std::string dm_path_;
+    std::unique_ptr<ImageManager> manager_;
+};
+
+// The actual size of the block device should be the requested size. For
+// example, a 16KB image should be mapped as a 16KB device, even if the
+// underlying filesystem requires 32KB to be fallocated.
+TEST_F(VfatTest, DeviceIsRequestedSize) {
+    manager_ = ImageManager::Open(kMetadataPath, mntpoint_);
+    ASSERT_NE(manager_, nullptr);
+
+    manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
+
+    // Create something not aligned to the backing fs block size.
+    constexpr uint64_t kTestSize = (kBlockSize * 64) - (kBlockSize / 2);
+    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestSize, false, nullptr));
+
+    std::string path;
+    ASSERT_TRUE(manager_->MapImageDevice(base_name_, 10s, &path));
+
+    unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(fd, 0);
+    ASSERT_EQ(get_block_device_size(fd.get()), kTestSize);
+}
+
 }  // namespace
 
 bool Mkdir(const std::string& path) {
@@ -194,13 +311,27 @@
     if (argc >= 2) {
         gDataPath = argv[1];
     } else {
-        gDataPath = "/data/gsi/test";
+        gDataPath = "/data/local/tmp";
     }
-    gDataMountPath = gDataPath + "/mnt"s;
 
-    if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(gDataMountPath) ||
-        !Mkdir(kMetadataPath + "/mnt"s)) {
+    if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(kMetadataPath + "/mnt"s)) {
         return 1;
     }
-    return RUN_ALL_TESTS();
+
+    std::string tempdir = gDataPath + "/XXXXXX";
+    if (!mkdtemp(tempdir.data())) {
+        std::cerr << "unable to create tempdir on " << tempdir << "\n";
+        exit(EXIT_FAILURE);
+    }
+    if (!android::base::Realpath(tempdir, &gTestDir)) {
+        std::cerr << "unable to find realpath for " << tempdir;
+        exit(EXIT_FAILURE);
+    }
+
+    auto rv = RUN_ALL_TESTS();
+
+    std::string cmd = "rm -rf " + gTestDir;
+    system(cmd.c_str());
+
+    return rv;
 }
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index be18b42..762aebb 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -196,9 +196,10 @@
 // Expect space of |path| is multiple of 4K.
 bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
                      std::string* hash = nullptr);
-bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
 std::string HashSnapshot(ISnapshotWriter* writer);
 
+std::string ToHexString(const uint8_t* buf, size_t len);
+
 std::optional<std::string> GetHash(const std::string& path);
 
 // Add partitions and groups described by |manifest|.
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index b889fd4..f0e91de 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -40,6 +40,7 @@
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
 #include <liblp/builder.h>
+#include <openssl/sha.h>
 #include <storage_literals/storage_literals.h>
 
 #include <android/snapshot/snapshot.pb.h>
@@ -1040,14 +1041,25 @@
         }
     }
 
-    AssertionResult WriteSnapshotAndHash(const std::string& name) {
+    AssertionResult WriteSnapshots() {
+        for (const auto& partition : {sys_, vnd_, prd_}) {
+            auto res = WriteSnapshotAndHash(partition);
+            if (!res) {
+                return res;
+            }
+        }
+        return AssertionSuccess();
+    }
+
+    AssertionResult WriteSnapshotAndHash(PartitionUpdate* partition) {
+        std::string name = partition->partition_name() + "_b";
         if (ShouldUseCompression()) {
             std::unique_ptr<ISnapshotWriter> writer;
             auto res = MapUpdateSnapshot(name, &writer);
             if (!res) {
                 return res;
             }
-            if (!WriteRandomData(writer.get(), &hashes_[name])) {
+            if (!WriteRandomSnapshotData(writer.get(), &hashes_[name])) {
                 return AssertionFailure() << "Unable to write random data to snapshot " << name;
             }
             if (!writer->Finalize()) {
@@ -1071,6 +1083,42 @@
                                   << ", hash: " << hashes_[name];
     }
 
+    bool WriteRandomSnapshotData(ICowWriter* writer, std::string* hash) {
+        unique_fd rand(open("/dev/urandom", O_RDONLY));
+        if (rand < 0) {
+            PLOG(ERROR) << "open /dev/urandom";
+            return false;
+        }
+
+        SHA256_CTX ctx;
+        SHA256_Init(&ctx);
+
+        if (!writer->options().max_blocks) {
+            LOG(ERROR) << "CowWriter must specify maximum number of blocks";
+            return false;
+        }
+        const auto num_blocks = writer->options().max_blocks.value();
+
+        const auto block_size = writer->options().block_size;
+        std::string block(block_size, '\0');
+        for (uint64_t i = 0; i < num_blocks; i++) {
+            if (!ReadFully(rand, block.data(), block.size())) {
+                PLOG(ERROR) << "read /dev/urandom";
+                return false;
+            }
+            if (!writer->AddRawBlocks(i, block.data(), block.size())) {
+                LOG(ERROR) << "Failed to add raw block " << i;
+                return false;
+            }
+            SHA256_Update(&ctx, block.data(), block.size());
+        }
+
+        uint8_t out[32];
+        SHA256_Final(out, &ctx);
+        *hash = ToHexString(out, sizeof(out));
+        return true;
+    }
+
     // Generate a snapshot that moves all the upper blocks down to the start.
     // It doesn't really matter the order, we just want copies that reference
     // blocks that won't exist if the partition shrinks.
@@ -1179,9 +1227,7 @@
     ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     // Assert that source partitions aren't affected.
     for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1245,9 +1291,7 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};
     for (auto* partition : partitions) {
@@ -1311,8 +1355,8 @@
         ASSERT_EQ(status.old_partition_size(), 3145728);
     }
 
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
-    ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
+    ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
     ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
 
     sync();
@@ -1415,8 +1459,8 @@
 
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
-    ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
+    ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
     ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
 
     sync();
@@ -1577,9 +1621,7 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     // Assert that source partitions aren't affected.
     for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1738,9 +1780,7 @@
     ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     // Assert that source partitions aren't affected.
     for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -2012,9 +2052,7 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
 
@@ -2054,9 +2092,7 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     // Create a stale snapshot that should not exist.
     {
@@ -2139,7 +2175,7 @@
 
     // Map and write some data to target partition.
     ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
 
     // Finish update.
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -2175,7 +2211,7 @@
 
     // Map and write some data to target partitions.
     ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
 
     std::vector<android::dm::DeviceMapper::TargetInfo> table;
     ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
@@ -2235,8 +2271,8 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
+    for (const auto& partition : {sys_, vnd_, prd_, dlkm}) {
+        ASSERT_TRUE(WriteSnapshotAndHash(partition));
     }
 
     // Assert that source partitions aren't affected.
@@ -2360,9 +2396,7 @@
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
     ASSERT_TRUE(sm->MapAllSnapshots(10s));
 
@@ -2412,13 +2446,6 @@
     // fit in super, but not |prd|.
     constexpr uint64_t partition_size = 3788_KiB;
     SetSize(sys_, partition_size);
-    SetSize(vnd_, partition_size);
-    SetSize(prd_, 18_MiB);
-
-    // Make sure |prd| does not fit in super at all. On VABC, this means we
-    // fake an extra large COW for |vnd| to fill up super.
-    vnd_->set_estimate_cow_size(30_MiB);
-    prd_->set_estimate_cow_size(30_MiB);
 
     AddOperationForPartitions();
 
@@ -2430,23 +2457,7 @@
         GTEST_SKIP() << "Test does not apply to userspace snapshots";
     }
 
-    // Test that partitions prioritize using space in super.
-    auto tgt = MetadataBuilder::New(*opener_, "super", 1);
-    ASSERT_NE(tgt, nullptr);
-    ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
-    ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
-    ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
+    ASSERT_TRUE(WriteSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     ASSERT_TRUE(UnmapAll());
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index 17f3c4f..63f47d6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -71,16 +71,16 @@
 }
 
 bool Worker::MergeReplaceZeroOps() {
-    // Flush every 8192 ops. Since all ops are independent and there is no
+    // Flush after merging 2MB. Since all ops are independent and there is no
     // dependency between COW ops, we will flush the data and the number
-    // of ops merged in COW file for every 8192 ops. If there is a crash,
-    // we will end up replaying some of the COW ops which were already merged.
-    // That is ok.
+    // of ops merged in COW block device. If there is a crash, we will
+    // end up replaying some of the COW ops which were already merged. That is
+    // ok.
     //
-    // Why 8192 ops ? Increasing this may improve merge time 3-4 seconds but
-    // we need to make sure that we checkpoint; 8k ops seems optimal. In-case
-    // if there is a crash merge should always make forward progress.
-    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
+    // Although increasing this greater than 2MB may help in improving merge
+    // times; however, on devices with low memory, this can be problematic
+    // when there are multiple merge threads in parallel.
+    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 2;
     int num_ops_merged = 0;
 
     SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 71fe124..3e889a0 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -128,48 +128,6 @@
     return true;
 }
 
-bool WriteRandomData(ICowWriter* writer, std::string* hash) {
-    unique_fd rand(open("/dev/urandom", O_RDONLY));
-    if (rand < 0) {
-        PLOG(ERROR) << "open /dev/urandom";
-        return false;
-    }
-
-    SHA256_CTX ctx;
-    if (hash) {
-        SHA256_Init(&ctx);
-    }
-
-    if (!writer->options().max_blocks) {
-        LOG(ERROR) << "CowWriter must specify maximum number of blocks";
-        return false;
-    }
-    uint64_t num_blocks = writer->options().max_blocks.value();
-
-    size_t block_size = writer->options().block_size;
-    std::string block(block_size, '\0');
-    for (uint64_t i = 0; i < num_blocks; i++) {
-        if (!ReadFully(rand, block.data(), block.size())) {
-            PLOG(ERROR) << "read /dev/urandom";
-            return false;
-        }
-        if (!writer->AddRawBlocks(i, block.data(), block.size())) {
-            LOG(ERROR) << "Failed to add raw block " << i;
-            return false;
-        }
-        if (hash) {
-            SHA256_Update(&ctx, block.data(), block.size());
-        }
-    }
-
-    if (hash) {
-        uint8_t out[32];
-        SHA256_Final(out, &ctx);
-        *hash = ToHexString(out, sizeof(out));
-    }
-    return true;
-}
-
 std::string HashSnapshot(ISnapshotWriter* writer) {
     auto reader = writer->OpenReader();
     if (!reader) {
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index ae8e459..b8b34e2 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -23,6 +23,9 @@
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
 
+using testing::Contains;
+using testing::Not;
+
 static int GetVsrLevel() {
     return android::base::GetIntProperty("ro.vendor.api_level", -1);
 }
@@ -117,3 +120,30 @@
     android::fs_mgr::Fstab fstab;
     EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
 }
+
+TEST(fs, NoLegacyVerifiedBoot) {
+    if (GetVsrLevel() < __ANDROID_API_T__) {
+        GTEST_SKIP();
+    }
+
+    const auto& default_fstab_path = android::fs_mgr::GetFstabPath();
+    EXPECT_FALSE(default_fstab_path.empty());
+
+    std::string fstab_str;
+    EXPECT_TRUE(android::base::ReadFileToString(default_fstab_path, &fstab_str,
+                                                /* follow_symlinks = */ true));
+
+    for (const auto& line : android::base::Split(fstab_str, "\n")) {
+        auto fields = android::base::Tokenize(line, " \t");
+        // Ignores empty lines and comments.
+        if (fields.empty() || android::base::StartsWith(fields.front(), '#')) {
+            continue;
+        }
+        // Each line in a fstab should have at least five entries.
+        //   <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
+        ASSERT_GE(fields.size(), 5);
+        EXPECT_THAT(android::base::Split(fields[4], ","), Not(Contains("verify")))
+                << "AVB 1.0 isn't supported now, but the 'verify' flag is found:\n"
+                << "  " << line;
+    }
+}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index ff95487..07ce458 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -518,12 +518,6 @@
 
     SwitchRoot("/system");
 
-    // Make /system a mountpoint so that adb-remount can move submounts under /system.
-    if (access("/system", F_OK) == 0 &&
-        mount("/system", "/system", nullptr, MS_BIND, nullptr) != 0) {
-        PLOG(WARNING) << "Failed to bind mount /system for overlayfs";
-    }
-
     return true;
 }
 
diff --git a/init/init.cpp b/init/init.cpp
index 535033d..5f516b7 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -347,8 +347,8 @@
     }
 #endif  // RECOVERY
     parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, subcontext, std::nullopt,
-                                                            /*from_apex=*/true));
+                            std::make_unique<ServiceParser>(&service_list, subcontext,
+                            std::nullopt));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontext));
 
     return parser;
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index bce1cc3..fead371 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -190,15 +190,33 @@
     return success;
 }
 
+// Switch the mount namespace of the current process from bootstrap to default OR from default to
+// bootstrap. If the current mount namespace is neither bootstrap nor default, keep it that way.
 Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
     if (IsRecoveryMode() || !IsApexUpdatable()) {
         // we don't have multiple namespaces in recovery mode or if apex is not updatable
         return {};
     }
-    const auto& ns_id = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_id : default_ns_id;
+
+    const std::string current_namespace_id = GetMountNamespaceId();
+    MountNamespace current_mount_namespace;
+    if (current_namespace_id == bootstrap_ns_id) {
+        current_mount_namespace = NS_BOOTSTRAP;
+    } else if (current_namespace_id == default_ns_id) {
+        current_mount_namespace = NS_DEFAULT;
+    } else {
+        // services with `namespace mnt` start in its own mount namespace. So we need to keep it.
+        return {};
+    }
+
+    // We're already in the target mount namespace.
+    if (current_mount_namespace == target_mount_namespace) {
+        return {};
+    }
+
     const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
     const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
-    if (ns_id != GetMountNamespaceId() && ns_fd.get() != -1) {
+    if (ns_fd.get() != -1) {
         if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
             return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
         }
diff --git a/init/service.cpp b/init/service.cpp
index 8c4ee93..730b6b6 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -130,13 +130,13 @@
 std::chrono::time_point<std::chrono::steady_clock> Service::exec_service_started_;
 
 Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
-                 const std::vector<std::string>& args, bool from_apex)
-    : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, args, from_apex) {}
+                 const std::string& filename, const std::vector<std::string>& args)
+    : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, filename, args) {}
 
 Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
                  const std::vector<gid_t>& supp_gids, int namespace_flags,
                  const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
-                 const std::vector<std::string>& args, bool from_apex)
+                 const std::string& filename, const std::vector<std::string>& args)
     : name_(name),
       classnames_({"default"}),
       flags_(flags),
@@ -156,7 +156,7 @@
       oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
       start_order_(0),
       args_(args),
-      from_apex_(from_apex) {}
+      filename_(filename) {}
 
 void Service::NotifyStateChange(const std::string& new_state) const {
     if ((flags_ & SVC_TEMPORARY) != 0) {
@@ -860,7 +860,7 @@
     }
 
     return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, namespace_flags, seclabel,
-                                     nullptr, str_args, false);
+                                     nullptr, /*filename=*/"", str_args);
 }
 
 // This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's
diff --git a/init/service.h b/init/service.h
index 4adbaa2..f7f32d9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -66,12 +66,12 @@
 
   public:
     Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
-            const std::vector<std::string>& args, bool from_apex = false);
+            const std::string& filename, const std::vector<std::string>& args);
 
     Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
             const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
-            Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args,
-            bool from_apex = false);
+            Subcontext* subcontext_for_restart_commands, const std::string& filename,
+            const std::vector<std::string>& args);
 
     static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
             const std::vector<std::string>& args);
@@ -133,7 +133,7 @@
     const std::vector<std::string>& args() const { return args_; }
     bool is_updatable() const { return updatable_; }
     bool is_post_data() const { return post_data_; }
-    bool is_from_apex() const { return from_apex_; }
+    bool is_from_apex() const { return base::StartsWith(filename_, "/apex/"); }
     void set_oneshot(bool value) {
         if (value) {
             flags_ |= SVC_ONESHOT;
@@ -225,7 +225,7 @@
 
     std::optional<std::string> on_failure_reboot_target_;
 
-    bool from_apex_ = false;
+    std::string filename_;
 };
 
 }  // namespace init
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 9e914ee..32c57c4 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -647,7 +647,7 @@
         }
     }
 
-    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args, from_apex_);
+    service_ = std::make_unique<Service>(name, restart_action_subcontext, filename, str_args);
     return {};
 }
 
diff --git a/init/service_parser.h b/init/service_parser.h
index 0fd2da5..54503dd 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -31,13 +31,11 @@
   public:
     ServiceParser(
             ServiceList* service_list, Subcontext* subcontext,
-            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy,
-            bool from_apex = false)
+            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
         : service_list_(service_list),
           subcontext_(subcontext),
           interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
-          service_(nullptr),
-          from_apex_(from_apex) {}
+          service_(nullptr) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line) override;
     Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -92,7 +90,6 @@
     std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
     std::unique_ptr<Service> service_;
     std::string filename_;
-    bool from_apex_ = false;
 };
 
 }  // namespace init
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 22ee844..87a2ce5 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -39,7 +39,7 @@
 
     std::vector<std::string> dummy_args{"/bin/test"};
     Service* service_in_old_memory =
-        new (old_memory) Service("test_old_memory", nullptr, dummy_args);
+        new (old_memory) Service("test_old_memory", nullptr, /*filename=*/"", dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory->flags());
     EXPECT_EQ(0, service_in_old_memory->pid());
@@ -58,7 +58,8 @@
     }
 
     Service* service_in_old_memory2 = new (old_memory) Service(
-            "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "", nullptr, dummy_args);
+            "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "",
+            nullptr, /*filename=*/"", dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory2->flags());
     EXPECT_EQ(0, service_in_old_memory2->pid());
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index bd2bec5..961e006 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -251,11 +251,8 @@
 }
 
 bool Subcontext::PathMatchesSubcontext(const std::string& path) const {
-    static const std::string kApexDir = "/apex/";
-    if (StartsWith(path, kApexDir)) {
-        auto begin = kApexDir.size();
-        auto end = path.find('/', begin);
-        auto apex_name = path.substr(begin, end - begin);
+    auto apex_name = GetApexNameFromFileName(path);
+    if (!apex_name.empty()) {
         return std::find(apex_list_.begin(), apex_list_.end(), apex_name) != apex_list_.end();
     }
     for (const auto& prefix : path_prefixes_) {
diff --git a/init/util.cpp b/init/util.cpp
index 523cce4..bfc3fb6 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -738,5 +738,15 @@
     return has;
 }
 
+std::string GetApexNameFromFileName(const std::string& path) {
+    static const std::string kApexDir = "/apex/";
+    if (StartsWith(path, kApexDir)) {
+        auto begin = kApexDir.size();
+        auto end = path.find('/', begin);
+        return path.substr(begin, end - begin);
+    }
+    return "";
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index 099b9ee..daec470 100644
--- a/init/util.h
+++ b/init/util.h
@@ -107,5 +107,7 @@
 
 bool IsMicrodroid();
 bool Has32BitAbi();
+
+std::string GetApexNameFromFileName(const std::string& path);
 }  // namespace init
 }  // namespace android
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index aac6b57..32df91e 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -19,7 +19,7 @@
     sync_merge; # introduced=26
     sync_file_info; # introduced=26
     sync_file_info_free; # introduced=26
-    sync_wait; # llndk apex
+    sync_wait; # llndk systemapi
     sync_fence_info; # llndk
     sync_pt_info; # llndk
     sync_fence_info_free; # llndk
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index a44ed18..1d94b9d 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,8 +1,8 @@
 LIBVNDKSUPPORT {
   global:
-    android_is_in_vendor_process; # llndk apex
-    android_load_sphal_library; # llndk apex
-    android_unload_sphal_library; # llndk apex
+    android_is_in_vendor_process; # llndk systemapi
+    android_load_sphal_library; # llndk systemapi
+    android_unload_sphal_library; # llndk systemapi
   local:
     *;
 };
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 2620034..7cbc24f 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -70,49 +70,6 @@
     exit(code);
 }
 
-static int drop_privs(void) {
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-
-    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
-        return -1;
-    }
-
-    /*
-     * ensure we're running as the system user
-     */
-    if (setgid(AID_SYSTEM) != 0) {
-        return -1;
-    }
-
-    if (setuid(AID_SYSTEM) != 0) {
-        return -1;
-    }
-
-    /*
-     * drop all capabilities except SYS_RAWIO
-     */
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    capheader.pid = 0;
-
-    capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO);
-    capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO);
-
-    if (capset(&capheader, &capdata[0]) < 0) {
-        return -1;
-    }
-
-    /*
-     * No access for group and other. We need execute access for user to create
-     * an accessible directory.
-     */
-    umask(S_IRWXG | S_IRWXO);
-
-    return 0;
-}
-
 static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
     int rc;
 
@@ -260,8 +217,11 @@
 int main(int argc, char* argv[]) {
     int rc;
 
-    /* drop privileges */
-    if (drop_privs() < 0) return EXIT_FAILURE;
+    /*
+     * No access for group and other. We need execute access for user to create
+     * an accessible directory.
+     */
+    umask(S_IRWXG | S_IRWXO);
 
     /* parse arguments */
     parse_args(argc, argv);
diff --git a/trusty/test/binder/aidl/ByteEnum.aidl b/trusty/test/binder/aidl/ByteEnum.aidl
new file mode 100644
index 0000000..d3a13ac
--- /dev/null
+++ b/trusty/test/binder/aidl/ByteEnum.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+/*
+ * Hello, world!
+ */
+@Backing(type="byte")
+enum ByteEnum {
+    // Comment about FOO.
+    FOO = 1,
+    BAR = 2,
+    BAZ,
+}
diff --git a/trusty/test/binder/aidl/ITestService.aidl b/trusty/test/binder/aidl/ITestService.aidl
new file mode 100644
index 0000000..c6a99c8
--- /dev/null
+++ b/trusty/test/binder/aidl/ITestService.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+
+import ByteEnum;
+import IntEnum;
+import LongEnum;
+
+interface ITestService {
+    const @utf8InCpp String PORT = "com.android.trusty.binder.test.service";
+
+    const int TEST_CONSTANT = 42;
+    const int TEST_CONSTANT2 = -42;
+    const int TEST_CONSTANT3 = +42;
+    const int TEST_CONSTANT4 = +4;
+    const int TEST_CONSTANT5 = -4;
+    const int TEST_CONSTANT6 = -0;
+    const int TEST_CONSTANT7 = +0;
+    const int TEST_CONSTANT8 = 0;
+    const int TEST_CONSTANT9 = 0x56;
+    const int TEST_CONSTANT10 = 0xa5;
+    const int TEST_CONSTANT11 = 0xFA;
+    const int TEST_CONSTANT12 = 0xffffffff;
+
+    const byte BYTE_TEST_CONSTANT = 17;
+    const long LONG_TEST_CONSTANT = 1L << 40;
+
+    const String STRING_TEST_CONSTANT = "foo";
+    const String STRING_TEST_CONSTANT2 = "bar";
+
+    // Test that primitives work as parameters and return types.
+    boolean RepeatBoolean(boolean token);
+    byte RepeatByte(byte token);
+    char RepeatChar(char token);
+    int RepeatInt(int token);
+    long RepeatLong(long token);
+    float RepeatFloat(float token);
+    double RepeatDouble(double token);
+    String RepeatString(String token);
+    ByteEnum RepeatByteEnum(ByteEnum token);
+    IntEnum RepeatIntEnum(IntEnum token);
+    LongEnum RepeatLongEnum(LongEnum token);
+
+    // Test that arrays work as parameters and return types.
+    boolean[] ReverseBoolean(in boolean[] input, out boolean[] repeated);
+    byte[] ReverseByte(in byte[] input, out byte[] repeated);
+    char[] ReverseChar(in char[] input, out char[] repeated);
+    int[] ReverseInt(in int[] input, out int[] repeated);
+    long[] ReverseLong(in long[] input, out long[] repeated);
+    float[] ReverseFloat(in float[] input, out float[] repeated);
+    double[] ReverseDouble(in double[] input, out double[] repeated);
+    String[] ReverseString(in String[] input, out String[] repeated);
+    ByteEnum[] ReverseByteEnum(in ByteEnum[] input, out ByteEnum[] repeated);
+    IntEnum[] ReverseIntEnum(in IntEnum[] input, out IntEnum[] repeated);
+    LongEnum[] ReverseLongEnum(in LongEnum[] input, out LongEnum[] repeated);
+}
diff --git a/trusty/test/binder/aidl/IntEnum.aidl b/trusty/test/binder/aidl/IntEnum.aidl
new file mode 100644
index 0000000..120e44f
--- /dev/null
+++ b/trusty/test/binder/aidl/IntEnum.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+@JavaDerive(toString=true)
+@Backing(type="int")
+enum IntEnum {
+    FOO = 1000,
+    BAR = 2000,
+    BAZ,
+    /** @deprecated do not use this */
+    QUX,
+}
diff --git a/trusty/test/binder/aidl/LongEnum.aidl b/trusty/test/binder/aidl/LongEnum.aidl
new file mode 100644
index 0000000..0e9e933
--- /dev/null
+++ b/trusty/test/binder/aidl/LongEnum.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+@Backing(type="long")
+enum LongEnum {
+    FOO = 100000000000,
+    BAR = 200000000000,
+    BAZ,
+}
diff --git a/trusty/test/binder/aidl/rules.mk b/trusty/test/binder/aidl/rules.mk
new file mode 100644
index 0000000..6154abb
--- /dev/null
+++ b/trusty/test/binder/aidl/rules.mk
@@ -0,0 +1,26 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_AIDLS := \
+	$(LOCAL_DIR)/ByteEnum.aidl \
+	$(LOCAL_DIR)/IntEnum.aidl \
+	$(LOCAL_DIR)/ITestService.aidl \
+	$(LOCAL_DIR)/LongEnum.aidl \
+
+include make/aidl.mk