Merge "Init: error on oneway calls."
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 054cbac..bd5508c 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -501,6 +501,197 @@
     return EXIT_FAILURE;
 }
 
+int install_multi_package(int argc, const char** argv) {
+    // Find all APK arguments starting at end.
+    // All other arguments passed through verbatim.
+    int first_apk = -1;
+    for (int i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+            first_apk = i;
+        } else {
+            break;
+        }
+    }
+
+    if (first_apk == -1) error_exit("need APK file on command line");
+
+    if (use_legacy_install()) {
+        fprintf(stderr, "adb: multi-package install is not supported on this device\n");
+        return EXIT_FAILURE;
+    }
+    std::string install_cmd = "exec:cmd package";
+
+    std::string multi_package_cmd =
+            android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+
+    // Create multi-package install session
+    std::string error;
+    int fd = adb_connect(multi_package_cmd, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    char buf[BUFSIZ];
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    int parent_session_id = -1;
+    if (!strncmp("Success", buf, 7)) {
+        char* start = strrchr(buf, '[');
+        char* end = strrchr(buf, ']');
+        if (start && end) {
+            *end = '\0';
+            parent_session_id = strtol(start + 1, nullptr, 10);
+        }
+    }
+    if (parent_session_id < 0) {
+        fprintf(stderr, "adb: failed to create multi-package session\n");
+        fputs(buf, stderr);
+        return EXIT_FAILURE;
+    }
+
+    fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
+
+    std::vector<int> session_ids;
+
+    // Valid session, now create the individual sessions and stream the APKs
+    int success = EXIT_FAILURE;
+    std::string individual_cmd =
+            android::base::StringPrintf("%s install-create", install_cmd.c_str());
+    std::string all_session_ids = "";
+    for (int i = 1; i < first_apk; i++) {
+        individual_cmd += " " + escape_arg(argv[i]);
+    }
+    std::string cmd = "";
+    for (int i = first_apk; i < argc; i++) {
+        // Create individual install session
+        fd = adb_connect(individual_cmd, &error);
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+            goto finalize_multi_package_session;
+        }
+        char buf[BUFSIZ];
+        read_status_line(fd, buf, sizeof(buf));
+        adb_close(fd);
+
+        int session_id = -1;
+        if (!strncmp("Success", buf, 7)) {
+            char* start = strrchr(buf, '[');
+            char* end = strrchr(buf, ']');
+            if (start && end) {
+                *end = '\0';
+                session_id = strtol(start + 1, nullptr, 10);
+            }
+        }
+        if (session_id < 0) {
+            fprintf(stderr, "adb: failed to create multi-package session\n");
+            fputs(buf, stderr);
+            goto finalize_multi_package_session;
+        }
+
+        fprintf(stdout, "Created child session ID %d.\n", session_id);
+        session_ids.push_back(session_id);
+
+        const char* file = argv[i];
+        struct stat sb;
+        if (stat(file, &sb) == -1) {
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            goto finalize_multi_package_session;
+        }
+
+        std::string cmd =
+                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
+                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
+                                            session_id, i, android::base::Basename(file).c_str());
+
+        int localFd = adb_open(file, O_RDONLY);
+        if (localFd < 0) {
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            goto finalize_multi_package_session;
+        }
+
+        std::string error;
+        int remoteFd = adb_connect(cmd, &error);
+        if (remoteFd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            adb_close(localFd);
+            goto finalize_multi_package_session;
+        }
+
+        copy_to_file(localFd, remoteFd);
+        read_status_line(remoteFd, buf, sizeof(buf));
+
+        adb_close(localFd);
+        adb_close(remoteFd);
+
+        if (strncmp("Success", buf, 7)) {
+            fprintf(stderr, "adb: failed to write %s\n", file);
+            fputs(buf, stderr);
+            goto finalize_multi_package_session;
+        }
+
+        all_session_ids += android::base::StringPrintf(" %d", session_id);
+    }
+
+    cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
+                                      parent_session_id, all_session_ids.c_str());
+    fd = adb_connect(cmd, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+        goto finalize_multi_package_session;
+    }
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    if (strncmp("Success", buf, 7)) {
+        fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+        fputs(buf, stderr);
+        goto finalize_multi_package_session;
+    }
+
+    // no failures means we can proceed with the assumption of success
+    success = 0;
+
+finalize_multi_package_session:
+    // Commit session if we streamed everything okay; otherwise abandon
+    std::string service =
+            android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
+                                        success == 0 ? "commit" : "abandon", parent_session_id);
+    fd = adb_connect(service, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        if (success == 0) {
+            return 0;
+        }
+    } else {
+        fprintf(stderr, "adb: failed to finalize session\n");
+        fputs(buf, stderr);
+    }
+
+    // try to abandon all remaining sessions
+    for (std::size_t i = 0; i < session_ids.size(); i++) {
+        service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
+                                              session_ids[i]);
+        fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
+        fd = adb_connect(service, &error);
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+            continue;
+        }
+        read_status_line(fd, buf, sizeof(buf));
+        adb_close(fd);
+    }
+    return EXIT_FAILURE;
+}
+
 int delete_device_file(const std::string& filename) {
     std::string cmd = "rm -f " + escape_arg(filename);
     return send_shell_command(cmd);
diff --git a/adb/client/adb_install.h b/adb/client/adb_install.h
index 5b6c4cb..9946604 100644
--- a/adb/client/adb_install.h
+++ b/adb/client/adb_install.h
@@ -20,6 +20,7 @@
 
 int install_app(int argc, const char** argv);
 int install_multiple_app(int argc, const char** argv);
+int install_multi_package(int argc, const char** argv);
 int uninstall_app(int argc, const char** argv);
 
 int delete_device_file(const std::string& filename);
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 20fd036..9bc42e1 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -142,10 +142,13 @@
         "     -x: disable remote exit codes and stdout/stderr separation\n"
         " emu COMMAND              run emulator console command\n"
         "\n"
-        "app installation:\n"
+        "app installation (see also `adb shell cmd package help`):\n"
         " install [-lrtsdg] [--instant] PACKAGE\n"
+        "     push a single package to the device and install it\n"
         " install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
-        "     push package(s) to the device and install them\n"
+        "     push multiple APKs to the device for a single package and install them\n"
+        " install-multi-package [-lrtsdpg] [--instant] PACKAGE...\n"
+        "     push one or more packages to the device and install them atomically\n"
         "     -r: replace existing application\n"
         "     -t: allow test packages\n"
         "     -d: allow version code downgrade (debuggable packages only)\n"
@@ -1734,6 +1737,9 @@
     } else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) error_exit("install-multiple requires an argument");
         return install_multiple_app(argc, argv);
+    } else if (!strcmp(argv[0], "install-multi-package")) {
+        if (argc < 3) error_exit("install-multi-package requires an argument");
+        return install_multi_package(argc, argv);
     } else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) error_exit("uninstall requires an argument");
         return uninstall_app(argc, argv);
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 4e6c879..c8d12cf 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -103,7 +103,7 @@
   void reset(int new_value = -1) { reset(new_value, nullptr); }
 
   int get() const { return fd_; }
-  operator int() const { return get(); }
+  operator int() const { return get(); }  // NOLINT
 
   int release() __attribute__((warn_unused_result)) {
     tag(fd_, this, nullptr);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 1616a61..d9f2837 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -99,31 +99,11 @@
         "fs_mgr_boot_config.cpp",
         "fs_mgr_slotselect.cpp",
     ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
     export_include_dirs: ["include_fstab"],
     header_libs: ["libbase_headers"],
 }
-
-cc_library_static {
-    name: "libfs_avb",
-    defaults: ["fs_mgr_defaults"],
-    recovery_available: true,
-    export_include_dirs: ["libfs_avb/include"],
-    srcs: [
-        "libfs_avb/avb_ops.cpp",
-        "libfs_avb/fs_avb.cpp",
-    ],
-    static_libs: [
-        "libavb",
-        "libfstab",
-        "libdm",
-    ],
-    export_static_lib_headers: [
-        "libfstab",
-    ],
-    shared_libs: [
-        "libcrypto",
-    ],
-    header_libs: [
-        "libbase_headers",
-    ],
-}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 943fe10..9f6c550 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -523,13 +523,13 @@
 }
 
 // Mark the given block device as read-only, using the BLKROSET ioctl.
-bool fs_mgr_set_blk_ro(const std::string& blockdev) {
+bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd < 0) {
         return false;
     }
 
-    int ON = 1;
+    int ON = readonly;
     return ioctl(fd, BLKROSET, &ON) == 0;
 }
 
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 7dae7f1..6364ca9 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -277,6 +277,9 @@
     // Don't check entries that are managed by vold.
     if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
 
+    // *_other doesn't want overlayfs.
+    if (entry->fs_mgr_flags.slot_select_other) return false;
+
     // Only concerned with readonly partitions.
     if (!(entry->flags & MS_RDONLY)) return false;
 
@@ -595,7 +598,11 @@
     entry.mount_point = kScratchMountPoint;
     entry.fs_type = mnt_type;
     entry.flags = MS_RELATIME;
-    if (readonly) entry.flags |= MS_RDONLY;
+    if (readonly) {
+        entry.flags |= MS_RDONLY;
+    } else {
+        fs_mgr_set_blk_ro(device_path, false);
+    }
     auto save_errno = errno;
     auto mounted = fs_mgr_do_mount_one(entry) == 0;
     if (!mounted) {
@@ -653,6 +660,7 @@
         return false;
     }
     command += " " + scratch_device;
+    fs_mgr_set_blk_ro(scratch_device, false);
     auto ret = system(command.c_str());
     if (ret) {
         LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7d1159b..7842ca2 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -134,7 +134,7 @@
                           const std::chrono::milliseconds relative_timeout,
                           FileWaitMode wait_mode = FileWaitMode::Exists);
 
-bool fs_mgr_set_blk_ro(const std::string& blockdev);
+bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
 bool fs_mgr_update_for_slotselect(Fstab* fstab);
 bool fs_mgr_is_device_unlocked();
 const std::string& get_android_dt_dir();
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
new file mode 100644
index 0000000..6f368e4
--- /dev/null
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -0,0 +1,105 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+    name: "libfs_avb",
+    defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
+    host_supported: true,
+    export_include_dirs: ["include"],
+    srcs: [
+        "avb_ops.cpp",
+        "avb_util.cpp",
+        "fs_avb.cpp",
+        "util.cpp",
+    ],
+    static_libs: [
+        "libavb",
+        "libdm",
+        "libfstab",
+    ],
+    export_static_lib_headers: [
+        "libfstab",
+    ],
+    shared_libs: [
+        "libcrypto",
+    ],
+    header_libs: [
+        "libbase_headers",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+cc_defaults {
+    name: "libfs_avb_host_test_defaults",
+    required: [
+        "avbtool",
+    ],
+    data: [
+        "tests/data/*",
+    ],
+    static_libs: [
+        "libgtest_host",
+    ],
+    shared_libs: [
+        "libbase",
+        "libchrome",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    cflags: [
+        "-DHOST_TEST",
+    ],
+}
+
+cc_library_host_static {
+    name: "libfs_avb_test_util",
+    defaults: ["libfs_avb_host_test_defaults"],
+    srcs: [
+        "tests/fs_avb_test_util.cpp",
+    ],
+}
+
+cc_test_host {
+    name: "libfs_avb_test",
+    defaults: ["libfs_avb_host_test_defaults"],
+    static_libs: [
+        "libfs_avb_test_util",
+    ],
+    srcs: [
+        "tests/basic_test.cpp",
+    ],
+}
+
+cc_test_host {
+    name: "libfs_avb_internal_test",
+    defaults: ["libfs_avb_host_test_defaults"],
+    static_libs: [
+        "libfs_avb_test_util",
+        "libfstab",
+    ],
+    srcs: [
+        "util.cpp",
+        "tests/util_test.cpp",
+    ],
+}
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index c985a97..3b0ef0b 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -37,7 +37,7 @@
 #include <libavb/libavb.h>
 #include <utils/Compat.h>
 
-#include "fs_mgr_priv.h"
+#include "util.h"
 
 using namespace std::literals;
 
@@ -127,7 +127,7 @@
     const std::string path = "/dev/block/by-name/"s + partition;
 
     // Ensures the device path (a symlink created by init) is ready to access.
-    if (!fs_mgr_wait_for_file(path, 1s)) {
+    if (!WaitForFile(path, 1s)) {
         return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
     }
 
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
new file mode 100644
index 0000000..0ceb6ee
--- /dev/null
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "avb_util.h"
+
+#include <array>
+#include <sstream>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::unique_fd;
+
+namespace android {
+namespace fs_mgr {
+
+// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
+// See the following link for more details:
+// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
+bool ConstructVerityTable(const AvbHashtreeDescriptor& hashtree_desc, const std::string& salt,
+                          const std::string& root_digest, const std::string& blk_device,
+                          android::dm::DmTable* table) {
+    // Loads androidboot.veritymode from kernel cmdline.
+    std::string verity_mode;
+    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+        verity_mode = "enforcing";  // Defaults to enforcing when it's absent.
+    }
+
+    // Converts veritymode to the format used in kernel.
+    std::string dm_verity_mode;
+    if (verity_mode == "enforcing") {
+        dm_verity_mode = "restart_on_corruption";
+    } else if (verity_mode == "logging") {
+        dm_verity_mode = "ignore_corruption";
+    } else if (verity_mode != "eio") {  // Default dm_verity_mode is eio.
+        LERROR << "Unknown androidboot.veritymode: " << verity_mode;
+        return false;
+    }
+
+    std::ostringstream hash_algorithm;
+    hash_algorithm << hashtree_desc.hash_algorithm;
+
+    android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
+                                       hashtree_desc.dm_verity_version, blk_device, blk_device,
+                                       hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
+                                       hashtree_desc.image_size / hashtree_desc.data_block_size,
+                                       hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
+                                       hash_algorithm.str(), root_digest, salt);
+    if (hashtree_desc.fec_size > 0) {
+        target.UseFec(blk_device, hashtree_desc.fec_num_roots,
+                      hashtree_desc.fec_offset / hashtree_desc.data_block_size,
+                      hashtree_desc.fec_offset / hashtree_desc.data_block_size);
+    }
+    if (!dm_verity_mode.empty()) {
+        target.SetVerityMode(dm_verity_mode);
+    }
+    // Always use ignore_zero_blocks.
+    target.IgnoreZeroBlocks();
+
+    LINFO << "Built verity table: '" << target.GetParameterString() << "'";
+
+    return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
+}
+
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& hashtree_desc,
+                           const std::string& salt, const std::string& root_digest,
+                           bool wait_for_verity_dev) {
+    android::dm::DmTable table;
+    if (!ConstructVerityTable(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
+        !table.valid()) {
+        LERROR << "Failed to construct verity table.";
+        return false;
+    }
+    table.set_readonly(true);
+
+    const std::string mount_point(basename(fstab_entry->mount_point.c_str()));
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+    if (!dm.CreateDevice(mount_point, table)) {
+        LERROR << "Couldn't create verity device!";
+        return false;
+    }
+
+    std::string dev_path;
+    if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
+        LERROR << "Couldn't get verity device path!";
+        return false;
+    }
+
+    // Marks the underlying block device as read-only.
+    SetBlockDeviceReadOnly(fstab_entry->blk_device);
+
+    // Updates fstab_rec->blk_device to verity device name.
+    fstab_entry->blk_device = dev_path;
+
+    // Makes sure we've set everything up properly.
+    if (wait_for_verity_dev && !WaitForFile(dev_path, 1s)) {
+        return false;
+    }
+
+    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) {
+    bool found = false;
+    const uint8_t* desc_partition_name;
+
+    for (const auto& vbmeta : vbmeta_images) {
+        size_t num_descriptors;
+        std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
+                avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+        if (!descriptors || num_descriptors < 1) {
+            continue;
+        }
+
+        for (size_t n = 0; n < num_descriptors && !found; n++) {
+            AvbDescriptor desc;
+            if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+                LWARNING << "Descriptor[" << n << "] is invalid";
+                continue;
+            }
+            if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
+                desc_partition_name =
+                        (const uint8_t*)descriptors[n] + sizeof(AvbHashtreeDescriptor);
+                if (!avb_hashtree_descriptor_validate_and_byteswap(
+                            (AvbHashtreeDescriptor*)descriptors[n], out_hashtree_desc)) {
+                    continue;
+                }
+                if (out_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);
+                if (hashtree_partition_name == partition_name) {
+                    found = true;
+                }
+            }
+        }
+
+        if (found) break;
+    }
+
+    if (!found) {
+        LERROR << "Partition descriptor not found: " << partition_name.c_str();
+        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);
+
+    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;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
new file mode 100644
index 0000000..b81e931
--- /dev/null
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "fs_avb/fs_avb.h"
+
+namespace android {
+namespace fs_mgr {
+
+// 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);
+
+bool ConstructVerityTable(const AvbHashtreeDescriptor& hashtree_desc, const std::string& salt,
+                          const std::string& root_digest, const std::string& blk_device,
+                          android::dm::DmTable* table);
+
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const AvbHashtreeDescriptor& hashtree_desc,
+                           const std::string& salt, const std::string& root_digest,
+                           bool wait_for_verity_dev);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index cf920f9..957aa87 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -28,84 +28,30 @@
 
 #include <android-base/file.h>
 #include <android-base/parseint.h>
-#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <libavb/libavb.h>
 #include <libdm/dm.h>
 
 #include "avb_ops.h"
-#include "fs_mgr_priv.h"
+#include "avb_util.h"
 #include "sha.h"
+#include "util.h"
+
+using android::base::Basename;
+using android::base::ParseUint;
+using android::base::StringPrintf;
 
 namespace android {
 namespace fs_mgr {
 
-static inline bool nibble_value(const char& c, uint8_t* value) {
-    FS_MGR_CHECK(value != nullptr);
-
-    switch (c) {
-        case '0' ... '9':
-            *value = c - '0';
-            break;
-        case 'a' ... 'f':
-            *value = c - 'a' + 10;
-            break;
-        case 'A' ... 'F':
-            *value = c - 'A' + 10;
-            break;
-        default:
-            return false;
-    }
-
-    return true;
-}
-
-static bool hex_to_bytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {
-    FS_MGR_CHECK(bytes != nullptr);
-
-    if (hex.size() % 2 != 0) {
-        return false;
-    }
-    if (hex.size() / 2 > bytes_len) {
-        return false;
-    }
-    for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
-        uint8_t high;
-        if (!nibble_value(hex[i], &high)) {
-            return false;
-        }
-        uint8_t low;
-        if (!nibble_value(hex[i + 1], &low)) {
-            return false;
-        }
-        bytes[j] = (high << 4) | low;
-    }
-    return true;
-}
-
-static std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
-    FS_MGR_CHECK(bytes != nullptr);
-
-    static const char* hex_digits = "0123456789abcdef";
-    std::string hex;
-
-    for (size_t i = 0; i < bytes_len; i++) {
-        hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
-        hex.push_back(hex_digits[bytes[i] & 0x0F]);
-    }
-    return hex;
-}
-
 template <typename Hasher>
-static std::pair<size_t, bool> verify_vbmeta_digest(const std::vector<VBMetaData>& vbmeta_images,
-                                                    const uint8_t* expected_digest) {
+std::pair<size_t, bool> VerifyVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images,
+                                           const uint8_t* expected_digest) {
     size_t total_size = 0;
     Hasher hasher;
-    for (size_t n = 0; n < vbmeta_images.size(); n++) {
-        hasher.update(vbmeta_images[n].vbmeta_data(), vbmeta_images[n].vbmeta_size());
-        total_size += vbmeta_images[n].vbmeta_size();
+    for (const auto& vbmeta : vbmeta_images) {
+        hasher.update(vbmeta.data(), vbmeta.size());
+        total_size += vbmeta.size();
     }
 
     bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);
@@ -148,7 +94,7 @@
 
     std::string value;
     if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
-        !android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
+        !ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
         LERROR << "Invalid hash size: " << value.c_str();
         return nullptr;
     }
@@ -177,7 +123,7 @@
         return nullptr;
     }
 
-    if (!hex_to_bytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {
+    if (!HexToBytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {
         LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
         return nullptr;
     }
@@ -196,10 +142,10 @@
 
     if (hash_alg_ == kSHA256) {
         std::tie(total_size, digest_matched) =
-                verify_vbmeta_digest<SHA256Hasher>(vbmeta_images, digest_);
+                VerifyVbmetaDigest<SHA256Hasher>(vbmeta_images, digest_);
     } else if (hash_alg_ == kSHA512) {
         std::tie(total_size, digest_matched) =
-                verify_vbmeta_digest<SHA512Hasher>(vbmeta_images, digest_);
+                VerifyVbmetaDigest<SHA512Hasher>(vbmeta_images, digest_);
     }
 
     if (total_size != vbmeta_size_) {
@@ -216,155 +162,9 @@
     return true;
 }
 
-// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
-// See the following link for more details:
-// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
-static bool construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
-                                   const std::string& salt, const std::string& root_digest,
-                                   const std::string& blk_device, android::dm::DmTable* table) {
-    // Loads androidboot.veritymode from kernel cmdline.
-    std::string verity_mode;
-    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
-        verity_mode = "enforcing";  // Defaults to enforcing when it's absent.
-    }
-
-    // Converts veritymode to the format used in kernel.
-    std::string dm_verity_mode;
-    if (verity_mode == "enforcing") {
-        dm_verity_mode = "restart_on_corruption";
-    } else if (verity_mode == "logging") {
-        dm_verity_mode = "ignore_corruption";
-    } else if (verity_mode != "eio") {  // Default dm_verity_mode is eio.
-        LERROR << "Unknown androidboot.veritymode: " << verity_mode;
-        return false;
-    }
-
-    std::ostringstream hash_algorithm;
-    hash_algorithm << hashtree_desc.hash_algorithm;
-
-    android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
-                                       hashtree_desc.dm_verity_version, blk_device, blk_device,
-                                       hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
-                                       hashtree_desc.image_size / hashtree_desc.data_block_size,
-                                       hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
-                                       hash_algorithm.str(), root_digest, salt);
-    if (hashtree_desc.fec_size > 0) {
-        target.UseFec(blk_device, hashtree_desc.fec_num_roots,
-                      hashtree_desc.fec_offset / hashtree_desc.data_block_size,
-                      hashtree_desc.fec_offset / hashtree_desc.data_block_size);
-    }
-    if (!dm_verity_mode.empty()) {
-        target.SetVerityMode(dm_verity_mode);
-    }
-    // Always use ignore_zero_blocks.
-    target.IgnoreZeroBlocks();
-
-    LINFO << "Built verity table: '" << target.GetParameterString() << "'";
-
-    return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
-}
-
-static bool hashtree_dm_verity_setup(FstabEntry* fstab_entry,
-                                     const AvbHashtreeDescriptor& hashtree_desc,
-                                     const std::string& salt, const std::string& root_digest,
-                                     bool wait_for_verity_dev) {
-    android::dm::DmTable table;
-    if (!construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device,
-                                &table) ||
-        !table.valid()) {
-        LERROR << "Failed to construct verity table.";
-        return false;
-    }
-    table.set_readonly(true);
-
-    const std::string mount_point(basename(fstab_entry->mount_point.c_str()));
-    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
-    if (!dm.CreateDevice(mount_point, table)) {
-        LERROR << "Couldn't create verity device!";
-        return false;
-    }
-
-    std::string dev_path;
-    if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
-        LERROR << "Couldn't get verity device path!";
-        return false;
-    }
-
-    // Marks the underlying block device as read-only.
-    fs_mgr_set_blk_ro(fstab_entry->blk_device);
-
-    // Updates fstab_rec->blk_device to verity device name.
-    fstab_entry->blk_device = dev_path;
-
-    // Makes sure we've set everything up properly.
-    if (wait_for_verity_dev && !fs_mgr_wait_for_file(dev_path, 1s)) {
-        return false;
-    }
-
-    return true;
-}
-
-static bool get_hashtree_descriptor(const std::string& partition_name,
-                                    const std::vector<VBMetaData>& vbmeta_images,
-                                    AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt,
-                                    std::string* out_digest) {
-    bool found = false;
-    const uint8_t* desc_partition_name;
-
-    for (size_t i = 0; i < vbmeta_images.size() && !found; i++) {
-        // Get descriptors from vbmeta_images[i].
-        size_t num_descriptors;
-        std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
-                avb_descriptor_get_all(vbmeta_images[i].vbmeta_data(),
-                                       vbmeta_images[i].vbmeta_size(), &num_descriptors),
-                avb_free);
-
-        if (!descriptors || num_descriptors < 1) {
-            continue;
-        }
-
-        for (size_t j = 0; j < num_descriptors && !found; j++) {
-            AvbDescriptor desc;
-            if (!avb_descriptor_validate_and_byteswap(descriptors[j], &desc)) {
-                LWARNING << "Descriptor[" << j << "] is invalid";
-                continue;
-            }
-            if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
-                desc_partition_name =
-                        (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
-                if (!avb_hashtree_descriptor_validate_and_byteswap(
-                            (AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
-                    continue;
-                }
-                if (out_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);
-                if (hashtree_partition_name == partition_name) {
-                    found = true;
-                }
-            }
-        }
-    }
-
-    if (!found) {
-        LERROR << "Partition descriptor not found: " << partition_name.c_str();
-        return false;
-    }
-
-    const uint8_t* desc_salt = desc_partition_name + out_hashtree_desc->partition_name_len;
-    *out_salt = bytes_to_hex(desc_salt, out_hashtree_desc->salt_len);
-
-    const uint8_t* desc_digest = desc_salt + out_hashtree_desc->salt_len;
-    *out_digest = bytes_to_hex(desc_digest, out_hashtree_desc->root_digest_len);
-
-    return true;
-}
 
 AvbUniquePtr AvbHandle::Open() {
-    bool is_device_unlocked = fs_mgr_is_device_unlocked();
+    bool is_device_unlocked = IsDeviceUnlocked();
 
     AvbUniquePtr avb_handle(new AvbHandle());
     if (!avb_handle) {
@@ -407,8 +207,7 @@
     }
 
     // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
-    avb_handle->avb_version_ =
-            android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+    avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
 
     // Checks whether FLAGS_VERIFICATION_DISABLED is set:
     //   - Only the top-level vbmeta struct is read.
@@ -416,7 +215,7 @@
     //     and AVB HASHTREE descriptor(s).
     AvbVBMetaImageHeader vbmeta_header;
     avb_vbmeta_image_header_to_host_byte_order(
-            (AvbVBMetaImageHeader*)avb_handle->vbmeta_images_[0].vbmeta_data(), &vbmeta_header);
+            (AvbVBMetaImageHeader*)avb_handle->vbmeta_images_[0].data(), &vbmeta_header);
     bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
                                   AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
 
@@ -462,7 +261,7 @@
     if (fstab_entry->fs_mgr_flags.logical) {
         partition_name = fstab_entry->logical_partition_name;
     } else {
-        partition_name = basename(fstab_entry->blk_device.c_str());
+        partition_name = Basename(fstab_entry->blk_device);
     }
 
     if (fstab_entry->fs_mgr_flags.slot_select) {
@@ -475,14 +274,14 @@
     AvbHashtreeDescriptor hashtree_descriptor;
     std::string salt;
     std::string root_digest;
-    if (!get_hashtree_descriptor(partition_name, vbmeta_images_, &hashtree_descriptor, &salt,
-                                 &root_digest)) {
+    if (!GetHashtreeDescriptor(partition_name, vbmeta_images_, &hashtree_descriptor, &salt,
+                               &root_digest)) {
         return AvbHashtreeResult::kFail;
     }
 
     // Converts HASHTREE descriptor to verity_table_params.
-    if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
-                                  wait_for_verity_dev)) {
+    if (!HashtreeDmVeritySetup(fstab_entry, hashtree_descriptor, salt, root_digest,
+                               wait_for_verity_dev)) {
         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 0c2b231..eca6984 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -37,7 +37,7 @@
     // Constructors
     VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
 
-    VBMetaData(uint8_t* data, size_t size)
+    VBMetaData(const uint8_t* data, size_t size)
         : vbmeta_ptr_(new (std::nothrow) uint8_t[size]), vbmeta_size_(size) {
         // The ownership of data is NOT transferred, i.e., the caller still
         // needs to release the memory as we make a copy here.
@@ -49,8 +49,8 @@
 
     // Get methods for each data member.
     const std::string& device_path() const { return device_path_; }
-    uint8_t* vbmeta_data() const { return vbmeta_ptr_.get(); }
-    const size_t& vbmeta_size() const { return vbmeta_size_; }
+    uint8_t* data() const { return vbmeta_ptr_.get(); }
+    const size_t& size() const { return vbmeta_size_; }
 
     // Maximum size of a vbmeta data - 64 KiB.
     static const size_t kMaxVBMetaSize = 64 * 1024;
diff --git a/fs_mgr/libfs_avb/tests/Android.bp b/fs_mgr/libfs_avb/tests/Android.bp
deleted file mode 100644
index 24e1d76..0000000
--- a/fs_mgr/libfs_avb/tests/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_test_host {
-    name: "libfs_avb_host_unittest",
-    required: [
-        "avbtool",
-    ],
-    data: [
-        "data/*",
-    ],
-    static_libs: [
-        "libgtest_host",
-    ],
-    shared_libs: [
-        "libbase",
-        "libchrome",
-    ],
-    srcs: [
-        "fs_avb_unittest_util.cpp",
-    ],
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    },
-}
diff --git a/fs_mgr/libfs_avb/tests/basic_test.cpp b/fs_mgr/libfs_avb/tests/basic_test.cpp
new file mode 100644
index 0000000..5a1cd0d
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/basic_test.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_avb_test_util.h"
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+
+namespace fs_avb_host_test {
+
+TEST_F(BaseFsAvbTest, GenerateImage) {
+    const size_t image_size = 5 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", image_size);
+    EXPECT_NE(0U, boot_path.value().size());
+
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+
+    // Checks file content is as expected.
+    std::vector<uint8_t> expected_content;
+    expected_content.resize(image_size);
+    for (size_t n = 0; n < image_size; n++) {
+        expected_content[n] = uint8_t(n);
+    }
+    std::vector<uint8_t> actual_content;
+    actual_content.resize(image_size);
+    EXPECT_TRUE(
+            base::ReadFile(boot_path, reinterpret_cast<char*>(actual_content.data()), image_size));
+    EXPECT_EQ(expected_content, actual_content);
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImage) {
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0, data_dir_.Append("testkey_rsa2048.pem"),
+                        {}, /* include_descriptor_image_paths */
+                        {}, /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+    EXPECT_EQ("5eba9ad4e775645e7eac441a563c200681ae868158d06f6a6cd36d06c07bd781",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     320 bytes\n"
+            "Auxiliary Block:          576 bytes\n"
+            "Algorithm:                SHA256_RSA2048\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    (none)\n",
+            InfoImage("vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, AddHashFooter) {
+    // Generates a raw boot.img
+    const size_t image_size = 5 * 1024 * 1024;
+    const size_t partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", image_size);
+    EXPECT_NE(0U, boot_path.value().size());
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+    // Appends AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+    ExtractVBMetaImage(boot_path, "boot-vbmeta.img");
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     576 bytes\n"
+            "Auxiliary Block:          1216 bytes\n"
+            "Algorithm:                SHA256_RSA4096\n"
+            "Rollback Index:           10\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hash descriptor:\n"
+            "      Image Size:            5242880 bytes\n"
+            "      Hash Algorithm:        sha256\n"
+            "      Partition Name:        boot\n"
+            "      Salt:                  d00df00d\n"
+            "      Digest:                "
+            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+            "      Flags:                 0\n",
+            InfoImage("boot-vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, AddHashtreeFooter) {
+    // Generates a raw system.img
+    const size_t image_size = 50 * 1024 * 1024;
+    const size_t partition_size = 60 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+    EXPECT_NE(0U, system_path.value().size());
+    // Checks file size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+    EXPECT_EQ(file_size, image_size);
+    // Appends AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+                 data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+    // Extracts system vbmeta from system.img into system-vbmeta.img.
+    ExtractVBMetaImage(system_path, "system-vbmeta.img");
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          2304 bytes\n"
+            "Algorithm:                SHA512_RSA8192\n"
+            "Rollback Index:           20\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            52428800 bytes\n"
+            "      Tree Offset:           52428800\n"
+            "      Tree Size:             413696 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            52842496\n"
+            "      FEC size:              417792 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           d20d40c02298e385ab6d398a61a3b91dc9947d99\n"
+            "      Flags:                 0\n",
+            InfoImage("system-vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithDescriptors) {
+    // 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_RSA4096", 10,
+                 data_dir_.Append("testkey_rsa4096.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_RSA8192", 20,
+                 data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta.img including both 'boot' and 'system' descriptors.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0, data_dir_.Append("testkey_rsa2048.pem"),
+                        {boot_path, system_path}, /* include_descriptor_image_paths */
+                        {},                       /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+    EXPECT_EQ("a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     320 bytes\n"
+            "Auxiliary Block:          960 bytes\n"
+            "Algorithm:                SHA256_RSA2048\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Hash descriptor:\n"
+            "      Image Size:            5242880 bytes\n"
+            "      Hash Algorithm:        sha256\n"
+            "      Partition Name:        boot\n"
+            "      Salt:                  d00df00d\n"
+            "      Digest:                "
+            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+            "      Flags:                 0\n"
+            "    Hashtree descriptor:\n"
+            "      Version of dm-verity:  1\n"
+            "      Image Size:            10485760 bytes\n"
+            "      Tree Offset:           10485760\n"
+            "      Tree Size:             86016 bytes\n"
+            "      Data Block Size:       4096 bytes\n"
+            "      Hash Block Size:       4096 bytes\n"
+            "      FEC num roots:         2\n"
+            "      FEC offset:            10571776\n"
+            "      FEC size:              90112 bytes\n"
+            "      Hash Algorithm:        sha1\n"
+            "      Partition Name:        system\n"
+            "      Salt:                  d00df00d\n"
+            "      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+            "      Flags:                 0\n",
+            InfoImage("vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithChainDescriptors) {
+    // 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\"");
+
+    // Make a vbmeta image with chain partitions.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+                        {},                               /* include_descriptor_image_paths */
+                        {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                         {"system", 2, rsa4096_public_key}},
+                        "--internal_release_string \"unit test\"");
+
+    // vbmeta digest calculation includes the chained vbmeta from boot.img and system.img.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     1088 bytes\n"
+            "Auxiliary Block:          3840 bytes\n"
+            "Algorithm:                SHA256_RSA8192\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          boot\n"
+            "      Rollback Index Location: 1\n"
+            "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          system\n"
+            "      Rollback Index Location: 2\n"
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            InfoImage("vbmeta.img"));
+}
+
+}  // namespace fs_avb_host_test
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
new file mode 100644
index 0000000..95b17d8
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_avb_test_util.h"
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+
+namespace fs_avb_host_test {
+
+// Need to match the data setting in Android.bp:
+//     data: ["tests/data/*"]
+base::FilePath BaseFsAvbTest::data_dir_ = base::FilePath("tests/data");
+
+void BaseFsAvbTest::SetUp() {
+    // Changes current directory to test executable directory so that relative path
+    // references to test dependencies don't rely on being manually run from
+    // the executable directory. With this, we can just open "./tests/data/testkey_rsa2048.pem"
+    // from the source.
+    base::SetCurrentDirectory(base::FilePath(android::base::GetExecutableDirectory()));
+
+    // Creates a temporary directory, e.g., /tmp/libfs_avb-tests.XXXXXX to stash images in.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+    base::CreateTemporaryDirInDir(tmp_dir, "libfs_avb-tests.", &test_dir_);
+}
+
+void BaseFsAvbTest::TearDown() {
+    // Nukes temporary directory.
+    ASSERT_NE(std::string::npos, test_dir_.value().find("libfs_avb-tests"));
+    ASSERT_TRUE(base::DeleteFile(test_dir_, true /* recursive */));
+}
+
+std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,
+                                            const std::string& hash_algorithm) {
+    auto iter = vbmeta_images_.find(file_name);
+    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
+
+    // Gets the image path from iterator->second.path: VBMetaImage.path.
+    base::FilePath image_path = iter->second.path;
+    base::FilePath vbmeta_digest_path = test_dir_.Append("vbmeta_digest");
+    EXPECT_COMMAND(0,
+                   "avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s"
+                   " --output %s",
+                   image_path.value().c_str(), hash_algorithm.c_str(),
+                   vbmeta_digest_path.value().c_str());
+    // Reads the content of the output digest file.
+    std::string vbmeta_digest_data;
+    EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
+    // Returns the trimmed digest.
+    std::string trimmed_digest_data;
+    base::TrimString(vbmeta_digest_data, " \t\n", &trimmed_digest_data);
+    return trimmed_digest_data;
+}
+
+void BaseFsAvbTest::GenerateVBMetaImage(
+        const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,
+        const base::FilePath& key_path,
+        const std::vector<base::FilePath>& include_descriptor_image_paths,
+        const std::vector<ChainPartitionConfig>& chain_partitions,
+        const std::string& additional_options) {
+    // --algorithm and --key
+    std::string signing_options;
+    if (avb_algorithm == "") {
+        signing_options = " --algorithm NONE ";
+    } else {
+        signing_options =
+                std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
+    }
+    // --include_descriptors_from_image
+    std::string include_descriptor_options;
+    for (const auto& path : include_descriptor_image_paths) {
+        include_descriptor_options += " --include_descriptors_from_image " + path.value();
+    }
+    // --chain_partitions
+    std::string chain_partition_options;
+    for (const auto& partition : chain_partitions) {
+        chain_partition_options += base::StringPrintf(
+                " --chain_partition %s:%u:%s", partition.partition_name.c_str(),
+                partition.rollback_index_location, partition.key_blob_path.value().c_str());
+    }
+    // Starts to 'make_vbmeta_image'.
+    VBMetaImage vbmeta_image;
+    vbmeta_image.path = test_dir_.Append(file_name);
+    EXPECT_COMMAND(0,
+                   "avbtool make_vbmeta_image"
+                   " --rollback_index %" PRIu64
+                   " %s %s %s %s"
+                   " --output %s",
+                   rollback_index, signing_options.c_str(), include_descriptor_options.c_str(),
+                   chain_partition_options.c_str(), additional_options.c_str(),
+                   vbmeta_image.path.value().c_str());
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
+    vbmeta_image.content.resize(file_size);
+    ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
+                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
+    // Stores the generated vbmeta image into vbmeta_images_ member object.
+    vbmeta_images_.emplace(file_name, std::move(vbmeta_image));
+}
+
+void BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,
+                                       const std::string& output_file_name,
+                                       const size_t padding_size) {
+    VBMetaImage vbmeta_image;
+    vbmeta_image.path = test_dir_.Append(output_file_name);
+    EXPECT_COMMAND(0,
+                   "avbtool extract_vbmeta_image"
+                   " --image %s"
+                   " --output %s"
+                   " --padding_size %zu",
+                   image_path.value().c_str(), vbmeta_image.path.value().c_str(), padding_size);
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
+    vbmeta_image.content.resize(file_size);
+    ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
+                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
+    // Stores the extracted vbmeta image into vbmeta_images_ member object.
+    vbmeta_images_.emplace(output_file_name, std::move(vbmeta_image));
+}
+
+// Generates a file with name |file_name| of size |image_size| with
+// known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+base::FilePath BaseFsAvbTest::GenerateImage(const std::string& file_name, size_t image_size,
+                                            uint8_t start_byte) {
+    std::vector<uint8_t> image;
+    image.resize(image_size);
+    for (size_t n = 0; n < image_size; n++) {
+        image[n] = uint8_t(n + start_byte);
+    }
+    base::FilePath image_path = test_dir_.Append(file_name);
+    EXPECT_EQ(image_size,
+              static_cast<const size_t>(base::WriteFile(
+                      image_path, reinterpret_cast<const char*>(image.data()), image.size())));
+    return image_path;
+}
+
+void BaseFsAvbTest::AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
+                                 const std::string& partition_name, const uint64_t partition_size,
+                                 const std::string& avb_algorithm, uint64_t rollback_index,
+                                 const base::FilePath& key_path, const std::string& salt,
+                                 const std::string& additional_options) {
+    // 'add_hash_footer' or 'add_hashtree_footer'.
+    EXPECT_TRUE(footer_type == "hash" or footer_type == "hashtree");
+    std::string add_footer_option = "add_" + footer_type + "_footer";
+
+    std::string signing_options;
+    if (avb_algorithm == "") {
+        signing_options = " --algorithm NONE ";
+    } else {
+        signing_options =
+                std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
+    }
+    EXPECT_COMMAND(0,
+                   "avbtool %s"
+                   " --image %s"
+                   " --partition_name %s "
+                   " --partition_size %" PRIu64 " --rollback_index %" PRIu64
+                   " --salt %s"
+                   " %s %s",
+                   add_footer_option.c_str(), image_path.value().c_str(), partition_name.c_str(),
+                   partition_size, rollback_index, salt.c_str(), signing_options.c_str(),
+                   additional_options.c_str());
+}
+
+std::string BaseFsAvbTest::InfoImage(const base::FilePath& image_path) {
+    base::FilePath tmp_path = test_dir_.Append("info_output.txt");
+    EXPECT_COMMAND(0, "avbtool info_image --image %s --output %s", image_path.value().c_str(),
+                   tmp_path.value().c_str());
+    std::string info_data;
+    EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
+    return info_data;
+}
+
+std::string BaseFsAvbTest::InfoImage(const std::string& file_name) {
+    auto iter = vbmeta_images_.find(file_name);
+    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
+    // Gets the image path from iterator->second.path: VBMetaImage.path.
+    base::FilePath image_path = iter->second.path;
+    return InfoImage(image_path);
+}
+
+base::FilePath BaseFsAvbTest::ExtractPublicKeyAvb(const base::FilePath& key_path) {
+    std::string file_name = key_path.RemoveExtension().BaseName().value();
+    base::FilePath tmp_path = test_dir_.Append(file_name + "public_key.bin");
+    EXPECT_COMMAND(0,
+                   "avbtool extract_public_key --key %s"
+                   " --output %s",
+                   key_path.value().c_str(), tmp_path.value().c_str());
+    return tmp_path;
+}
+
+std::string BaseFsAvbTest::ExtractPublicKeyAvbBlob(const base::FilePath& key_path) {
+    base::FilePath tmp_path = test_dir_.Append("public_key.bin");
+    EXPECT_COMMAND(0,
+                   "avbtool extract_public_key --key %s"
+                   " --output %s",
+                   key_path.value().c_str(), tmp_path.value().c_str());
+    std::string key_data;
+    EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
+    return key_data;
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.h b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
similarity index 97%
rename from fs_mgr/libfs_avb/tests/fs_avb_unittest_util.h
rename to fs_mgr/libfs_avb/tests/fs_avb_test_util.h
index f329466..f80dc5f 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.h
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
@@ -79,7 +79,7 @@
 
     // Generate a file with name |file_name| of size |image_size| with
     // known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
-    base::FilePath GenerateImage(const std::string file_name, size_t image_size,
+    base::FilePath GenerateImage(const std::string& file_name, size_t image_size,
                                  uint8_t start_byte = 0);
     // Invokes 'avbtool add_hash_footer' or 'avbtool add_hashtree_footer' to sign
     // the |image_path|. The |footer_type| can be either "hash" or "hashtree".
@@ -107,6 +107,8 @@
     base::FilePath test_dir_;
     // Maps vbmeta image name (e.g., vbmeta_a.img, system_a.img) to VBMetaImage.
     std::map<std::string, VBMetaImage> vbmeta_images_;
+
+    static base::FilePath data_dir_;
 };
 
 }  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.cpp
deleted file mode 100644
index 216d1cb..0000000
--- a/fs_mgr/libfs_avb/tests/fs_avb_unittest_util.cpp
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "fs_avb_unittest_util.h"
-
-#include <stdlib.h>
-
-#include <android-base/file.h>
-#include <base/files/file_util.h>
-#include <base/strings/string_util.h>
-
-namespace fs_avb_host_test {
-
-void BaseFsAvbTest::SetUp() {
-    // Changes current directory to test executable directory so that relative path
-    // references to test dependencies don't rely on being manually run from
-    // the executable directory. With this, we can just open "./data/testkey_rsa2048.pem"
-    // from the source.
-    base::SetCurrentDirectory(base::FilePath(android::base::GetExecutableDirectory()));
-
-    // Creates a temporary directory, e.g., /tmp/libfs_avb-tests.XXXXXX to stash images in.
-    base::FilePath tmp_dir;
-    ASSERT_TRUE(GetTempDir(&tmp_dir));
-    base::CreateTemporaryDirInDir(tmp_dir, "libfs_avb-tests.", &test_dir_);
-}
-
-void BaseFsAvbTest::TearDown() {
-    // Nukes temporary directory.
-    ASSERT_NE(std::string::npos, test_dir_.value().find("libfs_avb-tests"));
-    ASSERT_TRUE(base::DeleteFile(test_dir_, true /* recursive */));
-}
-
-std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,
-                                            const std::string& hash_algorithm) {
-    auto iter = vbmeta_images_.find(file_name);
-    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
-
-    // Gets the image path from iterator->second.path: VBMetaImage.path.
-    base::FilePath image_path = iter->second.path;
-    base::FilePath vbmeta_digest_path = test_dir_.Append("vbmeta_digest");
-    EXPECT_COMMAND(0,
-                   "avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s"
-                   " --output %s",
-                   image_path.value().c_str(), hash_algorithm.c_str(),
-                   vbmeta_digest_path.value().c_str());
-    // Reads the content of the output digest file.
-    std::string vbmeta_digest_data;
-    EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
-    // Returns the trimmed digest.
-    std::string trimmed_digest_data;
-    base::TrimString(vbmeta_digest_data, " \t\n", &trimmed_digest_data);
-    return trimmed_digest_data;
-}
-
-void BaseFsAvbTest::GenerateVBMetaImage(
-        const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,
-        const base::FilePath& key_path,
-        const std::vector<base::FilePath>& include_descriptor_image_paths,
-        const std::vector<ChainPartitionConfig>& chain_partitions,
-        const std::string& additional_options) {
-    // --algorithm and --key
-    std::string signing_options;
-    if (avb_algorithm == "") {
-        signing_options = " --algorithm NONE ";
-    } else {
-        signing_options =
-                std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
-    }
-    // --include_descriptors_from_image
-    std::string include_descriptor_options;
-    for (const auto& path : include_descriptor_image_paths) {
-        include_descriptor_options += " --include_descriptors_from_image " + path.value();
-    }
-    // --chain_partitions
-    std::string chain_partition_options;
-    for (const auto& partition : chain_partitions) {
-        chain_partition_options += base::StringPrintf(
-                " --chain_partition %s:%u:%s", partition.partition_name.c_str(),
-                partition.rollback_index_location, partition.key_blob_path.value().c_str());
-    }
-    // Starts to 'make_vbmeta_image'.
-    VBMetaImage vbmeta_image;
-    vbmeta_image.path = test_dir_.Append(file_name);
-    EXPECT_COMMAND(0,
-                   "avbtool make_vbmeta_image"
-                   " --rollback_index %" PRIu64
-                   " %s %s %s %s"
-                   " --output %s",
-                   rollback_index, signing_options.c_str(), include_descriptor_options.c_str(),
-                   chain_partition_options.c_str(), additional_options.c_str(),
-                   vbmeta_image.path.value().c_str());
-    int64_t file_size;
-    ASSERT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
-    vbmeta_image.content.resize(file_size);
-    ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
-                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
-    // Stores the generated vbmeta image into vbmeta_images_ member object.
-    vbmeta_images_.emplace(file_name, std::move(vbmeta_image));
-}
-
-void BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,
-                                       const std::string& output_file_name,
-                                       const size_t padding_size) {
-    VBMetaImage vbmeta_image;
-    vbmeta_image.path = test_dir_.Append(output_file_name);
-    EXPECT_COMMAND(0,
-                   "avbtool extract_vbmeta_image"
-                   " --image %s"
-                   " --output %s"
-                   " --padding_size %zu",
-                   image_path.value().c_str(), vbmeta_image.path.value().c_str(), padding_size);
-    int64_t file_size;
-    ASSERT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
-    vbmeta_image.content.resize(file_size);
-    ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
-                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
-    // Stores the extracted vbmeta image into vbmeta_images_ member object.
-    vbmeta_images_.emplace(output_file_name, std::move(vbmeta_image));
-}
-
-// Generates a file with name |file_name| of size |image_size| with
-// known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
-base::FilePath BaseFsAvbTest::GenerateImage(const std::string file_name, size_t image_size,
-                                            uint8_t start_byte) {
-    std::vector<uint8_t> image;
-    image.resize(image_size);
-    for (size_t n = 0; n < image_size; n++) {
-        image[n] = uint8_t(n + start_byte);
-    }
-    base::FilePath image_path = test_dir_.Append(file_name);
-    EXPECT_EQ(image_size,
-              static_cast<const size_t>(base::WriteFile(
-                      image_path, reinterpret_cast<const char*>(image.data()), image.size())));
-    return image_path;
-}
-
-void BaseFsAvbTest::AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
-                                 const std::string& partition_name, const uint64_t partition_size,
-                                 const std::string& avb_algorithm, uint64_t rollback_index,
-                                 const base::FilePath& key_path, const std::string& salt,
-                                 const std::string& additional_options) {
-    // 'add_hash_footer' or 'add_hashtree_footer'.
-    EXPECT_TRUE(footer_type == "hash" or footer_type == "hashtree");
-    std::string add_footer_option = "add_" + footer_type + "_footer";
-
-    std::string signing_options;
-    if (avb_algorithm == "") {
-        signing_options = " --algorithm NONE ";
-    } else {
-        signing_options =
-                std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
-    }
-    EXPECT_COMMAND(0,
-                   "avbtool %s"
-                   " --image %s"
-                   " --partition_name %s "
-                   " --partition_size %" PRIu64 " --rollback_index %" PRIu64
-                   " --salt %s"
-                   " %s %s",
-                   add_footer_option.c_str(), image_path.value().c_str(), partition_name.c_str(),
-                   partition_size, rollback_index, salt.c_str(), signing_options.c_str(),
-                   additional_options.c_str());
-}
-
-std::string BaseFsAvbTest::InfoImage(const base::FilePath& image_path) {
-    base::FilePath tmp_path = test_dir_.Append("info_output.txt");
-    EXPECT_COMMAND(0, "avbtool info_image --image %s --output %s", image_path.value().c_str(),
-                   tmp_path.value().c_str());
-    std::string info_data;
-    EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
-    return info_data;
-}
-
-std::string BaseFsAvbTest::InfoImage(const std::string& file_name) {
-    auto iter = vbmeta_images_.find(file_name);
-    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.
-    // Gets the image path from iterator->second.path: VBMetaImage.path.
-    base::FilePath image_path = iter->second.path;
-    return InfoImage(image_path);
-}
-
-base::FilePath BaseFsAvbTest::ExtractPublicKeyAvb(const base::FilePath& key_path) {
-    std::string file_name = key_path.RemoveExtension().BaseName().value();
-    base::FilePath tmp_path = test_dir_.Append(file_name + "public_key.bin");
-    EXPECT_COMMAND(0,
-                   "avbtool extract_public_key --key %s"
-                   " --output %s",
-                   key_path.value().c_str(), tmp_path.value().c_str());
-    return tmp_path;
-}
-
-std::string BaseFsAvbTest::ExtractPublicKeyAvbBlob(const base::FilePath& key_path) {
-    base::FilePath tmp_path = test_dir_.Append("public_key.bin");
-    EXPECT_COMMAND(0,
-                   "avbtool extract_public_key --key %s"
-                   " --output %s",
-                   key_path.value().c_str(), tmp_path.value().c_str());
-    std::string key_data;
-    EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
-    return key_data;
-}
-
-TEST_F(BaseFsAvbTest, GenerateImage) {
-    const size_t image_size = 5 * 1024 * 1024;
-    base::FilePath boot_path = GenerateImage("boot.img", image_size);
-    EXPECT_NE(0U, boot_path.value().size());
-
-    // Checks file size is as expected.
-    int64_t file_size;
-    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
-    EXPECT_EQ(file_size, image_size);
-
-    // Checks file content is as expected.
-    std::vector<uint8_t> expected_content;
-    expected_content.resize(image_size);
-    for (size_t n = 0; n < image_size; n++) {
-        expected_content[n] = uint8_t(n);
-    }
-    std::vector<uint8_t> actual_content;
-    actual_content.resize(image_size);
-    EXPECT_TRUE(
-            base::ReadFile(boot_path, reinterpret_cast<char*>(actual_content.data()), image_size));
-    EXPECT_EQ(expected_content, actual_content);
-}
-
-TEST_F(BaseFsAvbTest, GenerateVBMetaImage) {
-    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0,
-                        base::FilePath("data/testkey_rsa2048.pem"),
-                        {}, /* include_descriptor_image_paths */
-                        {}, /* chain_partitions */
-                        "--internal_release_string \"unit test\"");
-    EXPECT_EQ("5eba9ad4e775645e7eac441a563c200681ae868158d06f6a6cd36d06c07bd781",
-              CalcVBMetaDigest("vbmeta.img", "sha256"));
-    EXPECT_EQ(
-            "Minimum libavb version:   1.0\n"
-            "Header Block:             256 bytes\n"
-            "Authentication Block:     320 bytes\n"
-            "Auxiliary Block:          576 bytes\n"
-            "Algorithm:                SHA256_RSA2048\n"
-            "Rollback Index:           0\n"
-            "Flags:                    0\n"
-            "Release String:           'unit test'\n"
-            "Descriptors:\n"
-            "    (none)\n",
-            InfoImage("vbmeta.img"));
-}
-
-TEST_F(BaseFsAvbTest, AddHashFooter) {
-    // Generates a raw boot.img
-    const size_t image_size = 5 * 1024 * 1024;
-    const size_t partition_size = 10 * 1024 * 1024;
-    base::FilePath boot_path = GenerateImage("boot.img", image_size);
-    EXPECT_NE(0U, boot_path.value().size());
-    // Checks file size is as expected.
-    int64_t file_size;
-    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
-    EXPECT_EQ(file_size, image_size);
-    // Appends AVB Hash Footer.
-    AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
-                 base::FilePath("data/testkey_rsa4096.pem"), "d00df00d",
-                 "--internal_release_string \"unit test\"");
-    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
-    ExtractVBMetaImage(boot_path, "boot-vbmeta.img");
-    EXPECT_EQ(
-            "Minimum libavb version:   1.0\n"
-            "Header Block:             256 bytes\n"
-            "Authentication Block:     576 bytes\n"
-            "Auxiliary Block:          1216 bytes\n"
-            "Algorithm:                SHA256_RSA4096\n"
-            "Rollback Index:           10\n"
-            "Flags:                    0\n"
-            "Release String:           'unit test'\n"
-            "Descriptors:\n"
-            "    Hash descriptor:\n"
-            "      Image Size:            5242880 bytes\n"
-            "      Hash Algorithm:        sha256\n"
-            "      Partition Name:        boot\n"
-            "      Salt:                  d00df00d\n"
-            "      Digest:                "
-            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
-            "      Flags:                 0\n",
-            InfoImage("boot-vbmeta.img"));
-}
-
-TEST_F(BaseFsAvbTest, AddHashtreeFooter) {
-    // Generates a raw system.img
-    const size_t image_size = 50 * 1024 * 1024;
-    const size_t partition_size = 60 * 1024 * 1024;
-    base::FilePath system_path = GenerateImage("system.img", image_size);
-    EXPECT_NE(0U, system_path.value().size());
-    // Checks file size is as expected.
-    int64_t file_size;
-    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
-    EXPECT_EQ(file_size, image_size);
-    // Appends AVB Hashtree Footer.
-    AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
-                 base::FilePath("data/testkey_rsa8192.pem"), "d00df00d",
-                 "--internal_release_string \"unit test\"");
-    // Extracts system vbmeta from system.img into system-vbmeta.img.
-    ExtractVBMetaImage(system_path, "system-vbmeta.img");
-    EXPECT_EQ(
-            "Minimum libavb version:   1.0\n"
-            "Header Block:             256 bytes\n"
-            "Authentication Block:     1088 bytes\n"
-            "Auxiliary Block:          2304 bytes\n"
-            "Algorithm:                SHA512_RSA8192\n"
-            "Rollback Index:           20\n"
-            "Flags:                    0\n"
-            "Release String:           'unit test'\n"
-            "Descriptors:\n"
-            "    Hashtree descriptor:\n"
-            "      Version of dm-verity:  1\n"
-            "      Image Size:            52428800 bytes\n"
-            "      Tree Offset:           52428800\n"
-            "      Tree Size:             413696 bytes\n"
-            "      Data Block Size:       4096 bytes\n"
-            "      Hash Block Size:       4096 bytes\n"
-            "      FEC num roots:         2\n"
-            "      FEC offset:            52842496\n"
-            "      FEC size:              417792 bytes\n"
-            "      Hash Algorithm:        sha1\n"
-            "      Partition Name:        system\n"
-            "      Salt:                  d00df00d\n"
-            "      Root Digest:           d20d40c02298e385ab6d398a61a3b91dc9947d99\n"
-            "      Flags:                 0\n",
-            InfoImage("system-vbmeta.img"));
-}
-
-TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithDescriptors) {
-    // 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_RSA4096", 10,
-                 base::FilePath("data/testkey_rsa4096.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_RSA8192", 20,
-                 base::FilePath("data/testkey_rsa8192.pem"), "d00df00d",
-                 "--internal_release_string \"unit test\"");
-
-    // Makes a vbmeta.img including both 'boot' and 'system' descriptors.
-    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0,
-                        base::FilePath("data/testkey_rsa2048.pem"),
-                        {boot_path, system_path}, /* include_descriptor_image_paths */
-                        {},                       /* chain_partitions */
-                        "--internal_release_string \"unit test\"");
-    EXPECT_EQ("a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f",
-              CalcVBMetaDigest("vbmeta.img", "sha256"));
-    EXPECT_EQ(
-            "Minimum libavb version:   1.0\n"
-            "Header Block:             256 bytes\n"
-            "Authentication Block:     320 bytes\n"
-            "Auxiliary Block:          960 bytes\n"
-            "Algorithm:                SHA256_RSA2048\n"
-            "Rollback Index:           0\n"
-            "Flags:                    0\n"
-            "Release String:           'unit test'\n"
-            "Descriptors:\n"
-            "    Hash descriptor:\n"
-            "      Image Size:            5242880 bytes\n"
-            "      Hash Algorithm:        sha256\n"
-            "      Partition Name:        boot\n"
-            "      Salt:                  d00df00d\n"
-            "      Digest:                "
-            "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
-            "      Flags:                 0\n"
-            "    Hashtree descriptor:\n"
-            "      Version of dm-verity:  1\n"
-            "      Image Size:            10485760 bytes\n"
-            "      Tree Offset:           10485760\n"
-            "      Tree Size:             86016 bytes\n"
-            "      Data Block Size:       4096 bytes\n"
-            "      Hash Block Size:       4096 bytes\n"
-            "      FEC num roots:         2\n"
-            "      FEC offset:            10571776\n"
-            "      FEC size:              90112 bytes\n"
-            "      Hash Algorithm:        sha1\n"
-            "      Partition Name:        system\n"
-            "      Salt:                  d00df00d\n"
-            "      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\n"
-            "      Flags:                 0\n",
-            InfoImage("vbmeta.img"));
-}
-
-TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithChainDescriptors) {
-    // 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,
-                 base::FilePath("data/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,
-                 base::FilePath("data/testkey_rsa4096.pem"), "d00df00d",
-                 "--internal_release_string \"unit test\"");
-
-    // Make a vbmeta image with chain partitions.
-    base::FilePath rsa2048_public_key =
-            ExtractPublicKeyAvb(base::FilePath("data/testkey_rsa2048.pem"));
-    base::FilePath rsa4096_public_key =
-            ExtractPublicKeyAvb(base::FilePath("data/testkey_rsa4096.pem"));
-    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
-                        base::FilePath("data/testkey_rsa8192.pem"),
-                        {},                               /* include_descriptor_image_paths */
-                        {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
-                         {"system", 2, rsa4096_public_key}},
-                        "--internal_release_string \"unit test\"");
-
-    // vbmeta digest calculation includes the chained vbmeta from boot.img and system.img.
-    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
-              CalcVBMetaDigest("vbmeta.img", "sha256"));
-    EXPECT_EQ(
-            "Minimum libavb version:   1.0\n"
-            "Header Block:             256 bytes\n"
-            "Authentication Block:     1088 bytes\n"
-            "Auxiliary Block:          3840 bytes\n"
-            "Algorithm:                SHA256_RSA8192\n"
-            "Rollback Index:           0\n"
-            "Flags:                    0\n"
-            "Release String:           'unit test'\n"
-            "Descriptors:\n"
-            "    Chain Partition descriptor:\n"
-            "      Partition Name:          boot\n"
-            "      Rollback Index Location: 1\n"
-            "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
-            "    Chain Partition descriptor:\n"
-            "      Partition Name:          system\n"
-            "      Rollback Index Location: 2\n"
-            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
-            InfoImage("vbmeta.img"));
-}
-
-}  // namespace fs_avb_host_test
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    return RUN_ALL_TESTS();
-}
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
new file mode 100644
index 0000000..835e8fd
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <future>
+#include <string>
+#include <thread>
+
+#include <base/files/file_util.h>
+
+#include "fs_avb_test_util.h"
+#include "util.h"
+
+// Target functions to test:
+using android::fs_mgr::BytesToHex;
+using android::fs_mgr::HexToBytes;
+using android::fs_mgr::NibbleValue;
+using android::fs_mgr::WaitForFile;
+
+namespace fs_avb_host_test {
+
+TEST(BasicUtilTest, NibbleValue09) {
+    uint8_t value;
+
+    EXPECT_TRUE(NibbleValue('0', &value));
+    EXPECT_EQ(0, value);
+    EXPECT_TRUE(NibbleValue('1', &value));
+    EXPECT_EQ(1, value);
+    EXPECT_TRUE(NibbleValue('2', &value));
+    EXPECT_EQ(2, value);
+    EXPECT_TRUE(NibbleValue('3', &value));
+    EXPECT_EQ(3, value);
+    EXPECT_TRUE(NibbleValue('4', &value));
+    EXPECT_EQ(4, value);
+    EXPECT_TRUE(NibbleValue('5', &value));
+    EXPECT_EQ(5, value);
+    EXPECT_TRUE(NibbleValue('6', &value));
+    EXPECT_EQ(6, value);
+    EXPECT_TRUE(NibbleValue('7', &value));
+    EXPECT_EQ(7, value);
+    EXPECT_TRUE(NibbleValue('8', &value));
+    EXPECT_EQ(8, value);
+    EXPECT_TRUE(NibbleValue('9', &value));
+    EXPECT_EQ(9, value);
+}
+
+TEST(BasicUtilTest, NibbleValueAF) {
+    uint8_t value;
+
+    EXPECT_TRUE(NibbleValue('a', &value));
+    EXPECT_EQ(10, value);
+    EXPECT_TRUE(NibbleValue('b', &value));
+    EXPECT_EQ(11, value);
+    EXPECT_TRUE(NibbleValue('c', &value));
+    EXPECT_EQ(12, value);
+    EXPECT_TRUE(NibbleValue('d', &value));
+    EXPECT_EQ(13, value);
+    EXPECT_TRUE(NibbleValue('e', &value));
+    EXPECT_EQ(14, value);
+    EXPECT_TRUE(NibbleValue('f', &value));
+    EXPECT_EQ(15, value);
+
+    EXPECT_TRUE(NibbleValue('A', &value));
+    EXPECT_EQ(10, value);
+    EXPECT_TRUE(NibbleValue('B', &value));
+    EXPECT_EQ(11, value);
+    EXPECT_TRUE(NibbleValue('C', &value));
+    EXPECT_EQ(12, value);
+    EXPECT_TRUE(NibbleValue('D', &value));
+    EXPECT_EQ(13, value);
+    EXPECT_TRUE(NibbleValue('E', &value));
+    EXPECT_EQ(14, value);
+    EXPECT_TRUE(NibbleValue('F', &value));
+    EXPECT_EQ(15, value);
+}
+
+TEST(BasicUtilTest, NibbleValueInvalid) {
+    uint8_t value;
+
+    EXPECT_FALSE(NibbleValue('G', &value));
+    EXPECT_FALSE(NibbleValue('H', &value));
+    EXPECT_FALSE(NibbleValue('I', &value));
+    EXPECT_FALSE(NibbleValue('x', &value));
+    EXPECT_FALSE(NibbleValue('y', &value));
+    EXPECT_FALSE(NibbleValue('z', &value));
+}
+
+TEST(BasicUtilTest, HexToBytes) {
+    std::string hex = "000102030405060708090A0B0C0D0E0F";
+    uint8_t bytes[16];
+
+    EXPECT_TRUE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+    for (size_t i = 0; i < sizeof(bytes); i++) {
+        EXPECT_EQ(i, bytes[i]);
+    }
+}
+
+TEST(BasicUtilTest, HexToBytes2) {
+    std::string hex = "101112131415161718191A1B1C1D1E1F";
+    uint8_t bytes[16];
+
+    EXPECT_TRUE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+    for (size_t i = 0; i < sizeof(bytes); i++) {
+        EXPECT_EQ(16 + i, bytes[i]);
+    }
+}
+
+TEST(BasicUtilTest, BytesToHex) {
+    const uint8_t bytes[16]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+
+    EXPECT_EQ("0102", BytesToHex((uint8_t*)bytes, 2));
+    EXPECT_EQ("01020304", BytesToHex((uint8_t*)bytes, 4));
+    EXPECT_EQ("0102030405060708", BytesToHex((uint8_t*)bytes, 8));
+    EXPECT_EQ("0102030405060708090a0b0c0d0e0f10", BytesToHex((uint8_t*)bytes, 16));
+
+    EXPECT_EQ("01", BytesToHex((uint8_t*)bytes, 1));
+    EXPECT_EQ("010203", BytesToHex((uint8_t*)bytes, 3));
+    EXPECT_EQ("0102030405", BytesToHex((uint8_t*)bytes, 5));
+}
+
+TEST(BasicUtilTest, HexToBytesInValidOddLenHex) {
+    std::string hex = "12345";
+    uint8_t bytes[16];
+
+    EXPECT_FALSE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+}
+
+TEST(BasicUtilTest, HexToBytesInsufficientByteLen) {
+    std::string hex = "101112131415161718191A1B1C1D1E1F";
+    uint8_t bytes[8];
+
+    EXPECT_FALSE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+}
+
+TEST(BasicUtilTest, WaitForFile) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Waits this path.
+    base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+
+    EXPECT_TRUE(base::CreateDirectory(wait_path));
+    EXPECT_TRUE(WaitForFile(wait_path.value(), 1s));
+
+    // Removes the wait_path.
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+TEST(BasicUtilTest, WaitForFileNonExist) {
+    base::FilePath wait_path("/path/not/exist");
+    EXPECT_FALSE(WaitForFile(wait_path.value(), 200ms));
+}
+
+TEST(BasicUtilTest, WaitForFileDeferCreation) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Waits this path.
+    base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+    auto wait_file = std::async(WaitForFile, wait_path.value(), 500ms);
+
+    // Sleeps 100ms before creating the wait_path.
+    std::this_thread::sleep_for(100ms);
+    EXPECT_TRUE(base::CreateDirectory(wait_path));
+
+    // Checks WaitForFile() returns success.
+    EXPECT_TRUE(wait_file.get());
+
+    // Removes the wait_path.
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+TEST(BasicUtilTest, WaitForFileDeferCreationFailure) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Waits this path.
+    base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+    auto wait_file = std::async(WaitForFile, wait_path.value(), 50ms);
+
+    // Sleeps 100ms before creating the wait_path.
+    std::this_thread::sleep_for(100ms);
+    EXPECT_TRUE(base::CreateDirectory(wait_path));
+
+    // Checks WaitForFile() returns failure, because it only waits 50ms.
+    EXPECT_FALSE(wait_file.get());
+
+    // Removes the wait_path.
+    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/util.cpp b/fs_mgr/libfs_avb/util.cpp
new file mode 100644
index 0000000..17d47d9
--- /dev/null
+++ b/fs_mgr/libfs_avb/util.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util.h"
+
+#include <sys/ioctl.h>
+#include <thread>
+
+#include <android-base/unique_fd.h>
+#include <linux/fs.h>
+
+namespace android {
+namespace fs_mgr {
+
+bool NibbleValue(const char& c, uint8_t* value) {
+    CHECK(value != nullptr);
+
+    switch (c) {
+        case '0' ... '9':
+            *value = c - '0';
+            break;
+        case 'a' ... 'f':
+            *value = c - 'a' + 10;
+            break;
+        case 'A' ... 'F':
+            *value = c - 'A' + 10;
+            break;
+        default:
+            return false;
+    }
+
+    return true;
+}
+
+bool HexToBytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {
+    CHECK(bytes != nullptr);
+
+    if (hex.size() % 2 != 0) {
+        return false;
+    }
+    if (hex.size() / 2 > bytes_len) {
+        return false;
+    }
+    for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
+        uint8_t high;
+        if (!NibbleValue(hex[i], &high)) {
+            return false;
+        }
+        uint8_t low;
+        if (!NibbleValue(hex[i + 1], &low)) {
+            return false;
+        }
+        bytes[j] = (high << 4) | low;
+    }
+    return true;
+}
+
+std::string BytesToHex(const uint8_t* bytes, size_t bytes_len) {
+    CHECK(bytes != nullptr);
+
+    static const char* hex_digits = "0123456789abcdef";
+    std::string hex;
+
+    for (size_t i = 0; i < bytes_len; i++) {
+        hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
+        hex.push_back(hex_digits[bytes[i] & 0x0F]);
+    }
+    return hex;
+}
+
+bool WaitForFile(const std::string& filename, const std::chrono::milliseconds relative_timeout) {
+    auto start_time = std::chrono::steady_clock::now();
+
+    while (true) {
+        if (0 == access(filename.c_str(), F_OK) || errno != ENOENT) {
+            return true;
+        }
+
+        std::this_thread::sleep_for(50ms);
+
+        auto now = std::chrono::steady_clock::now();
+        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+        if (time_elapsed > relative_timeout) return false;
+    }
+}
+
+bool IsDeviceUnlocked() {
+    std::string verified_boot_state;
+
+    if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+        return verified_boot_state == "orange";
+    }
+    return false;
+}
+
+bool SetBlockDeviceReadOnly(const std::string& blockdev) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        return false;
+    }
+
+    int ON = 1;
+    return ioctl(fd, BLKROSET, &ON) == 0;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/util.h b/fs_mgr/libfs_avb/util.h
new file mode 100644
index 0000000..cb861f4
--- /dev/null
+++ b/fs_mgr/libfs_avb/util.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <string>
+
+#ifdef HOST_TEST
+#include <base/logging.h>
+#else
+#include <android-base/logging.h>
+#endif
+
+#define FS_AVB_TAG "[libfs_avb]"
+
+// Logs a message to kernel
+#define LINFO LOG(INFO) << FS_AVB_TAG
+#define LWARNING LOG(WARNING) << FS_AVB_TAG
+#define LERROR LOG(ERROR) << FS_AVB_TAG
+#define LFATAL LOG(FATAL) << FS_AVB_TAG
+
+// Logs a message with strerror(errno) at the end
+#define PINFO PLOG(INFO) << FS_AVB_TAG
+#define PWARNING PLOG(WARNING) << FS_AVB_TAG
+#define PERROR PLOG(ERROR) << FS_AVB_TAG
+#define PFATAL PLOG(FATAL) << FS_AVB_TAG
+
+extern bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace fs_mgr {
+
+bool NibbleValue(const char& c, uint8_t* value);
+
+bool HexToBytes(uint8_t* bytes, size_t bytes_len, const std::string& hex);
+
+std::string BytesToHex(const uint8_t* bytes, size_t bytes_len);
+
+bool WaitForFile(const std::string& filename, const std::chrono::milliseconds relative_timeout);
+
+bool IsDeviceUnlocked();
+
+bool SetBlockDeviceReadOnly(const std::string& blockdev);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 07e3c8a..b99ff8f 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -33,6 +33,8 @@
 bool MetadataBuilder::sABOverrideSet;
 bool MetadataBuilder::sABOverrideValue;
 
+static const std::string kDefaultGroup = "default";
+
 bool LinearExtent::AddTo(LpMetadata* out) const {
     if (device_index_ >= out->block_devices.size()) {
         LERROR << "Extent references unknown block device.";
@@ -403,7 +405,7 @@
     geometry_.metadata_slot_count = metadata_slot_count;
     geometry_.logical_block_size = logical_block_size;
 
-    if (!AddGroup("default", 0)) {
+    if (!AddGroup(kDefaultGroup, 0)) {
         return false;
     }
     return true;
@@ -419,7 +421,7 @@
 }
 
 Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
-    return AddPartition(name, "default", attributes);
+    return AddPartition(name, kDefaultGroup, attributes);
 }
 
 Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
@@ -675,6 +677,10 @@
 }
 
 std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+    if (!ValidatePartitionGroups()) {
+        return nullptr;
+    }
+
     std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
     metadata->header = header_;
     metadata->geometry = geometry_;
@@ -695,7 +701,7 @@
             LERROR << "Partition group name is too long: " << group->name();
             return nullptr;
         }
-        if (auto_slot_suffixing_ && group->name() != "default") {
+        if (auto_slot_suffixing_ && group->name() != kDefaultGroup) {
             out.flags |= LP_GROUP_SLOT_SUFFIXED;
         }
         strncpy(out.name, group->name().c_str(), sizeof(out.name));
@@ -877,7 +883,7 @@
 }
 
 void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
-    if (group_name == "default") {
+    if (group_name == kDefaultGroup) {
         // Cannot remove the default group.
         return;
     }
@@ -1000,5 +1006,53 @@
     return true;
 }
 
+std::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(const std::string& group_name) {
+    std::vector<Partition*> partitions;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() == group_name) {
+            partitions.emplace_back(partition.get());
+        }
+    }
+    return partitions;
+}
+
+bool MetadataBuilder::ChangePartitionGroup(Partition* partition, const std::string& group_name) {
+    if (!FindGroup(group_name)) {
+        LERROR << "Partition cannot change to unknown group: " << group_name;
+        return false;
+    }
+    partition->set_group_name(group_name);
+    return true;
+}
+
+bool MetadataBuilder::ValidatePartitionGroups() const {
+    for (const auto& group : groups_) {
+        if (!group->maximum_size()) {
+            continue;
+        }
+        uint64_t used = TotalSizeOfGroup(group.get());
+        if (used > group->maximum_size()) {
+            LERROR << "Partition group " << group->name() << " exceeds maximum size (" << used
+                   << " bytes used, maximum " << group->maximum_size() << ")";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool MetadataBuilder::ChangeGroupSize(const std::string& group_name, uint64_t maximum_size) {
+    if (group_name == kDefaultGroup) {
+        LERROR << "Cannot change the size of the default group";
+        return false;
+    }
+    PartitionGroup* group = FindGroup(group_name);
+    if (!group) {
+        LERROR << "Cannot change size of unknown partition group: " << group_name;
+        return false;
+    }
+    group->set_maximum_size(maximum_size);
+    return true;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 3793964..7d615a3 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -549,6 +549,58 @@
     EXPECT_EQ(partition->size(), 16384);
 }
 
+TEST_F(BuilderTest, ListPartitionsInGroup) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("groupA", 16384));
+    ASSERT_TRUE(builder->AddGroup("groupB", 16384));
+
+    Partition* system = builder->AddPartition("system", "groupA", 0);
+    Partition* vendor = builder->AddPartition("vendor", "groupA", 0);
+    Partition* product = builder->AddPartition("product", "groupB", 0);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    ASSERT_NE(product, nullptr);
+
+    auto groupA = builder->ListPartitionsInGroup("groupA");
+    auto groupB = builder->ListPartitionsInGroup("groupB");
+    auto groupC = builder->ListPartitionsInGroup("groupC");
+    ASSERT_THAT(groupA, ElementsAre(system, vendor));
+    ASSERT_THAT(groupB, ElementsAre(product));
+    ASSERT_TRUE(groupC.empty());
+}
+
+TEST_F(BuilderTest, ChangeGroups) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("groupA", 16384));
+    ASSERT_TRUE(builder->AddGroup("groupB", 32768));
+
+    Partition* system = builder->AddPartition("system", "groupA", 0);
+    Partition* vendor = builder->AddPartition("vendor", "groupB", 0);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    ASSERT_NE(builder->Export(), nullptr);
+
+    ASSERT_FALSE(builder->ChangePartitionGroup(system, "groupXYZ"));
+    ASSERT_TRUE(builder->ChangePartitionGroup(system, "groupB"));
+    ASSERT_NE(builder->Export(), nullptr);
+
+    // Violate group constraint by reassigning groups.
+    ASSERT_TRUE(builder->ResizePartition(system, 16384 + 4096));
+    ASSERT_TRUE(builder->ChangePartitionGroup(system, "groupA"));
+    ASSERT_EQ(builder->Export(), nullptr);
+
+    ASSERT_FALSE(builder->ChangeGroupSize("default", 2));
+    ASSERT_FALSE(builder->ChangeGroupSize("unknown", 2));
+    ASSERT_TRUE(builder->ChangeGroupSize("groupA", 32768));
+    ASSERT_NE(builder->Export(), nullptr);
+}
+
 constexpr unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
     return x << 30;
 }
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 57cce21..53f480f 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -80,6 +80,8 @@
 };
 
 class PartitionGroup final {
+    friend class MetadataBuilder;
+
   public:
     explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
         : name_(name), maximum_size_(maximum_size) {}
@@ -88,6 +90,8 @@
     uint64_t maximum_size() const { return maximum_size_; }
 
   private:
+    void set_maximum_size(uint64_t maximum_size) { maximum_size_ = maximum_size; }
+
     std::string name_;
     uint64_t maximum_size_;
 };
@@ -116,6 +120,7 @@
 
   private:
     void ShrinkTo(uint64_t aligned_size);
+    void set_group_name(const std::string& group_name) { group_name_ = group_name; }
 
     std::string name_;
     std::string group_name_;
@@ -235,6 +240,21 @@
     // underlying filesystem or contents of the partition on disk.
     bool ResizePartition(Partition* partition, uint64_t requested_size);
 
+    // Return the list of partitions belonging to a group.
+    std::vector<Partition*> ListPartitionsInGroup(const std::string& group_name);
+
+    // Changes a partition's group. Size constraints will not be checked until
+    // the metadata is exported, to avoid errors during potential group and
+    // size shuffling operations. This will return false if the new group does
+    // not exist.
+    bool ChangePartitionGroup(Partition* partition, const std::string& group_name);
+
+    // Changes the size of a partition group. Size constraints will not be
+    // checked until metadata is exported, to avoid errors during group
+    // reshuffling. This will return false if the group does not exist, or if
+    // the group name is "default".
+    bool ChangeGroupSize(const std::string& group_name, uint64_t maximum_size);
+
     // Amount of space that can be allocated to logical partitions.
     uint64_t AllocatableSpace() const;
     uint64_t UsedSpace() const;
@@ -283,6 +303,7 @@
     bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
     bool IsABDevice() const;
     bool IsRetrofitDevice() const;
+    bool ValidatePartitionGroups() const;
 
     struct Interval {
         uint32_t device_index;
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 9ccabe9..ecf94a4 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -19,6 +19,11 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#if defined(__linux__)
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#endif
+
 #include <android-base/file.h>
 #include <ext4_utils/ext4_utils.h>
 #include <openssl/sha.h>
@@ -155,5 +160,16 @@
     return true;
 }
 
+bool SetBlockReadonly(int fd, bool readonly) {
+#if defined(__linux__)
+    int val = readonly;
+    return ioctl(fd, BLKROSET, &val) == 0;
+#else
+    (void)fd;
+    (void)readonly;
+    return true;
+#endif
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 8b70919..e8b2ca9 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -29,6 +29,7 @@
 #define LWARN LOG(WARNING) << LP_TAG
 #define LINFO LOG(INFO) << LP_TAG
 #define LERROR LOG(ERROR) << LP_TAG
+#define PWARNING PLOG(WARNING) << LP_TAG
 #define PERROR PLOG(ERROR) << LP_TAG
 
 namespace android {
@@ -88,6 +89,9 @@
 bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
 bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
 
+// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
+bool SetBlockReadonly(int fd, bool readonly);
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 454258b..54a1883 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -259,6 +259,12 @@
         return false;
     }
 
+    // On retrofit devices, super_partition is system_other and might be set to readonly by
+    // fs_mgr_set_blk_ro(). Unset readonly so that fd can be written to.
+    if (!SetBlockReadonly(fd.get(), false)) {
+        PWARNING << __PRETTY_FUNCTION__ << " BLKROSET 0 failed: " << super_partition;
+    }
+
     // Write zeroes to the first block.
     std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
     if (SeekFile64(fd, 0, SEEK_SET) < 0) {
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index a1519da..06c8176 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -26,6 +26,8 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
+
+#include <algorithm>
 #include <memory>
 
 #include <android-base/file.h>
@@ -476,10 +478,16 @@
 
         while ((entry = readdir(dir.get()))) {
             const char* name = entry->d_name;
+            std::vector<String8>::iterator itIgnoreName;
 
             if (!strcmp(name, ".") || !strcmp(name, ".."))
                 continue;
 
+            itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
+                                hc->ignorePowerSupplyNames.end(), String8(name));
+            if (itIgnoreName != hc->ignorePowerSupplyNames.end())
+                continue;
+
             // Look for "type" file in each subdirectory
             path.clear();
             path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index c01e8d7..a900071 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -22,6 +22,8 @@
 #include <utils/Errors.h>
 #include <utils/String8.h>
 
+#include <vector>
+
 // periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
 // which healthd wakes up to poll health state and perform periodic chores,
 // in units of seconds:
@@ -71,6 +73,7 @@
     int (*energyCounter)(int64_t *);
     int boot_min_cap;
     bool (*screen_on)(android::BatteryProperties *props);
+    std::vector<android::String8> ignorePowerSupplyNames;
 };
 
 enum EventWakeup {
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7fd4e27..4a66e46 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -100,6 +100,9 @@
 }
 
 static Result<Success> do_class_start(const BuiltinArguments& args) {
+    // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
+    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+        return Success();
     // Starting a class does not start services which are explicitly disabled.
     // They must  be started individually.
     for (const auto& service : ServiceList::GetInstance()) {
@@ -124,6 +127,9 @@
 }
 
 static Result<Success> do_class_restart(const BuiltinArguments& args) {
+    // Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
+    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+        return Success();
     ForEachServiceInClass(args[1], &Service::Restart);
     return Success();
 }
@@ -1087,6 +1093,86 @@
     }
 }
 
+static Result<Success> bind_mount_file(const char* source, const char* mount_point,
+                                       bool remount_private) {
+    if (remount_private && mount(nullptr, mount_point, nullptr, MS_PRIVATE, nullptr) == -1) {
+        return ErrnoError() << "Could not change " << mount_point << " to a private mount point";
+    }
+    if (mount(source, mount_point, nullptr, MS_BIND, nullptr) == -1) {
+        return ErrnoError() << "Could not bind-mount " << source << " to " << mount_point;
+    }
+    return Success();
+}
+
+static Result<Success> bind_mount_bionic(const char* linker_source, const char* lib_dir_source,
+                                         const char* linker_mount_point, const char* lib_mount_dir,
+                                         bool remount_private) {
+    if (access(linker_source, F_OK) != 0) {
+        return Success();
+    }
+    if (auto result = bind_mount_file(linker_source, linker_mount_point, remount_private);
+        !result) {
+        return result;
+    }
+    for (auto libname : kBionicLibFileNames) {
+        std::string mount_point = lib_mount_dir + libname;
+        std::string source = lib_dir_source + libname;
+        if (auto result = bind_mount_file(source.c_str(), mount_point.c_str(), remount_private);
+            !result) {
+            return result;
+        }
+    }
+    return Success();
+}
+
+// The bootstrap bionic libs and the bootstrap linker are bind-mounted to
+// the mount points for pre-apexd processes.
+static Result<Success> do_prepare_bootstrap_bionic(const BuiltinArguments& args) {
+    static bool prepare_bootstrap_bionic_done = false;
+    if (prepare_bootstrap_bionic_done) {
+        return Error() << "prepare_bootstrap_bionic was already executed. Cannot be executed again";
+    }
+    if (auto result = bind_mount_bionic(kBootstrapLinkerPath, kBootstrapBionicLibsDir,
+                                        kLinkerMountPoint, kBionicLibsMountPointDir, false);
+        !result) {
+        return result;
+    }
+    if (auto result = bind_mount_bionic(kBootstrapLinkerPath64, kBootstrapBionicLibsDir64,
+                                        kLinkerMountPoint64, kBionicLibsMountPointDir64, false);
+        !result) {
+        return result;
+    }
+
+    LOG(INFO) << "prepare_bootstrap_bionic done";
+    prepare_bootstrap_bionic_done = true;
+    return Success();
+}
+
+// The bionic libs and the dynamic linker from the runtime APEX are bind-mounted
+// to the mount points. As a result, the previous mounts done by
+// prepare_bootstrap_bionic become hidden.
+static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
+    static bool setup_runtime_bionic_done = false;
+    if (setup_runtime_bionic_done) {
+        return Error() << "setup_runtime_bionic was already executed. Cannot be executed again";
+    }
+    if (auto result = bind_mount_bionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir,
+                                        kLinkerMountPoint, kBionicLibsMountPointDir, true);
+        !result) {
+        return result;
+    }
+    if (auto result = bind_mount_bionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64,
+                                        kLinkerMountPoint64, kBionicLibsMountPointDir64, true);
+        !result) {
+        return result;
+    }
+
+    ServiceList::GetInstance().MarkRuntimeAvailable();
+    LOG(INFO) << "setup_runtime_bionic done";
+    setup_runtime_bionic_done = true;
+    return Success();
+}
+
 // Builtin-function-map start
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1125,6 +1211,7 @@
         {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
         {"mount",                   {3,     kMax, {false,  do_mount}}},
         {"parse_apex_configs",      {0,     0,    {false,  do_parse_apex_configs}}},
+        {"prepare_bootstrap_bionic",{0,     0,    {false,  do_prepare_bootstrap_bionic}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
         {"restart",                 {1,     1,    {false,  do_restart}}},
@@ -1133,6 +1220,7 @@
         {"rm",                      {1,     1,    {true,   do_rm}}},
         {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
         {"setprop",                 {2,     2,    {true,   do_setprop}}},
+        {"setup_runtime_bionic",    {0,     0,    {false,  do_setup_runtime_bionic}}},
         {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
         {"start",                   {1,     1,    {false,  do_start}}},
         {"stop",                    {1,     1,    {false,  do_stop}}},
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index b5ff658..71fe401 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -414,16 +414,32 @@
         return entry.mount_point == "/system";
     });
 
-    if (system_partition != fstab_.end()) {
-        if (!MountPartition(&(*system_partition))) {
-            return false;
+    if (system_partition == fstab_.end()) return true;
+
+    bool mounted = false;
+    bool no_fail = false;
+    for (auto it = system_partition; it != fstab_.end();) {
+        if (it->mount_point != "/system") {
+            break;
         }
-
-        SwitchRoot((*system_partition).mount_point);
-
-        fstab_.erase(system_partition);
+        no_fail |= (it->fs_mgr_flags).no_fail;
+        if (MountPartition(&(*it))) {
+            mounted = true;
+            SwitchRoot("/system");
+            break;
+        }
+        it++;
     }
 
+    if (!mounted && !no_fail) {
+        LOG(ERROR) << "Failed to mount /system";
+        return false;
+    }
+
+    auto it = std::remove_if(fstab_.begin(), fstab_.end(),
+                             [](const auto& entry) { return entry.mount_point == "/system"; });
+    fstab_.erase(it, fstab_.end());
+
     return true;
 }
 
@@ -444,14 +460,12 @@
         if (skip_mount_point.empty()) {
             continue;
         }
-        auto removing_entry =
-                std::find_if(fstab_.begin(), fstab_.end(), [&skip_mount_point](const auto& entry) {
-                    return entry.mount_point == skip_mount_point;
-                });
-        if (removing_entry != fstab_.end()) {
-            fstab_.erase(removing_entry);
-            LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
-        }
+        auto it = std::remove_if(fstab_.begin(), fstab_.end(),
+                                 [&skip_mount_point](const auto& entry) {
+                                     return entry.mount_point == skip_mount_point;
+                                 });
+        fstab_.erase(it, fstab_.end());
+        LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
     }
 
     return true;
@@ -462,8 +476,21 @@
 
     if (!TrySkipMountingPartitions()) return false;
 
-    for (auto& fstab_entry : fstab_) {
-        if (!MountPartition(&fstab_entry) && !fstab_entry.fs_mgr_flags.no_fail) {
+    for (auto it = fstab_.begin(); it != fstab_.end();) {
+        bool mounted = false;
+        bool no_fail = false;
+        auto start_mount_point = it->mount_point;
+        do {
+            no_fail |= (it->fs_mgr_flags).no_fail;
+            if (!mounted)
+                mounted = MountPartition(&(*it));
+            else
+                LOG(INFO) << "Skip already-mounted partition: " << start_mount_point;
+            it++;
+        } while (it != fstab_.end() && it->mount_point == start_mount_point);
+
+        if (!mounted && !no_fail) {
+            LOG(ERROR) << start_mount_point << " mounted unsuccessfully but it is required!";
             return false;
         }
     }
diff --git a/init/service.cpp b/init/service.cpp
index 272809f..eec55c3 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -140,6 +140,43 @@
     return Success();
 }
 
+Result<Success> Service::SetUpPreApexdMounts() const {
+    // If a pre-apexd service is 're' launched after the runtime APEX is
+    // available, unmount the linker and bionic libs which are currently
+    // bind mounted to the files in the runtime APEX. This will reveal
+    // the hidden mount points (targetting the bootstrap ones in the
+    // system partition) which were setup before the runtime APEX was
+    // started. Note that these unmounts are done in a separate mount namespace
+    // for the process. It does not affect other processes including the init.
+    if (pre_apexd_ && ServiceList::GetInstance().IsRuntimeAvailable()) {
+        if (access(kLinkerMountPoint, F_OK) == 0) {
+            if (umount(kLinkerMountPoint) == -1) {
+                return ErrnoError() << "Could not umount " << kLinkerMountPoint;
+            }
+            for (const auto& libname : kBionicLibFileNames) {
+                std::string mount_point = kBionicLibsMountPointDir + libname;
+                if (umount(mount_point.c_str()) == -1) {
+                    return ErrnoError() << "Could not umount " << mount_point;
+                }
+            }
+        }
+
+        if (access(kLinkerMountPoint64, F_OK) == 0) {
+            if (umount(kLinkerMountPoint64) == -1) {
+                return ErrnoError() << "Could not umount " << kLinkerMountPoint64;
+            }
+            for (const auto& libname : kBionicLibFileNames) {
+                std::string mount_point = kBionicLibsMountPointDir64 + libname;
+                std::string source = kBootstrapBionicLibsDir64 + libname;
+                if (umount(mount_point.c_str()) == -1) {
+                    return ErrnoError() << "Could not umount " << mount_point;
+                }
+            }
+        }
+    }
+    return Success();
+}
+
 Result<Success> Service::SetUpPidNamespace() const {
     if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
         return ErrnoError() << "Could not set name";
@@ -929,6 +966,14 @@
         scon = *result;
     }
 
+    if (!ServiceList::GetInstance().IsRuntimeAvailable() && !pre_apexd_) {
+        // If this service is started before the runtime APEX gets available,
+        // mark it as pre-apexd one. Note that this marking is permanent. So
+        // for example, if the service is re-launched (e.g., due to crash),
+        // it is still recognized as pre-apexd... for consistency.
+        pre_apexd_ = true;
+    }
+
     LOG(INFO) << "starting service '" << name_ << "'...";
 
     pid_t pid = -1;
@@ -945,6 +990,37 @@
             LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
         }
 
+        // b/122559956: mount namespace is not cloned for the devices that don't support
+        // the update of bionic libraries via APEX. In that case, because the bionic
+        // libraries in the runtime APEX and the bootstrap bionic libraries are
+        // identical, it doesn't matter which libs are used. This is also to avoid the
+        // bug in sdcardfs which is triggered when we have multiple mount namespaces
+        // across vold and the others. BIONIC_UPDATABLE shall be true only for the
+        // devices where kernel has the fix for the sdcardfs bug (see the commit message
+        // for the fix).
+        static bool bionic_updatable =
+                android::base::GetBoolProperty("ro.apex.bionic_updatable", false);
+
+        if (bionic_updatable && pre_apexd_) {
+            // pre-apexd process gets a private copy of the mount namespace.
+            // However, this does not mean that mount/unmount events are not
+            // shared across pre-apexd processes and post-apexd processes.
+            // *Most* of the events are still shared because the propagation
+            // type of / is set to 'shared'. (see `mount rootfs rootfs /shared
+            // rec` in init.rc)
+            //
+            // This unsharing is required to not propagate the mount events
+            // under /system/lib/{libc|libdl|libm}.so and /system/bin/linker(64)
+            // whose propagation type is set to private. With this,
+            // bind-mounting the bionic libs and the dynamic linker from the
+            // runtime APEX to the mount points does not affect pre-apexd
+            // processes which should use the bootstrap ones.
+            if (unshare(CLONE_NEWNS) != 0) {
+                LOG(FATAL) << "Creating a new mount namespace for service"
+                           << " '" << name_ << "' failed: " << strerror(errno);
+            }
+        }
+
         if (namespace_flags_ & CLONE_NEWNS) {
             if (auto result = SetUpMountNamespace(); !result) {
                 LOG(FATAL) << "Service '" << name_
@@ -952,6 +1028,14 @@
             }
         }
 
+        // b/122559956: same as above
+        if (bionic_updatable && pre_apexd_ && ServiceList::GetInstance().IsRuntimeAvailable()) {
+            if (auto result = SetUpPreApexdMounts(); !result) {
+                LOG(FATAL) << "Pre-apexd service '" << name_
+                           << "' could not setup the mount points: " << result.error();
+            }
+        }
+
         if (namespace_flags_ & CLONE_NEWPID) {
             // This will fork again to run an init process inside the PID
             // namespace.
@@ -1324,6 +1408,10 @@
     delayed_service_names_.clear();
 }
 
+void ServiceList::MarkRuntimeAvailable() {
+    runtime_available_ = true;
+}
+
 void ServiceList::DelayService(const Service& service) {
     if (services_update_finished_) {
         LOG(ERROR) << "Cannot delay the start of service '" << service.name()
diff --git a/init/service.h b/init/service.h
index 56e75b0..676111f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -62,6 +62,24 @@
 namespace android {
 namespace init {
 
+static constexpr const char* kLinkerMountPoint = "/system/bin/linker";
+static constexpr const char* kBootstrapLinkerPath = "/system/bin/linker";
+static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker";
+
+static constexpr const char* kBionicLibsMountPointDir = "/system/lib/";
+static constexpr const char* kBootstrapBionicLibsDir = "/system/lib/";
+static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/";
+
+static constexpr const char* kLinkerMountPoint64 = "/system/bin/linker64";
+static constexpr const char* kBootstrapLinkerPath64 = "/system/bin/linker64";
+static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64";
+
+static constexpr const char* kBionicLibsMountPointDir64 = "/system/lib64/";
+static constexpr const char* kBootstrapBionicLibsDir64 = "/system/lib64/";
+static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/";
+
+static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"};
+
 class Service {
   public:
     Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
@@ -124,6 +142,7 @@
     std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
     const std::vector<std::string>& args() const { return args_; }
     bool is_updatable() const { return updatable_; }
+    bool is_pre_apexd() const { return pre_apexd_; }
 
   private:
     using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
@@ -132,6 +151,7 @@
     Result<Success> SetUpMountNamespace() const;
     Result<Success> SetUpPidNamespace() const;
     Result<Success> EnterNamespaces() const;
+    Result<Success> SetUpPreApexdMounts() const;
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void ZapStdio() const;
@@ -242,6 +262,8 @@
     std::vector<std::string> args_;
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
+
+    bool pre_apexd_ = false;
 };
 
 class ServiceList {
@@ -284,13 +306,16 @@
     const std::vector<Service*> services_in_shutdown_order() const;
 
     void MarkServicesUpdate();
+    void MarkRuntimeAvailable();
     bool IsServicesUpdated() const { return services_update_finished_; }
+    bool IsRuntimeAvailable() const { return runtime_available_; }
     void DelayService(const Service& service);
 
   private:
     std::vector<std::unique_ptr<Service>> services_;
 
     bool services_update_finished_ = false;
+    bool runtime_available_ = false;
     std::vector<std::string> delayed_service_names_;
 };
 
diff --git a/janitors/OWNERS b/janitors/OWNERS
new file mode 100644
index 0000000..0610b41
--- /dev/null
+++ b/janitors/OWNERS
@@ -0,0 +1,4 @@
+# OWNERS file for projects that don't really have owners so much as volunteer janitors.
+enh@google.com
+hhb@google.com
+narayan@google.com
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 4a165a0..619a94b 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -17,7 +17,6 @@
 liblog_sources = [
     "config_read.c",
     "config_write.c",
-    "local_logger.c",
     "log_event_list.c",
     "log_event_write.c",
     "log_ratelimit.cpp",
diff --git a/liblog/README.md b/liblog/README.md
index 886fe25..98bee9f 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -145,10 +145,9 @@
 these cases.
 
 `android_set_log_transport()` selects transport filters.  Argument is either `LOGGER_DEFAULT`,
-`LOGGER_LOGD`, `LOGGER_NULL` or `LOGGER_LOCAL`. Log to logger daemon for default or logd, drop
-contents on floor, or log into local memory respectively.  `Both android_set_log_transport()` and
-`android_get_log_transport()` return the current transport mask, or a negative errno for any
-problems.
+`LOGGER_LOGD`, or `LOGGER_NULL`. Log to logger daemon for default or logd, or drop contents on floor
+respectively.  `Both android_set_log_transport()` and `android_get_log_transport()` return the
+current transport mask, or a negative errno for any problems.
 
 Errors
 ------
diff --git a/liblog/config_read.c b/liblog/config_read.c
index ca80c80..51ffff6 100644
--- a/liblog/config_read.c
+++ b/liblog/config_read.c
@@ -55,12 +55,6 @@
 }
 
 LIBLOG_HIDDEN void __android_log_config_read() {
-  if (__android_log_transport & LOGGER_LOCAL) {
-    extern struct android_log_transport_read localLoggerRead;
-
-    __android_log_add_transport(&__android_log_transport_read, &localLoggerRead);
-  }
-
 #if (FAKE_LOG_DEVICE == 0)
   if ((__android_log_transport == LOGGER_DEFAULT) ||
       (__android_log_transport & LOGGER_LOGD)) {
diff --git a/liblog/config_write.c b/liblog/config_write.c
index 0a8b52f..003ec8f 100644
--- a/liblog/config_write.c
+++ b/liblog/config_write.c
@@ -55,13 +55,6 @@
 }
 
 LIBLOG_HIDDEN void __android_log_config_write() {
-  if (__android_log_transport & LOGGER_LOCAL) {
-    extern struct android_log_transport_write localLoggerWrite;
-
-    __android_log_add_transport(&__android_log_transport_write,
-                                &localLoggerWrite);
-  }
-
   if ((__android_log_transport == LOGGER_DEFAULT) ||
       (__android_log_transport & LOGGER_LOGD)) {
 #if (FAKE_LOG_DEVICE == 0)
diff --git a/liblog/include/log/log_transport.h b/liblog/include/log/log_transport.h
index 80b30db..8b02995 100644
--- a/liblog/include/log/log_transport.h
+++ b/liblog/include/log/log_transport.h
@@ -22,7 +22,7 @@
 #define LOGGER_LOGD    0x01
 #define LOGGER_KERNEL  0x02 /* Reserved/Deprecated */
 #define LOGGER_NULL    0x04 /* Does not release resources of other selections */
-#define LOGGER_LOCAL   0x08 /* logs sent to local memory */
+#define LOGGER_RESERVED 0x08 /* Reserved, previously for logging to local memory */
 #define LOGGER_STDERR  0x10 /* logs sent to stderr */
 /* clang-format on */
 
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
deleted file mode 100644
index 563cb3f..0000000
--- a/liblog/local_logger.c
+++ /dev/null
@@ -1,550 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#if !defined(__MINGW32__)
-#include <pwd.h>
-#endif
-#include <log/uio.h>
-#include <sched.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <cutils/list.h> /* template, no library dependency */
-#include <log/log_transport.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-#include <system/thread_defs.h>
-
-#include "config_read.h"
-#include "config_write.h"
-#include "log_portability.h"
-#include "logger.h"
-
-static const char baseServiceName[] = "android.logd";
-
-static int writeToLocalInit();
-static int writeToLocalAvailable(log_id_t logId);
-static void writeToLocalReset();
-static int writeToLocalWrite(log_id_t logId, struct timespec* ts,
-                             struct iovec* vec, size_t nr);
-
-LIBLOG_HIDDEN struct android_log_transport_write localLoggerWrite = {
-  .node = { &localLoggerWrite.node, &localLoggerWrite.node },
-  .context.priv = NULL,
-  .name = "local",
-  .available = writeToLocalAvailable,
-  .open = writeToLocalInit,
-  .close = writeToLocalReset,
-  .write = writeToLocalWrite,
-};
-
-static int writeToLocalVersion(struct android_log_logger* logger,
-                               struct android_log_transport_context* transp);
-static int writeToLocalRead(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp,
-                            struct log_msg* log_msg);
-static int writeToLocalPoll(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp);
-static void writeToLocalClose(struct android_log_logger_list* logger_list,
-                              struct android_log_transport_context* transp);
-static int writeToLocalClear(struct android_log_logger* logger,
-                             struct android_log_transport_context* transp);
-static ssize_t writeToLocalGetSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp);
-static ssize_t writeToLocalSetSize(
-    struct android_log_logger* logger,
-    struct android_log_transport_context* transp __unused, size_t size);
-static ssize_t writeToLocalGetReadbleSize(
-    struct android_log_logger* logger,
-    struct android_log_transport_context* transp);
-
-struct android_log_transport_read localLoggerRead = {
-  .node = { &localLoggerRead.node, &localLoggerRead.node },
-  .name = "local",
-  .available = writeToLocalAvailable,
-  .version = writeToLocalVersion,
-  .read = writeToLocalRead,
-  .poll = writeToLocalPoll,
-  .close = writeToLocalClose,
-  .clear = writeToLocalClear,
-  .getSize = writeToLocalGetSize,
-  .setSize = writeToLocalSetSize,
-  .getReadableSize = writeToLocalGetReadbleSize,
-  .getPrune = NULL,
-  .setPrune = NULL,
-  .getStats = NULL,
-};
-
-struct LogBufferElement {
-  struct listnode node;
-  log_id_t logId;
-  pid_t tid;
-  log_time timestamp;
-  unsigned short len;
-  char msg[];
-};
-
-static const size_t MAX_SIZE_DEFAULT = 32768;
-
-/*
- * Number of log buffers we support with the following assumption:
- *  . . .
- *   LOG_ID_SECURITY = 5, // security logs go to the system logs only
- *   LOG_ID_KERNEL = 6,   // place last, third-parties can not use it
- *   LOG_ID_MAX
- * } log_id_t;
- *
- * Confirm the following should <log/log_id.h> be adjusted in the future.
- */
-#define NUMBER_OF_LOG_BUFFERS \
-  ((LOG_ID_SECURITY == (LOG_ID_MAX - 2)) ? LOG_ID_SECURITY : LOG_ID_KERNEL)
-#define BLOCK_LOG_BUFFERS(id) \
-  (((id) == LOG_ID_SECURITY) || ((id) == LOG_ID_KERNEL))
-
-static struct LogBuffer {
-  struct listnode head;
-  pthread_rwlock_t listLock;
-  char* serviceName; /* Also indicates ready by having a value */
-  /* Order and proximity important for memset */
-  size_t number[NUMBER_OF_LOG_BUFFERS];         /* clear memset          */
-  size_t size[NUMBER_OF_LOG_BUFFERS];           /* clear memset          */
-  size_t totalSize[NUMBER_OF_LOG_BUFFERS];      /* init memset           */
-  size_t maxSize[NUMBER_OF_LOG_BUFFERS];        /* init MAX_SIZE_DEFAULT */
-  struct listnode* last[NUMBER_OF_LOG_BUFFERS]; /* init &head            */
-} logbuf = {
-  .head = { &logbuf.head, &logbuf.head }, .listLock = PTHREAD_RWLOCK_INITIALIZER,
-};
-
-static void LogBufferInit(struct LogBuffer* log) {
-  size_t i;
-
-  pthread_rwlock_wrlock(&log->listLock);
-  list_init(&log->head);
-  memset(log->number, 0,
-         sizeof(log->number) + sizeof(log->size) + sizeof(log->totalSize));
-  for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
-    log->maxSize[i] = MAX_SIZE_DEFAULT;
-    log->last[i] = &log->head;
-  }
-#ifdef __BIONIC__
-  asprintf(&log->serviceName, "%s@%d:%d", baseServiceName, __android_log_uid(),
-           getpid());
-#else
-  char buffer[sizeof(baseServiceName) + 1 + 5 + 1 + 5 + 8];
-  snprintf(buffer, sizeof(buffer), "%s@%d:%d", baseServiceName,
-           __android_log_uid(), getpid());
-  log->serviceName = strdup(buffer);
-#endif
-  pthread_rwlock_unlock(&log->listLock);
-}
-
-static void LogBufferClear(struct LogBuffer* log) {
-  size_t i;
-  struct listnode* node;
-
-  pthread_rwlock_wrlock(&log->listLock);
-  memset(log->number, 0, sizeof(log->number) + sizeof(log->size));
-  for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
-    log->last[i] = &log->head;
-  }
-  while ((node = list_head(&log->head)) != &log->head) {
-    struct LogBufferElement* element;
-
-    element = node_to_item(node, struct LogBufferElement, node);
-    list_remove(node);
-    free(element);
-  }
-  pthread_rwlock_unlock(&log->listLock);
-}
-
-static inline void LogBufferFree(struct LogBuffer* log) {
-  pthread_rwlock_wrlock(&log->listLock);
-  free(log->serviceName);
-  log->serviceName = NULL;
-  pthread_rwlock_unlock(&log->listLock);
-  LogBufferClear(log);
-}
-
-static int LogBufferLog(struct LogBuffer* log,
-                        struct LogBufferElement* element) {
-  log_id_t logId = element->logId;
-
-  pthread_rwlock_wrlock(&log->listLock);
-  log->number[logId]++;
-  log->size[logId] += element->len;
-  log->totalSize[logId] += element->len;
-  /* prune entry(s) until enough space is available */
-  if (log->last[logId] == &log->head) {
-    log->last[logId] = list_tail(&log->head);
-  }
-  while (log->size[logId] > log->maxSize[logId]) {
-    struct listnode* node = log->last[logId];
-    struct LogBufferElement* e;
-    struct android_log_logger_list* logger_list;
-
-    e = node_to_item(node, struct LogBufferElement, node);
-    log->number[logId]--;
-    log->size[logId] -= e->len;
-    logger_list_rdlock();
-    logger_list_for_each(logger_list) {
-      struct android_log_transport_context* transp;
-
-      transport_context_for_each(transp, logger_list) {
-        if ((transp->transport == &localLoggerRead) &&
-            (transp->context.node == node)) {
-          if (node == &log->head) {
-            transp->context.node = &log->head;
-          } else {
-            transp->context.node = node->next;
-          }
-        }
-      }
-    }
-    logger_list_unlock();
-    if (node != &log->head) {
-      log->last[logId] = node->prev;
-    }
-    list_remove(node);
-    LOG_ALWAYS_FATAL_IF(node == log->last[logId], "corrupted list");
-    free(e);
-  }
-  /* add entry to list */
-  list_add_head(&log->head, &element->node);
-  /* ToDo: wake up all readers */
-  pthread_rwlock_unlock(&log->listLock);
-
-  return element->len;
-}
-
-/*
- * return zero if permitted to log directly to logd,
- * return 1 if binder server started and
- * return negative error number if failed to start binder server.
- */
-static int writeToLocalInit() {
-  pthread_attr_t attr;
-  struct LogBuffer* log;
-
-  if (writeToLocalAvailable(LOG_ID_MAIN) < 0) {
-    return -EPERM;
-  }
-
-  log = &logbuf;
-  if (!log->serviceName) {
-    LogBufferInit(log);
-  }
-
-  if (!log->serviceName) {
-    LogBufferFree(log);
-    return -ENOMEM;
-  }
-
-  return EPERM; /* successful local-only logging */
-}
-
-static void writeToLocalReset() {
-  LogBufferFree(&logbuf);
-}
-
-static int writeToLocalAvailable(log_id_t logId) {
-#if !defined(__MINGW32__)
-  uid_t uid;
-#endif
-
-  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
-    return -EINVAL;
-  }
-
-/* Android hard coded permitted, system goes to logd */
-#if !defined(__MINGW32__)
-  if (__android_log_transport == LOGGER_DEFAULT) {
-    uid = __android_log_uid();
-    if ((uid < AID_APP) && (getpwuid(uid) != NULL)) {
-      return -EPERM;
-    }
-  }
-#endif
-
-  /* ToDo: Ask package manager for LOGD permissions */
-  /* Assume we do _not_ have permissions to go to LOGD, so must go local */
-  return 0;
-}
-
-static int writeToLocalWrite(log_id_t logId, struct timespec* ts,
-                             struct iovec* vec, size_t nr) {
-  size_t len, i;
-  struct LogBufferElement* element;
-
-  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
-    return -EINVAL;
-  }
-
-  len = 0;
-  for (i = 0; i < nr; ++i) {
-    len += vec[i].iov_len;
-  }
-
-  if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
-    len = LOGGER_ENTRY_MAX_PAYLOAD;
-  }
-  element = (struct LogBufferElement*)calloc(
-      1, sizeof(struct LogBufferElement) + len + 1);
-  if (!element) {
-    return errno ? -errno : -ENOMEM;
-  }
-  element->timestamp.tv_sec = ts->tv_sec;
-  element->timestamp.tv_nsec = ts->tv_nsec;
-#ifdef __BIONIC__
-  element->tid = gettid();
-#else
-  element->tid = getpid();
-#endif
-  element->logId = logId;
-  element->len = len;
-
-  char* cp = element->msg;
-  for (i = 0; i < nr; ++i) {
-    size_t iov_len = vec[i].iov_len;
-    if (iov_len > len) {
-      iov_len = len;
-    }
-    memcpy(cp, vec[i].iov_base, iov_len);
-    len -= iov_len;
-    if (len == 0) {
-      break;
-    }
-    cp += iov_len;
-  }
-
-  return LogBufferLog(&logbuf, element);
-}
-
-static int writeToLocalVersion(struct android_log_logger* logger __unused,
-                               struct android_log_transport_context* transp
-                                   __unused) {
-  return 3;
-}
-
-/* within reader lock, serviceName already validated */
-static struct listnode* writeToLocalNode(
-    struct android_log_logger_list* logger_list,
-    struct android_log_transport_context* transp) {
-  struct listnode* node;
-  unsigned logMask;
-  unsigned int tail;
-
-  node = transp->context.node;
-  if (node) {
-    return node;
-  }
-
-  if (!logger_list->tail) {
-    return transp->context.node = &logbuf.head;
-  }
-
-  logMask = transp->logMask;
-  tail = logger_list->tail;
-
-  for (node = list_head(&logbuf.head); node != &logbuf.head; node = node->next) {
-    struct LogBufferElement* element;
-    log_id_t logId;
-
-    element = node_to_item(node, struct LogBufferElement, node);
-    logId = element->logId;
-
-    if ((logMask & (1 << logId)) && !--tail) {
-      node = node->next;
-      break;
-    }
-  }
-  return transp->context.node = node;
-}
-
-static int writeToLocalRead(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp,
-                            struct log_msg* log_msg) {
-  int ret;
-  struct listnode* node;
-  unsigned logMask;
-
-  pthread_rwlock_rdlock(&logbuf.listLock);
-  if (!logbuf.serviceName) {
-    pthread_rwlock_unlock(&logbuf.listLock);
-    return (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
-  }
-
-  logMask = transp->logMask;
-
-  node = writeToLocalNode(logger_list, transp);
-
-  ret = 0;
-
-  while (node != list_head(&logbuf.head)) {
-    struct LogBufferElement* element;
-    log_id_t logId;
-
-    node = node->prev;
-    element = node_to_item(node, struct LogBufferElement, node);
-    logId = element->logId;
-
-    if (logMask & (1 << logId)) {
-      ret = log_msg->entry_v3.len = element->len;
-      log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3);
-      log_msg->entry_v3.pid = getpid();
-      log_msg->entry_v3.tid = element->tid;
-      log_msg->entry_v3.sec = element->timestamp.tv_sec;
-      log_msg->entry_v3.nsec = element->timestamp.tv_nsec;
-      log_msg->entry_v3.lid = logId;
-      memcpy(log_msg->entry_v3.msg, element->msg, ret);
-      ret += log_msg->entry_v3.hdr_size;
-      break;
-    }
-  }
-
-  transp->context.node = node;
-
-  /* ToDo: if blocking, and no entry, put reader to sleep */
-  pthread_rwlock_unlock(&logbuf.listLock);
-  return ret;
-}
-
-static int writeToLocalPoll(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp) {
-  int ret = (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
-
-  pthread_rwlock_rdlock(&logbuf.listLock);
-
-  if (logbuf.serviceName) {
-    unsigned logMask = transp->logMask;
-    struct listnode* node = writeToLocalNode(logger_list, transp);
-
-    ret = (node != list_head(&logbuf.head));
-    if (ret) {
-      do {
-        ret = !!(logMask &
-                 (1 << (node_to_item(node->prev, struct LogBufferElement, node))
-                           ->logId));
-      } while (!ret && ((node = node->prev) != list_head(&logbuf.head)));
-    }
-
-    transp->context.node = node;
-  }
-
-  pthread_rwlock_unlock(&logbuf.listLock);
-
-  return ret;
-}
-
-static void writeToLocalClose(struct android_log_logger_list* logger_list
-                                  __unused,
-                              struct android_log_transport_context* transp) {
-  pthread_rwlock_wrlock(&logbuf.listLock);
-  transp->context.node = list_head(&logbuf.head);
-  pthread_rwlock_unlock(&logbuf.listLock);
-}
-
-static int writeToLocalClear(struct android_log_logger* logger,
-                             struct android_log_transport_context* unused
-                                 __unused) {
-  log_id_t logId = logger->logId;
-  struct listnode *node, *n;
-
-  if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
-    return -EINVAL;
-  }
-
-  pthread_rwlock_wrlock(&logbuf.listLock);
-  logbuf.number[logId] = 0;
-  logbuf.last[logId] = &logbuf.head;
-  list_for_each_safe(node, n, &logbuf.head) {
-    struct LogBufferElement* element;
-    element = node_to_item(node, struct LogBufferElement, node);
-
-    if (logId == element->logId) {
-      struct android_log_logger_list* logger_list;
-
-      logger_list_rdlock();
-      logger_list_for_each(logger_list) {
-        struct android_log_transport_context* transp;
-
-        transport_context_for_each(transp, logger_list) {
-          if ((transp->transport == &localLoggerRead) &&
-              (transp->context.node == node)) {
-            transp->context.node = node->next;
-          }
-        }
-      }
-      logger_list_unlock();
-      list_remove(node);
-      free(element);
-    }
-  }
-
-  pthread_rwlock_unlock(&logbuf.listLock);
-
-  return 0;
-}
-
-static ssize_t writeToLocalGetSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp
-                                       __unused) {
-  ssize_t ret = -EINVAL;
-  log_id_t logId = logger->logId;
-
-  if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
-    pthread_rwlock_rdlock(&logbuf.listLock);
-    ret = logbuf.maxSize[logId];
-    pthread_rwlock_unlock(&logbuf.listLock);
-  }
-
-  return ret;
-}
-
-static ssize_t writeToLocalSetSize(
-    struct android_log_logger* logger,
-    struct android_log_transport_context* transp __unused, size_t size) {
-  ssize_t ret = -EINVAL;
-
-  if ((size > LOGGER_ENTRY_MAX_LEN) || (size < (4 * 1024 * 1024))) {
-    log_id_t logId = logger->logId;
-    if ((logId < NUMBER_OF_LOG_BUFFERS) || !BLOCK_LOG_BUFFERS(logId)) {
-      pthread_rwlock_wrlock(&logbuf.listLock);
-      ret = logbuf.maxSize[logId] = size;
-      pthread_rwlock_unlock(&logbuf.listLock);
-    }
-  }
-
-  return ret;
-}
-
-static ssize_t writeToLocalGetReadbleSize(
-    struct android_log_logger* logger,
-    struct android_log_transport_context* transp __unused) {
-  ssize_t ret = -EINVAL;
-  log_id_t logId = logger->logId;
-
-  if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
-    pthread_rwlock_rdlock(&logbuf.listLock);
-    ret = logbuf.serviceName ? (ssize_t)logbuf.size[logId] : -EBADF;
-    pthread_rwlock_unlock(&logbuf.listLock);
-  }
-
-  return ret;
-}
diff --git a/liblog/logger.h b/liblog/logger.h
index 246b33c..af83228 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -98,7 +98,6 @@
 };
 
 struct android_log_logger_list {
-  struct listnode node;
   struct listnode logger;
   struct listnode transport;
   int mode;
@@ -144,37 +143,6 @@
        (logp) =                                                     \
            node_to_item((logp)->node.next, struct android_log_logger, node))
 
-/*
- *    Global list of log readers.
- *
- * Usage case: search out transport contexts for all readers
- */
-
-LIBLOG_HIDDEN struct listnode __android_log_readers;
-
-#if defined(_WIN32)
-#define logger_list_rdlock()
-#define logger_list_wrlock()
-#define logger_list_unlock()
-#else
-LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock;
-
-#define logger_list_rdlock() pthread_rwlock_rdlock(&__android_log_readers_lock)
-#define logger_list_wrlock() pthread_rwlock_wrlock(&__android_log_readers_lock)
-#define logger_list_unlock() pthread_rwlock_unlock(&__android_log_readers_lock)
-#endif
-
-/* Must be called with logger_list_rdlock() or logger_list_wrlock() held */
-#define logger_list_for_each(logger_list)                                     \
-  for ((logger_list) = node_to_item(&__android_log_readers,                   \
-                                    struct android_log_logger_list, node);    \
-       (logger_list) != node_to_item(&__android_log_readers,                  \
-                                     struct android_log_logger_list, node) && \
-       (logger_list) != node_to_item((logger_list)->node.next,                \
-                                     struct android_log_logger_list, node);   \
-       (logger_list) = node_to_item((logger_list)->node.next,                 \
-                                    struct android_log_logger_list, node))
-
 /* OS specific dribs and drabs */
 
 #if defined(_WIN32)
diff --git a/liblog/logger_read.c b/liblog/logger_read.c
index 0fd6efa..29ebaf7 100644
--- a/liblog/logger_read.c
+++ b/liblog/logger_read.c
@@ -213,13 +213,6 @@
   LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
 }
 
-LIBLOG_HIDDEN struct listnode __android_log_readers = { &__android_log_readers,
-                                                        &__android_log_readers };
-#if !defined(_WIN32)
-LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock =
-    PTHREAD_RWLOCK_INITIALIZER;
-#endif
-
 LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc(
     int mode, unsigned int tail, pid_t pid) {
   struct android_log_logger_list* logger_list;
@@ -235,10 +228,6 @@
   logger_list->tail = tail;
   logger_list->pid = pid;
 
-  logger_list_wrlock();
-  list_add_tail(&__android_log_readers, &logger_list->node);
-  logger_list_unlock();
-
   return (struct logger_list*)logger_list;
 }
 
@@ -257,10 +246,6 @@
   logger_list->start = start;
   logger_list->pid = pid;
 
-  logger_list_wrlock();
-  list_add_tail(&__android_log_readers, &logger_list->node);
-  logger_list_unlock();
-
   return (struct logger_list*)logger_list;
 }
 
@@ -472,10 +457,6 @@
     return;
   }
 
-  logger_list_wrlock();
-  list_remove(&logger_list_internal->node);
-  logger_list_unlock();
-
   while (!list_empty(&logger_list_internal->transport)) {
     struct listnode* node = list_head(&logger_list_internal->transport);
     struct android_log_transport_context* transp =
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index 2754e6e..6dcda9b 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -684,9 +684,9 @@
     return retval;
   }
 
-  __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
+  __android_log_transport &= LOGGER_LOGD | LOGGER_STDERR;
 
-  transport_flag &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
+  transport_flag &= LOGGER_LOGD | LOGGER_STDERR;
 
   if (__android_log_transport != transport_flag) {
     __android_log_transport = transport_flag;
@@ -714,7 +714,7 @@
   if (write_to_log == __write_to_log_null) {
     ret = LOGGER_NULL;
   } else {
-    __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
+    __android_log_transport &= LOGGER_LOGD | LOGGER_STDERR;
     ret = __android_log_transport;
     if ((write_to_log != __write_to_log_init) &&
         (write_to_log != __write_to_log_daemon)) {
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index e6a9c0c..2c47fd6 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -54,9 +54,7 @@
     srcs: [
         "libc_test.cpp",
         "liblog_test_default.cpp",
-        "liblog_test_local.cpp",
         "liblog_test_stderr.cpp",
-        "liblog_test_stderr_local.cpp",
         "log_id_test.cpp",
         "log_radio_test.cpp",
         "log_read_test.cpp",
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 383d0e7..2d0fc9b 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -52,22 +52,6 @@
 #endif
 #endif
 
-#if (!defined(USING_LOGGER_DEFAULT) || !defined(USING_LOGGER_LOCAL) || \
-     !defined(USING_LOGGER_STDERR))
-#ifdef liblog  // a binary clue that we are overriding the test names
-// Does not support log reading blocking feature yet
-// Does not support LOG_ID_SECURITY (unless we set LOGGER_LOCAL | LOGGER_LOGD)
-// Assume some common aspects are tested by USING_LOGGER_DEFAULT:
-// Does not need to _retest_ pmsg functionality
-// Does not need to _retest_ property handling as it is a higher function
-// Does not need to _retest_ event mapping functionality
-// Does not need to _retest_ ratelimit
-// Does not need to _retest_ logprint
-#define USING_LOGGER_LOCAL
-#else
-#define USING_LOGGER_DEFAULT
-#endif
-#endif
 #ifdef USING_LOGGER_STDERR
 #define SUPPORTS_END_TO_END 0
 #else
@@ -175,7 +159,7 @@
 #endif
 
 TEST(liblog, __android_log_btwrite__android_logger_list_read) {
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
 #ifdef TEST_PREFIX
   TEST_PREFIX
 #endif
@@ -269,7 +253,7 @@
 #endif
 }
 
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
 static void print_transport(const char* prefix, int logger) {
   static const char orstr[] = " | ";
 
@@ -297,16 +281,11 @@
     fprintf(stderr, "%sLOGGER_NULL", prefix);
     prefix = orstr;
   }
-  if (logger & LOGGER_LOCAL) {
-    fprintf(stderr, "%sLOGGER_LOCAL", prefix);
-    prefix = orstr;
-  }
   if (logger & LOGGER_STDERR) {
     fprintf(stderr, "%sLOGGER_STDERR", prefix);
     prefix = orstr;
   }
-  logger &= ~(LOGGER_LOGD | LOGGER_KERNEL | LOGGER_NULL | LOGGER_LOCAL |
-              LOGGER_STDERR);
+  logger &= ~(LOGGER_LOGD | LOGGER_KERNEL | LOGGER_NULL | LOGGER_STDERR);
   if (logger) {
     fprintf(stderr, "%s0x%x", prefix, logger);
     prefix = orstr;
@@ -321,7 +300,7 @@
 // and behind us, to make us whole.  We could incorporate a prefix and
 // suffix test to make this standalone, but opted to not complicate this.
 TEST(liblog, android_set_log_transport) {
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
 #ifdef TEST_PREFIX
   TEST_PREFIX
 #endif
@@ -632,7 +611,7 @@
   buf_write_test("\n Hello World \n");
 }
 
-#ifndef USING_LOGGER_LOCAL  // requires blocking reader functionality
+#ifdef USING_LOGGER_DEFAULT  // requires blocking reader functionality
 #ifdef TEST_PREFIX
 static unsigned signaled;
 static log_time signal_time;
@@ -944,7 +923,7 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // !USING_LOGGER_LOCAL
+#endif  // USING_LOGGER_DEFAULT
 
 #ifdef TEST_PREFIX
 static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
@@ -2417,7 +2396,7 @@
 }
 
 // Do not retest logger list handling
-#if (defined(TEST_PREFIX) || !defined(USING_LOGGER_LOCAL))
+#ifdef TEST_PREFIX
 static int is_real_element(int type) {
   return ((type == EVENT_TYPE_INT) || (type == EVENT_TYPE_LONG) ||
           (type == EVENT_TYPE_STRING) || (type == EVENT_TYPE_FLOAT));
@@ -2572,7 +2551,7 @@
 
   return 0;
 }
-#endif  // TEST_PREFIX || !USING_LOGGER_LOCAL
+#endif  // TEST_PREFIX
 
 #ifdef TEST_PREFIX
 static const char* event_test_int32(uint32_t tag, size_t& expected_len) {
diff --git a/liblog/tests/liblog_test_local.cpp b/liblog/tests/liblog_test_local.cpp
deleted file mode 100644
index 451beca..0000000
--- a/liblog/tests/liblog_test_local.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include <log/log_transport.h>
-#define liblog liblog_local
-#define TEST_LOGGER LOGGER_LOCAL
-#include "liblog_test.cpp"
diff --git a/liblog/tests/liblog_test_stderr_local.cpp b/liblog/tests/liblog_test_stderr_local.cpp
deleted file mode 100644
index bb5c7ae..0000000
--- a/liblog/tests/liblog_test_stderr_local.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include <log/log_transport.h>
-#define liblog liblog_stderr_local
-#define TEST_LOGGER (LOGGER_LOCAL | LOGGER_STDERR)
-#include "liblog_test.cpp"
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 92fd2a2..59cd033 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -17,6 +17,11 @@
         "liblog",
     ],
 
+    stubs: {
+        symbol_file: "libnativebridge.map.txt",
+        versions: ["1"],
+    },
+
     export_include_dirs: ["include"],
 
     cflags: [
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
index 28f1927..5aea967 100644
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -17,12 +17,17 @@
 #ifndef NATIVE_BRIDGE_H_
 #define NATIVE_BRIDGE_H_
 
-#include "jni.h"
 #include <signal.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <sys/types.h>
 
+#include "jni.h"
+
+#ifdef __cplusplus
 namespace android {
+extern "C" {
+#endif  // __cplusplus
 
 struct NativeBridgeRuntimeCallbacks;
 struct NativeBridgeRuntimeValues;
@@ -32,11 +37,10 @@
 // to the chain.
 typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*);
 
-
 // Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename
 // signals that we do not want to load a native bridge.
 bool LoadNativeBridge(const char* native_bridge_library_filename,
-                      const NativeBridgeRuntimeCallbacks* runtime_callbacks);
+                      const struct NativeBridgeRuntimeCallbacks* runtime_callbacks);
 
 // Quick check whether a native bridge will be needed. This is based off of the instruction set
 // of the process.
@@ -138,19 +142,17 @@
 //
 // Starting with v3, NativeBridge has two scenarios: with/without namespace.
 // Should not use in non-namespace scenario.
-native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
-                                                       const char* ld_library_path,
-                                                       const char* default_library_path,
-                                                       uint64_t type,
-                                                       const char* permitted_when_isolated_path,
-                                                       native_bridge_namespace_t* parent_ns);
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+    const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+    const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns);
 
 // Creates a link which shares some libraries from one namespace to another.
 // NativeBridge's peer of android_link_namespaces() of dynamic linker.
 //
 // Starting with v3, NativeBridge has two scenarios: with/without namespace.
 // Should not use in non-namespace scenario.
-bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+                                struct native_bridge_namespace_t* to,
                                 const char* shared_libs_sonames);
 
 // Load a shared library with namespace key that is supported by the native bridge.
@@ -159,10 +161,11 @@
 //
 // Starting with v3, NativeBridge has two scenarios: with/without namespace.
 // Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
-void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+                                 struct native_bridge_namespace_t* ns);
 
 // Returns vendor namespace if it is enabled for the device and null otherwise
-native_bridge_namespace_t* NativeBridgeGetVendorNamespace();
+struct native_bridge_namespace_t* NativeBridgeGetVendorNamespace();
 
 // Native bridge interfaces to runtime.
 struct NativeBridgeCallbacks {
@@ -177,8 +180,8 @@
   //   runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.
   // Returns:
   //   true if initialization was successful.
-  bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir,
-                     const char* instruction_set);
+  bool (*initialize)(const struct NativeBridgeRuntimeCallbacks* runtime_cbs,
+                     const char* private_dir, const char* instruction_set);
 
   // Load a shared library that is supported by the native bridge.
   //
@@ -314,12 +317,12 @@
   //
   // Starting with v3, NativeBridge has two scenarios: with/without namespace.
   // Should not use in non-namespace scenario.
-  native_bridge_namespace_t* (*createNamespace)(const char* name,
-                                                const char* ld_library_path,
-                                                const char* default_library_path,
-                                                uint64_t type,
-                                                const char* permitted_when_isolated_path,
-                                                native_bridge_namespace_t* parent_ns);
+  struct native_bridge_namespace_t* (*createNamespace)(const char* name,
+                                                       const char* ld_library_path,
+                                                       const char* default_library_path,
+                                                       uint64_t type,
+                                                       const char* permitted_when_isolated_path,
+                                                       struct native_bridge_namespace_t* parent_ns);
 
   // Creates a link which shares some libraries from one namespace to another.
   // NativeBridge's peer of android_link_namespaces() of dynamic linker.
@@ -334,8 +337,8 @@
   //
   // Starting with v3, NativeBridge has two scenarios: with/without namespace.
   // Should not use in non-namespace scenario.
-  bool (*linkNamespaces)(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
-                         const char* shared_libs_sonames);
+  bool (*linkNamespaces)(struct native_bridge_namespace_t* from,
+                         struct native_bridge_namespace_t* to, const char* shared_libs_sonames);
 
   // Load a shared library within a namespace.
   // NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
@@ -350,7 +353,7 @@
   //
   // Starting with v3, NativeBridge has two scenarios: with/without namespace.
   // Use loadLibrary instead in non-namespace scenario.
-  void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
+  void* (*loadLibraryExt)(const char* libpath, int flag, struct native_bridge_namespace_t* ns);
 
   // Get native bridge version of vendor namespace.
   // The vendor namespace is the namespace used to load vendor public libraries.
@@ -359,7 +362,7 @@
   //
   // Returns:
   //   vendor namespace or null if it was not set up for the device
-  native_bridge_namespace_t* (*getVendorNamespace)();
+  struct native_bridge_namespace_t* (*getVendorNamespace)();
 };
 
 // Runtime interfaces to native bridge.
@@ -396,6 +399,9 @@
                                uint32_t method_count);
 };
 
-};  // namespace android
+#ifdef __cplusplus
+}  // extern "C"
+}  // namespace android
+#endif  // __cplusplus
 
 #endif  // NATIVE_BRIDGE_H_
diff --git a/libnativebridge/libnativebridge.map.txt b/libnativebridge/libnativebridge.map.txt
new file mode 100644
index 0000000..a616b85
--- /dev/null
+++ b/libnativebridge/libnativebridge.map.txt
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# TODO(b/122710865): Most of these uses come from libnativeloader, which should be bundled
+# together with libnativebridge in the APEX. Once this happens, prune this list.
+LIBNATIVEBRIDGE_1 {
+  global:
+    NativeBridgeIsSupported;
+    NativeBridgeLoadLibrary;
+    NativeBridgeUnloadLibrary;
+    NativeBridgeGetError;
+    NativeBridgeIsPathSupported;
+    NativeBridgeCreateNamespace;
+    NativeBridgeGetVendorNamespace;
+    NativeBridgeLinkNamespaces;
+    NativeBridgeLoadLibraryExt;
+    NativeBridgeInitAnonymousNamespace;
+    NativeBridgeInitialized;
+    NativeBridgeGetTrampoline;
+    LoadNativeBridge;
+    PreInitializeNativeBridge;
+    InitializeNativeBridge;
+    NativeBridgeGetVersion;
+    NativeBridgeGetSignalHandler;
+    UnloadNativeBridge;
+    NativeBridgeAvailable;
+    NeedsNativeBridge;
+    NativeBridgeError;
+    NativeBridgeNameAcceptable;
+  local:
+    *;
+};
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index e24307a..a2d8d81 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -33,6 +33,13 @@
 
 namespace android {
 
+#ifdef __APPLE__
+template <typename T>
+void UNUSED(const T&) {}
+#endif
+
+extern "C" {
+
 // Environment values required by the apps running with native bridge.
 struct NativeBridgeRuntimeValues {
     const char* os_arch;
@@ -252,10 +259,6 @@
   return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
 }
 
-#ifdef __APPLE__
-template<typename T> void UNUSED(const T&) {}
-#endif
-
 bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) {
   if (state != NativeBridgeState::kOpened) {
     ALOGE("Invalid state: native bridge is expected to be opened.");
@@ -626,4 +629,6 @@
   return nullptr;
 }
 
-};  // namespace android
+}  // extern "C"
+
+}  // namespace android
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 222bc4c..744a4a8 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -86,3 +86,14 @@
     ],
     header_libs: ["libbase_headers"],
 }
+
+// Build the test for the C API.
+cc_test {
+    name: "libnativebridge-api-tests",
+    host_supported: true,
+    test_per_src: true,
+    srcs: [
+        "NativeBridgeApi.c",
+    ],
+    header_libs: ["libnativebridge-dummy-headers"],
+}
diff --git a/libnativebridge/tests/NativeBridgeApi.c b/libnativebridge/tests/NativeBridgeApi.c
new file mode 100644
index 0000000..7ab71fe
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeApi.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* The main purpose of this test is to ensure this C header compiles in C, so
+ * that no C++ features inadvertently leak into the C ABI. */
+#include "nativebridge/native_bridge.h"
+
+int main(int argc, char** argv) {
+  (void)argc;
+  (void)argv;
+  return 0;
+}
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
index d602c26..8bcc1e2 100644
--- a/libpackagelistparser/include/packagelistparser/packagelistparser.h
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -53,6 +53,7 @@
     char *seinfo;
     gid_list gids;
     void *private_data;
+    bool profileable_from_shell;
 };
 
 /**
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index 3e1a3d1..4ce2363 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -223,6 +223,23 @@
             }
         }
 
+        cur = strsep(&next, " \t\r\n");
+        if (cur) {
+            tmp = strtoul(cur, &endptr, 10);
+            if (*endptr != '\0') {
+                errmsg = "Could not convert field \"profileable_from_shell\" to integer value";
+                goto err;
+            }
+
+            /* should be a valid boolean of 1 or 0 */
+            if (!(tmp == 0 || tmp == 1)) {
+                errmsg = "Field \"profileable_from_shell\" is not 0 or 1 boolean value";
+                goto err;
+            }
+
+            pkg_info->profileable_from_shell = (bool)tmp;
+        }
+
         rc = callback(pkg_info, userdata);
         if (rc == false) {
             /*
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 89d4fc0..e267f58 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -221,7 +221,6 @@
         "liblog",
         "liblzma",
         "libunwindstack",
-        "libdexfile",
         "libdexfile_support",
     ],
 
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index 9b0b232..eaf867f 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -23,7 +23,7 @@
 #include <memory>
 
 #include <android-base/unique_fd.h>
-#include <art_api/ext_dex_file.h>
+#include <art_api/dex_file_support.h>
 
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
@@ -46,7 +46,7 @@
 
 bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
                                    uint64_t* method_offset) {
-  art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset);
+  art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset, false);
   if (method_info.offset == 0) {
     return false;
   }
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 5797dee..ca658e6 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -25,7 +25,7 @@
 #include <utility>
 #include <vector>
 
-#include <art_api/ext_dex_file.h>
+#include <art_api/dex_file_support.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 21ca47b..0149a42 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -21,7 +21,6 @@
 #include <unordered_map>
 
 #include <android-base/file.h>
-#include <dex/dex_file.h>
 #include <gtest/gtest.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
@@ -40,17 +39,15 @@
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
 
-  ASSERT_EQ(sizeof(art::DexFile::Header) - 1,
-            static_cast<size_t>(
-                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header) - 1))));
+  ASSERT_EQ(size_t{10}, static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, 10))));
 
   // Header too small.
   EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 
   // Header correct, file too small.
   ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
-  ASSERT_EQ(sizeof(art::DexFile::Header), static_cast<size_t>(TEMP_FAILURE_RETRY(write(
-                                              tf.fd, kDexData, sizeof(art::DexFile::Header)))));
+  ASSERT_EQ(sizeof(kDexData) - 1,
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 1))));
   EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 }
 
@@ -78,7 +75,7 @@
 TEST(DexFileTest, from_memory_fail_too_small_for_header) {
   MemoryFake memory;
 
-  memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
+  memory.SetMemory(0x1000, kDexData, 10);
 
   EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
 }
@@ -187,15 +184,6 @@
   ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
   EXPECT_EQ("Main.main", method);
   EXPECT_EQ(0U, method_offset);
-
-  // Make sure that any data that is cached is still retrievable.
-  ASSERT_TRUE(dex_file->GetMethodInformation(0x104, &method, &method_offset));
-  EXPECT_EQ("Main.<init>", method);
-  EXPECT_EQ(4U, method_offset);
-
-  ASSERT_TRUE(dex_file->GetMethodInformation(0x119, &method, &method_offset));
-  EXPECT_EQ("Main.main", method);
-  EXPECT_EQ(1U, method_offset);
 }
 
 TEST(DexFileTest, get_method_empty) {
@@ -210,12 +198,6 @@
   EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));
 
   EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
-
-  // Make sure that once the whole dex file has been cached, no problems occur.
-  EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
-
-  // Choose a value that is in the cached map, but not in a valid method.
-  EXPECT_FALSE(dex_file->GetMethodInformation(0x110, &method, &method_offset));
 }
 
 }  // namespace unwindstack
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 3c295b5..b26ad4d 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -525,7 +525,7 @@
         // NOTREACHED
         return;
     }
-    ::sync();
+    // Wish could ::sync() here, if storage is locked up, we will not continue.
     if (dump) {
         // Show all locks that are held
         android::base::WriteStringToFd("d", sysrqTriggerFd);
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 5601e53..2dda648 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -109,8 +109,7 @@
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.default.links = runtime
-namespace.default.link.runtime.shared_libs  = libc.so:libdl.so:libm.so
-namespace.default.link.runtime.shared_libs += libart.so:libartd.so
+namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
@@ -163,12 +162,8 @@
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
-namespace.sphal.links = runtime,default,vndk,rs
+namespace.sphal.links = default,vndk,rs
 
-namespace.sphal.link.runtime.shared_libs = libc.so:libdl.so:libm.so
-
-# LLNDK_LIBRARIES includes the runtime libs above, but the order here ensures
-# that they are loaded from the runtime namespace.
 namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
@@ -215,9 +210,7 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = runtime,default,vndk
-
-namespace.rs.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+namespace.rs.links = default,vndk
 
 namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -265,13 +258,10 @@
 namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
 namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
 
-# The "vndk" namespace links to "runtime" for Bionic libs, "default" namespace
-# for LLNDK libs, and links to "sphal" namespace for vendor libs. The ordering
-# matters. The "default" namespace has higher priority than the "sphal"
-# namespace.
-namespace.vndk.links = runtime,default,sphal
-
-namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+# The "vndk" namespace links to "default" namespace for LLNDK libs and links to
+# "sphal" namespace for vendor libs.  The ordering matters.  The "default"
+# namespace has higher priority than the "sphal" namespace.
+namespace.vndk.links = default,sphal
 
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
@@ -290,7 +280,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = runtime,system,vndk
+additional.namespaces = system,vndk
 
 ###############################################################################
 # "default" namespace
@@ -321,24 +311,12 @@
 namespace.default.asan.permitted.paths += /data/asan/vendor
 namespace.default.asan.permitted.paths +=           /vendor
 
-namespace.default.links = runtime,system,vndk
-namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+namespace.default.links = system,vndk
 namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
 namespace.default.link.vndk.shared_libs  = %VNDK_SAMEPROCESS_LIBRARIES%
 namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
 
 ###############################################################################
-# "runtime" APEX namespace
-#
-# This namespace pulls in externally accessible libs from the Runtime APEX.
-###############################################################################
-namespace.runtime.isolated = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.links = system
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
-namespace.runtime.link.system.allow_all_shared_libs = true
-
-###############################################################################
 # "vndk" namespace
 #
 # This namespace is where VNDK and VNDK-SP libraries are loaded for
@@ -369,9 +347,7 @@
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the system namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = runtime,system,default
-
-namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+namespace.vndk.links = system,default
 
 namespace.vndk.link.system.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -397,36 +373,16 @@
 namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
-namespace.system.links = runtime
-namespace.system.link.runtime.shared_libs = libc.so:libdl.so:libm.so
-
-
 ###############################################################################
 # Namespace config for binaries under /postinstall.
-# Only default and runtime namespaces are defined and default has no directories
+# Only default namespace is defined and default has no directories
 # other than /system/lib in the search paths. This is because linker calls
 # realpath on the search paths and this causes selinux denial if the paths
 # (/vendor, /odm) are not allowed to the postinstall binaries. There is no
 # reason to allow the binaries to access the paths.
 ###############################################################################
 [postinstall]
-additional.namespaces = runtime
-
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
-
-namespace.default.links = runtime
-namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
-
-###############################################################################
-# "runtime" APEX namespace
-#
-# This namespace pulls in externally accessible libs from the Runtime APEX.
-###############################################################################
-namespace.runtime.isolated = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
-namespace.runtime.link.default.allow_all_shared_libs = true
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 7ca45ff..33b4698 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -57,8 +57,7 @@
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.default.links = runtime
-namespace.default.link.runtime.shared_libs  = libc.so:libdl.so:libm.so
-namespace.default.link.runtime.shared_libs += libart.so:libartd.so
+namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
 
@@ -110,12 +109,8 @@
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
-namespace.sphal.links = runtime,default,vndk,rs
+namespace.sphal.links = default,vndk,rs
 
-namespace.sphal.link.runtime.shared_libs = libc.so:libdl.so:libm.so
-
-# LLNDK_LIBRARIES includes the runtime libs above, but the order here ensures
-# that they are loaded from the runtime namespace.
 namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
@@ -162,9 +157,7 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = runtime,default,vndk
-
-namespace.rs.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+namespace.rs.links = default,vndk
 
 namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -215,9 +208,7 @@
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = runtime,default
-
-namespace.vndk.link.runtime.shared_libs = libc.so:libdl.so:libm.so
+namespace.vndk.links = default
 
 namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -230,7 +221,6 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = runtime
 namespace.default.isolated = false
 
 namespace.default.search.paths  = /odm/${LIB}
@@ -270,47 +260,16 @@
 namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
-namespace.default.links = runtime
-namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
-
-###############################################################################
-# "runtime" APEX namespace
-#
-# This namespace pulls in externally accessible libs from the Runtime APEX.
-###############################################################################
-namespace.runtime.isolated = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
-namespace.runtime.link.default.allow_all_shared_libs = true
-
-
 ###############################################################################
 # Namespace config for binaries under /postinstall.
-# Only default and runtime namespaces are defined and default has no directories
+# Only default namespace is defined and default has no directories
 # other than /system/lib in the search paths. This is because linker calls
 # realpath on the search paths and this causes selinux denial if the paths
 # (/vendor, /odm) are not allowed to the postinstall binaries. There is no
 # reason to allow the binaries to access the paths.
 ###############################################################################
 [postinstall]
-additional.namespaces = runtime
-
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
-
-namespace.default.links = runtime
-namespace.default.link.runtime.shared_libs = libc.so:libdl.so:libm.so
-
-###############################################################################
-# "runtime" APEX namespace
-#
-# This namespace pulls in externally accessible libs from the Runtime APEX.
-###############################################################################
-namespace.runtime.isolated = true
-namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
-# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
-namespace.runtime.link.default.allow_all_shared_libs = true
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 0ec6e17..b34399d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -12,6 +12,12 @@
 import /init.${ro.zygote}.rc
 
 on early-init
+    # Mount shared so changes propagate into child namespaces
+    # Do this before other processes are started from init. Otherwise,
+    # processes launched while the propagation type of / is 'private'
+    # won't get mount events from others.
+    mount rootfs rootfs / shared rec
+
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000
 
@@ -40,6 +46,8 @@
     # cgroup for system_server and surfaceflinger
     mkdir /dev/memcg/system 0550 system system
 
+    prepare_bootstrap_bionic
+
     start ueventd
 
 on init
@@ -350,8 +358,6 @@
     # Once everything is setup, no need to modify /.
     # The bind+remount combination allows this to work in containers.
     mount rootfs rootfs / remount bind ro nodev
-    # Mount shared so changes propagate into child namespaces
-    mount rootfs rootfs / shared rec
     # Mount default storage into root namespace
     mount none /mnt/runtime/default /storage bind rec
     mount none none /storage slave rec
@@ -587,6 +593,14 @@
     # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
 
+    # Wait for apexd to finish activating APEXes before starting more processes.
+    # This certainly reduces the parallelism but is required to make as many processes
+    # as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
+    # so the impact on the booting time is not significant.
+    wait_for_prop apexd.status ready
+    setup_runtime_bionic
+    parse_apex_configs
+
     # If there is no post-fs-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
     # won't work.
@@ -808,6 +822,3 @@
 service flash_recovery /system/bin/install-recovery.sh
     class main
     oneshot
-
-on property:apexd.status=ready
-    parse_apex_configs
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index f49bdf7..8752eef 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -26,6 +26,7 @@
 #include <unistd.h>
 
 #include <string>
+#include <vector>
 
 #include <libminijail.h>
 #include <scoped_minijail.h>
@@ -131,6 +132,25 @@
   return check_directory(data_path, uid);
 }
 
+std::vector<gid_t> get_supplementary_gids(uid_t userAppId) {
+  std::vector<gid_t> gids;
+  int size = getgroups(0, &gids[0]);
+  if (size < 0) {
+    error(1, errno, "getgroups failed");
+  }
+  gids.resize(size);
+  size = getgroups(size, &gids[0]);
+  if (size != static_cast<int>(gids.size())) {
+    error(1, errno, "getgroups failed");
+  }
+  // Profile guide compiled oat files (like /data/app/xxx/oat/arm64/base.odex) are not readable
+  // worldwide (DEXOPT_PUBLIC flag isn't set). To support reading them (needed by simpleperf for
+  // profiling), add shared app gid to supplementary groups.
+  gid_t shared_app_gid = userAppId % AID_USER_OFFSET - AID_APP_START + AID_SHARED_GID_START;
+  gids.push_back(shared_app_gid);
+  return gids;
+}
+
 int main(int argc, char* argv[]) {
   // Check arguments.
   if (argc < 2) {
@@ -210,10 +230,11 @@
   // same time to avoid nasty surprises.
   uid_t uid = userAppId;
   uid_t gid = userAppId;
+  std::vector<gid_t> supplementary_gids = get_supplementary_gids(userAppId);
   ScopedMinijail j(minijail_new());
   minijail_change_uid(j.get(), uid);
   minijail_change_gid(j.get(), gid);
-  minijail_keep_supplementary_gids(j.get());
+  minijail_set_supplementary_gids(j.get(), supplementary_gids.size(), supplementary_gids.data());
   minijail_enter(j.get());
 
   std::string seinfo = std::string(info.seinfo) + ":fromRunAs";