Merge "meminfo: handle multiple buffer references in dmabufinfo"
diff --git a/adb/Android.bp b/adb/Android.bp
index 8199fff..3813578 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -156,8 +156,6 @@
         "client/usb_libusb.cpp",
         "client/usb_dispatch.cpp",
         "client/transport_mdns.cpp",
-        "client/fastdeploy.cpp",
-        "client/fastdeploycallbacks.cpp",
     ],
 
     generated_headers: ["platform_tools_version"],
@@ -192,9 +190,6 @@
         "libdiagnose_usb",
         "libmdnssd",
         "libusb",
-        "libandroidfw",
-        "libziparchive",
-        "libz",
         "libutils",
         "liblog",
         "libcutils",
@@ -280,9 +275,6 @@
         "liblog",
         "libmdnssd",
         "libusb",
-        "libandroidfw",
-        "libziparchive",
-        "libz",
         "libutils",
         "liblog",
         "libcutils",
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index d56a25f..3869945 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -36,7 +36,9 @@
 #include "commandline.h"
 #include "fastdeploy.h"
 
+#if defined(ENABLE_FASTDEPLOY)
 static constexpr int kFastDeployMinApi = 24;
+#endif
 
 static bool can_use_feature(const char* feature) {
     FeatureSet features;
@@ -130,10 +132,12 @@
     *buf = '\0';
 }
 
+#if defined(ENABLE_FASTDEPLOY)
 static int delete_device_patch_file(const char* apkPath) {
     std::string patchDevicePath = get_patch_path(apkPath);
     return delete_device_file(patchDevicePath);
 }
+#endif
 
 static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
                                 bool use_localagent) {
@@ -159,6 +163,7 @@
     }
 
     if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
         TemporaryFile metadataTmpFile;
         std::string patchTmpFilePath;
         {
@@ -179,6 +184,9 @@
         adb_unlink(patchTmpFilePath.c_str());
         delete_device_patch_file(file);
         return 0;
+#else
+        error_exit("fastdeploy is disabled");
+#endif
     } else {
         struct stat sb;
         if (stat(file, &sb) == -1) {
@@ -252,6 +260,7 @@
             "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
 
     if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
         TemporaryFile metadataTmpFile;
         TemporaryFile patchTmpFile;
 
@@ -261,6 +270,9 @@
 
         create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
         apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
+#else
+        error_exit("fastdeploy is disabled");
+#endif
     } else {
         if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
     }
@@ -270,7 +282,9 @@
 
 cleanup_apk:
     if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
         delete_device_patch_file(apk_file[0]);
+#endif
     }
     delete_device_file(apk_dest);
     return result;
@@ -334,12 +348,14 @@
         error_exit("Attempting to use streaming install on unsupported device");
     }
 
+#if defined(ENABLE_FASTDEPLOY)
     if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
         printf("Fast Deploy is only compatible with devices of API version %d or higher, "
                "ignoring.\n",
                kFastDeployMinApi);
         use_fastdeploy = false;
     }
+#endif
 
     std::vector<const char*> passthrough_argv;
     for (int i = 0; i < argc; i++) {
@@ -353,12 +369,16 @@
     }
 
     if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
         fastdeploy_set_local_agent(use_localagent);
         update_agent(agent_update_strategy);
 
         // The last argument must be the APK file
         const char* file = passthrough_argv.back();
         use_fastdeploy = find_package(file);
+#else
+        error_exit("fastdeploy is disabled");
+#endif
     }
 
     switch (installMode) {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 26ce3b2..943a071 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -998,9 +998,7 @@
     if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
         return false;
     }
-    auto it = std::find_if(fstab.begin(), fstab.end(),
-                           [&](const auto& entry) { return entry.mount_point == mount_point; });
-    return it != fstab.end();
+    return GetEntryForMountPoint(&fstab, mount_point) != nullptr;
 }
 
 // When multiple fstab records share the same mount_point, it will try to mount each
@@ -1090,6 +1088,13 @@
                 // Skips mounting the device.
                 continue;
             }
+        } else if (!current_entry.avb_key.empty()) {
+            if (AvbHandle::SetUpStandaloneAvbHashtree(&current_entry) == AvbHashtreeResult::kFail) {
+                LERROR << "Failed to set up AVB on standalone partition: "
+                       << current_entry.mount_point << ", skipping!";
+                // Skips mounting the device.
+                continue;
+            }
         } else if ((current_entry.fs_mgr_flags.verify)) {
             int rc = fs_mgr_setup_verity(&current_entry, true);
             if (__android_log_is_debuggable() &&
@@ -1326,6 +1331,13 @@
                 // Skips mounting the device.
                 continue;
             }
+        } else if (!fstab_entry.avb_key.empty()) {
+            if (AvbHandle::SetUpStandaloneAvbHashtree(&fstab_entry) == AvbHashtreeResult::kFail) {
+                LERROR << "Failed to set up AVB on standalone partition: "
+                       << fstab_entry.mount_point << ", skipping!";
+                // Skips mounting the device.
+                continue;
+            }
         } else if (fstab_entry.fs_mgr_flags.verify) {
             int rc = fs_mgr_setup_verity(&fstab_entry, true);
             if (__android_log_is_debuggable() &&
@@ -1384,6 +1396,15 @@
                                   needs_checkpoint);
 }
 
+int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+}
+
+int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+                    bool needs_checkpoint) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
+}
+
 /*
  * mount a tmpfs filesystem at the given point.
  * return 0 on success, non-zero on failure.
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 0482f6c..146e82f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -338,6 +338,8 @@
         } else if (StartsWith(flag, "zram_backing_dev_path=")) {
             entry->fs_mgr_flags.zram_backing_dev_path = true;
             entry->zram_backing_dev_path = arg;
+        } else if (StartsWith(flag, "avb_key=")) {
+            entry->avb_key = arg;
         } else {
             LWARNING << "Warning: unknown flag: " << flag;
         }
@@ -787,10 +789,8 @@
     free(fstab);
 }
 
-/*
- * Returns the fstab_rec* whose mount_point is path.
- * Returns nullptr if not found.
- */
+// Returns the fstab_rec* whose mount_point is path.
+// Returns nullptr if not found.
 struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) {
     if (!fstab) {
         return nullptr;
@@ -803,6 +803,20 @@
     return nullptr;
 }
 
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {
+    if (fstab == nullptr) {
+        return nullptr;
+    }
+
+    for (auto& entry : *fstab) {
+        if (entry.mount_point == path) {
+            return &entry;
+        }
+    }
+
+    return nullptr;
+}
+
 std::set<std::string> fs_mgr_get_boot_devices() {
     // First check the kernel commandline, then try the device tree otherwise
     std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 2c4299a..c7d2cb9 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -564,9 +564,8 @@
     if (std::find(verity.begin(), verity.end(), "system") != verity.end()) return mounts;
 
     // confirm that fstab is missing system
-    if (std::find_if(fstab->begin(), fstab->end(), [](const auto& entry) {
-            return entry.mount_point == "/" || entry.mount_point == "/system ";
-        }) != fstab->end()) {
+    if (GetEntryForMountPoint(fstab, "/") != nullptr ||
+        GetEntryForMountPoint(fstab, "/system") != nullptr) {
         return mounts;
     }
 
@@ -847,9 +846,7 @@
 std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab* fstab) {
     if (fs_mgr_overlayfs_invalid()) return {};
 
-    if (std::find_if(fstab->begin(), fstab->end(), [](const auto& entry) {
-            return entry.mount_point == kScratchMountPoint;
-        }) != fstab->end()) {
+    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
         return {};
     }
 
@@ -889,9 +886,7 @@
         if (overlay_mount_point == kScratchMountPoint) {
             if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
         } else {
-            if (std::find_if(fstab.begin(), fstab.end(), [&overlay_mount_point](const auto& entry) {
-                    return entry.mount_point == overlay_mount_point;
-                }) == fstab.end()) {
+            if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
                 continue;
             }
         }
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index 32a5d21..58ef9b6 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -37,9 +37,8 @@
     if (path.empty()) return nullptr;
     std::string str(path);
     while (true) {
-        auto it = std::find_if(fstab->begin(), fstab->end(),
-                               [&str](const auto& entry) { return entry.mount_point == str; });
-        if (it != fstab->end()) return &*it;
+        auto entry = GetEntryForMountPoint(fstab, str);
+        if (entry != nullptr) return entry;
         if (str == "/") break;
         auto slash = str.find_last_of('/');
         if (slash == std::string::npos) break;
@@ -65,10 +64,8 @@
         return MountState::ERROR;
     }
 
-    auto mv = std::find_if(
-            mounted_fstab.begin(), mounted_fstab.end(),
-            [&mount_point](const auto& entry) { return entry.mount_point == mount_point; });
-    if (mv != mounted_fstab.end()) {
+    auto mv = GetEntryForMountPoint(&mounted_fstab, mount_point);
+    if (mv != nullptr) {
         return MountState::MOUNTED;
     }
     return MountState::NOT_MOUNTED;
@@ -178,9 +175,8 @@
         return "";
     }
 
-    auto it = std::find_if(fstab.begin(), fstab.end(),
-                           [](const auto& entry) { return entry.mount_point == kSystemRoot; });
-    if (it == fstab.end()) {
+    auto entry = GetEntryForMountPoint(&fstab, kSystemRoot);
+    if (entry == nullptr) {
         return "/";
     }
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 1685e50..2934363 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -65,9 +65,11 @@
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
 #define FS_MGR_DOMNT_SUCCESS 0
-
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+                    bool needs_checkpoint);
+int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
+int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
                     bool need_cp);
 int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point = "");
 int fs_mgr_do_mount_one(fstab_rec* rec);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 100e076..549ff68 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -116,6 +116,7 @@
     std::string zram_loopback_path;
     uint64_t zram_loopback_size = 512 * 1024 * 1024;  // 512MB by default;
     std::string zram_backing_dev_path;
+    std::string avb_key;
 
     // TODO: Remove this union once fstab_rec is deprecated. It only serves as a
     // convenient way to convert between fstab_rec::fs_mgr_flags and these bools.
@@ -184,6 +185,8 @@
 bool ReadFstabFromDt(Fstab* fstab, bool log = true);
 bool ReadDefaultFstab(Fstab* fstab);
 
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
+
 // Temporary conversion functions.
 FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec);
 Fstab LegacyFstabToFstab(const struct fstab* legacy_fstab);
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 6ccdb57..b9b75f8 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -374,14 +374,6 @@
     return IsFilePinned(fd, file_path, sfs.f_type);
 }
 
-static void LogExtent(uint32_t num, const struct fiemap_extent& ext) {
-    LOG(INFO) << "Extent #" << num;
-    LOG(INFO) << "  fe_logical:  " << ext.fe_logical;
-    LOG(INFO) << "  fe_physical: " << ext.fe_physical;
-    LOG(INFO) << "  fe_length:   " << ext.fe_length;
-    LOG(INFO) << "  fe_flags:    0x" << std::hex << ext.fe_flags;
-}
-
 static bool ReadFiemap(int file_fd, const std::string& file_path,
                        std::vector<struct fiemap_extent>* extents) {
     uint64_t fiemap_size =
@@ -473,7 +465,7 @@
     }
 
     ::android::base::unique_fd bdev_fd(
-            TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDWR | O_CLOEXEC)));
+            TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
     if (bdev_fd < 0) {
         PLOG(ERROR) << "Failed to open block device: " << bdev_path;
         cleanup(file_path, create);
@@ -530,7 +522,6 @@
     fmap->file_path_ = abs_path;
     fmap->bdev_path_ = bdev_path;
     fmap->file_fd_ = std::move(file_fd);
-    fmap->bdev_fd_ = std::move(bdev_fd);
     fmap->file_size_ = file_size;
     fmap->bdev_size_ = bdevsz;
     fmap->fs_type_ = fs_type;
@@ -541,120 +532,9 @@
     return fmap;
 }
 
-bool FiemapWriter::Flush() const {
-    if (fsync(bdev_fd_)) {
-        PLOG(ERROR) << "Failed to flush " << bdev_path_ << " with fsync";
-        return false;
-    }
-    return true;
-}
-
-// TODO: Test with fs block_size > bdev block_size
-bool FiemapWriter::Write(off64_t off, uint8_t* buffer, uint64_t size) {
-    if (!size || size > file_size_) {
-        LOG(ERROR) << "Failed write: size " << size << " is invalid for file's size " << file_size_;
-        return false;
-    }
-
-    if (off + size > file_size_) {
-        LOG(ERROR) << "Failed write: Invalid offset " << off << " or size " << size
-                   << " for file size " << file_size_;
-        return false;
-    }
-
-    if ((off & (block_size_ - 1)) || (size & (block_size_ - 1))) {
-        LOG(ERROR) << "Failed write: Unaligned offset " << off << " or size " << size
-                   << " for block size " << block_size_;
-        return false;
-    }
-
-    if (!IsFilePinned(file_fd_, file_path_, fs_type_)) {
-        LOG(ERROR) << "Failed write: file " << file_path_ << " is not pinned";
-        return false;
-    }
-
-    // find extents that must be written to and then write one at a time.
-    uint32_t num_extent = 1;
-    uint32_t buffer_offset = 0;
-    for (auto& extent : extents_) {
-        uint64_t e_start = extent.fe_logical;
-        uint64_t e_end = extent.fe_logical + extent.fe_length;
-        // Do we write in this extent ?
-        if (off >= e_start && off < e_end) {
-            uint64_t written = WriteExtent(extent, buffer + buffer_offset, off, size);
-            if (written == 0) {
-                return false;
-            }
-
-            buffer_offset += written;
-            off += written;
-            size -= written;
-
-            // Paranoid check to make sure we are done with this extent now
-            if (size && (off >= e_start && off < e_end)) {
-                LOG(ERROR) << "Failed to write extent fully";
-                LogExtent(num_extent, extent);
-                return false;
-            }
-
-            if (size == 0) {
-                // done
-                break;
-            }
-        }
-        num_extent++;
-    }
-
-    return true;
-}
-
 bool FiemapWriter::Read(off64_t off, uint8_t* buffer, uint64_t size) {
     return false;
 }
 
-// private helpers
-
-// WriteExtent() Returns the total number of bytes written. It will always be multiple of
-// block_size_. 0 is returned in one of the two cases.
-//  1. Any write failed between logical_off & logical_off + length.
-//  2. The logical_offset + length doesn't overlap with the extent passed.
-// The function can either partially for fully write the extent depending on the
-// logical_off + length. It is expected that alignment checks for size and offset are
-// performed before calling into this function.
-uint64_t FiemapWriter::WriteExtent(const struct fiemap_extent& ext, uint8_t* buffer,
-                                   off64_t logical_off, uint64_t length) {
-    uint64_t e_start = ext.fe_logical;
-    uint64_t e_end = ext.fe_logical + ext.fe_length;
-    if (logical_off < e_start || logical_off >= e_end) {
-        LOG(ERROR) << "Failed write extent, invalid offset " << logical_off << " and size "
-                   << length;
-        LogExtent(0, ext);
-        return 0;
-    }
-
-    off64_t bdev_offset = ext.fe_physical + (logical_off - e_start);
-    if (bdev_offset >= bdev_size_) {
-        LOG(ERROR) << "Failed write extent, invalid block # " << bdev_offset << " for block device "
-                   << bdev_path_ << " of size " << bdev_size_ << " bytes";
-        return 0;
-    }
-    if (TEMP_FAILURE_RETRY(lseek64(bdev_fd_, bdev_offset, SEEK_SET)) == -1) {
-        PLOG(ERROR) << "Failed write extent, seek offset for " << bdev_path_ << " offset "
-                    << bdev_offset;
-        return 0;
-    }
-
-    // Determine how much we want to write at once.
-    uint64_t logical_end = logical_off + length;
-    uint64_t write_size = (e_end <= logical_end) ? (e_end - logical_off) : length;
-    if (!android::base::WriteFully(bdev_fd_, buffer, write_size)) {
-        PLOG(ERROR) << "Failed write extent, write " << bdev_path_ << " at " << bdev_offset
-                    << " size " << write_size;
-        return 0;
-    }
-
-    return write_size;
-}
-
 }  // namespace fiemap_writer
 }  // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index 3d20ff3..41fa959 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -138,36 +138,31 @@
     EXPECT_GT(fptr->extents().size(), 0);
 }
 
-TEST_F(FiemapWriterTest, CheckWriteError) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
-    ASSERT_NE(fptr, nullptr);
-
-    // prepare buffer for writing the pattern - 0xa0
-    uint64_t blocksize = fptr->block_size();
-    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
-    ASSERT_NE(buffer, nullptr);
-    memset(buffer.get(), 0xa0, blocksize);
-
-    uint8_t* p = static_cast<uint8_t*>(buffer.get());
-    for (off64_t off = 0; off < testfile_size; off += blocksize) {
-        ASSERT_TRUE(fptr->Write(off, p, blocksize));
-    }
-
-    EXPECT_TRUE(fptr->Flush());
-}
-
 class TestExistingFile : public ::testing::Test {
   protected:
     void SetUp() override {
         std::string exec_dir = ::android::base::GetExecutableDirectory();
-        std::string unaligned_file = exec_dir + "/testdata/unaligned_file";
-        std::string file_4k = exec_dir + "/testdata/file_4k";
-        std::string file_32k = exec_dir + "/testdata/file_32k";
-        fptr_unaligned = FiemapWriter::Open(unaligned_file, 4097, false);
-        fptr_4k = FiemapWriter::Open(file_4k, 4096, false);
-        fptr_32k = FiemapWriter::Open(file_32k, 32768, false);
+        unaligned_file_ = exec_dir + "/testdata/unaligned_file";
+        file_4k_ = exec_dir + "/testdata/file_4k";
+        file_32k_ = exec_dir + "/testdata/file_32k";
+
+        CleanupFiles();
+        fptr_unaligned = FiemapWriter::Open(unaligned_file_, 4097);
+        fptr_4k = FiemapWriter::Open(file_4k_, 4096);
+        fptr_32k = FiemapWriter::Open(file_32k_, 32768);
     }
 
+    void TearDown() { CleanupFiles(); }
+
+    void CleanupFiles() {
+        unlink(unaligned_file_.c_str());
+        unlink(file_4k_.c_str());
+        unlink(file_32k_.c_str());
+    }
+
+    std::string unaligned_file_;
+    std::string file_4k_;
+    std::string file_32k_;
     FiemapUniquePtr fptr_unaligned;
     FiemapUniquePtr fptr_4k;
     FiemapUniquePtr fptr_32k;
@@ -184,33 +179,6 @@
     EXPECT_GT(fptr_32k->extents().size(), 0);
 }
 
-TEST_F(TestExistingFile, CheckWriteError) {
-    ASSERT_NE(fptr_4k, nullptr);
-    // prepare buffer for writing the pattern - 0xa0
-    uint64_t blocksize = fptr_4k->block_size();
-    auto buff_4k = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
-    ASSERT_NE(buff_4k, nullptr);
-    memset(buff_4k.get(), 0xa0, blocksize);
-
-    uint8_t* p = static_cast<uint8_t*>(buff_4k.get());
-    for (off64_t off = 0; off < 4096; off += blocksize) {
-        ASSERT_TRUE(fptr_4k->Write(off, p, blocksize));
-    }
-    EXPECT_TRUE(fptr_4k->Flush());
-
-    ASSERT_NE(fptr_32k, nullptr);
-    // prepare buffer for writing the pattern - 0xa0
-    blocksize = fptr_32k->block_size();
-    auto buff_32k = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
-    ASSERT_NE(buff_32k, nullptr);
-    memset(buff_32k.get(), 0xa0, blocksize);
-    p = static_cast<uint8_t*>(buff_32k.get());
-    for (off64_t off = 0; off < 4096; off += blocksize) {
-        ASSERT_TRUE(fptr_32k->Write(off, p, blocksize));
-    }
-    EXPECT_TRUE(fptr_32k->Flush());
-}
-
 class VerifyBlockWritesExt4 : public ::testing::Test {
     // 2GB Filesystem and 4k block size by default
     static constexpr uint64_t block_size = 4096;
@@ -253,46 +221,6 @@
     std::string fs_path;
 };
 
-TEST_F(VerifyBlockWritesExt4, CheckWrites) {
-    EXPECT_EQ(access(fs_path.c_str(), F_OK), 0);
-
-    std::string file_path = mntpoint + "/testfile";
-    uint64_t file_size = 100 * 1024 * 1024;
-    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
-    ASSERT_NE(buffer, nullptr);
-    memset(buffer.get(), 0xa0, getpagesize());
-    {
-        // scoped fiemap writer
-        FiemapUniquePtr fptr = FiemapWriter::Open(file_path, file_size);
-        ASSERT_NE(fptr, nullptr);
-        uint8_t* p = static_cast<uint8_t*>(buffer.get());
-        for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
-            ASSERT_TRUE(fptr->Write(off, p, getpagesize()));
-        }
-        EXPECT_TRUE(fptr->Flush());
-    }
-    // unmount file system here to make sure we invalidated all page cache and
-    // remount the filesystem again for verification
-    ASSERT_EQ(umount(mntpoint.c_str()), 0);
-
-    LoopDevice loop_dev(fs_path);
-    ASSERT_TRUE(loop_dev.valid());
-    ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0)
-            << "failed to mount: " << loop_dev.device() << " on " << mntpoint << ": "
-            << strerror(errno);
-
-    ::android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_SYNC));
-    ASSERT_NE(fd, -1);
-    auto filebuf = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
-    ASSERT_NE(filebuf, nullptr);
-    for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
-        memset(filebuf.get(), 0x00, getpagesize());
-        ASSERT_EQ(pread64(fd, filebuf.get(), getpagesize(), off), getpagesize());
-        ASSERT_EQ(memcmp(filebuf.get(), buffer.get(), getpagesize()), 0)
-                << "Invalid pattern at offset: " << off << " size " << getpagesize();
-    }
-}
-
 class VerifyBlockWritesF2fs : public ::testing::Test {
     // 2GB Filesystem and 4k block size by default
     static constexpr uint64_t block_size = 4096;
@@ -335,46 +263,6 @@
     std::string fs_path;
 };
 
-TEST_F(VerifyBlockWritesF2fs, CheckWrites) {
-    EXPECT_EQ(access(fs_path.c_str(), F_OK), 0);
-
-    std::string file_path = mntpoint + "/testfile";
-    uint64_t file_size = 100 * 1024 * 1024;
-    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
-    ASSERT_NE(buffer, nullptr);
-    memset(buffer.get(), 0xa0, getpagesize());
-    {
-        // scoped fiemap writer
-        FiemapUniquePtr fptr = FiemapWriter::Open(file_path, file_size);
-        ASSERT_NE(fptr, nullptr);
-        uint8_t* p = static_cast<uint8_t*>(buffer.get());
-        for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
-            ASSERT_TRUE(fptr->Write(off, p, getpagesize()));
-        }
-        EXPECT_TRUE(fptr->Flush());
-    }
-    // unmount file system here to make sure we invalidated all page cache and
-    // remount the filesystem again for verification
-    ASSERT_EQ(umount(mntpoint.c_str()), 0);
-
-    LoopDevice loop_dev(fs_path);
-    ASSERT_TRUE(loop_dev.valid());
-    ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0)
-            << "failed to mount: " << loop_dev.device() << " on " << mntpoint << ": "
-            << strerror(errno);
-
-    ::android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_SYNC));
-    ASSERT_NE(fd, -1);
-    auto filebuf = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
-    ASSERT_NE(filebuf, nullptr);
-    for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
-        memset(filebuf.get(), 0x00, getpagesize());
-        ASSERT_EQ(pread64(fd, filebuf.get(), getpagesize(), off), getpagesize());
-        ASSERT_EQ(memcmp(filebuf.get(), buffer.get(), getpagesize()), 0)
-                << "Invalid pattern at offset: " << off << " size " << getpagesize();
-    }
-}
-
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     if (argc <= 1) {
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
index a0085cf..edbae77 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -57,15 +57,6 @@
     // FiemapWriter::Open).
     static bool HasPinnedExtents(const std::string& file_path);
 
-    // Syncs block device writes.
-    bool Flush() const;
-
-    // Writes the file by using its FIEMAP and performing i/o on the raw block device.
-    // The return value is success / failure. This will happen in particular if the
-    // kernel write returns errors, extents are not writeable or more importantly, if the 'size' is
-    // not aligned to the block device's block size.
-    bool Write(off64_t off, uint8_t* buffer, uint64_t size);
-
     // The counter part of Write(). It is an error for the offset to be unaligned with
     // the block device's block size.
     // In case of error, the contents of buffer MUST be discarded.
@@ -93,7 +84,6 @@
 
     // File descriptors for the file and block device
     ::android::base::unique_fd file_fd_;
-    ::android::base::unique_fd bdev_fd_;
 
     // Size in bytes of the file this class is writing
     uint64_t file_size_;
@@ -112,9 +102,6 @@
     std::vector<struct fiemap_extent> extents_;
 
     FiemapWriter() = default;
-
-    uint64_t WriteExtent(const struct fiemap_extent& ext, uint8_t* buffer, off64_t logical_off,
-                         uint64_t length);
 };
 
 }  // namespace fiemap_writer
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index f57c9d6..945087f 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -27,6 +27,7 @@
 
 #include "util.h"
 
+using android::base::Basename;
 using android::base::StartsWith;
 using android::base::unique_fd;
 
@@ -61,7 +62,7 @@
 // class VBMetaData
 // ----------------
 std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
-    auto vbmeta_header(std::make_unique<AvbVBMetaImageHeader>());
+    auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>();
 
     if (!vbmeta_header) return nullptr;
 
@@ -136,7 +137,7 @@
     }
     table.set_readonly(true);
 
-    const std::string mount_point(basename(fstab_entry->mount_point.c_str()));
+    const std::string mount_point(Basename(fstab_entry->mount_point));
     android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
     if (!dm.CreateDevice(mount_point, table)) {
         LERROR << "Couldn't create verity device!";
@@ -163,12 +164,12 @@
     return true;
 }
 
-bool GetHashtreeDescriptor(const std::string& partition_name,
-                           const std::vector<VBMetaData>& vbmeta_images,
-                           AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt,
-                           std::string* out_digest) {
+std::unique_ptr<AvbHashtreeDescriptor> GetHashtreeDescriptor(
+    const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images,
+    std::string* out_salt, std::string* out_digest) {
     bool found = false;
     const uint8_t* desc_partition_name;
+    auto hashtree_desc = std::make_unique<AvbHashtreeDescriptor>();
 
     for (const auto& vbmeta : vbmeta_images) {
         size_t num_descriptors;
@@ -189,15 +190,15 @@
                 desc_partition_name =
                         (const uint8_t*)descriptors[n] + sizeof(AvbHashtreeDescriptor);
                 if (!avb_hashtree_descriptor_validate_and_byteswap(
-                            (AvbHashtreeDescriptor*)descriptors[n], out_hashtree_desc)) {
+                        (AvbHashtreeDescriptor*)descriptors[n], hashtree_desc.get())) {
                     continue;
                 }
-                if (out_hashtree_desc->partition_name_len != partition_name.length()) {
+                if (hashtree_desc->partition_name_len != partition_name.length()) {
                     continue;
                 }
                 // Notes that desc_partition_name is not NUL-terminated.
                 std::string hashtree_partition_name((const char*)desc_partition_name,
-                                                    out_hashtree_desc->partition_name_len);
+                                                    hashtree_desc->partition_name_len);
                 if (hashtree_partition_name == partition_name) {
                     found = true;
                 }
@@ -209,16 +210,43 @@
 
     if (!found) {
         LERROR << "Partition descriptor not found: " << partition_name.c_str();
+        return nullptr;
+    }
+
+    const uint8_t* desc_salt = desc_partition_name + hashtree_desc->partition_name_len;
+    *out_salt = BytesToHex(desc_salt, hashtree_desc->salt_len);
+
+    const uint8_t* desc_digest = desc_salt + hashtree_desc->salt_len;
+    *out_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len);
+
+    return hashtree_desc;
+}
+
+bool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev,
+                                   const std::vector<VBMetaData>& vbmeta_images,
+                                   const std::string& ab_suffix,
+                                   const std::string& ab_other_suffix) {
+    // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+    // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+    std::string partition_name = DeriveAvbPartitionName(*fstab_entry, ab_suffix, ab_other_suffix);
+
+    if (partition_name.empty()) {
+        LERROR << "partition name is empty, cannot lookup AVB descriptors";
         return false;
     }
 
-    const uint8_t* desc_salt = desc_partition_name + out_hashtree_desc->partition_name_len;
-    *out_salt = BytesToHex(desc_salt, out_hashtree_desc->salt_len);
+    std::string salt;
+    std::string root_digest;
+    std::unique_ptr<AvbHashtreeDescriptor> hashtree_descriptor =
+        GetHashtreeDescriptor(partition_name, vbmeta_images, &salt, &root_digest);
+    if (!hashtree_descriptor) {
+        return false;
+    }
 
-    const uint8_t* desc_digest = desc_salt + out_hashtree_desc->salt_len;
-    *out_digest = BytesToHex(desc_digest, out_hashtree_desc->root_digest_len);
-
-    return true;
+    // Converts HASHTREE descriptor to verity table to load into kernel.
+    // When success, the new device path will be returned, e.g., /dev/block/dm-2.
+    return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, salt, root_digest,
+                                 wait_for_verity_dev);
 }
 
 // Converts a AVB partition_name (without A/B suffix) to a device partition name.
@@ -244,6 +272,38 @@
     return sanitized_partition_name + append_suffix;
 }
 
+// Converts fstab_entry.blk_device (with ab_suffix) to a AVB partition name.
+// e.g., "/dev/block/by-name/system_a", slot_select       => "system",
+//       "/dev/block/by-name/system_b", slot_select_other => "system_other".
+//
+// Or for a logical partition (with ab_suffix):
+// e.g., "system_a", slot_select       => "system",
+//       "system_b", slot_select_other => "system_other".
+std::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix,
+                                   const std::string& ab_other_suffix) {
+    std::string partition_name;
+    if (fstab_entry.fs_mgr_flags.logical) {
+        partition_name = fstab_entry.logical_partition_name;
+    } else {
+        partition_name = Basename(fstab_entry.blk_device);
+    }
+
+    if (fstab_entry.fs_mgr_flags.slot_select) {
+        auto found = partition_name.rfind(ab_suffix);
+        if (found != std::string::npos) {
+            partition_name.erase(found);  // converts system_a => system
+        }
+    } else if (fstab_entry.fs_mgr_flags.slot_select_other) {
+        auto found = partition_name.rfind(ab_other_suffix);
+        if (found != std::string::npos) {
+            partition_name.erase(found);  // converts system_b => system
+        }
+        partition_name += "_other";  // converts system => system_other
+    }
+
+    return partition_name;
+}
+
 off64_t GetTotalSize(int fd) {
     off64_t saved_current = lseek64(fd, 0, SEEK_CUR);
     if (saved_current == -1) {
@@ -268,19 +328,19 @@
 
 std::unique_ptr<AvbFooter> GetAvbFooter(int fd) {
     std::array<uint8_t, AVB_FOOTER_SIZE> footer_buf;
-    auto footer(std::make_unique<AvbFooter>());
+    auto footer = std::make_unique<AvbFooter>();
 
     off64_t footer_offset = GetTotalSize(fd) - AVB_FOOTER_SIZE;
 
     ssize_t num_read =
             TEMP_FAILURE_RETRY(pread64(fd, footer_buf.data(), AVB_FOOTER_SIZE, footer_offset));
     if (num_read < 0 || num_read != AVB_FOOTER_SIZE) {
-        PERROR << "Failed to read AVB footer";
+        PERROR << "Failed to read AVB footer at offset: " << footer_offset;
         return nullptr;
     }
 
     if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf.data(), footer.get())) {
-        PERROR << "AVB footer verification failed.";
+        PERROR << "AVB footer verification failed at offset " << footer_offset;
         return nullptr;
     }
 
@@ -432,24 +492,22 @@
     return chain_partitions;
 }
 
-VBMetaVerifyResult LoadAndVerifyVbmetaImpl(
-        const std::string& partition_name, const std::string& ab_suffix,
-        const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
-        bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
-        std::function<std::string(const std::string&)> device_path_constructor,
-        bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images) {
+// Loads the vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+    const std::string& image_path, const std::string& partition_name,
+    const std::string& expected_public_key_blob, bool allow_verification_error,
+    bool rollback_protection, bool is_chained_vbmeta, bool* out_verification_disabled,
+    VBMetaVerifyResult* out_verify_result) {
     // Ensures the device path (might be a symlink created by init) is ready to access.
-    auto device_path = device_path_constructor(
-            AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix));
-    if (!WaitForFile(device_path, 1s)) {
-        PERROR << "No such partition: " << device_path;
-        return VBMetaVerifyResult::kError;
+    if (!WaitForFile(image_path, 1s)) {
+        PERROR << "No such path: " << image_path;
+        return nullptr;
     }
 
-    unique_fd fd(TEMP_FAILURE_RETRY(open(device_path.c_str(), O_RDONLY | O_CLOEXEC)));
+    unique_fd fd(TEMP_FAILURE_RETRY(open(image_path.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd < 0) {
-        PERROR << "Failed to open: " << device_path;
-        return VBMetaVerifyResult::kError;
+        PERROR << "Failed to open: " << image_path;
+        return nullptr;
     }
 
     VBMetaVerifyResult verify_result;
@@ -457,53 +515,84 @@
             VerifyVBMetaData(fd, partition_name, expected_public_key_blob, &verify_result);
     if (!vbmeta) {
         LERROR << partition_name << ": Failed to load vbmeta, result: " << verify_result;
-        return VBMetaVerifyResult::kError;
+        return nullptr;
     }
+    vbmeta->set_vbmeta_path(image_path);
 
     if (!allow_verification_error && verify_result == VBMetaVerifyResult::kErrorVerification) {
         LERROR << partition_name << ": allow verification error is not allowed";
-        return VBMetaVerifyResult::kError;
+        return nullptr;
     }
 
     std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
             vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
     if (!vbmeta_header) {
         LERROR << partition_name << ": Failed to get vbmeta header";
-        return VBMetaVerifyResult::kError;
+        return nullptr;
     }
 
     if (rollback_protection && RollbackDetected(partition_name, vbmeta_header->rollback_index)) {
-        return VBMetaVerifyResult::kError;
+        return nullptr;
     }
 
     // vbmeta flags can only be set by the top-level vbmeta image.
     if (is_chained_vbmeta && vbmeta_header->flags != 0) {
         LERROR << partition_name << ": chained vbmeta image has non-zero flags";
-        return VBMetaVerifyResult::kError;
+        return nullptr;
     }
 
+    // Checks if verification has been disabled by setting a bit in the image.
+    if (out_verification_disabled) {
+        if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
+            LWARNING << "VERIFICATION_DISABLED bit is set for partition: " << partition_name;
+            *out_verification_disabled = true;
+        } else {
+            *out_verification_disabled = false;
+        }
+    }
+
+    if (out_verify_result) {
+        *out_verify_result = verify_result;
+    }
+
+    return vbmeta;
+}
+
+VBMetaVerifyResult LoadAndVerifyVbmetaByPartition(
+    const std::string& partition_name, const std::string& ab_suffix,
+    const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+    bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+    std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta,
+    std::vector<VBMetaData>* out_vbmeta_images) {
+    auto image_path = device_path_constructor(
+        AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix));
+
+    bool verification_disabled = false;
+    VBMetaVerifyResult verify_result;
+    auto vbmeta = LoadAndVerifyVbmetaByPath(
+        image_path, partition_name, expected_public_key_blob, allow_verification_error,
+        rollback_protection, is_chained_vbmeta, &verification_disabled, &verify_result);
+
+    if (!vbmeta) {
+        return VBMetaVerifyResult::kError;
+    }
     if (out_vbmeta_images) {
         out_vbmeta_images->emplace_back(std::move(*vbmeta));
     }
 
-    // If verification has been disabled by setting a bit in the image, we're done.
-    if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
-        LWARNING << "VERIFICATION_DISABLED bit is set for partition: " << partition_name;
-        return verify_result;
-    }
-
-    if (load_chained_vbmeta) {
+    // Only loads chained vbmeta if AVB verification is NOT disabled.
+    if (!verification_disabled && load_chained_vbmeta) {
         bool fatal_error = false;
         auto chain_partitions = GetChainPartitionInfo(*out_vbmeta_images->rbegin(), &fatal_error);
         if (fatal_error) {
             return VBMetaVerifyResult::kError;
         }
         for (auto& chain : chain_partitions) {
-            auto sub_ret = LoadAndVerifyVbmetaImpl(
-                    chain.partition_name, ab_suffix, ab_other_suffix, chain.public_key_blob,
-                    allow_verification_error, load_chained_vbmeta, rollback_protection,
-                    device_path_constructor, true, /* is_chained_vbmeta */
-                    out_vbmeta_images);
+            auto sub_ret = LoadAndVerifyVbmetaByPartition(
+                chain.partition_name, ab_suffix, ab_other_suffix, chain.public_key_blob,
+                allow_verification_error, load_chained_vbmeta, rollback_protection,
+                device_path_constructor, true, /* is_chained_vbmeta */
+                out_vbmeta_images);
             if (sub_ret != VBMetaVerifyResult::kSuccess) {
                 verify_result = sub_ret;  // might be 'ERROR' or 'ERROR VERIFICATION'.
                 if (verify_result == VBMetaVerifyResult::kError) {
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index babbfef..14918f2 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -46,10 +46,9 @@
 };
 
 // AvbHashtreeDescriptor to dm-verity table setup.
-bool GetHashtreeDescriptor(const std::string& partition_name,
-                           const std::vector<VBMetaData>& vbmeta_images,
-                           AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt,
-                           std::string* out_digest);
+std::unique_ptr<AvbHashtreeDescriptor> GetHashtreeDescriptor(
+    const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images,
+    std::string* out_salt, std::string* out_digest);
 
 bool ConstructVerityTable(const AvbHashtreeDescriptor& hashtree_desc, const std::string& salt,
                           const std::string& root_digest, const std::string& blk_device,
@@ -59,11 +58,20 @@
                            const std::string& salt, const std::string& root_digest,
                            bool wait_for_verity_dev);
 
-// Maps AVB partition name to a device partition name.
+// Searches a Avb hashtree descriptor in vbmeta_images for fstab_entry, to enable dm-verity.
+bool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev,
+                                   const std::vector<VBMetaData>& vbmeta_images,
+                                   const std::string& ab_suffix, const std::string& ab_other_suffix);
+
+// Converts AVB partition name to a device partition name.
 std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
                                          const std::string& ab_suffix,
                                          const std::string& ab_other_suffix);
 
+// Converts by-name symlink to AVB partition name.
+std::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix,
+                                   const std::string& ab_other_suffix);
+
 // AvbFooter and AvbMetaImage maninpulations.
 off64_t GetTotalSize(int fd);
 
@@ -84,12 +92,22 @@
 // Extracts chain partition info.
 std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error);
 
-VBMetaVerifyResult LoadAndVerifyVbmetaImpl(
-        const std::string& partition_name, const std::string& ab_suffix,
-        const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
-        bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
-        std::function<std::string(const std::string&)> device_path_constructor,
-        bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images);
+// Loads the single vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+    const std::string& image_path, const std::string& partition_name,
+    const std::string& expected_public_key_blob, bool allow_verification_error,
+    bool rollback_protection, bool is_chained_vbmeta, bool* out_verification_disabled,
+    VBMetaVerifyResult* out_verify_result);
+
+// Loads the top-level vbmeta and all its chained vbmeta images.
+// The actual device path is constructed at runtime by:
+// partition_name, ab_suffix, ab_other_suffix, and device_path_constructor.
+VBMetaVerifyResult LoadAndVerifyVbmetaByPartition(
+    const std::string& partition_name, const std::string& ab_suffix,
+    const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+    bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+    std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta,
+    std::vector<VBMetaData>* out_vbmeta_images);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 9f8ad53..773baf4 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -233,10 +233,10 @@
 
     auto device_path = custom_device_path ? custom_device_path : android_by_name_symlink;
 
-    auto verify_result = LoadAndVerifyVbmetaImpl(
-            partition_name, ab_suffix, ab_other_suffix, expected_key_blob, allow_verification_error,
-            load_chained_vbmeta, rollback_protection, device_path, false,
-            /* is_chained_vbmeta */ &avb_handle->vbmeta_images_);
+    auto verify_result = LoadAndVerifyVbmetaByPartition(
+        partition_name, ab_suffix, ab_other_suffix, expected_key_blob, allow_verification_error,
+        load_chained_vbmeta, rollback_protection, device_path, false,
+        /* is_chained_vbmeta */ &avb_handle->vbmeta_images_);
     switch (verify_result) {
         case VBMetaVerifyResult::kSuccess:
             avb_handle->status_ = AvbHandleStatus::kSuccess;
@@ -245,10 +245,16 @@
             avb_handle->status_ = AvbHandleStatus::kVerificationError;
             break;
         default:
-            LERROR << "LoadAndVerifyVbmetaImpl failed, result: " << verify_result;
+            LERROR << "LoadAndVerifyVbmetaByPartition failed, result: " << verify_result;
             return nullptr;
     }
 
+    // Sanity check here because we have to use vbmeta_images_[0] below.
+    if (avb_handle->vbmeta_images_.size() < 1) {
+        LERROR << "LoadAndVerifyVbmetaByPartition failed, no vbmeta loaded";
+        return nullptr;
+    }
+
     // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
     avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
 
@@ -377,6 +383,54 @@
     return avb_handle;
 }
 
+AvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry) {
+    if (fstab_entry->avb_key.empty()) {
+        LERROR << "avb_key=/path/to/key is missing for " << fstab_entry->mount_point;
+        return AvbHashtreeResult::kFail;
+    }
+
+    // Binds allow_verification_error and rollback_protection to device unlock state.
+    bool allow_verification_error = IsDeviceUnlocked();
+    bool rollback_protection = !allow_verification_error;
+
+    std::string expected_key_blob;
+    if (!ReadFileToString(fstab_entry->avb_key, &expected_key_blob)) {
+        if (!allow_verification_error) {
+            LERROR << "Failed to load avb_key: " << fstab_entry->avb_key
+                   << " for mount point: " << fstab_entry->mount_point;
+            return AvbHashtreeResult::kFail;
+        }
+        // Use empty key blob, which means no expectation, if allow verification error.
+        expected_key_blob.clear();
+    }
+
+    bool verification_disabled = false;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+        fstab_entry->blk_device, "" /* partition_name, no need for a standalone path */,
+        expected_key_blob, allow_verification_error, rollback_protection,
+        false /* not is_chained_vbmeta */, &verification_disabled, nullptr /* out_verify_result */);
+
+    if (!vbmeta) {
+        LERROR << "Failed to load vbmeta: " << fstab_entry->blk_device;
+        return AvbHashtreeResult::kFail;
+    }
+
+    if (verification_disabled) {
+        LINFO << "AVB verification disabled on: " << fstab_entry->mount_point;
+        return AvbHashtreeResult::kDisabled;
+    }
+
+    // Puts the vbmeta into a vector, for LoadAvbHashtreeToEnableVerity() to use.
+    std::vector<VBMetaData> vbmeta_images;
+    vbmeta_images.emplace_back(std::move(*vbmeta));
+    if (!LoadAvbHashtreeToEnableVerity(fstab_entry, true /* wait_for_verity_dev */, vbmeta_images,
+                                       fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
+        return AvbHashtreeResult::kFail;
+    }
+
+    return AvbHashtreeResult::kSuccess;
+}
+
 AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
     if (!fstab_entry || status_ == AvbHandleStatus::kUninitialized || vbmeta_images_.size() < 1) {
         return AvbHashtreeResult::kFail;
@@ -388,33 +442,8 @@
         return AvbHashtreeResult::kDisabled;
     }
 
-    // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
-    // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
-    std::string partition_name;
-    if (fstab_entry->fs_mgr_flags.logical) {
-        partition_name = fstab_entry->logical_partition_name;
-    } else {
-        partition_name = Basename(fstab_entry->blk_device);
-    }
-
-    if (fstab_entry->fs_mgr_flags.slot_select) {
-        auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
-        if (ab_suffix != std::string::npos) {
-            partition_name.erase(ab_suffix);
-        }
-    }
-
-    AvbHashtreeDescriptor hashtree_descriptor;
-    std::string salt;
-    std::string root_digest;
-    if (!GetHashtreeDescriptor(partition_name, vbmeta_images_, &hashtree_descriptor, &salt,
-                               &root_digest)) {
-        return AvbHashtreeResult::kFail;
-    }
-
-    // Converts HASHTREE descriptor to verity_table_params.
-    if (!HashtreeDmVeritySetup(fstab_entry, hashtree_descriptor, salt, root_digest,
-                               wait_for_verity_dev)) {
+    if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images_,
+                                       fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
         return AvbHashtreeResult::kFail;
     }
 
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index 7af3c7e..55a320e 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -81,8 +81,15 @@
     // true to update vbmeta_size_ to the actual size with valid content.
     std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
 
+    // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file.
+    // e.g.,
+    // - /dev/block/by-name/system_a
+    // - /path/to/system_other.img.
+    void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); }
+
     // Get methods for each data member.
     const std::string& partition() const { return partition_name_; }
+    const std::string& vbmeta_path() const { return vbmeta_path_; }
     uint8_t* data() const { return vbmeta_ptr_.get(); }
     const size_t& size() const { return vbmeta_size_; }
 
@@ -93,6 +100,7 @@
     std::unique_ptr<uint8_t[]> vbmeta_ptr_;
     size_t vbmeta_size_;
     std::string partition_name_;
+    std::string vbmeta_path_;
 };
 
 class FsManagerAvbOps;
@@ -160,6 +168,9 @@
     //   - kDisabled: hashtree is disabled.
     AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev);
 
+    // Similar to above, but loads the offline vbmeta from the end of fstab_entry->blk_device.
+    static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry);
+
     const std::string& avb_version() const { return avb_version_; }
     const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }
     AvbHandleStatus status() const { return status_; }
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 26b3294..23faffc 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -14,20 +14,25 @@
  * limitations under the License.
  */
 
+#include <endian.h>
+
 #include <android-base/unique_fd.h>
 #include <base/files/file_util.h>
 #include <base/rand_util.h>
 #include <base/strings/string_util.h>
+#include <libavb/libavb.h>
 
 #include "avb_util.h"
 #include "fs_avb_test_util.h"
 
 // Target classes or functions to test:
 using android::fs_mgr::AvbPartitionToDevicePatition;
+using android::fs_mgr::DeriveAvbPartitionName;
 using android::fs_mgr::GetAvbFooter;
 using android::fs_mgr::GetChainPartitionInfo;
 using android::fs_mgr::GetTotalSize;
-using android::fs_mgr::LoadAndVerifyVbmetaImpl;
+using android::fs_mgr::LoadAndVerifyVbmetaByPartition;
+using android::fs_mgr::LoadAndVerifyVbmetaByPath;
 using android::fs_mgr::VBMetaData;
 using android::fs_mgr::VBMetaVerifyResult;
 using android::fs_mgr::VerifyPublicKeyBlob;
@@ -52,8 +57,34 @@
 
     // Loads the content of avb_image_path and comparies it with the content of vbmeta.
     bool CompareVBMeta(const base::FilePath& avb_image_path, const VBMetaData& expected_vbmeta);
+
+    // Sets the flas in vbmeta header, the image_path could be a vbmeta.img or a system.img.
+    void SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags);
 };
 
+void AvbUtilTest::SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags) {
+    if (!base::PathExists(image_path)) return;
+
+    std::string image_file_name = image_path.RemoveExtension().BaseName().value();
+    bool is_vbmeta_partition =
+        base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII);
+
+    android::base::unique_fd fd(open(image_path.value().c_str(), O_RDWR | O_CLOEXEC));
+    EXPECT_TRUE(fd > 0);
+
+    uint64_t vbmeta_offset = 0;  // for vbmeta.img
+    if (!is_vbmeta_partition) {
+        std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+        EXPECT_NE(nullptr, footer);
+        vbmeta_offset = footer->vbmeta_offset;
+    }
+
+    auto flags_offset = vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags);
+    uint32_t flags_data = htobe32(flags);
+    EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));
+    EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));
+}
+
 TEST_F(AvbUtilTest, AvbPartitionToDevicePatition) {
     EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", ""));
     EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", "_b"));
@@ -65,6 +96,63 @@
     EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "_a", "_b"));
 }
 
+TEST_F(AvbUtilTest, DeriveAvbPartitionName) {
+    // The fstab_entry to test.
+    FstabEntry fstab_entry = {
+        .blk_device = "/dev/block/dm-1",  // a dm-linear device (logical)
+        .mount_point = "/system",
+        .fs_type = "ext4",
+        .logical_partition_name = "system",
+    };
+
+    // Logical partitions.
+    // non-A/B
+    fstab_entry.fs_mgr_flags.logical = true;
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_dont_care"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "", ""));
+    // Active slot.
+    fstab_entry.fs_mgr_flags.slot_select = true;
+    fstab_entry.logical_partition_name = "system_a";
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_dont_care"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", ""));
+    EXPECT_EQ("system_a", DeriveAvbPartitionName(fstab_entry, "_wont_erase_a", "_dont_care"));
+    // The other slot.
+    fstab_entry.fs_mgr_flags.slot_select = false;
+    fstab_entry.fs_mgr_flags.slot_select_other = true;
+    fstab_entry.logical_partition_name = "system_b";
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_b"));
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "", "_b"));
+    EXPECT_EQ("system_b_other", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_wont_erase_b"));
+
+    // Non-logical partitions.
+    // non-A/B.
+    fstab_entry.fs_mgr_flags.logical = false;
+    fstab_entry.fs_mgr_flags.slot_select = false;
+    fstab_entry.fs_mgr_flags.slot_select_other = false;
+    fstab_entry.blk_device = "/dev/block/by-name/system";
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_dont_care"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "", ""));
+    // Active slot _a.
+    fstab_entry.fs_mgr_flags.slot_select = true;
+    fstab_entry.blk_device = "/dev/block/by-name/system_a";
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_dont_care"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", ""));
+    EXPECT_EQ("system_a", DeriveAvbPartitionName(fstab_entry, "_wont_erase_a", "_dont_care"));
+    // Inactive slot _b.
+    fstab_entry.fs_mgr_flags.slot_select = false;
+    fstab_entry.fs_mgr_flags.slot_select_other = true;
+    fstab_entry.blk_device = "/dev/block/by-name/system_b";
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "dont_care", "_b"));
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+    EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "", "_b"));
+    EXPECT_EQ("system_b_other", DeriveAvbPartitionName(fstab_entry, "dont_care", "_wont_erase_b"));
+}
+
 TEST_F(AvbUtilTest, GetFdTotalSize) {
     // Generates a raw test.img via BaseFsAvbTest.
     const size_t image_size = 5 * 1024 * 1024;
@@ -751,7 +839,213 @@
     EXPECT_EQ(false, fatal_error);
 }
 
-TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImpl) {
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPath) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+                 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    base::FilePath rsa4096_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+    bool verification_disabled;
+    VBMetaVerifyResult verify_result;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", expected_key_blob_4096,
+        false /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, &verification_disabled, &verify_result);
+
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+    EXPECT_EQ(false, verification_disabled);
+
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathErrorVerification) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+                 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    base::FilePath rsa4096_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+    // Modifies the auxiliary data of system_other.img
+    auto fd = OpenUniqueReadFd(system_path);
+    auto system_footer = GetAvbFooter(fd);
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system_other-vbmeta.img");
+    auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t header_block_offset = 0;
+    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+    size_t auxiliary_block_offset =
+        authentication_block_offset + system_header->authentication_data_block_size;
+
+    // Modifies the hash.
+    ModifyFile(
+        system_path,
+        (system_footer->vbmeta_offset + authentication_block_offset + system_header->hash_offset),
+        system_header->hash_size);
+
+    VBMetaVerifyResult verify_result;
+    // Not allow verification error.
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", expected_key_blob_4096,
+        false /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result);
+    EXPECT_EQ(nullptr, vbmeta);
+
+    // Allow verification error.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", expected_key_blob_4096,
+        true /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+    // Modifies the auxiliary data block.
+    ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,
+               system_header->auxiliary_data_block_size);
+
+    // Not allow verification error.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", expected_key_blob_4096,
+        false /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result);
+    EXPECT_EQ(nullptr, vbmeta);
+
+    // Allow verification error.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", expected_key_blob_4096,
+        true /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathUnexpectedPublicKey) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+                 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    base::FilePath rsa2048_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+    std::string unexpected_key_blob_2048;
+    EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &unexpected_key_blob_2048));
+
+    // Uses the correct expected public key.
+    VBMetaVerifyResult verify_result;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", expected_key_blob_4096,
+        false /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(verify_result, VBMetaVerifyResult::kSuccess);
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+    // Uses the wrong expected public key with allow_verification_error set to false.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", unexpected_key_blob_2048,
+        false /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result);
+    EXPECT_EQ(nullptr, vbmeta);
+
+    // Uses the wrong expected public key with allow_verification_error set to true.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", unexpected_key_blob_2048,
+        true /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, nullptr /* verification_disabled */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(verify_result, VBMetaVerifyResult::kErrorVerification);
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathVerificationDisabled) {
+    // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+                 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    base::FilePath rsa4096_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+    // Sets disabled flag and expect the returned verification_disabled is true.
+    SetVBMetaFlags(system_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    bool verification_disabled;
+    VBMetaVerifyResult verify_result;
+    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", expected_key_blob_4096,
+        true /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, &verification_disabled, &verify_result);
+
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+    EXPECT_EQ(true, verification_disabled);  // should be true.
+
+    EXPECT_EQ(2112UL, vbmeta->size());
+    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+    EXPECT_EQ("system_other", vbmeta->partition());
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+    // Since the vbmeta flags is modified, vbmeta will be nullptr
+    // if verification error isn't allowed.
+    vbmeta = LoadAndVerifyVbmetaByPath(
+        system_path.value(), "system_other", expected_key_blob_4096,
+        false /* allow_verification_error */, false /* rollback_protection */,
+        false /* is_chained_vbmeta */, &verification_disabled, &verify_result);
+    EXPECT_EQ(nullptr, vbmeta);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartition) {
     // Generates a raw boot.img
     const size_t boot_image_size = 5 * 1024 * 1024;
     const size_t boot_partition_size = 10 * 1024 * 1024;
@@ -795,18 +1089,18 @@
     EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
               CalcVBMetaDigest("vbmeta.img", "sha256"));
 
-    // Starts to test LoadAndVerifyVbmetaImpl.
+    // Starts to test LoadAndVerifyVbmetaByPartition.
     std::vector<VBMetaData> vbmeta_images;
     auto vbmeta_image_path = [this](const std::string& partition_name) {
         return test_dir_.Append(partition_name + ".img").value();
     };
 
     EXPECT_EQ(VBMetaVerifyResult::kSuccess,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
-                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
-                      true /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
 
     EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system
     // Binary comparison for each vbmeta image.
@@ -818,17 +1112,17 @@
     // Skip loading chained vbmeta images.
     vbmeta_images.clear();
     EXPECT_EQ(VBMetaVerifyResult::kSuccess,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
-                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
-                      false /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
     // Only vbmeta is loaded.
     EXPECT_EQ(1UL, vbmeta_images.size());
     EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
 }
 
-TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplWithSuffixes) {
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionWithSuffixes) {
     // Tests the following chained partitions.
     // vbmeta_a.img
     // |--> boot_b.img (boot_other)
@@ -874,18 +1168,18 @@
              {"vbmeta_system_other", 2, rsa4096_public_key}},
             "--internal_release_string \"unit test\"");
 
-    // Starts to test LoadAndVerifyVbmetaImpl with ab_suffix and ab_other_suffix.
+    // Starts to test LoadAndVerifyVbmetaByPartition with ab_suffix and ab_other_suffix.
     auto vbmeta_image_path = [this](const std::string& partition_name) {
         return test_dir_.Append(partition_name + ".img").value();
     };
 
     std::vector<VBMetaData> vbmeta_images;
     EXPECT_EQ(VBMetaVerifyResult::kSuccess,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
-                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
-                      true /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
 
     EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot_other, vbmeta_system_other and system
     // Binary comparison for each vbmeta image.
@@ -897,26 +1191,26 @@
     // Skips loading chained vbmeta images.
     vbmeta_images.clear();
     EXPECT_EQ(VBMetaVerifyResult::kSuccess,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
-                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
-                      false /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
     // Only vbmeta is loaded.
     EXPECT_EQ(1UL, vbmeta_images.size());
     EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
 
     // Using an invalid suffix for 'other' slot, checks it returns error.
     EXPECT_EQ(VBMetaVerifyResult::kError,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "_a" /* ab_suffix */,
-                      "_invalid_suffix" /* other_suffix */, "" /* expected_public_key_blob*/,
-                      false /* allow_verification_error */, true /* load_chained_vbmeta */,
-                      true /* rollback_protection */, vbmeta_image_path,
-                      false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "_a" /* ab_suffix */,
+                  "_invalid_suffix" /* other_suffix */, "" /* expected_public_key_blob*/,
+                  false /* allow_verification_error */, true /* load_chained_vbmeta */,
+                  true /* rollback_protection */, vbmeta_image_path, false /* is_chained_vbmeta*/,
+                  &vbmeta_images));
 }
 
-TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplErrorVerification) {
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionErrorVerification) {
     // Generates a raw boot.img
     const size_t boot_image_size = 5 * 1024 * 1024;
     const size_t boot_partition_size = 10 * 1024 * 1024;
@@ -964,27 +1258,27 @@
     // Modifies the hash.
     ModifyFile(vbmeta_path, authentication_block_offset + header->hash_offset, header->hash_size);
 
-    // Starts to test LoadAndVerifyVbmetaImpl.
+    // Starts to test LoadAndVerifyVbmetaByPartition.
     std::vector<VBMetaData> vbmeta_images;
     auto vbmeta_image_path = [this](const std::string& partition_name) {
         return test_dir_.Append(partition_name + ".img").value();
     };
     EXPECT_EQ(VBMetaVerifyResult::kError,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
-                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
-                      true /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
     // Stops to load vbmeta because the top-level vbmeta has verification error.
     EXPECT_EQ(0UL, vbmeta_images.size());
 
     // Tries again with verification error allowed.
     EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "", /* other_suffix */
-                      "" /* expected_public_key_blob*/, true /* allow_verification_error */,
-                      true /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "", /* other_suffix */
+                  "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
 
     EXPECT_EQ(3UL, vbmeta_images.size());  // vbmeta, boot, and system
     // Binary comparison for each vbmeta image.
@@ -1008,11 +1302,11 @@
                system_header->auxiliary_data_block_size);
     vbmeta_images.clear();
     EXPECT_EQ(VBMetaVerifyResult::kError,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
-                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
-                      true /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
     // 'vbmeta', 'boot' but no 'system', because of verification error.
     EXPECT_EQ(2UL, vbmeta_images.size());
     // Binary comparison for the loaded 'vbmeta' and 'boot'.
@@ -1020,20 +1314,113 @@
     EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
 
     // Resets the modification of the auxiliary data.
-    ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */);
+    ModifyFile(system_path, 0 /* offset */, -1 /* length */);
 
     // Sets the vbmeta header flags on a chained partition, which introduces an error.
     ModifyFile(system_path, system_footer->vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags),
                sizeof(uint32_t));
     EXPECT_EQ(VBMetaVerifyResult::kError,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
-                      "" /* expected_public_key_blob*/, true /* allow_verification_error */,
-                      true /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
 }
 
-TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplUnexpectedPublicKey) {
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionVerificationDisabled) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+        ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    // Makes a vbmeta_system.img including the 'system' chained descriptor.
+    auto vbmeta_system_path = GenerateVBMetaImage(
+        "vbmeta_system.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+        {},                                  /* include_descriptor_image_paths */
+        {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+        "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"vbmeta_system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    // Starts to test LoadAndVerifyVbmetaByPartition.
+    std::vector<VBMetaData> vbmeta_images;
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+    // Sets VERIFICATION_DISABLED to the top-level vbmeta.img
+    SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+    EXPECT_EQ(1UL, vbmeta_images.size());  // Only vbmeta is loaded
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+
+    // HASHTREE_DISABLED still loads the chained vbmeta.
+    SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+                  false /* is_chained_vbmeta*/, &vbmeta_images));
+    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionUnexpectedPublicKey) {
     // Generates chain partition descriptors.
     base::FilePath rsa2048_public_key =
             ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
@@ -1060,29 +1447,29 @@
     std::vector<VBMetaData> vbmeta_images;
     // Uses the correct expected public key.
     EXPECT_EQ(VBMetaVerifyResult::kSuccess,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
-                      expected_key_blob_8192, true /* allow_verification_error */,
-                      false /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  expected_key_blob_8192, true /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
 
     // Uses the wrong expected public key with allow_verification_error set to true.
     vbmeta_images.clear();
     EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
-                      expected_key_blob_4096, true /* allow_verification_error */,
-                      false /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  expected_key_blob_4096, true /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
 
     // Uses the wrong expected public key with allow_verification_error set to false.
     vbmeta_images.clear();
     EXPECT_EQ(VBMetaVerifyResult::kError,
-              LoadAndVerifyVbmetaImpl(
-                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
-                      expected_key_blob_4096, false /* allow_verification_error */,
-                      false /* load_chained_vbmeta */, true /* rollback_protection */,
-                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+              LoadAndVerifyVbmetaByPartition(
+                  "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                  expected_key_blob_4096, false /* allow_verification_error */,
+                  false /* load_chained_vbmeta */, true /* rollback_protection */,
+                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
 }
 
 }  // namespace fs_avb_host_test
diff --git a/healthd/OWNERS b/healthd/OWNERS
index 00df08a..d3f8758 100644
--- a/healthd/OWNERS
+++ b/healthd/OWNERS
@@ -1,2 +1,2 @@
 elsk@google.com
-toddpoynor@google.com
+hridya@google.com
diff --git a/healthd/animation.h b/healthd/animation.h
index f59fb38..9476c91 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -48,6 +48,25 @@
         GRFont* font;
     };
 
+    // When libminui loads PNG images:
+    // - When treating paths as relative paths, it adds ".png" suffix.
+    // - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix
+    //   is added here.
+    void set_resource_root(const std::string& root) {
+        if (!animation_file.empty()) {
+            animation_file = root + animation_file + ".png";
+        }
+        if (!fail_file.empty()) {
+            fail_file = root + fail_file + ".png";
+        }
+        if (!text_clock.font_file.empty()) {
+            text_clock.font_file = root + text_clock.font_file + ".png";
+        }
+        if (!text_percent.font_file.empty()) {
+            text_percent.font_file = root + text_percent.font_file + ".png";
+        }
+    }
+
     std::string animation_file;
     std::string fail_file;
 
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 2eb5497..8f2f727 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -80,8 +80,13 @@
 #define LOGW(x...) KLOG_WARNING("charger", x);
 #define LOGV(x...) KLOG_DEBUG("charger", x);
 
-static constexpr const char* animation_desc_path =
-    "/res/values/charger/animation.txt";
+// Resources in /product/etc/res overrides resources in /res.
+// If the device is using the Generic System Image (GSI), resources may exist in
+// both paths.
+static constexpr const char* product_animation_desc_path =
+        "/product/etc/res/values/charger/animation.txt";
+static constexpr const char* product_animation_root = "/product/etc/res/images/";
+static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
 
 struct key_state {
     bool pending;
@@ -600,7 +605,10 @@
     bool parse_success;
 
     std::string content;
-    if (base::ReadFileToString(animation_desc_path, &content)) {
+    if (base::ReadFileToString(product_animation_desc_path, &content)) {
+        parse_success = parse_animation_desc(content, &battery_animation);
+        battery_animation.set_resource_root(product_animation_root);
+    } else if (base::ReadFileToString(animation_desc_path, &content)) {
         parse_success = parse_animation_desc(content, &battery_animation);
     } else {
         LOGW("Could not open animation description at %s\n", animation_desc_path);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3199d45..91b7ddd 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -49,8 +49,6 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <bootimg.h>
-#include <fs_mgr.h>
 #include <property_info_parser/property_info_parser.h>
 #include <property_info_serializer/property_info_serializer.h>
 #include <selinux/android.h>
@@ -79,8 +77,6 @@
 using android::properties::PropertyInfoAreaFile;
 using android::properties::PropertyInfoEntry;
 
-#define RECOVERY_MOUNT_POINT "/recovery"
-
 namespace android {
 namespace init {
 
@@ -732,37 +728,6 @@
     property_set("ro.persistent_properties.ready", "true");
 }
 
-void load_recovery_id_prop() {
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) {
-        PLOG(ERROR) << "unable to read default fstab";
-        return;
-    }
-
-    fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), RECOVERY_MOUNT_POINT);
-    if (rec == NULL) {
-        LOG(ERROR) << "/recovery not specified in fstab";
-        return;
-    }
-
-    int fd = open(rec->blk_device, O_RDONLY | O_CLOEXEC);
-    if (fd == -1) {
-        PLOG(ERROR) << "error opening block device " << rec->blk_device;
-        return;
-    }
-
-    boot_img_hdr hdr;
-    if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
-        std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
-        property_set("ro.recovery_id", hex);
-    } else {
-        PLOG(ERROR) << "error reading /recovery";
-    }
-
-    close(fd);
-}
-
 void property_load_boot_defaults() {
     // TODO(b/117892318): merge prop.default and build.prop files into one
     // TODO(b/122864654): read the prop files from all partitions and then
@@ -783,7 +748,6 @@
     load_properties_from_file("/odm/build.prop", NULL);
     load_properties_from_file("/vendor/build.prop", NULL);
     load_properties_from_file("/factory/factory.prop", "ro.*");
-    load_recovery_id_prop();
 
     update_sys_usb_config();
 }
diff --git a/init/util.cpp b/init/util.cpp
index 3781141..80fb03d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -34,7 +34,6 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
@@ -269,16 +268,6 @@
 }
 
 /*
- * Writes hex_len hex characters (1/2 byte) to hex from bytes.
- */
-std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
-    std::string hex("0x");
-    for (size_t i = 0; i < bytes_len; i++)
-        android::base::StringAppendF(&hex, "%02x", bytes[i]);
-    return hex;
-}
-
-/*
  * Returns true is pathname is a directory
  */
 bool is_dir(const char* pathname) {
diff --git a/init/util.h b/init/util.h
index 53f4547..2b57910 100644
--- a/init/util.h
+++ b/init/util.h
@@ -51,7 +51,6 @@
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
 bool make_dir(const std::string& path, mode_t mode);
-std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
 bool is_dir(const char* pathname);
 bool expand_props(const std::string& src, std::string* dst);
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 03edfb5..63c3793 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -134,6 +134,7 @@
 #define AID_IORAPD 1071          /* input/output readahead and pin daemon */
 #define AID_GPU_SERVICE 1072     /* GPU service daemon */
 #define AID_NETWORK_STACK 1073   /* network stack service */
+#define AID_GSID 1074            /* GSI service daemon */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
index 7b64433..c167478 100644
--- a/liblog/tests/AndroidTest.xml
+++ b/liblog/tests/AndroidTest.xml
@@ -16,6 +16,8 @@
 <configuration description="Config for CTS Logging Library test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
index 3e191ad..fc022bd 100644
--- a/libmeminfo/Android.bp
+++ b/libmeminfo/Android.bp
@@ -46,7 +46,6 @@
 
     static_libs: [
         "libmeminfo",
-        "libpagemap",
         "libbase",
         "liblog",
     ],
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index ccc40d1..5451ca3 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -26,7 +26,6 @@
 #include <meminfo/pageacct.h>
 #include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
-#include <pagemap/pagemap.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -37,239 +36,12 @@
 
 pid_t pid = -1;
 
-class ValidateProcMemInfo : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        ASSERT_EQ(0, pm_kernel_create(&ker));
-        ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
-        proc_mem = new ProcMemInfo(pid);
-        ASSERT_NE(proc_mem, nullptr);
-    }
-
-    void TearDown() override {
-        delete proc_mem;
-        pm_process_destroy(proc);
-        pm_kernel_destroy(ker);
-    }
-
-    pm_kernel_t* ker;
-    pm_process_t* proc;
-    ProcMemInfo* proc_mem;
-};
-
-TEST_F(ValidateProcMemInfo, TestMapsSize) {
-    const std::vector<Vma>& maps = proc_mem->Maps();
-    ASSERT_FALSE(maps.empty()) << "Process " << getpid() << " maps are empty";
-}
-
-TEST_F(ValidateProcMemInfo, TestMapsEquality) {
-    const std::vector<Vma>& maps = proc_mem->Maps();
-    ASSERT_EQ(proc->num_maps, maps.size());
-
-    for (size_t i = 0; i < maps.size(); ++i) {
-        EXPECT_EQ(proc->maps[i]->start, maps[i].start);
-        EXPECT_EQ(proc->maps[i]->end, maps[i].end);
-        EXPECT_EQ(proc->maps[i]->offset, maps[i].offset);
-        EXPECT_EQ(std::string(proc->maps[i]->name), maps[i].name);
-    }
-}
-
-TEST_F(ValidateProcMemInfo, TestMaps) {
-    const std::vector<Vma>& maps = proc_mem->Maps();
-    ASSERT_FALSE(maps.empty());
-    ASSERT_EQ(proc->num_maps, maps.size());
-
-    pm_memusage_t map_usage, proc_usage;
-    pm_memusage_zero(&map_usage);
-    pm_memusage_zero(&proc_usage);
-    for (size_t i = 0; i < maps.size(); i++) {
-        ASSERT_EQ(0, pm_map_usage(proc->maps[i], &map_usage));
-        EXPECT_EQ(map_usage.vss, maps[i].usage.vss) << "VSS mismatch for map: " << maps[i].name;
-        EXPECT_EQ(map_usage.rss, maps[i].usage.rss) << "RSS mismatch for map: " << maps[i].name;
-        EXPECT_EQ(map_usage.pss, maps[i].usage.pss) << "PSS mismatch for map: " << maps[i].name;
-        EXPECT_EQ(map_usage.uss, maps[i].usage.uss) << "USS mismatch for map: " << maps[i].name;
-        pm_memusage_add(&proc_usage, &map_usage);
-    }
-
-    EXPECT_EQ(proc_usage.vss, proc_mem->Usage().vss);
-    EXPECT_EQ(proc_usage.rss, proc_mem->Usage().rss);
-    EXPECT_EQ(proc_usage.pss, proc_mem->Usage().pss);
-    EXPECT_EQ(proc_usage.uss, proc_mem->Usage().uss);
-}
-
-TEST_F(ValidateProcMemInfo, TestSwapUsage) {
-    const std::vector<Vma>& maps = proc_mem->Maps();
-    ASSERT_FALSE(maps.empty());
-    ASSERT_EQ(proc->num_maps, maps.size());
-
-    pm_memusage_t map_usage, proc_usage;
-    pm_memusage_zero(&map_usage);
-    pm_memusage_zero(&proc_usage);
-    for (size_t i = 0; i < maps.size(); i++) {
-        ASSERT_EQ(0, pm_map_usage(proc->maps[i], &map_usage));
-        EXPECT_EQ(map_usage.swap, maps[i].usage.swap) << "SWAP mismatch for map: " << maps[i].name;
-        pm_memusage_add(&proc_usage, &map_usage);
-    }
-
-    EXPECT_EQ(proc_usage.swap, proc_mem->Usage().swap);
-}
-
-TEST_F(ValidateProcMemInfo, TestSwapOffsets) {
-    const MemUsage& proc_usage = proc_mem->Usage();
-    const std::vector<uint16_t>& swap_offsets = proc_mem->SwapOffsets();
-
-    EXPECT_EQ(proc_usage.swap / getpagesize(), swap_offsets.size());
-}
-
-TEST_F(ValidateProcMemInfo, TestPageMap) {
-    std::vector<uint64_t> pagemap;
-
-    auto vma_callback = [&](const Vma& vma) {
-        uint64_t* pmap_out;
-        size_t len;
-        ASSERT_EQ(0, pm_process_pagemap_range(proc, vma.start, vma.end, &pmap_out, &len));
-        ASSERT_TRUE(proc_mem->PageMap(vma, &pagemap));
-
-        EXPECT_EQ(len, ((vma.end - vma.start) / getpagesize()));
-        for (size_t i = 0; i < len; i++) {
-            EXPECT_EQ(pmap_out[i], pagemap[i]);
-        }
-    };
-    ASSERT_TRUE(proc_mem->ForEachVma(vma_callback));
-}
-
-class ValidateProcMemInfoWss : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        ASSERT_EQ(0, pm_kernel_create(&ker));
-        ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
-        proc_mem = new ProcMemInfo(pid, true);
-        ASSERT_NE(proc_mem, nullptr);
-    }
-
-    void TearDown() override {
-        delete proc_mem;
-        pm_process_destroy(proc);
-        pm_kernel_destroy(ker);
-    }
-
-    pm_kernel_t* ker;
-    pm_process_t* proc;
-    ProcMemInfo* proc_mem;
-};
-
-TEST_F(ValidateProcMemInfoWss, TestWorkingTestReset) {
+TEST(ProcMemInfo, TestWorkingTestReset) {
     // Expect reset to succeed
     EXPECT_TRUE(ProcMemInfo::ResetWorkingSet(pid));
 }
 
-TEST_F(ValidateProcMemInfoWss, TestWssEquality) {
-    // Read wss using libpagemap
-    pm_memusage_t wss_pagemap;
-    EXPECT_EQ(0, pm_process_workingset(proc, &wss_pagemap, 0));
-
-    // Read wss using libmeminfo
-    MemUsage wss = proc_mem->Wss();
-
-    // compare
-    EXPECT_EQ(wss_pagemap.rss, wss.rss);
-    EXPECT_EQ(wss_pagemap.pss, wss.pss);
-    EXPECT_EQ(wss_pagemap.uss, wss.uss);
-}
-
-class ValidatePageAcct : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        ASSERT_EQ(0, pm_kernel_create(&ker));
-        ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
-    }
-
-    void TearDown() override {
-        pm_process_destroy(proc);
-        pm_kernel_destroy(ker);
-    }
-
-    pm_kernel_t* ker;
-    pm_process_t* proc;
-};
-
-TEST_F(ValidatePageAcct, TestPageFlags) {
-    PageAcct& pi = PageAcct::Instance();
-    pi.InitPageAcct(false);
-
-    uint64_t* pagemap;
-    size_t num_pages;
-    for (size_t i = 0; i < proc->num_maps; i++) {
-        ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
-        for (size_t j = 0; j < num_pages; j++) {
-            if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
-
-            uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
-            uint64_t page_flags_pagemap, page_flags_meminfo;
-
-            ASSERT_EQ(0, pm_kernel_flags(ker, pfn, &page_flags_pagemap));
-            ASSERT_TRUE(pi.PageFlags(pfn, &page_flags_meminfo));
-            // check if page flags equal
-            EXPECT_EQ(page_flags_pagemap, page_flags_meminfo);
-        }
-        free(pagemap);
-    }
-}
-
-TEST_F(ValidatePageAcct, TestPageCounts) {
-    PageAcct& pi = PageAcct::Instance();
-    pi.InitPageAcct(false);
-
-    uint64_t* pagemap;
-    size_t num_pages;
-    for (size_t i = 0; i < proc->num_maps; i++) {
-        ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
-        for (size_t j = 0; j < num_pages; j++) {
-            uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
-            uint64_t map_count_pagemap, map_count_meminfo;
-
-            ASSERT_EQ(0, pm_kernel_count(ker, pfn, &map_count_pagemap));
-            ASSERT_TRUE(pi.PageMapCount(pfn, &map_count_meminfo));
-            // check if map counts are equal
-            EXPECT_EQ(map_count_pagemap, map_count_meminfo);
-        }
-        free(pagemap);
-    }
-}
-
-TEST_F(ValidatePageAcct, TestPageIdle) {
-    // skip the test if idle page tracking isn't enabled
-    if (pm_kernel_init_page_idle(ker) != 0) {
-        return;
-    }
-
-    PageAcct& pi = PageAcct::Instance();
-    ASSERT_TRUE(pi.InitPageAcct(true));
-
-    uint64_t* pagemap;
-    size_t num_pages;
-    for (size_t i = 0; i < proc->num_maps; i++) {
-        ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
-        for (size_t j = 0; j < num_pages; j++) {
-            if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
-            uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
-
-            ASSERT_EQ(0, pm_kernel_mark_page_idle(ker, &pfn, 1));
-            int idle_status_pagemap = pm_kernel_get_page_idle(ker, pfn);
-            int idle_status_meminfo = pi.IsPageIdle(pfn);
-            EXPECT_EQ(idle_status_pagemap, idle_status_meminfo);
-        }
-        free(pagemap);
-    }
-}
-
-TEST(TestProcMemInfo, MapsEmpty) {
-    ProcMemInfo proc_mem(pid);
-    const std::vector<Vma>& maps = proc_mem.Maps();
-    EXPECT_GT(maps.size(), 0);
-}
-
-TEST(TestProcMemInfo, UsageEmpty) {
+TEST(ProcMemInfo, UsageEmpty) {
     // If we created the object for getting working set,
     // the usage must be empty
     ProcMemInfo proc_mem(pid, true);
@@ -281,7 +53,14 @@
     EXPECT_EQ(usage.swap, 0);
 }
 
-TEST(TestProcMemInfo, WssEmpty) {
+TEST(ProcMemInfo, MapsNotEmpty) {
+    // Make sure the process maps are never empty
+    ProcMemInfo proc_mem(pid);
+    const std::vector<Vma>& maps = proc_mem.Maps();
+    EXPECT_FALSE(maps.empty());
+}
+
+TEST(ProcMemInfo, WssEmpty) {
     // If we created the object for getting usage,
     // the working set must be empty
     ProcMemInfo proc_mem(pid, false);
@@ -293,7 +72,7 @@
     EXPECT_EQ(wss.swap, 0);
 }
 
-TEST(TestProcMemInfo, SwapOffsetsEmpty) {
+TEST(ProcMemInfo, SwapOffsetsEmpty) {
     // If we created the object for getting working set,
     // the swap offsets must be empty
     ProcMemInfo proc_mem(pid, true);
@@ -301,7 +80,10 @@
     EXPECT_EQ(swap_offsets.size(), 0);
 }
 
-TEST(TestProcMemInfo, IsSmapsSupportedTest) {
+TEST(ProcMemInfo, IsSmapsSupportedTest) {
+    // Get any pid and check if /proc/<pid>/smaps_rollup exists using the API.
+    // The API must return the appropriate value regardless of the after it succeeds
+    // once.
     std::string path = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid);
     bool supported = IsSmapsRollupSupported(pid);
     EXPECT_EQ(!access(path.c_str(), F_OK | R_OK), supported);
@@ -310,7 +92,8 @@
     EXPECT_EQ(supported, IsSmapsRollupSupported(-1));
 }
 
-TEST(TestProcMemInfo, SmapsOrRollupTest) {
+TEST(ProcMemInfo, SmapsOrRollupTest) {
+    // Make sure we can parse 'smaps_rollup' correctly
     std::string rollup =
             R"rollup(12c00000-7fe859e000 ---p 00000000 00:00 0                                [rollup]
 Rss:              331908 kB
@@ -342,8 +125,8 @@
     EXPECT_EQ(stats.swap_pss, 442);
 }
 
-TEST(TestProcMemInfo, SmapsOrRollupSmapsTest) {
-    // This is a made up smaps for the test
+TEST(ProcMemInfo, SmapsOrRollupSmapsTest) {
+    // Make sure /proc/<pid>/smaps is parsed correctly
     std::string smaps =
             R"smaps(12c00000-13440000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
 Name:           [anon:dalvik-main space (region space)]
@@ -382,8 +165,9 @@
     EXPECT_EQ(stats.swap_pss, 70);
 }
 
-TEST(TestProcMemInfo, SmapsOrRollupPssRollupTest) {
-    // This is a made up smaps for the test
+TEST(ProcMemInfo, SmapsOrRollupPssRollupTest) {
+    // Make sure /proc/<pid>/smaps is parsed correctly
+    // to get the PSS
     std::string smaps =
             R"smaps(12c00000-13440000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
 Name:           [anon:dalvik-main space (region space)]
@@ -417,7 +201,8 @@
     EXPECT_EQ(pss, 2652);
 }
 
-TEST(TestProcMemInfo, SmapsOrRollupPssSmapsTest) {
+TEST(ProcMemInfo, SmapsOrRollupPssSmapsTest) {
+    // Correctly parse smaps file to gather pss
     std::string exec_dir = ::android::base::GetExecutableDirectory();
     std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
 
@@ -426,7 +211,8 @@
     EXPECT_EQ(pss, 19119);
 }
 
-TEST(TestProcMemInfo, ForEachVmaFromFileTest) {
+TEST(ProcMemInfo, ForEachVmaFromFileTest) {
+    // Parse smaps file correctly to make callbacks for each virtual memory area (vma)
     std::string exec_dir = ::android::base::GetExecutableDirectory();
     std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
     ProcMemInfo proc_mem(pid);
@@ -519,13 +305,14 @@
     EXPECT_EQ(vmas[5].usage.swap_pss, 0);
 }
 
-TEST(TestProcMemInfo, SmapsReturnTest) {
+TEST(ProcMemInfo, SmapsReturnTest) {
+    // Make sure Smaps() is never empty for any process
     ProcMemInfo proc_mem(pid);
     auto vmas = proc_mem.Smaps();
     EXPECT_FALSE(vmas.empty());
 }
 
-TEST(TestProcMemInfo, SmapsTest) {
+TEST(ProcMemInfo, SmapsTest) {
     std::string exec_dir = ::android::base::GetExecutableDirectory();
     std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
     ProcMemInfo proc_mem(pid);
@@ -616,56 +403,7 @@
     EXPECT_EQ(vmas[5].usage.swap_pss, 0);
 }
 
-TEST(ValidateProcMemInfoFlags, TestPageFlags1) {
-    // Create proc object using libpagemap
-    pm_kernel_t* ker;
-    ASSERT_EQ(0, pm_kernel_create(&ker));
-    pm_process_t* proc;
-    ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
-
-    // count swapbacked pages using libpagemap
-    pm_memusage_t proc_usage;
-    pm_memusage_zero(&proc_usage);
-    ASSERT_EQ(0, pm_process_usage_flags(proc, &proc_usage, (1 << KPF_SWAPBACKED),
-                                        (1 << KPF_SWAPBACKED)));
-
-    // Create ProcMemInfo that counts swapbacked pages
-    ProcMemInfo proc_mem(pid, false, (1 << KPF_SWAPBACKED), (1 << KPF_SWAPBACKED));
-
-    EXPECT_EQ(proc_usage.vss, proc_mem.Usage().vss);
-    EXPECT_EQ(proc_usage.rss, proc_mem.Usage().rss);
-    EXPECT_EQ(proc_usage.pss, proc_mem.Usage().pss);
-    EXPECT_EQ(proc_usage.uss, proc_mem.Usage().uss);
-
-    pm_process_destroy(proc);
-    pm_kernel_destroy(ker);
-}
-
-TEST(ValidateProcMemInfoFlags, TestPageFlags2) {
-    // Create proc object using libpagemap
-    pm_kernel_t* ker;
-    ASSERT_EQ(0, pm_kernel_create(&ker));
-    pm_process_t* proc;
-    ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
-
-    // count non-swapbacked pages using libpagemap
-    pm_memusage_t proc_usage;
-    pm_memusage_zero(&proc_usage);
-    ASSERT_EQ(0, pm_process_usage_flags(proc, &proc_usage, (1 << KPF_SWAPBACKED), 0));
-
-    // Create ProcMemInfo that counts non-swapbacked pages
-    ProcMemInfo proc_mem(pid, false, 0, (1 << KPF_SWAPBACKED));
-
-    EXPECT_EQ(proc_usage.vss, proc_mem.Usage().vss);
-    EXPECT_EQ(proc_usage.rss, proc_mem.Usage().rss);
-    EXPECT_EQ(proc_usage.pss, proc_mem.Usage().pss);
-    EXPECT_EQ(proc_usage.uss, proc_mem.Usage().uss);
-
-    pm_process_destroy(proc);
-    pm_kernel_destroy(ker);
-}
-
-TEST(SysMemInfoParser, TestSysMemInfoFile) {
+TEST(SysMemInfo, TestSysMemInfoFile) {
     std::string meminfo = R"meminfo(MemTotal:        3019740 kB
 MemFree:         1809728 kB
 MemAvailable:    2546560 kB
@@ -733,7 +471,7 @@
     EXPECT_EQ(mi.mem_kernel_stack_kb(), 4880);
 }
 
-TEST(SysMemInfoParser, TestEmptyFile) {
+TEST(SysMemInfo, TestEmptyFile) {
     TemporaryFile tf;
     std::string empty_string = "";
     ASSERT_TRUE(tf.fd != -1);
@@ -744,7 +482,7 @@
     EXPECT_EQ(mi.mem_total_kb(), 0);
 }
 
-TEST(SysMemInfoParser, TestZramTotal) {
+TEST(SysMemInfo, TestZramTotal) {
     std::string exec_dir = ::android::base::GetExecutableDirectory();
 
     SysMemInfo mi;
@@ -774,7 +512,7 @@
     MEMINFO_COUNT
 };
 
-TEST(SysMemInfoParser, TestZramWithTags) {
+TEST(SysMemInfo, TestZramWithTags) {
     std::string meminfo = R"meminfo(MemTotal:        3019740 kB
 MemFree:         1809728 kB
 MemAvailable:    2546560 kB
@@ -849,7 +587,7 @@
     EXPECT_EQ(mem[MEMINFO_KERNEL_STACK], 4880);
 }
 
-TEST(SysMemInfoParser, TestVmallocInfoNoMemory) {
+TEST(SysMemInfo, TestVmallocInfoNoMemory) {
     std::string vmallocinfo =
             R"vmallocinfo(0x0000000000000000-0x0000000000000000   69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
 0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
@@ -864,7 +602,7 @@
     EXPECT_EQ(ReadVmallocInfo(file), 0);
 }
 
-TEST(SysMemInfoParser, TestVmallocInfoKernel) {
+TEST(SysMemInfo, TestVmallocInfoKernel) {
     std::string vmallocinfo =
             R"vmallocinfo(0x0000000000000000-0x0000000000000000    8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc)vmallocinfo";
 
@@ -876,7 +614,7 @@
     EXPECT_EQ(ReadVmallocInfo(file), getpagesize());
 }
 
-TEST(SysMemInfoParser, TestVmallocInfoModule) {
+TEST(SysMemInfo, TestVmallocInfoModule) {
     std::string vmallocinfo =
             R"vmallocinfo(0x0000000000000000-0x0000000000000000   28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";
 
@@ -888,7 +626,7 @@
     EXPECT_EQ(ReadVmallocInfo(file), 6 * getpagesize());
 }
 
-TEST(SysMemInfoParser, TestVmallocInfoAll) {
+TEST(SysMemInfo, TestVmallocInfoAll) {
     std::string vmallocinfo =
             R"vmallocinfo(0x0000000000000000-0x0000000000000000   69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
 0x0000000000000000-0x0000000000000000    8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
@@ -907,11 +645,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
-    if (argc <= 1) {
-        cerr << "Pid of a permanently sleeping process must be provided." << endl;
-        exit(EXIT_FAILURE);
-    }
     ::android::base::InitLogging(argv, android::base::StderrLogger);
-    pid = std::stoi(std::string(argv[1]));
+    pid = getpid();
     return RUN_ALL_TESTS();
 }
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 1d43775..268496f 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -6,6 +6,7 @@
     },
 
     srcs: [
+        "checksum.c",
         "dhcpclient.c",
         "dhcpmsg.c",
         "ifc_utils.c",
diff --git a/libnetutils/checksum.c b/libnetutils/checksum.c
new file mode 100644
index 0000000..74b5fdd
--- /dev/null
+++ b/libnetutils/checksum.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * 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.
+ *
+ * checksum.c - ipv4/ipv6 checksum calculation
+ */
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include "netutils/checksum.h"
+
+/* function: ip_checksum_add
+ * adds data to a checksum. only known to work on little-endian hosts
+ * current - the current checksum (or 0 to start a new checksum)
+ *   data        - the data to add to the checksum
+ *   len         - length of data
+ */
+uint32_t ip_checksum_add(uint32_t current, const void* data, int len) {
+    uint32_t checksum = current;
+    int left = len;
+    const uint16_t* data_16 = data;
+
+    while (left > 1) {
+        checksum += *data_16;
+        data_16++;
+        left -= 2;
+    }
+    if (left) {
+        checksum += *(uint8_t*)data_16;
+    }
+
+    return checksum;
+}
+
+/* function: ip_checksum_fold
+ * folds a 32-bit partial checksum into 16 bits
+ *   temp_sum - sum from ip_checksum_add
+ *   returns: the folded checksum in network byte order
+ */
+uint16_t ip_checksum_fold(uint32_t temp_sum) {
+    while (temp_sum > 0xffff) {
+        temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
+    }
+    return temp_sum;
+}
+
+/* function: ip_checksum_finish
+ * folds and closes the checksum
+ *   temp_sum - sum from ip_checksum_add
+ *   returns: a header checksum value in network byte order
+ */
+uint16_t ip_checksum_finish(uint32_t temp_sum) {
+    return ~ip_checksum_fold(temp_sum);
+}
+
+/* function: ip_checksum
+ * combined ip_checksum_add and ip_checksum_finish
+ *   data - data to checksum
+ *   len  - length of data
+ */
+uint16_t ip_checksum(const void* data, int len) {
+    // TODO: consider starting from 0xffff so the checksum of a buffer entirely consisting of zeros
+    // is correctly calculated as 0.
+    uint32_t temp_sum;
+
+    temp_sum = ip_checksum_add(0, data, len);
+    return ip_checksum_finish(temp_sum);
+}
+
+/* function: ipv6_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp/icmp headers
+ *   ip6      - the ipv6 header
+ *   len      - the transport length (transport header + payload)
+ *   protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments
+ */
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol) {
+    uint32_t checksum_len = htonl(len);
+    uint32_t checksum_next = htonl(protocol);
+
+    uint32_t current = 0;
+
+    current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
+    current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
+    current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
+    current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next));
+
+    return current;
+}
+
+/* function: ipv4_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp headers
+ *   ip      - the ipv4 header
+ *   len     - the transport length (transport header + payload)
+ */
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len) {
+    uint16_t temp_protocol, temp_length;
+
+    temp_protocol = htons(ip->protocol);
+    temp_length = htons(len);
+
+    uint32_t current = 0;
+
+    current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
+    current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
+    current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
+    current = ip_checksum_add(current, &temp_length, sizeof(uint16_t));
+
+    return current;
+}
+
+/* function: ip_checksum_adjust
+ * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
+ *   checksum    - the header checksum in the original packet in network byte order
+ *   old_hdr_sum - the pseudo-header checksum of the original packet
+ *   new_hdr_sum - the pseudo-header checksum of the translated packet
+ *   returns: the new header checksum in network byte order
+ */
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
+    // Algorithm suggested in RFC 1624.
+    // http://tools.ietf.org/html/rfc1624#section-3
+    checksum = ~checksum;
+    uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
+    uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
+    if (folded_sum > folded_old) {
+        return ~(folded_sum - folded_old);
+    } else {
+        return ~(folded_sum - folded_old - 1);  // end-around borrow
+    }
+}
diff --git a/libnetutils/include/netutils/checksum.h b/libnetutils/include/netutils/checksum.h
new file mode 100644
index 0000000..868217c
--- /dev/null
+++ b/libnetutils/include/netutils/checksum.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * 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.
+ *
+ * checksum.h - checksum functions
+ */
+#ifndef __CHECKSUM_H__
+#define __CHECKSUM_H__
+
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <stdint.h>
+
+uint32_t ip_checksum_add(uint32_t current, const void* data, int len);
+uint16_t ip_checksum_finish(uint32_t temp_sum);
+uint16_t ip_checksum(const void* data, int len);
+
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol);
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len);
+
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
+
+#endif /* __CHECKSUM_H__ */
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index f4788d7..ddda7fd 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -81,6 +81,12 @@
 
   const std::vector<FrameData>& frames() { return frames_; }
 
+  std::vector<FrameData> ConsumeFrames() {
+    std::vector<FrameData> frames = std::move(frames_);
+    frames_.clear();
+    return frames;
+  }
+
   std::string FormatFrame(size_t frame_num);
   static std::string FormatFrame(const FrameData& frame, bool is32bit);
 
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 3f5b88b..19982d8 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -44,10 +44,10 @@
   }
 
   printf("ARM Unwind Information:\n");
+  uint64_t load_bias = elf->GetLoadBias();
   for (const auto& entry : interface->pt_loads()) {
-    uint64_t load_bias = entry.second.table_offset;
     printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
-           entry.second.table_size + load_bias);
+           entry.second.offset + entry.second.table_size + load_bias);
     for (auto pc : *interface) {
       std::string name;
       printf("  PC 0x%" PRIx64, pc + load_bias);
diff --git a/lmkd/libpsi/Android.bp b/lmkd/libpsi/Android.bp
new file mode 100644
index 0000000..8a97094
--- /dev/null
+++ b/lmkd/libpsi/Android.bp
@@ -0,0 +1,22 @@
+cc_library_headers {
+    name: "libpsi_headers",
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libpsi",
+    srcs: ["psi.c"],
+    shared_libs: [
+        "liblog"
+    ],
+    header_libs: [
+        "libpsi_headers",
+    ],
+    export_header_lib_headers: [
+        "libpsi_headers",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/lmkd/libpsi/OWNERS b/lmkd/libpsi/OWNERS
new file mode 100644
index 0000000..b15bb48
--- /dev/null
+++ b/lmkd/libpsi/OWNERS
@@ -0,0 +1 @@
+surenb@google.com
diff --git a/lmkd/libpsi/include/psi/psi.h b/lmkd/libpsi/include/psi/psi.h
new file mode 100644
index 0000000..cd49e8b
--- /dev/null
+++ b/lmkd/libpsi/include/psi/psi.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ANDROID_PSI_H__
+#define __ANDROID_PSI_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+enum psi_stall_type {
+    PSI_SOME,
+    PSI_FULL,
+    PSI_TYPE_COUNT
+};
+
+/*
+ * Initializes psi monitor.
+ * stall_type, threshold_us and window_us are monitor parameters
+ * When successful, the function returns file descriptor that can
+ * be used with poll/epoll syscalls to wait for EPOLLPRI events.
+ * When unsuccessful, the function returns -1 and errno is set
+ * appropriately.
+ */
+int init_psi_monitor(enum psi_stall_type stall_type,
+        int threshold_us, int window_us);
+
+/*
+ * Registers psi monitor file descriptor fd on the epoll instance
+ * referred to by the file descriptor epollfd.
+ * data parameter will be associated with event's epoll_data.ptr
+ * member.
+ */
+int register_psi_monitor(int epollfd, int fd, void* data);
+
+/*
+ * Unregisters psi monitor file descriptor fd from the epoll instance
+ * referred to by the file descriptor epollfd.
+ */
+int unregister_psi_monitor(int epollfd, int fd);
+
+/*
+ * Destroys psi monitor.
+ * fd is the file descriptor returned by psi monitor initialization
+ * routine.
+ * Note that if user process exits without calling this routine
+ * kernel will destroy the monitor as its lifetime is linked to
+ * the file descriptor.
+ */
+void destroy_psi_monitor(int fd);
+
+__END_DECLS
+
+#endif  // __ANDROID_PSI_H__
diff --git a/lmkd/libpsi/psi.c b/lmkd/libpsi/psi.c
new file mode 100644
index 0000000..f4d5d18
--- /dev/null
+++ b/lmkd/libpsi/psi.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libpsi"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+
+#include <log/log.h>
+#include "psi/psi.h"
+
+#define PSI_MON_FILE_MEMORY "/proc/pressure/memory"
+
+static const char* stall_type_name[] = {
+        "some",
+        "full",
+};
+
+int init_psi_monitor(enum psi_stall_type stall_type,
+             int threshold_us, int window_us) {
+    int fd;
+    int res;
+    char buf[256];
+
+    fd = TEMP_FAILURE_RETRY(open(PSI_MON_FILE_MEMORY, O_WRONLY | O_CLOEXEC));
+    if (fd < 0) {
+        ALOGE("No kernel psi monitor support (errno=%d)", errno);
+        return -1;
+    }
+
+    switch (stall_type) {
+    case (PSI_SOME):
+    case (PSI_FULL):
+        res = snprintf(buf, sizeof(buf), "%s %d %d",
+            stall_type_name[stall_type], threshold_us, window_us);
+        break;
+    default:
+        ALOGE("Invalid psi stall type: %d", stall_type);
+        errno = EINVAL;
+        goto err;
+    }
+
+    if (res >= (ssize_t)sizeof(buf)) {
+        ALOGE("%s line overflow for psi stall type '%s'",
+            PSI_MON_FILE_MEMORY, stall_type_name[stall_type]);
+        errno = EINVAL;
+        goto err;
+    }
+
+    res = TEMP_FAILURE_RETRY(write(fd, buf, strlen(buf) + 1));
+    if (res < 0) {
+        ALOGE("%s write failed for psi stall type '%s'; errno=%d",
+            PSI_MON_FILE_MEMORY, stall_type_name[stall_type], errno);
+        goto err;
+    }
+
+    return fd;
+
+err:
+    close(fd);
+    return -1;
+}
+
+int register_psi_monitor(int epollfd, int fd, void* data) {
+    int res;
+    struct epoll_event epev;
+
+    epev.events = EPOLLPRI;
+    epev.data.ptr = data;
+    res = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &epev);
+    if (res < 0) {
+        ALOGE("epoll_ctl for psi monitor failed; errno=%d", errno);
+    }
+    return res;
+}
+
+int unregister_psi_monitor(int epollfd, int fd) {
+    return epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
+}
+
+void destroy_psi_monitor(int fd) {
+    if (fd >= 0) {
+        close(fd);
+    }
+}
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
index 84f0764..9a18edb 100644
--- a/logd/tests/AndroidTest.xml
+++ b/logd/tests/AndroidTest.xml
@@ -16,6 +16,8 @@
 <configuration description="Config for CTS Logging Daemon test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index a416825..a854e93 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -38,6 +38,12 @@
 additional.namespaces = runtime,conscrypt,media,resolv
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace.And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
 namespace.default.links = runtime,resolv
 namespace.default.asan.links = runtime,resolv
 # Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
@@ -49,6 +55,9 @@
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
 
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
 namespace.default.link.resolv.shared_libs = libnetd_resolv.so
 
 ###############################################################################
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index d0e84df..bc8568e 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -106,6 +106,12 @@
 namespace.default.asan.permitted.paths += /mnt/expand
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace.And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
 namespace.default.links = runtime,resolv
 # Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
 # libart.
@@ -116,6 +122,9 @@
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
 
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
 namespace.default.link.resolv.shared_libs = libnetd_resolv.so
 
 ###############################################################################
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index c97baeb..ae486ea 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -57,6 +57,12 @@
 
 # Keep in sync with the platform namespace in the com.android.runtime APEX
 # ld.config.txt.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace.And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
 namespace.default.links = runtime,resolv
 # Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
 # libart.
@@ -67,6 +73,9 @@
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
 
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
 namespace.default.link.resolv.shared_libs = libnetd_resolv.so
 
 ###############################################################################