Merge "Move APEX symlink creation to alternative module"
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 7c57258..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/TEST_MAPPING b/TEST_MAPPING
index f9c3b4a..bc5685b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -7,6 +7,9 @@
       "name": "debuggerd_test"
     },
     {
+      "name": "fs_mgr_unit_test"
+    },
+    {
       "name": "init_tests"
     },
     {
diff --git a/adb/Android.bp b/adb/Android.bp
index 36bfad4..8199fff 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -22,35 +22,16 @@
         "-Wexit-time-destructors",
         "-Wno-unused-parameter",
         "-Wno-missing-field-initializers",
+        "-Wthread-safety",
         "-Wvla",
+        "-DADB_HOST=1", // overridden by adbd_defaults
     ],
     cpp_std: "experimental",
 
     use_version_lib: true,
-
     compile_multilib: "first",
-    product_variables: {
-        debuggable: {
-            cflags: [
-                "-DALLOW_ADBD_ROOT",
-                "-DALLOW_ADBD_DISABLE_VERITY",
-                "-DALLOW_ADBD_NO_AUTH",
-            ],
-        },
-    },
 
     target: {
-        android: {
-            cflags: [
-                "-DADB_HOST=0",
-                "-Wthread-safety",
-            ],
-        },
-
-        host: {
-            cflags: ["-DADB_HOST=1"],
-        },
-
         darwin: {
             host_ldlibs: [
                 "-lpthread",
@@ -76,6 +57,9 @@
 
                 // MinGW hides some things behind _POSIX_SOURCE.
                 "-D_POSIX_SOURCE",
+
+                // Not supported yet.
+                "-Wno-thread-safety",
             ],
 
             host_ldlibs: [
@@ -84,15 +68,46 @@
                 "-luserenv",
             ],
         },
+    },
+}
 
-        not_windows: {
+cc_defaults {
+    name: "adbd_defaults",
+    defaults: ["adb_defaults"],
+
+    cflags: ["-UADB_HOST", "-DADB_HOST=0"],
+    product_variables: {
+        debuggable: {
             cflags: [
-                "-Wthread-safety",
+                "-DALLOW_ADBD_ROOT",
+                "-DALLOW_ADBD_DISABLE_VERITY",
+                "-DALLOW_ADBD_NO_AUTH",
             ],
         },
     },
 }
 
+cc_defaults {
+    name: "host_adbd_supported",
+
+    host_supported: true,
+    target: {
+        linux: {
+            enabled: true,
+            host_ldlibs: [
+                "-lresolv", // b64_pton
+                "-lutil", // forkpty
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+}
+
 // libadb
 // =========================================================
 // These files are compiled for both the host and the device.
@@ -313,7 +328,7 @@
 // libadbd_core contains the common sources to build libadbd and libadbd_services.
 cc_library_static {
     name: "libadbd_core",
-    defaults: ["adb_defaults"],
+    defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
 
     // libminadbd wants both, as it's used to build native tests.
@@ -322,9 +337,6 @@
     srcs: libadb_srcs + libadb_posix_srcs + [
         "daemon/auth.cpp",
         "daemon/jdwp_service.cpp",
-        "daemon/usb.cpp",
-        "daemon/usb_ffs.cpp",
-        "daemon/usb_legacy.cpp",
     ],
 
     local_include_dirs: [
@@ -335,7 +347,6 @@
 
     static_libs: [
         "libdiagnose_usb",
-        "libqemu_pipe",
     ],
 
     shared_libs: [
@@ -346,22 +357,36 @@
         "libcutils",
         "liblog",
     ],
+
+    target: {
+        android: {
+            whole_static_libs: [
+                "libqemu_pipe",
+            ],
+            srcs: [
+                "daemon/transport_qemu.cpp",
+                "daemon/usb.cpp",
+                "daemon/usb_ffs.cpp",
+                "daemon/usb_legacy.cpp",
+            ]
+        },
+        linux_glibc: {
+            srcs: [
+                "daemon/usb_dummy.cpp",
+            ]
+        }
+    },
 }
 
 cc_library {
     name: "libadbd_services",
-    defaults: ["adb_defaults"],
+    defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
     compile_multilib: "both",
 
     srcs: [
-        "daemon/abb_service.cpp",
         "daemon/file_sync_service.cpp",
-        "daemon/framebuffer_service.cpp",
-        "daemon/mdns.cpp",
-        "daemon/remount_service.cpp",
         "daemon/services.cpp",
-        "daemon/set_verity_enable_state_service.cpp",
         "daemon/shell_service.cpp",
         "shell_service_protocol.cpp",
     ],
@@ -373,27 +398,41 @@
 
     static_libs: [
         "libadbd_core",
-        "libavb_user",
         "libdiagnose_usb",
-        "libqemu_pipe",
     ],
 
     shared_libs: [
         "libasyncio",
         "libbase",
-        "libbootloader_message",
         "libcrypto",
         "libcrypto_utils",
         "libcutils",
-        "libext4_utils",
-        "libfec",
-        "libfs_mgr",
         "liblog",
-        "libmdnssd",
-        "libselinux",
     ],
 
     target: {
+        android: {
+            srcs: [
+                "daemon/abb_service.cpp",
+                "daemon/framebuffer_service.cpp",
+                "daemon/mdns.cpp",
+                "daemon/reboot_service.cpp",
+                "daemon/remount_service.cpp",
+                "daemon/restart_service.cpp",
+                "daemon/set_verity_enable_state_service.cpp",
+            ],
+            static_libs: [
+                "libavb_user",
+            ],
+            shared_libs: [
+                "libbootloader_message",
+                "libmdnssd",
+                "libext4_utils",
+                "libfec",
+                "libfs_mgr",
+                "libselinux",
+            ],
+        },
         recovery: {
             exclude_srcs: [
                 "daemon/abb_service.cpp",
@@ -404,7 +443,7 @@
 
 cc_library {
     name: "libadbd",
-    defaults: ["adb_defaults"],
+    defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
 
     // Avoid getting duplicate symbol of android::build::GetBuildNumber().
@@ -435,7 +474,7 @@
 
 cc_binary {
     name: "adbd",
-    defaults: ["adb_defaults"],
+    defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
 
     srcs: [
@@ -467,7 +506,7 @@
 cc_binary {
     name: "abb",
 
-    defaults: ["adb_defaults"],
+    defaults: ["adbd_defaults"],
     recovery_available: false,
 
     srcs: [
@@ -500,7 +539,7 @@
 
 cc_test {
     name: "adbd_test",
-    defaults: ["adb_defaults"],
+    defaults: ["adbd_defaults"],
     srcs: libadb_test_srcs + [
         "daemon/services.cpp",
         "daemon/shell_service.cpp",
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 2bd6a3e..80f146c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -32,7 +32,9 @@
 
 #if !ADB_HOST
 const char* adb_device_banner = "device";
+#if defined(__ANDROID__)
 static android::base::LogdLogger gLogdLogger;
+#endif
 #else
 const char* adb_device_banner = "host";
 #endif
@@ -46,7 +48,7 @@
     fflush(stderr);
 #endif
 
-#if !ADB_HOST
+#if !ADB_HOST && defined(__ANDROID__)
     // Only print logs of INFO or higher to logcat, so that `adb logcat` with adbd tracing on
     // doesn't result in exponential logging.
     if (severity >= android::base::INFO) {
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index eda4b77..9a25d10 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -144,53 +144,51 @@
     }
 
     std::string reason;
-    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
-    if (fd < 0) {
+    unique_fd fd;
+    if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
         *error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
                                              __adb_server_socket_spec, reason.c_str());
         return -2;
     }
 
-    if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
+    if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd.get(), error)) {
         return -1;
     }
 
-    if (!SendProtocolString(fd, service)) {
+    if (!SendProtocolString(fd.get(), service)) {
         *error = perror_str("write failure during connection");
-        adb_close(fd);
         return -1;
     }
 
-    if (!adb_status(fd, error)) {
-        adb_close(fd);
+    if (!adb_status(fd.get(), error)) {
         return -1;
     }
 
-    D("_adb_connect: return fd %d", fd);
-    return fd;
+    D("_adb_connect: return fd %d", fd.get());
+    return fd.release();
 }
 
 bool adb_kill_server() {
     D("adb_kill_server");
     std::string reason;
-    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
-    if (fd < 0) {
+    unique_fd fd;
+    if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
         fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
                 reason.c_str());
         return true;
     }
 
-    if (!SendProtocolString(fd, "host:kill")) {
+    if (!SendProtocolString(fd.get(), "host:kill")) {
         fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
         return false;
     }
 
     // The server might send OKAY, so consume that.
     char buf[4];
-    ReadFdExactly(fd, buf, 4);
+    ReadFdExactly(fd.get(), buf, 4);
     // Now that no more data is expected, wait for socket orderly shutdown or error, indicating
     // server death.
-    ReadOrderlyShutdown(fd);
+    ReadOrderlyShutdown(fd.get());
     return true;
 }
 
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index bd5508c..d56a25f 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -24,16 +24,17 @@
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
 #include "adb.h"
 #include "adb_client.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
-#include "android-base/file.h"
-#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
 #include "client/file_sync_client.h"
 #include "commandline.h"
 #include "fastdeploy.h"
-#include "sysdeps.h"
 
 static constexpr int kFastDeployMinApi = 24;
 
@@ -159,17 +160,23 @@
 
     if (use_fastdeploy == true) {
         TemporaryFile metadataTmpFile;
-        TemporaryFile patchTmpFile;
+        std::string patchTmpFilePath;
+        {
+            TemporaryFile patchTmpFile;
+            patchTmpFile.DoNotRemove();
+            patchTmpFilePath = patchTmpFile.path;
+        }
 
         FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
         extract_metadata(file, metadataFile);
         fclose(metadataFile);
 
-        create_patch(file, metadataTmpFile.path, patchTmpFile.path);
+        create_patch(file, metadataTmpFile.path, patchTmpFilePath.c_str());
         // pass all but 1st (command) and last (apk path) parameters through to pm for
         // session creation
         std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
-        install_patch(file, patchTmpFile.path, pm_args.size(), pm_args.data());
+        install_patch(file, patchTmpFilePath.c_str(), pm_args.size(), pm_args.data());
+        adb_unlink(patchTmpFilePath.c_str());
         delete_device_patch_file(file);
         return 0;
     } else {
@@ -179,8 +186,8 @@
             return 1;
         }
 
-        int localFd = adb_open(file, O_RDONLY);
-        if (localFd < 0) {
+        unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
+        if (local_fd < 0) {
             fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
             return 1;
         }
@@ -201,19 +208,15 @@
             cmd += " --apex";
         }
 
-        int remoteFd = adb_connect(cmd, &error);
-        if (remoteFd < 0) {
+        unique_fd remote_fd(adb_connect(cmd, &error));
+        if (remote_fd < 0) {
             fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-            adb_close(localFd);
             return 1;
         }
 
         char buf[BUFSIZ];
-        copy_to_file(localFd, remoteFd);
-        read_status_line(remoteFd, buf, sizeof(buf));
-
-        adb_close(localFd);
-        adb_close(remoteFd);
+        copy_to_file(local_fd.get(), remote_fd.get());
+        read_status_line(remote_fd.get(), buf, sizeof(buf));
 
         if (!strncmp("Success", buf, 7)) {
             fputs(buf, stdout);
@@ -410,14 +413,15 @@
 
     // Create install session
     std::string error;
-    int fd = adb_connect(cmd, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
-        return EXIT_FAILURE;
-    }
     char buf[BUFSIZ];
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
+    {
+        unique_fd fd(adb_connect(cmd, &error));
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+            return EXIT_FAILURE;
+        }
+        read_status_line(fd.get(), buf, sizeof(buf));
+    }
 
     int session_id = -1;
     if (!strncmp("Success", buf, 7)) {
@@ -450,27 +454,23 @@
                                             install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
                                             session_id, android::base::Basename(file).c_str());
 
-        int localFd = adb_open(file, O_RDONLY);
-        if (localFd < 0) {
+        unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
+        if (local_fd < 0) {
             fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
             success = 0;
             goto finalize_session;
         }
 
         std::string error;
-        int remoteFd = adb_connect(cmd, &error);
-        if (remoteFd < 0) {
+        unique_fd remote_fd(adb_connect(cmd, &error));
+        if (remote_fd < 0) {
             fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-            adb_close(localFd);
             success = 0;
             goto finalize_session;
         }
 
-        copy_to_file(localFd, remoteFd);
-        read_status_line(remoteFd, buf, sizeof(buf));
-
-        adb_close(localFd);
-        adb_close(remoteFd);
+        copy_to_file(local_fd.get(), remote_fd.get());
+        read_status_line(remote_fd.get(), buf, sizeof(buf));
 
         if (strncmp("Success", buf, 7)) {
             fprintf(stderr, "adb: failed to write %s\n", file);
@@ -484,13 +484,14 @@
     // Commit session if we streamed everything okay; otherwise abandon
     std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
                                                       success ? "commit" : "abandon", 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;
+    {
+        unique_fd 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.get(), buf, sizeof(buf));
     }
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
 
     if (!strncmp("Success", buf, 7)) {
         fputs(buf, stdout);
@@ -504,17 +505,22 @@
 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;
+    bool apex_found = false;
+    int first_package = -1;
     for (int i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
-        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
-            first_apk = i;
+        if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+            android::base::EndsWithIgnoreCase(file, ".apex")) {
+            first_package = i;
+            if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+                apex_found = true;
+            }
         } else {
             break;
         }
     }
 
-    if (first_apk == -1) error_exit("need APK file on command line");
+    if (first_package == -1) error_exit("need APK or APEX files on command line");
 
     if (use_legacy_install()) {
         fprintf(stderr, "adb: multi-package install is not supported on this device\n");
@@ -524,17 +530,21 @@
 
     std::string multi_package_cmd =
             android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+    if (apex_found) {
+        multi_package_cmd += " --staged";
+    }
 
     // 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);
+    {
+        unique_fd 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;
+        }
+        read_status_line(fd.get(), buf, sizeof(buf));
+    }
 
     int parent_session_id = -1;
     if (!strncmp("Success", buf, 7)) {
@@ -560,20 +570,31 @@
     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++) {
+    for (int i = 1; i < first_package; i++) {
         individual_cmd += " " + escape_arg(argv[i]);
     }
+    if (apex_found) {
+        individual_cmd += " --staged";
+    }
+    std::string individual_apex_cmd = individual_cmd + " --apex";
     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;
-        }
+    for (int i = first_package; i < argc; i++) {
+        const char* file = argv[i];
         char buf[BUFSIZ];
-        read_status_line(fd, buf, sizeof(buf));
-        adb_close(fd);
+        {
+            unique_fd fd;
+            // Create individual install session
+            if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+                fd.reset(adb_connect(individual_apex_cmd, &error));
+            } else {
+                fd.reset(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;
+            }
+            read_status_line(fd.get(), buf, sizeof(buf));
+        }
 
         int session_id = -1;
         if (!strncmp("Success", buf, 7)) {
@@ -593,7 +614,6 @@
         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));
@@ -605,25 +625,21 @@
                                             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) {
+        unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
+        if (local_fd < 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) {
+        unique_fd remote_fd(adb_connect(cmd, &error));
+        if (remote_fd < 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);
+        copy_to_file(local_fd.get(), remote_fd.get());
+        read_status_line(remote_fd.get(), buf, sizeof(buf));
 
         if (strncmp("Success", buf, 7)) {
             fprintf(stderr, "adb: failed to write %s\n", file);
@@ -636,13 +652,14 @@
 
     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;
+    {
+        unique_fd fd(adb_connect(cmd, &error));
+        if (fd < 0) {
+            fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
+            goto finalize_multi_package_session;
+        }
+        read_status_line(fd.get(), buf, sizeof(buf));
     }
-    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());
@@ -658,13 +675,14 @@
     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;
+    {
+        unique_fd 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.get(), buf, sizeof(buf));
     }
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
 
     if (!strncmp("Success", buf, 7)) {
         fputs(buf, stdout);
@@ -681,13 +699,12 @@
         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);
+        unique_fd 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);
+        read_status_line(fd.get(), buf, sizeof(buf));
     }
     return EXIT_FAILURE;
 }
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 9bc42e1..e963e3d 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -762,6 +762,17 @@
 }
 
 static int adb_abb(int argc, const char** argv) {
+    FeatureSet features;
+    std::string error_message;
+    if (!adb_get_feature_set(&features, &error_message)) {
+        fprintf(stderr, "error: %s\n", error_message.c_str());
+        return 1;
+    }
+
+    if (!CanUseFeature(features, kFeatureAbb)) {
+        error_exit("abb is not supported by the device");
+    }
+
     // Defaults.
     constexpr char escape_char = '~';  // -e
     constexpr bool use_shell_protocol = true;
diff --git a/adb/client/line_printer.cpp b/adb/client/line_printer.cpp
index 4dc2d28..50c03e8 100644
--- a/adb/client/line_printer.cpp
+++ b/adb/client/line_printer.cpp
@@ -31,6 +31,8 @@
 // Stuff from ninja's util.h that's needed below.
 #include <vector>
 using namespace std;
+// This does not account for multiple UTF-8 bytes corresponding to a single Unicode code point, or
+// multiple code points corresponding to a single grapheme cluster (user-perceived character).
 string ElideMiddle(const string& str, size_t width) {
   const int kMargin = 3;  // Space for "...".
   string result = str;
@@ -85,9 +87,10 @@
     CONSOLE_SCREEN_BUFFER_INFO csbi;
     GetConsoleScreenBufferInfo(console_, &csbi);
 
-    // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)...
-    // TODO: wstring ElideMiddle.
     to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
+    std::wstring to_print_wide;
+    // ElideMiddle may create invalid UTF-8, so ignore conversion errors.
+    (void)android::base::UTF8ToWide(to_print, &to_print_wide);
     // We don't want to have the cursor spamming back and forth, so instead of
     // printf use WriteConsoleOutput which updates the contents of the buffer,
     // but doesn't move the cursor position.
@@ -100,12 +103,10 @@
     };
     vector<CHAR_INFO> char_data(csbi.dwSize.X);
     for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
-      // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
-      char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
-      char_data[i].Attributes = csbi.wAttributes;
+        char_data[i].Char.UnicodeChar = i < to_print_wide.size() ? to_print_wide[i] : L' ';
+        char_data[i].Attributes = csbi.wAttributes;
     }
-    // TODO: WriteConsoleOutputW.
-    WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
+    WriteConsoleOutputW(console_, &char_data[0], buf_size, zero_zero, &target);
 #else
     // Limit output to width of the terminal if provided so we don't cause
     // line-wrapping.
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 180df8f..1800f84 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -67,7 +67,7 @@
                 // b64_pton requires one additional byte in the target buffer for
                 // decoding to succeed. See http://b/28035006 for details.
                 uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
-                if (__b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
+                if (b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
                     LOG(ERROR) << "Invalid base64 key " << line.c_str() << " in " << path;
                     continue;
                 }
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index d55096a..62c18c5 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -22,13 +22,11 @@
 
 #include <dirent.h>
 #include <errno.h>
-#include <linux/xattr.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/xattr.h>
 #include <unistd.h>
 #include <utime.h>
 
@@ -37,11 +35,17 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
+
+#if defined(__ANDROID__)
 #include <selinux/android.h>
+#include <sys/xattr.h>
+#endif
 
 #include "adb.h"
 #include "adb_io.h"
@@ -55,11 +59,17 @@
 using android::base::StringPrintf;
 
 static bool should_use_fs_config(const std::string& path) {
+#if defined(__ANDROID__)
     // TODO: use fs_config to configure permissions on /data too.
     return !android::base::StartsWith(path, "/data/");
+#else
+    UNUSED(path);
+    return false;
+#endif
 }
 
 static bool update_capabilities(const char* path, uint64_t capabilities) {
+#if defined(__ANDROID__)
     if (capabilities == 0) {
         // Ensure we clean up in case the capabilities weren't 0 in the past.
         removexattr(path, XATTR_NAME_CAPS);
@@ -73,6 +83,10 @@
     cap_data.data[1].permitted = (capabilities >> 32);
     cap_data.data[1].inheritable = 0;
     return setxattr(path, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) != -1;
+#else
+    UNUSED(path, capabilities);
+    return true;
+#endif
 }
 
 static bool secure_mkdirs(const std::string& path) {
@@ -105,8 +119,10 @@
         } else {
             if (chown(partial_path.c_str(), uid, gid) == -1) return false;
 
+#if defined(__ANDROID__)
             // Not all filesystems support setting SELinux labels. http://b/23530370.
             selinux_android_restorecon(partial_path.c_str(), 0);
+#endif
 
             if (!update_capabilities(partial_path.c_str(), capabilities)) return false;
         }
@@ -242,8 +258,10 @@
             goto fail;
         }
 
+#if defined(__ANDROID__)
         // Not all filesystems support setting SELinux labels. http://b/23530370.
         selinux_android_restorecon(path, 0);
+#endif
 
         // fchown clears the setuid bit - restore it if present.
         // Ignore the result of calling fchmod. It's not supported
diff --git a/adb/daemon/framebuffer_service.h b/adb/daemon/framebuffer_service.h
index 264da59..bab44be 100644
--- a/adb/daemon/framebuffer_service.h
+++ b/adb/daemon/framebuffer_service.h
@@ -18,4 +18,6 @@
 
 #include "adb_unique_fd.h"
 
+#if defined(__ANDROID__)
 void framebuffer_service(unique_fd fd);
+#endif
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index f6f1acc..fce3a4f 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -18,7 +18,10 @@
 
 #include "sysdeps.h"
 
+#if defined(__BIONIC__)
 #include <android/fdsan.h>
+#endif
+
 #include <errno.h>
 #include <getopt.h>
 #include <malloc.h>
@@ -34,12 +37,15 @@
 #include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+
+#if defined(__ANDROID__)
 #include <libminijail.h>
 #include <log/log_properties.h>
 #include <scoped_minijail.h>
 
 #include <private/android_filesystem_config.h>
 #include "selinux/android.h"
+#endif
 
 #include "adb.h"
 #include "adb_auth.h"
@@ -49,6 +55,7 @@
 
 #include "mdns.h"
 
+#if defined(__ANDROID__)
 static const char* root_seclabel = nullptr;
 
 static bool should_drop_capabilities_bounding_set() {
@@ -167,10 +174,14 @@
         }
     }
 }
+#endif
 
 static void setup_port(int port) {
+    LOG(INFO) << "adbd listening on port " << port;
     local_init(port);
+#if defined(__ANDROID__)
     setup_mdns(port);
+#endif
 }
 
 int adbd_main(int server_port) {
@@ -178,10 +189,12 @@
 
     signal(SIGPIPE, SIG_IGN);
 
+#if defined(__BIONIC__)
     auto fdsan_level = android_fdsan_get_error_level();
     if (fdsan_level == ANDROID_FDSAN_ERROR_LEVEL_DISABLED) {
         android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
     }
+#endif
 
     init_transport_registration();
 
@@ -206,14 +219,19 @@
           " unchanged.\n");
     }
 
+#if defined(__ANDROID__)
     drop_privileges(server_port);
+#endif
 
     bool is_usb = false;
+
+#if defined(__ANDROID__)
     if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
         // Listen on USB.
         usb_init();
         is_usb = true;
     }
+#endif
 
     // If one of these properties is set, also listen on that port.
     // If one of the properties isn't set and we couldn't listen on usb, listen
@@ -244,8 +262,10 @@
 }
 
 int main(int argc, char** argv) {
+#if defined(__BIONIC__)
     // Set M_DECAY_TIME so that our allocations aren't immediately purged on free.
     mallopt(M_DECAY_TIME, 1);
+#endif
 
     while (true) {
         static struct option opts[] = {
@@ -261,19 +281,21 @@
         }
 
         switch (c) {
-        case 's':
-            root_seclabel = optarg;
-            break;
-        case 'b':
-            adb_device_banner = optarg;
-            break;
-        case 'v':
-            printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR,
-                   ADB_VERSION_MINOR, ADB_SERVER_VERSION);
-            return 0;
-        default:
-            // getopt already prints "adbd: invalid option -- %c" for us.
-            return 1;
+#if defined(__ANDROID__)
+            case 's':
+                root_seclabel = optarg;
+                break;
+#endif
+            case 'b':
+                adb_device_banner = optarg;
+                break;
+            case 'v':
+                printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR,
+                       ADB_VERSION_MINOR, ADB_SERVER_VERSION);
+                return 0;
+            default:
+                // getopt already prints "adbd: invalid option -- %c" for us.
+                return 1;
         }
     }
 
diff --git a/adb/daemon/reboot_service.cpp b/adb/daemon/reboot_service.cpp
new file mode 100644
index 0000000..a5a11b8
--- /dev/null
+++ b/adb/daemon/reboot_service.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG SERVICES
+
+#include "sysdeps.h"
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+
+void reboot_service(unique_fd fd, const std::string& arg) {
+    std::string reboot_arg = arg;
+    sync();
+
+    if (reboot_arg.empty()) reboot_arg = "adb";
+    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
+
+    if (reboot_arg == "fastboot" &&
+        android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+        access("/dev/socket/recovery", F_OK) == 0) {
+        LOG(INFO) << "Recovery specific reboot fastboot";
+        /*
+         * The socket is created to allow switching between recovery and
+         * fastboot.
+         */
+        android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
+        if (sock < 0) {
+            WriteFdFmt(fd, "reboot (%s) create\n", strerror(errno));
+            PLOG(ERROR) << "Creating recovery socket failed";
+            return;
+        }
+
+        sockaddr_un addr = {.sun_family = AF_UNIX};
+        strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
+        if (connect(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+            WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
+            PLOG(ERROR) << "Couldn't connect to recovery socket";
+            return;
+        }
+        const char msg_switch_to_fastboot = 'f';
+        auto ret = adb_write(sock, &msg_switch_to_fastboot, sizeof(msg_switch_to_fastboot));
+        if (ret != sizeof(msg_switch_to_fastboot)) {
+            WriteFdFmt(fd, "reboot (%s) write\n", strerror(errno));
+            PLOG(ERROR) << "Couldn't write message to recovery socket to switch to fastboot";
+            return;
+        }
+    } else {
+        if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
+            WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
+            return;
+        }
+    }
+    // Don't return early. Give the reboot command time to take effect
+    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+    while (true) {
+        pause();
+    }
+}
diff --git a/adb/daemon/reboot_service.h b/adb/daemon/reboot_service.h
new file mode 100644
index 0000000..f68913e
--- /dev/null
+++ b/adb/daemon/reboot_service.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "adb_unique_fd.h"
+
+#if defined(__ANDROID__)
+void reboot_service(unique_fd fd, const std::string& arg);
+#endif
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
index e4e2550..c847403 100644
--- a/adb/daemon/remount_service.h
+++ b/adb/daemon/remount_service.h
@@ -20,5 +20,7 @@
 
 #include "adb_unique_fd.h"
 
+#if defined(__ANDROID__)
 bool make_block_device_writable(const std::string&);
 void remount_service(unique_fd, const std::string&);
+#endif
diff --git a/adb/daemon/restart_service.cpp b/adb/daemon/restart_service.cpp
new file mode 100644
index 0000000..6803d93
--- /dev/null
+++ b/adb/daemon/restart_service.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#define TRACE_TAG SERVICES
+
+#include "sysdeps.h"
+
+#include <unistd.h>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <log/log_properties.h>
+
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+
+void restart_root_service(unique_fd fd) {
+    if (getuid() == 0) {
+        WriteFdExactly(fd.get(), "adbd is already running as root\n");
+        return;
+    }
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
+        return;
+    }
+
+    android::base::SetProperty("service.adb.root", "1");
+    WriteFdExactly(fd.get(), "restarting adbd as root\n");
+}
+
+void restart_unroot_service(unique_fd fd) {
+    if (getuid() != 0) {
+        WriteFdExactly(fd.get(), "adbd not running as root\n");
+        return;
+    }
+    android::base::SetProperty("service.adb.root", "0");
+    WriteFdExactly(fd.get(), "restarting adbd as non root\n");
+}
+
+void restart_tcp_service(unique_fd fd, int port) {
+    if (port <= 0) {
+        WriteFdFmt(fd.get(), "invalid port %d\n", port);
+        return;
+    }
+
+    android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
+    WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
+}
+
+void restart_usb_service(unique_fd fd) {
+    android::base::SetProperty("service.adb.tcp.port", "0");
+    WriteFdExactly(fd.get(), "restarting in USB mode\n");
+}
diff --git a/adb/daemon/restart_service.h b/adb/daemon/restart_service.h
new file mode 100644
index 0000000..19840bd
--- /dev/null
+++ b/adb/daemon/restart_service.h
@@ -0,0 +1,26 @@
+/*
+ * 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 "adb_unique_fd.h"
+
+#if defined(__ANDROID__)
+void restart_root_service(unique_fd fd);
+void restart_unroot_service(unique_fd fd);
+void restart_tcp_service(unique_fd fd, int port);
+void restart_usb_service(unique_fd fd);
+#endif
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 5ae210f..d1f0345 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -39,8 +39,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
 #include <log/log_properties.h>
 
@@ -55,96 +53,12 @@
 
 #include "daemon/file_sync_service.h"
 #include "daemon/framebuffer_service.h"
+#include "daemon/reboot_service.h"
 #include "daemon/remount_service.h"
+#include "daemon/restart_service.h"
 #include "daemon/set_verity_enable_state_service.h"
 #include "daemon/shell_service.h"
 
-void restart_root_service(unique_fd fd) {
-    if (getuid() == 0) {
-        WriteFdExactly(fd.get(), "adbd is already running as root\n");
-        return;
-    }
-    if (!__android_log_is_debuggable()) {
-        WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
-        return;
-    }
-
-    android::base::SetProperty("service.adb.root", "1");
-    WriteFdExactly(fd.get(), "restarting adbd as root\n");
-}
-
-void restart_unroot_service(unique_fd fd) {
-    if (getuid() != 0) {
-        WriteFdExactly(fd.get(), "adbd not running as root\n");
-        return;
-    }
-    android::base::SetProperty("service.adb.root", "0");
-    WriteFdExactly(fd.get(), "restarting adbd as non root\n");
-}
-
-void restart_tcp_service(unique_fd fd, int port) {
-    if (port <= 0) {
-        WriteFdFmt(fd.get(), "invalid port %d\n", port);
-        return;
-    }
-
-    android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
-    WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
-}
-
-void restart_usb_service(unique_fd fd) {
-    android::base::SetProperty("service.adb.tcp.port", "0");
-    WriteFdExactly(fd.get(), "restarting in USB mode\n");
-}
-
-void reboot_service(unique_fd fd, const std::string& arg) {
-    std::string reboot_arg = arg;
-    sync();
-
-    if (reboot_arg.empty()) reboot_arg = "adb";
-    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
-
-    if (reboot_arg == "fastboot" &&
-        android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
-        access("/dev/socket/recovery", F_OK) == 0) {
-        LOG(INFO) << "Recovery specific reboot fastboot";
-        /*
-         * The socket is created to allow switching between recovery and
-         * fastboot.
-         */
-        android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
-        if (sock < 0) {
-            WriteFdFmt(fd, "reboot (%s) create\n", strerror(errno));
-            PLOG(ERROR) << "Creating recovery socket failed";
-            return;
-        }
-
-        sockaddr_un addr = {.sun_family = AF_UNIX};
-        strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
-        if (connect(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
-            WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
-            PLOG(ERROR) << "Couldn't connect to recovery socket";
-            return;
-        }
-        const char msg_switch_to_fastboot = 'f';
-        auto ret = adb_write(sock, &msg_switch_to_fastboot, sizeof(msg_switch_to_fastboot));
-        if (ret != sizeof(msg_switch_to_fastboot)) {
-            WriteFdFmt(fd, "reboot (%s) write\n", strerror(errno));
-            PLOG(ERROR) << "Couldn't write message to recovery socket to switch to fastboot";
-            return;
-        }
-    } else {
-        if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
-            WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
-            return;
-        }
-    }
-    // Don't return early. Give the reboot command time to take effect
-    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
-    while (true) {
-        pause();
-    }
-}
 
 void reconnect_service(unique_fd fd, atransport* t) {
     WriteFdExactly(fd.get(), "done");
@@ -221,7 +135,8 @@
     }
 
     fdevent_run_on_main_thread([fd = pipe_read.release()]() {
-        fdevent* fde = fdevent_create(fd, [](int, unsigned, void*) {}, nullptr);
+        fdevent* fde = fdevent_create(
+                fd, [](int, unsigned, void*) {}, nullptr);
         fdevent_add(fde, FDE_READ);
     });
 
@@ -328,31 +243,16 @@
 }
 
 unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
-#ifndef __ANDROID_RECOVERY__
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
     if (name.starts_with("abb:")) {
         name.remove_prefix(strlen("abb:"));
         return execute_binder_command(name);
     }
 #endif
 
-    if (name.starts_with("dev:")) {
-        name.remove_prefix(strlen("dev:"));
-        return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
-    } else if (name.starts_with("framebuffer:")) {
+#if defined(__ANDROID__)
+    if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
-    } else if (name.starts_with("jdwp:")) {
-        name.remove_prefix(strlen("jdwp:"));
-        std::string str(name);
-        return create_jdwp_connection_fd(atoi(str.c_str()));
-    } else if (name.starts_with("shell")) {
-        name.remove_prefix(strlen("shell"));
-        return ShellService(name, transport);
-    } else if (name.starts_with("exec:")) {
-        name.remove_prefix(strlen("exec:"));
-        return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
-                               SubprocessProtocol::kNone);
-    } else if (name.starts_with("sync:")) {
-        return create_service_thread("sync", file_sync_service);
     } else if (name.starts_with("remount:")) {
         std::string arg(name.begin() + strlen("remount:"), name.end());
         return create_service_thread("remount",
@@ -373,6 +273,12 @@
     } else if (name.starts_with("restore:")) {
         return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
+    } else if (name.starts_with("disable-verity:")) {
+        return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
+                                                            std::placeholders::_1, false));
+    } else if (name.starts_with("enable-verity:")) {
+        return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+                                                             std::placeholders::_1, true));
     } else if (name.starts_with("tcpip:")) {
         name.remove_prefix(strlen("tcpip:"));
         std::string str(name);
@@ -385,15 +291,28 @@
                                      std::bind(restart_tcp_service, std::placeholders::_1, port));
     } else if (name.starts_with("usb:")) {
         return create_service_thread("usb", restart_usb_service);
+    }
+#endif
+
+    if (name.starts_with("dev:")) {
+        name.remove_prefix(strlen("dev:"));
+        return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
+    } else if (name.starts_with("jdwp:")) {
+        name.remove_prefix(strlen("jdwp:"));
+        std::string str(name);
+        return create_jdwp_connection_fd(atoi(str.c_str()));
+    } else if (name.starts_with("shell")) {
+        name.remove_prefix(strlen("shell"));
+        return ShellService(name, transport);
+    } else if (name.starts_with("exec:")) {
+        name.remove_prefix(strlen("exec:"));
+        return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
+    } else if (name.starts_with("sync:")) {
+        return create_service_thread("sync", file_sync_service);
     } else if (name.starts_with("reverse:")) {
         name.remove_prefix(strlen("reverse:"));
         return reverse_service(name, transport);
-    } else if (name.starts_with("disable-verity:")) {
-        return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
-                                                            std::placeholders::_1, false));
-    } else if (name.starts_with("enable-verity:")) {
-        return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
-                                                             std::placeholders::_1, true));
     } else if (name == "reconnect") {
         return create_service_thread(
                 "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
diff --git a/adb/daemon/set_verity_enable_state_service.h b/adb/daemon/set_verity_enable_state_service.h
index c1413c8..c0ed98e 100644
--- a/adb/daemon/set_verity_enable_state_service.h
+++ b/adb/daemon/set_verity_enable_state_service.h
@@ -18,4 +18,6 @@
 
 #include "adb_unique_fd.h"
 
+#if defined(__ANDROID__)
 void set_verity_enabled_state_service(unique_fd fd, bool enable);
+#endif
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 455595f..0794bcd 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -98,7 +98,10 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <private/android_logger.h>
+
+#if defined(__ANDROID__)
 #include <selinux/android.h>
+#endif
 
 #include "adb.h"
 #include "adb_io.h"
diff --git a/adb/daemon/transport_qemu.cpp b/adb/daemon/transport_qemu.cpp
new file mode 100644
index 0000000..8ad2572
--- /dev/null
+++ b/adb/daemon/transport_qemu.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 qemu_pipe.h before sysdeps, since it has inlined references to open, read, write.
+#include <qemu_pipe.h>
+
+#define TRACE_TAG TRANSPORT
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <android-base/properties.h>
+
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+
+/* A worker thread that monitors host connections, and registers a transport for
+ * every new host connection. This thread replaces server_socket_thread on
+ * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
+ * pipe to communicate with adbd daemon inside the guest. This is done in order
+ * to provide more robust communication channel between ADB host and guest. The
+ * main issue with server_socket_thread approach is that it runs on top of TCP,
+ * and thus is sensitive to network disruptions. For instance, the
+ * ConnectionManager may decide to reset all network connections, in which case
+ * the connection between ADB host and guest will be lost. To make ADB traffic
+ * independent from the network, we use here 'adb' QEMUD service to transfer data
+ * between the host, and the guest. See external/qemu/android/adb-*.* that
+ * implements the emulator's side of the protocol. Another advantage of using
+ * QEMUD approach is that ADB will be up much sooner, since it doesn't depend
+ * anymore on network being set up.
+ * The guest side of the protocol contains the following phases:
+ * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
+ *   is opened, and it becomes clear whether or not emulator supports that
+ *   protocol.
+ * - Wait for the ADB host to create connection with the guest. This is done by
+ *   sending an 'accept' request to the adb QEMUD service, and waiting on
+ *   response.
+ * - When new ADB host connection is accepted, the connection with adb QEMUD
+ *   service is registered as the transport, and a 'start' request is sent to the
+ *   adb QEMUD service, indicating that the guest is ready to receive messages.
+ *   Note that the guest will ignore messages sent down from the emulator before
+ *   the transport registration is completed. That's why we need to send the
+ *   'start' request after the transport is registered.
+ */
+void qemu_socket_thread(int port) {
+    /* 'accept' request to the adb QEMUD service. */
+    static const char _accept_req[] = "accept";
+    /* 'start' request to the adb QEMUD service. */
+    static const char _start_req[] = "start";
+    /* 'ok' reply from the adb QEMUD service. */
+    static const char _ok_resp[] = "ok";
+
+    char tmp[256];
+    char con_name[32];
+
+    adb_thread_setname("qemu socket");
+    D("transport: qemu_socket_thread() starting");
+
+    /* adb QEMUD service connection request. */
+    snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
+
+    /* Connect to the adb QEMUD service. */
+    unique_fd fd(qemu_pipe_open(con_name));
+    if (fd < 0) {
+        /* This could be an older version of the emulator, that doesn't
+         * implement adb QEMUD service. Fall back to the old TCP way. */
+        D("adb service is not available. Falling back to TCP socket.");
+        std::thread(server_socket_thread, port).detach();
+        return;
+    }
+
+    while (true) {
+        /*
+         * Wait till the host creates a new connection.
+         */
+
+        /* Send the 'accept' request. */
+        if (WriteFdExactly(fd.get(), _accept_req, strlen(_accept_req))) {
+            /* Wait for the response. In the response we expect 'ok' on success,
+             * or 'ko' on failure. */
+            if (!ReadFdExactly(fd.get(), tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
+                D("Accepting ADB host connection has failed.");
+            } else {
+                /* Host is connected. Register the transport, and start the
+                 * exchange. */
+                std::string serial = android::base::StringPrintf("host-%d", fd.get());
+                WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
+                register_socket_transport(std::move(fd), std::move(serial), port, 1,
+                                          [](atransport*) { return ReconnectResult::Abort; });
+            }
+
+            /* Prepare for accepting of the next ADB host connection. */
+            fd.reset(qemu_pipe_open(con_name));
+            if (fd < 0) {
+                D("adb service become unavailable.");
+                return;
+            }
+        } else {
+            D("Unable to send the '%s' request to ADB service.", _accept_req);
+            return;
+        }
+    }
+    D("transport: qemu_socket_thread() exiting");
+    return;
+}
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+bool use_qemu_goldfish() {
+    // Legacy way to detect if adbd should use the goldfish pipe is to check for
+    // ro.kernel.qemu, keep that behaviour for backward compatibility.
+    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+        return true;
+    }
+    // If service.adb.transport is present and is set to "goldfish", use the
+    // QEMUD pipe.
+    if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+        return true;
+    }
+    return false;
+}
diff --git a/adb/daemon/usb_dummy.cpp b/adb/daemon/usb_dummy.cpp
new file mode 100644
index 0000000..984bc25
--- /dev/null
+++ b/adb/daemon/usb_dummy.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <adbd/usb.h>
+
+#include <android-base/logging.h>
+
+int usb_write(usb_handle*, const void*, int) {
+    LOG(FATAL) << "unimplemented";
+    return -1;
+}
+
+int usb_read(usb_handle*, void*, int) {
+    LOG(FATAL) << "unimplemented";
+    return -1;
+}
+
+int usb_close(usb_handle*) {
+    LOG(FATAL) << "unimplemented";
+    return -1;
+}
+
+void usb_kick(usb_handle*) {
+    LOG(FATAL) << "unimplemented";
+}
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 400b12f..1ba0de0 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -22,6 +22,9 @@
     wrapper: "deployagent/deployagent.sh",
     proto: {
         type: "lite",
+    },
+    dex_preopt: {
+        enabled: false,
     }
 }
 
diff --git a/adb/services.cpp b/adb/services.cpp
index 8636657..73fed09 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -72,24 +72,23 @@
 }
 
 int service_to_fd(std::string_view name, atransport* transport) {
-    int ret = -1;
+    unique_fd ret;
 
     if (is_socket_spec(name)) {
         std::string error;
-        ret = socket_spec_connect(name, &error);
-        if (ret < 0) {
+        if (!socket_spec_connect(&ret, name, nullptr, nullptr, &error)) {
             LOG(ERROR) << "failed to connect to socket '" << name << "': " << error;
         }
     } else {
 #if !ADB_HOST
-        ret = daemon_service_to_fd(name, transport).release();
+        ret = daemon_service_to_fd(name, transport);
 #endif
     }
 
     if (ret >= 0) {
         close_on_exec(ret);
     }
-    return ret;
+    return ret.release();
 }
 
 #if ADB_HOST
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 4cddc84..cc67b6b 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -67,7 +67,7 @@
 });
 
 bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
-                           std::string* error) {
+                           std::string* serial, std::string* error) {
     if (!spec.starts_with("tcp:")) {
         *error = "specification is not tcp: ";
         *error += spec;
@@ -92,7 +92,7 @@
 
         // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
         //        on an address that isn't 'localhost' is unsupported.
-        if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, nullptr, error)) {
+        if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, serial, error)) {
             return false;
         }
 
@@ -139,63 +139,68 @@
 
     std::string error;
     std::string hostname;
-    if (!parse_tcp_socket_spec(spec, &hostname, nullptr, &error)) {
+    if (!parse_tcp_socket_spec(spec, &hostname, nullptr, nullptr, &error)) {
         return false;
     }
     return tcp_host_is_local(hostname);
 }
 
-int socket_spec_connect(std::string_view spec, std::string* error) {
-    if (spec.starts_with("tcp:")) {
+bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std::string* serial,
+                         std::string* error) {
+    if (address.starts_with("tcp:")) {
         std::string hostname;
-        int port;
-        if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
-            return -1;
+        int port_value = port ? *port : 0;
+        if (!parse_tcp_socket_spec(address, &hostname, &port_value, serial, error)) {
+            return false;
         }
 
-        int result;
         if (tcp_host_is_local(hostname)) {
-            result = network_loopback_client(port, SOCK_STREAM, error);
+            fd->reset(network_loopback_client(port_value, SOCK_STREAM, error));
         } else {
 #if ADB_HOST
-            result = network_connect(hostname, port, SOCK_STREAM, 0, error);
+            fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
 #else
             // Disallow arbitrary connections in adbd.
             *error = "adbd does not support arbitrary tcp connections";
-            return -1;
+            return false;
 #endif
         }
 
-        if (result >= 0) {
-            disable_tcp_nagle(result);
+        if (fd->get() > 0) {
+            disable_tcp_nagle(fd->get());
+            if (port) {
+                *port = port_value;
+            }
+            return true;
         }
-        return result;
+        return false;
     }
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (spec.starts_with(prefix)) {
+        if (address.starts_with(prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("socket type %s is unavailable on this platform",
                                       it.first.c_str());
-                return -1;
+                return false;
             }
 
-            return network_local_client(&spec[prefix.length()], it.second.socket_namespace,
-                                        SOCK_STREAM, error);
+            fd->reset(network_local_client(&address[prefix.length()], it.second.socket_namespace,
+                                           SOCK_STREAM, error));
+            return true;
         }
     }
 
     *error = "unknown socket specification: ";
-    *error += spec;
-    return -1;
+    *error += address;
+    return false;
 }
 
 int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port) {
     if (spec.starts_with("tcp:")) {
         std::string hostname;
         int port;
-        if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
+        if (!parse_tcp_socket_spec(spec, &hostname, &port, nullptr, error)) {
             return -1;
         }
 
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 5b06973..687d751 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -17,14 +17,18 @@
 #pragma once
 
 #include <string>
+#include <tuple>
+
+#include "adb_unique_fd.h"
 
 // Returns true if the argument starts with a plausible socket prefix.
 bool is_socket_spec(std::string_view spec);
 bool is_local_socket_spec(std::string_view spec);
 
-int socket_spec_connect(std::string_view spec, std::string* error);
+bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std::string* serial,
+                         std::string* error);
 int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
 
 // Exposed for testing.
 bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
-                           std::string* error);
+                           std::string* serial, std::string* error);
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index 40ce21c..f5ec0f1 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -21,34 +21,37 @@
 #include <gtest/gtest.h>
 
 TEST(socket_spec, parse_tcp_socket_spec) {
-    std::string hostname, error;
+    std::string hostname, error, serial;
     int port;
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &serial, &error));
     EXPECT_EQ("", hostname);
     EXPECT_EQ(5037, port);
+    EXPECT_EQ("", serial);
 
     // Bad ports:
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &serial, &error));
 
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("localhost", hostname);
     EXPECT_EQ(1234, port);
+    EXPECT_EQ("localhost:1234", serial);
 
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &serial, &error));
 
     // IPv6:
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("::1", hostname);
     EXPECT_EQ(1234, port);
+    EXPECT_EQ("[::1]:1234", serial);
 
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &serial, &error));
 }
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index dbc8920..d587589 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -719,7 +719,7 @@
 /**************************************************************************/
 /**************************************************************************/
 
-static int _init_winsock(void) {
+static void _init_winsock() {
     static std::once_flag once;
     std::call_once(once, []() {
         WSADATA wsaData;
@@ -743,11 +743,8 @@
         //    crypt32.dll which calls atexit() which tries to acquire the C
         //    Runtime lock that the other thread holds.
     });
-    return 0;
 }
 
-static int _winsock_init = _init_winsock();
-
 // Map a socket type to an explicit socket protocol instead of using the socket
 // protocol of 0. Explicit socket protocols are used by most apps and we should
 // do the same to reduce the chance of exercising uncommon code-paths that might
@@ -2621,14 +2618,13 @@
 }
 
 // Shadow UTF-8 environment variable name/value pairs that are created from
-// _wenviron the first time that adb_getenv() is called. Note that this is not
-// currently updated if putenv, setenv, unsetenv are called. Note that no
-// thread synchronization is done, but we're called early enough in
+// _wenviron by _init_env(). Note that this is not currently updated if putenv, setenv, unsetenv are
+// called. Note that no thread synchronization is done, but we're called early enough in
 // single-threaded startup that things work ok.
 static auto& g_environ_utf8 = *new std::unordered_map<std::string, char*>();
 
-// Make sure that shadow UTF-8 environment variables are setup.
-static void _ensure_env_setup() {
+// Setup shadow UTF-8 environment variables.
+static void _init_env() {
     // If some name/value pairs exist, then we've already done the setup below.
     if (g_environ_utf8.size() != 0) {
         return;
@@ -2681,8 +2677,6 @@
 // Version of getenv() that takes a UTF-8 environment variable name and
 // retrieves a UTF-8 value. Case-insensitive to match getenv() on Windows.
 char* adb_getenv(const char* name) {
-    _ensure_env_setup();
-
     // Case-insensitive search by searching for lowercase name in a map of
     // lowercase names.
     const auto it = g_environ_utf8.find(ToLower(std::string(name)));
@@ -2757,3 +2751,65 @@
 
     return 0;
 }
+
+#if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#if !defined(DISABLE_NEWLINE_AUTO_RETURN)
+#define DISABLE_NEWLINE_AUTO_RETURN 0x0008
+#endif
+
+static void _init_console() {
+    DWORD old_out_console_mode;
+
+    const HANDLE out = _get_console_handle(STDOUT_FILENO, &old_out_console_mode);
+    if (out == nullptr) {
+        return;
+    }
+
+    // Try to use ENABLE_VIRTUAL_TERMINAL_PROCESSING on the output console to process virtual
+    // terminal sequences on newer versions of Windows 10 and later.
+    // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
+    // On older OSes that don't support the flag, SetConsoleMode() will return an error.
+    // ENABLE_VIRTUAL_TERMINAL_PROCESSING also solves a problem where the last column of the
+    // console cannot be overwritten.
+    //
+    // Note that we don't use DISABLE_NEWLINE_AUTO_RETURN because it doesn't seem to be necessary.
+    // If we use DISABLE_NEWLINE_AUTO_RETURN, _console_write_utf8() would need to be modified to
+    // translate \n to \r\n.
+    if (!SetConsoleMode(out, old_out_console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
+        return;
+    }
+
+    // If SetConsoleMode() succeeded, the console supports virtual terminal processing, so we
+    // should set the TERM env var to match so that it will be propagated to adbd on devices.
+    //
+    // Below's direct manipulation of env vars and not g_environ_utf8 assumes that _init_env() has
+    // not yet been called. If this fails, _init_env() should be called after _init_console().
+    if (g_environ_utf8.size() > 0) {
+        LOG(FATAL) << "environment variables have already been converted to UTF-8";
+    }
+
+#pragma push_macro("getenv")
+#undef getenv
+#pragma push_macro("putenv")
+#undef putenv
+    if (getenv("TERM") == nullptr) {
+        // This is the same TERM value used by Gnome Terminal and the version of ssh included with
+        // Windows.
+        putenv("TERM=xterm-256color");
+    }
+#pragma pop_macro("putenv")
+#pragma pop_macro("getenv")
+}
+
+static bool _init_sysdeps() {
+    // _init_console() depends on _init_env() not being called yet.
+    _init_console();
+    _init_env();
+    _init_winsock();
+    return true;
+}
+
+static bool _sysdeps_init = _init_sysdeps();
diff --git a/adb/transport.cpp b/adb/transport.cpp
index f59a135..ae53597 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -68,6 +68,7 @@
 const char* const kFeaturePushSync = "push_sync";
 const char* const kFeatureApex = "apex";
 const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
+const char* const kFeatureAbb = "abb";
 
 namespace {
 
@@ -1013,7 +1014,8 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-            kFeatureShell2, kFeatureCmd, kFeatureStat2, kFeatureFixedPushMkdir, kFeatureApex
+            kFeatureShell2,         kFeatureCmd,  kFeatureStat2,
+            kFeatureFixedPushMkdir, kFeatureApex, kFeatureAbb,
             // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
             // to know about. Otherwise, the client can be stuck running an old
             // version of the server even after upgrading their copy of adb.
diff --git a/adb/transport.h b/adb/transport.h
index 790004f..3baeb1c 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -64,6 +64,8 @@
 extern const char* const kFeatureApex;
 // adbd has b/110953234 fixed.
 extern const char* const kFeatureFixedPushMkdir;
+// adbd supports android binder bridge (abb).
+extern const char* const kFeatureAbb;
 
 TransportId NextTransportId();
 
@@ -397,4 +399,14 @@
 
 asocket* create_device_tracker(bool long_output);
 
+#if !ADB_HOST
+void server_socket_thread(int port);
+
+#if defined(__ANDROID__)
+void qemu_socket_thread(int port);
+bool use_qemu_goldfish();
+#endif
+
+#endif
+
 #endif   /* __TRANSPORT_H */
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index dc87ac7..8885db4 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -45,6 +45,7 @@
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "socket_spec.h"
 #include "sysdeps/chrono.h"
 
 #if ADB_HOST
@@ -70,32 +71,17 @@
 
 std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
                                                     std::string* response) {
-    std::string serial;
-    std::string host;
+    unique_fd fd;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
-        D("failed to parse address: '%s'", address.c_str());
-        return std::make_tuple(unique_fd(), port, serial);
-    }
-
-    std::string error;
-    unique_fd fd(network_connect(host.c_str(), port, SOCK_STREAM, 10, &error));
-    if (fd == -1) {
-        *response = android::base::StringPrintf("unable to connect to %s: %s",
-                                                serial.c_str(), error.c_str());
+    std::string serial;
+    if (socket_spec_connect(&fd, "tcp:" + address, &port, &serial, response)) {
+        close_on_exec(fd);
+        if (!set_tcp_keepalive(fd, 1)) {
+            D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
+        }
         return std::make_tuple(std::move(fd), port, serial);
     }
-
-    D("client: connected %s remote on fd %d", serial.c_str(), fd.get());
-    close_on_exec(fd);
-    disable_tcp_nagle(fd);
-
-    // Send a TCP keepalive ping to the device every second so we can detect disconnects.
-    if (!set_tcp_keepalive(fd, 1)) {
-        D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
-    }
-
-    return std::make_tuple(std::move(fd), port, serial);
+    return std::make_tuple(unique_fd(), 0, "");
 }
 
 void connect_device(const std::string& address, std::string* response) {
@@ -243,16 +229,17 @@
     }
 }
 
-#else // ADB_HOST
+#else  // !ADB_HOST
 
-static void server_socket_thread(int port) {
+void server_socket_thread(int port) {
     unique_fd serverfd;
 
     adb_thread_setname("server socket");
     D("transport: server_socket_thread() starting");
     while (serverfd == -1) {
+        std::string spec = android::base::StringPrintf("tcp:%d", port);
         std::string error;
-        serverfd.reset(network_inaddr_any_server(port, SOCK_STREAM, &error));
+        serverfd.reset(socket_spec_listen(spec, &error));
         if (serverfd < 0) {
             D("server: cannot bind socket yet: %s", error.c_str());
             std::this_thread::sleep_for(1s);
@@ -276,143 +263,19 @@
     D("transport: server_socket_thread() exiting");
 }
 
-/* This is relevant only for ADB daemon running inside the emulator. */
-/*
- * Redefine open and write for qemu_pipe.h that contains inlined references
- * to those routines. We will redefine them back after qemu_pipe.h inclusion.
- */
-#undef open
-#undef read
-#undef write
-#define open    adb_open
-#define read    adb_read
-#define write   adb_write
-#include <qemu_pipe.h>
-#undef open
-#undef read
-#undef write
-#define open    ___xxx_open
-#define read    ___xxx_read
-#define write   ___xxx_write
+#endif
 
-/* A worker thread that monitors host connections, and registers a transport for
- * every new host connection. This thread replaces server_socket_thread on
- * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
- * pipe to communicate with adbd daemon inside the guest. This is done in order
- * to provide more robust communication channel between ADB host and guest. The
- * main issue with server_socket_thread approach is that it runs on top of TCP,
- * and thus is sensitive to network disruptions. For instance, the
- * ConnectionManager may decide to reset all network connections, in which case
- * the connection between ADB host and guest will be lost. To make ADB traffic
- * independent from the network, we use here 'adb' QEMUD service to transfer data
- * between the host, and the guest. See external/qemu/android/adb-*.* that
- * implements the emulator's side of the protocol. Another advantage of using
- * QEMUD approach is that ADB will be up much sooner, since it doesn't depend
- * anymore on network being set up.
- * The guest side of the protocol contains the following phases:
- * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
- *   is opened, and it becomes clear whether or not emulator supports that
- *   protocol.
- * - Wait for the ADB host to create connection with the guest. This is done by
- *   sending an 'accept' request to the adb QEMUD service, and waiting on
- *   response.
- * - When new ADB host connection is accepted, the connection with adb QEMUD
- *   service is registered as the transport, and a 'start' request is sent to the
- *   adb QEMUD service, indicating that the guest is ready to receive messages.
- *   Note that the guest will ignore messages sent down from the emulator before
- *   the transport registration is completed. That's why we need to send the
- *   'start' request after the transport is registered.
- */
-static void qemu_socket_thread(int port) {
-    /* 'accept' request to the adb QEMUD service. */
-    static const char _accept_req[] = "accept";
-    /* 'start' request to the adb QEMUD service. */
-    static const char _start_req[] = "start";
-    /* 'ok' reply from the adb QEMUD service. */
-    static const char _ok_resp[] = "ok";
-
-    char tmp[256];
-    char con_name[32];
-
-    adb_thread_setname("qemu socket");
-    D("transport: qemu_socket_thread() starting");
-
-    /* adb QEMUD service connection request. */
-    snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
-
-    /* Connect to the adb QEMUD service. */
-    unique_fd fd(qemu_pipe_open(con_name));
-    if (fd < 0) {
-        /* This could be an older version of the emulator, that doesn't
-         * implement adb QEMUD service. Fall back to the old TCP way. */
-        D("adb service is not available. Falling back to TCP socket.");
-        std::thread(server_socket_thread, port).detach();
-        return;
-    }
-
-    while (true) {
-        /*
-         * Wait till the host creates a new connection.
-         */
-
-        /* Send the 'accept' request. */
-        if (WriteFdExactly(fd.get(), _accept_req, strlen(_accept_req))) {
-            /* Wait for the response. In the response we expect 'ok' on success,
-             * or 'ko' on failure. */
-            if (!ReadFdExactly(fd.get(), tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
-                D("Accepting ADB host connection has failed.");
-            } else {
-                /* Host is connected. Register the transport, and start the
-                 * exchange. */
-                std::string serial = android::base::StringPrintf("host-%d", fd.get());
-                WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
-                register_socket_transport(std::move(fd), std::move(serial), port, 1,
-                                          [](atransport*) { return ReconnectResult::Abort; });
-            }
-
-            /* Prepare for accepting of the next ADB host connection. */
-            fd.reset(qemu_pipe_open(con_name));
-            if (fd < 0) {
-                D("adb service become unavailable.");
-                return;
-            }
-        } else {
-            D("Unable to send the '%s' request to ADB service.", _accept_req);
-            return;
-        }
-    }
-    D("transport: qemu_socket_thread() exiting");
-    return;
-}
-
-// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
-// goldfish) as the transport. This can either be explicitly set by the
-// service.adb.transport property, or be inferred from ro.kernel.qemu that is
-// set to "1" for ranchu/goldfish.
-static bool use_qemu_goldfish() {
-    // Legacy way to detect if adbd should use the goldfish pipe is to check for
-    // ro.kernel.qemu, keep that behaviour for backward compatibility.
-    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
-        return true;
-    }
-    // If service.adb.transport is present and is set to "goldfish", use the
-    // QEMUD pipe.
-    if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
-        return true;
-    }
-    return false;
-}
-
-#endif  // !ADB_HOST
-
-void local_init(int port)
-{
+void local_init(int port) {
     void (*func)(int);
     const char* debug_name = "";
 
 #if ADB_HOST
     func = client_socket_thread;
     debug_name = "client";
+#elif !defined(__ANDROID__)
+    // Host adbd.
+    func = server_socket_thread;
+    debug_name = "server";
 #else
     // For the adbd daemon in the system image we need to distinguish
     // between the device, and the emulator.
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index c8d12cf..2c890b4 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -161,22 +161,35 @@
 
 // Inline functions, so that they can be used header-only.
 template <typename Closer>
-inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write) {
+inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
+                 int flags = O_CLOEXEC) {
   int pipefd[2];
 
 #if defined(__linux__)
-  if (pipe2(pipefd, O_CLOEXEC) != 0) {
+  if (pipe2(pipefd, flags) != 0) {
     return false;
   }
 #else  // defined(__APPLE__)
+  if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
+    return false;
+  }
   if (pipe(pipefd) != 0) {
     return false;
   }
 
-  if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
-    close(pipefd[0]);
-    close(pipefd[1]);
-    return false;
+  if (flags & O_CLOEXEC) {
+    if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+      close(pipefd[0]);
+      close(pipefd[1]);
+      return false;
+    }
+  }
+  if (flags & O_NONBLOCK) {
+    if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+      close(pipefd[0]);
+      close(pipefd[1]);
+      return false;
+    }
   }
 #endif
 
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index ead2105..716fe95 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -132,6 +132,7 @@
         "libext2_uuid",
         "libext4_utils",
         "libfs_mgr",
+        "libgsi",
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 81f0560..8a72627 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -33,6 +33,7 @@
 #define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
 #define FB_CMD_UPDATE_SUPER "update-super"
 #define FB_CMD_OEM "oem"
+#define FB_CMD_GSI "gsi"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index e91598d..a2336bf 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -28,6 +28,7 @@
 #include <cutils/android_reboot.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr.h>
+#include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <uuid/uuid.h>
@@ -460,3 +461,22 @@
     bool wipe = (args.size() >= 3 && args[2] == "wipe");
     return UpdateSuper(device, args[1], wipe);
 }
+
+bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (!android::gsi::IsGsiInstalled()) {
+        return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
+    }
+    if (args.size() != 2) {
+        return device->WriteFail("Invalid arguments");
+    }
+    if (args[1] == "wipe") {
+        if (!android::gsi::UninstallGsi()) {
+            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
+        }
+    } else if (args[1] == "disable") {
+        if (!android::gsi::DisableGsi()) {
+            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
+        }
+    }
+    return device->WriteStatus(FastbootResult::OKAY, "Success");
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index bb1f988..afd6d08 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -48,3 +48,4 @@
 bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 7be721a..56fafab 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -53,6 +53,7 @@
               {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
               {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
               {FB_CMD_OEM, OemCmdHandler},
+              {FB_CMD_GSI, GsiHandler},
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0b8d9b2..d753f0f 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -387,6 +387,7 @@
             "                            Format a flash partition.\n"
             " set_active SLOT            Set the active slot.\n"
             " oem [COMMAND...]           Execute OEM-specific command.\n"
+            " gsi wipe|disable           Wipe or disable a GSI installation (fastbootd only).\n"
             "\n"
             "boot image:\n"
             " boot KERNEL [RAMDISK [SECOND]]\n"
@@ -1926,6 +1927,16 @@
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
             fb->ResizePartition(partition, size);
+        } else if (command == "gsi") {
+            if (args.empty()) {
+                syntax_error("missing 'wipe' or 'disable' argument");
+            } else if (args.size() == 1 && args[0] == "wipe") {
+                fb->RawCommand("gsi:wipe", "wiping GSI");
+            } else if (args.size() == 1 && args[0] == "disable") {
+                fb->RawCommand("gsi:disable", "disabling GSI");
+            } else {
+                syntax_error("expected 'wipe' or 'disable'");
+            }
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index bf58315..974e13e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -59,6 +59,7 @@
         "libfs_avb",
         "libfstab",
         "libdm",
+        "libgsi",
     ],
     export_static_lib_headers: [
         "libfs_avb",
@@ -99,6 +100,14 @@
         "fs_mgr_boot_config.cpp",
         "fs_mgr_slotselect.cpp",
     ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
     export_include_dirs: ["include_fstab"],
-    header_libs: ["libbase_headers"],
+    header_libs: [
+        "libbase_headers",
+        "libgsi_headers",
+    ],
 }
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index 817a0b8..cbbd3bc 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,2 +1,3 @@
 bowgotsai@google.com
+dvander@google.com
 tomcherry@google.com
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index 960410c..8784c94 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -53,9 +53,13 @@
     $ adb disable-verity
     $ adb reboot
 
-can be replaced with:
+*or*
 
-    $ adb reboot -R
+    $ adb remount
+
+can be replaced in both places with:
+
+    $ adb remount -R
 
 which will not reboot if everything is already prepared and ready
 to go.
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 9f6c550..ded3678 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -79,6 +79,9 @@
 
 #define ZRAM_CONF_DEV   "/sys/block/zram0/disksize"
 #define ZRAM_CONF_MCS   "/sys/block/zram0/max_comp_streams"
+#define ZRAM_BACK_DEV   "/sys/block/zram0/backing_dev"
+
+#define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
@@ -109,6 +112,7 @@
     FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
     FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
     FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
+    FS_STAT_ENABLE_VERITY_FAILED = 0x80000,
 };
 
 // TODO: switch to inotify()
@@ -439,6 +443,43 @@
     }
 }
 
+// Enable fs-verity if needed.
+static void tune_verity(const std::string& blk_device, const FstabEntry& entry,
+                        const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_verity = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_VERITY)) != 0;
+    bool want_verity = entry.fs_mgr_flags.fs_verity;
+
+    if (has_verity || !want_verity) {
+        return;
+    }
+
+    std::string verity_support;
+    if (!android::base::ReadFileToString(SYSFS_EXT4_VERITY, &verity_support)) {
+        LERROR << "Failed to open " << SYSFS_EXT4_VERITY;
+        return;
+    }
+
+    if (!(android::base::Trim(verity_support) == "supported")) {
+        LERROR << "Current ext4 verity not supported by kernel";
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable ext4 verity on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    LINFO << "Enabling ext4 verity on " << blk_device;
+
+    const char* argv[] = {TUNE2FS_BIN, "-O", "verity", blk_device.c_str()};
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 verity on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_VERITY_FAILED;
+    }
+}
+
 // Read the primary superblock from an f2fs filesystem.  On failure return
 // false.  If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
 #define F2FS_BLKSIZE 4096
@@ -510,12 +551,14 @@
     }
 
     if (is_extfs(entry.fs_type) &&
-        (entry.fs_mgr_flags.reserved_size || entry.fs_mgr_flags.file_encryption)) {
+        (entry.fs_mgr_flags.reserved_size || entry.fs_mgr_flags.file_encryption ||
+         entry.fs_mgr_flags.fs_verity)) {
         struct ext4_super_block sb;
 
         if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
             tune_reserved_size(blk_device, entry, &sb, &fs_stat);
             tune_encrypt(blk_device, entry, &sb, &fs_stat);
+            tune_verity(blk_device, entry, &sb, &fs_stat);
         }
     }
 
@@ -556,9 +599,17 @@
     mkdir(target.c_str(), 0755);
     errno = 0;
     unsigned long mountflags = entry.flags;
-    int ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
+    int ret = 0;
+    int save_errno = 0;
+    do {
+        if (save_errno == EAGAIN) {
+            PINFO << "Retrying mount (source=" << source << ",target=" << target
+                  << ",type=" << entry.fs_type << ")=" << ret << "(" << save_errno << ")";
+        }
+        ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
                     entry.fs_options.c_str());
-    int save_errno = errno;
+        save_errno = errno;
+    } while (ret && save_errno == EAGAIN);
     const char* target_missing = "";
     const char* source_missing = "";
     if (save_errno == ENOENT) {
@@ -954,6 +1005,17 @@
     std::map<std::string, std::string> device_map_;
 };
 
+static bool IsMountPointMounted(const std::string& mount_point) {
+    // Check if this is already mounted.
+    Fstab fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+        return false;
+    }
+    auto it = std::find_if(fstab.begin(), fstab.end(),
+                           [&](const auto& entry) { return entry.mount_point == mount_point; });
+    return it != fstab.end();
+}
+
 // When multiple fstab records share the same mount_point, it will try to mount each
 // one in turn, and ignore any duplicates after a first successful mount.
 // Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
@@ -970,9 +1032,18 @@
     for (size_t i = 0; i < fstab->size(); i++) {
         auto& current_entry = (*fstab)[i];
 
+        // If a filesystem should have been mounted in the first stage, we
+        // ignore it here. With one exception, if the filesystem is
+        // formattable, then it can only be formatted in the second stage,
+        // so we allow it to mount here.
+        if (current_entry.fs_mgr_flags.first_stage_mount &&
+            (!current_entry.fs_mgr_flags.formattable ||
+             IsMountPointMounted(current_entry.mount_point))) {
+            continue;
+        }
+
         // Don't mount entries that are managed by vold or not for the mount mode.
         if (current_entry.fs_mgr_flags.vold_managed || current_entry.fs_mgr_flags.recovery_only ||
-            current_entry.fs_mgr_flags.first_stage_mount ||
             ((mount_mode == MOUNT_MODE_LATE) && !current_entry.fs_mgr_flags.late_mount) ||
             ((mount_mode == MOUNT_MODE_EARLY) && current_entry.fs_mgr_flags.late_mount)) {
             continue;
@@ -1345,6 +1416,70 @@
     return 0;
 }
 
+static bool InstallZramDevice(const std::string& device) {
+    if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) {
+        PERROR << "Cannot write " << device << " in: " << ZRAM_BACK_DEV;
+        return false;
+    }
+    LINFO << "Success to set " << device << " to " << ZRAM_BACK_DEV;
+    return true;
+}
+
+static bool PrepareZramDevice(const std::string& loop, off64_t size, const std::string& bdev) {
+    if (loop.empty() && bdev.empty()) return true;
+
+    if (bdev.length()) {
+        return InstallZramDevice(bdev);
+    }
+
+    // Get free loopback
+    unique_fd loop_fd(TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
+    if (loop_fd.get() == -1) {
+        PERROR << "Cannot open loop-control";
+        return false;
+    }
+
+    int num = ioctl(loop_fd.get(), LOOP_CTL_GET_FREE);
+    if (num == -1) {
+        PERROR << "Cannot get free loop slot";
+        return false;
+    }
+
+    // Prepare target path
+    unique_fd target_fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0664)));
+    if (target_fd.get() == -1) {
+        PERROR << "Cannot open target path: " << loop;
+        return false;
+    }
+    if (fallocate(target_fd.get(), 0, 0, size) < 0) {
+        PERROR << "Cannot truncate target path: " << loop;
+        return false;
+    }
+
+    // Connect loopback (device_fd) to target path (target_fd)
+    std::string device = android::base::StringPrintf("/dev/block/loop%d", num);
+    unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
+    if (device_fd.get() == -1) {
+        PERROR << "Cannot open /dev/block/loop" << num;
+        return false;
+    }
+
+    if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get())) {
+        PERROR << "Cannot set loopback to target path";
+        return false;
+    }
+
+    // set block size & direct IO
+    if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096)) {
+        PWARNING << "Cannot set 4KB blocksize to /dev/block/loop" << num;
+    }
+    if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1)) {
+        PWARNING << "Cannot set direct_io to /dev/block/loop" << num;
+    }
+
+    return InstallZramDevice(device);
+}
+
 bool fs_mgr_swapon_all(const Fstab& fstab) {
     bool ret = true;
     for (const auto& entry : fstab) {
@@ -1353,6 +1488,10 @@
             continue;
         }
 
+        if (!PrepareZramDevice(entry.zram_loopback_path, entry.zram_loopback_size, entry.zram_backing_dev_path)) {
+            LERROR << "Skipping losetup for '" << entry.blk_device << "'";
+        }
+
         if (entry.zram_size > 0) {
             // A zram_size was specified, so we need to configure the
             // device.  There is no point in having multiple zram devices
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 53b47be..9d4f280 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -28,8 +28,10 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <libgsi/libgsi.h>
 
 #include "fs_mgr_priv.h"
 
@@ -43,6 +45,9 @@
     std::string key_dir;
     std::string verity_loc;
     std::string sysfs_path;
+    std::string zram_loopback_path;
+    uint64_t zram_loopback_size = 512 * 1024 * 1024; // 512MB by default
+    std::string zram_backing_dev_path;
     off64_t part_length = 0;
     std::string label;
     int partnum = -1;
@@ -117,6 +122,10 @@
         {"checkpoint=block", MF_CHECKPOINT_BLK},
         {"checkpoint=fs", MF_CHECKPOINT_FS},
         {"slotselect_other", MF_SLOTSELECT_OTHER},
+        {"zram_loopback_path=", MF_ZRAM_LOOPBACK_PATH},
+        {"zram_loopback_size=", MF_ZRAM_LOOPBACK_SIZE},
+        {"zram_backing_dev_path=", MF_ZRAM_BACKING_DEV_PATH},
+        {"fsverity", MF_FS_VERITY},
         {0, 0},
 };
 
@@ -344,6 +353,16 @@
                 } else if (flag == MF_SYSFS) {
                     /* The path to trigger device gc by idle-maint of vold. */
                     flag_vals->sysfs_path = arg;
+                } else if (flag == MF_ZRAM_LOOPBACK_PATH) {
+                    /* The path to use loopback for zram. */
+                    flag_vals->zram_loopback_path = arg;
+                } else if (flag == MF_ZRAM_LOOPBACK_SIZE) {
+                    if (!android::base::ParseByteCount(arg, &flag_vals->zram_loopback_size)) {
+                        LERROR << "Warning: zram_loopback_size = flag malformed";
+                    }
+                } else if (flag == MF_ZRAM_BACKING_DEV_PATH) {
+                    /* The path to use loopback for zram. */
+                    flag_vals->zram_backing_dev_path = arg;
                 }
                 break;
             }
@@ -569,6 +588,9 @@
         entry.logical_blk_size = flag_vals.logical_blk_size;
         entry.sysfs_path = std::move(flag_vals.sysfs_path);
         entry.vbmeta_partition = std::move(flag_vals.vbmeta_partition);
+        entry.zram_loopback_path = std::move(flag_vals.zram_loopback_path);
+        entry.zram_loopback_size = std::move(flag_vals.zram_loopback_size);
+        entry.zram_backing_dev_path = std::move(flag_vals.zram_backing_dev_path);
         if (entry.fs_mgr_flags.logical) {
             entry.logical_partition_name = entry.blk_device;
         }
@@ -638,6 +660,35 @@
     return boot_devices;
 }
 
+static void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+    auto iter = std::remove_if(fstab->begin(), fstab->end(),
+                               [&](const auto& entry) { return entry.mount_point == mount_point; });
+    fstab->erase(iter, fstab->end());
+}
+
+static void TransformFstabForGsi(Fstab* fstab) {
+    EraseFstabEntry(fstab, "/system");
+    EraseFstabEntry(fstab, "/data");
+
+    fstab->emplace_back(BuildGsiSystemFstabEntry());
+
+    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
+
+    FstabEntry userdata = {
+            .blk_device = "userdata_gsi",
+            .mount_point = "/data",
+            .fs_type = "ext4",
+            .flags = kFlags,
+            .reserved_size = 128 * 1024 * 1024,
+    };
+    userdata.fs_mgr_flags.wait = true;
+    userdata.fs_mgr_flags.check = true;
+    userdata.fs_mgr_flags.logical = true;
+    userdata.fs_mgr_flags.quota = true;
+    userdata.fs_mgr_flags.late_mount = true;
+    fstab->emplace_back(userdata);
+}
+
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
     auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (!fstab_file) {
@@ -645,10 +696,15 @@
         return false;
     }
 
-    if (!fs_mgr_read_fstab_file(fstab_file.get(), path == "/proc/mounts", fstab)) {
+    bool is_proc_mounts = path == "/proc/mounts";
+
+    if (!fs_mgr_read_fstab_file(fstab_file.get(), is_proc_mounts, fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
+    if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+        TransformFstabForGsi(fstab);
+    }
 
     return true;
 }
@@ -776,6 +832,8 @@
         free(fstab->recs[i].key_dir);
         free(fstab->recs[i].label);
         free(fstab->recs[i].sysfs_path);
+        free(fstab->recs[i].zram_loopback_path);
+        free(fstab->recs[i].zram_backing_dev_path);
     }
 
     /* Free the fstab_recs array created by calloc(3) */
@@ -873,6 +931,9 @@
     entry.erase_blk_size = fstab_rec->erase_blk_size;
     entry.logical_blk_size = fstab_rec->logical_blk_size;
     entry.sysfs_path = fstab_rec->sysfs_path;
+    entry.zram_loopback_path = fstab_rec->zram_loopback_path;
+    entry.zram_loopback_size = fstab_rec->zram_loopback_size;
+    entry.zram_backing_dev_path = fstab_rec->zram_backing_dev_path;
 
     return entry;
 }
@@ -916,6 +977,9 @@
         legacy_fstab->recs[i].erase_blk_size = fstab[i].erase_blk_size;
         legacy_fstab->recs[i].logical_blk_size = fstab[i].logical_blk_size;
         legacy_fstab->recs[i].sysfs_path = strdup(fstab[i].sysfs_path.c_str());
+        legacy_fstab->recs[i].zram_loopback_path = strdup(fstab[i].zram_loopback_path.c_str());
+        legacy_fstab->recs[i].zram_loopback_size = fstab[i].zram_loopback_size;
+        legacy_fstab->recs[i].zram_backing_dev_path = strdup(fstab[i].zram_backing_dev_path.c_str());
     }
     return legacy_fstab;
 }
@@ -1023,3 +1087,21 @@
 int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_CHECKPOINT_BLK;
 }
+
+int fs_mgr_is_fs_verity(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_FS_VERITY;
+}
+
+FstabEntry BuildGsiSystemFstabEntry() {
+    FstabEntry system = {
+            .blk_device = "system_gsi",
+            .mount_point = "/system",
+            .fs_type = "ext4",
+            .flags = MS_RDONLY,
+            .fs_options = "barrier=1",
+    };
+    system.fs_mgr_flags.wait = true;
+    system.fs_mgr_flags.logical = true;
+    system.fs_mgr_flags.first_stage_mount = true;
+    return system;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 6364ca9..2c4299a 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -48,6 +48,7 @@
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
+#include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 
@@ -136,9 +137,9 @@
 
 bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
     // readonly filesystem, can not be mount -o remount,rw
-    // if squashfs or if free space is (near) zero making such a remount
+    // for squashfs, erofs or if free space is (near) zero making such a remount
     // virtually useless, or if there are shared blocks that prevent remount,rw
-    if ("squashfs" == entry->fs_type || !fs_mgr_filesystem_has_space(entry->mount_point)) {
+    if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
         return true;
     }
     if (entry->fs_mgr_flags.logical) {
@@ -625,8 +626,8 @@
 
 // Only a suggestion for _first_ try during mounting
 std::string fs_mgr_overlayfs_scratch_mount_type() {
-    if (!access(kMkF2fs.c_str(), X_OK)) return "f2fs";
-    if (!access(kMkExt4.c_str(), X_OK)) return "ext4";
+    if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_access("/sys/fs/f2fs")) return "f2fs";
+    if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_access("/sys/fs/ext4")) return "ext4";
     return "auto";
 }
 
@@ -641,7 +642,11 @@
         // Create from within single super device;
         auto& dm = DeviceMapper::Instance();
         const auto partition_name = android::base::Basename(kScratchMountPoint);
-        if (!dm.GetDmDevicePathByName(partition_name, &path)) return "";
+        if (!dm.GetDmDevicePathByName(partition_name, &path)) {
+            // non-DAP A/B device?
+            if (fs_mgr_access(super_device)) return "";
+            path = kPhysicalDevice + "system" + (slot_number ? "_a" : "_b");
+        }
     }
     return scratch_device_cache = path;
 }
@@ -802,8 +807,9 @@
 bool fs_mgr_overlayfs_invalid() {
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
 
-    // in recovery or fastbootd mode, not allowed!
+    // in recovery, fastbootd, or gsi mode, not allowed!
     if (fs_mgr_access("/system/bin/recovery")) return true;
+    if (android::gsi::IsGsiRunning()) return true;
 
     return false;
 }
@@ -827,8 +833,9 @@
                                                    true /* readonly */)) {
                     auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
                     fs_mgr_overlayfs_umount_scratch();
-                    if (has_overlayfs_dir)
+                    if (has_overlayfs_dir) {
                         fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
+                    }
                 }
             }
         }
@@ -880,10 +887,6 @@
     for (const auto& overlay_mount_point : kOverlayMountPoints) {
         if (backing && backing[0] && (overlay_mount_point != backing)) continue;
         if (overlay_mount_point == kScratchMountPoint) {
-            if (!fs_mgr_rw_access(fs_mgr_overlayfs_super_device(fs_mgr_overlayfs_slot_number())) ||
-                !fs_mgr_overlayfs_has_logical(fstab)) {
-                continue;
-            }
             if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
         } else {
             if (std::find_if(fstab.begin(), fstab.end(), [&overlay_mount_point](const auto& entry) {
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7842ca2..3b9ddee 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -122,6 +122,14 @@
                           0x80000000
 #define MF_SLOTSELECT_OTHER  \
                          0x100000000
+#define MF_ZRAM_LOOPBACK_PATH    \
+                         0x200000000
+#define MF_ZRAM_LOOPBACK_SIZE    \
+                         0x400000000
+#define MF_ZRAM_BACKING_DEV_PATH \
+                         0x800000000
+#define MF_FS_VERITY  \
+                         0x1000000000
 // clang-format on
 
 #define DM_BUF_SIZE 4096
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 3cb718c..41cd7dd 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -34,6 +34,12 @@
     return "";
 }
 
+// Returns "_b" or "_a", which is *the other* slot of androidboot.slot_suffix
+// in kernel cmdline, or an empty string if that parameter does not exist.
+std::string fs_mgr_get_other_slot_suffix() {
+    return other_suffix(fs_mgr_get_slot_suffix());
+}
+
 // Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
 // if that parameter does not exist.
 std::string fs_mgr_get_slot_suffix() {
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 0997254..38f96c0 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -59,6 +59,9 @@
     off64_t erase_blk_size;
     off64_t logical_blk_size;
     char* sysfs_path;
+    char* zram_loopback_path;
+    uint64_t zram_loopback_size;
+    char* zram_backing_dev_path;
 };
 
 struct fstab* fs_mgr_read_fstab_default();
@@ -92,8 +95,10 @@
 int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab);
 int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab);
 int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
+int fs_mgr_is_fs_verity(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
+std::string fs_mgr_get_other_slot_suffix();
 std::set<std::string> fs_mgr_get_boot_devices();
 
 struct FstabEntry {
@@ -119,6 +124,9 @@
     off64_t logical_blk_size = 0;
     std::string sysfs_path;
     std::string vbmeta_partition;
+    std::string zram_loopback_path;
+    uint64_t zram_loopback_size;
+    std::string zram_backing_dev_path;
 
     // TODO: Remove this union once fstab_rec is deprecated. It only serves as a
     // convenient way to convert between fstab_rec::fs_mgr_flags and these bools.
@@ -168,6 +176,10 @@
 
             // bit 32
             bool slot_select_other : 1;
+            bool zram_loopback_path : 1;
+            bool zram_loopback_size : 1;
+            bool zram_backing_dev_path : 1;
+            bool fs_verity : 1;
         };
     } fs_mgr_flags;
 
@@ -187,3 +199,6 @@
 FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec);
 Fstab LegacyFstabToFstab(const struct fstab* legacy_fstab);
 fstab* FstabToLegacyFstab(const Fstab& fstab);
+
+// Helper method to build a GSI fstab entry for mounting /system.
+FstabEntry BuildGsiSystemFstabEntry();
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index fc1aafb..c8c2d83 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -33,6 +33,11 @@
         "libbase_headers",
         "liblog_headers",
     ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 cc_test {
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 4a0d4b5..6ccdb57 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -220,7 +220,7 @@
 }
 
 static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
-                         uint64_t file_size) {
+                         uint64_t file_size, std::function<bool(uint64_t, uint64_t)> on_progress) {
     // Reserve space for the file on the file system and write it out to make sure the extents
     // don't come back unwritten. Return from this function with the kernel file offset set to 0.
     // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
@@ -245,12 +245,22 @@
         return false;
     }
 
+    int permille = -1;
     for (; offset < file_size; offset += blocksz) {
         if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
             PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
                         << " in file " << file_path;
             return false;
         }
+        // Don't invoke the callback every iteration - wait until a significant
+        // chunk (here, 1/1000th) of the data has been processed.
+        int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
+        if (new_permille != permille) {
+            if (on_progress && !on_progress(offset, file_size)) {
+                return false;
+            }
+            permille = new_permille;
+        }
     }
 
     if (lseek64(file_fd, 0, SEEK_SET) < 0) {
@@ -264,6 +274,10 @@
         return false;
     }
 
+    // Send one last progress notification.
+    if (on_progress && !on_progress(file_size, file_size)) {
+        return false;
+    }
     return true;
 }
 
@@ -304,7 +318,6 @@
     return true;
 }
 
-#if 0
 static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
     if (fs_type == EXT4_SUPER_MAGIC) {
         // No pinning necessary for ext4. The blocks, once allocated, are expected
@@ -345,7 +358,21 @@
     }
     return moved_blocks_nr == 0;
 }
-#endif
+
+bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
+    android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));
+    if (fd < 0) {
+        PLOG(ERROR) << "open: " << file_path;
+        return false;
+    }
+
+    struct statfs64 sfs;
+    if (fstatfs64(fd, &sfs)) {
+        PLOG(ERROR) << "fstatfs64: " << file_path;
+        return false;
+    }
+    return IsFilePinned(fd, file_path, sfs.f_type);
+}
 
 static void LogExtent(uint32_t num, const struct fiemap_extent& ext) {
     LOG(INFO) << "Extent #" << num;
@@ -414,7 +441,8 @@
     return last_extent_seen;
 }
 
-FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create) {
+FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
+                                   std::function<bool(uint64_t, uint64_t)> progress) {
     // if 'create' is false, open an existing file and do not truncate.
     int open_flags = O_RDWR | O_CLOEXEC;
     if (create) {
@@ -476,14 +504,16 @@
     }
 
     if (create) {
-        if (!AllocateFile(file_fd, abs_path, blocksz, file_size)) {
+        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, std::move(progress))) {
+            LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
+                       << " bytes";
             cleanup(abs_path, create);
             return nullptr;
         }
     }
 
     // f2fs may move the file blocks around.
-    if (!PinFile(file_fd, file_path, fs_type)) {
+    if (!PinFile(file_fd, abs_path, fs_type)) {
         cleanup(abs_path, create);
         LOG(ERROR) << "Failed to pin the file in storage";
         return nullptr;
@@ -538,13 +568,11 @@
         return false;
     }
 
-#if 0
-    // TODO(b/122138114): check why this fails.
     if (!IsFilePinned(file_fd_, file_path_, fs_type_)) {
         LOG(ERROR) << "Failed write: file " << file_path_ << " is not pinned";
         return false;
     }
-#endif
+
     // find extents that must be written to and then write one at a time.
     uint32_t num_extent = 1;
     uint32_t buffer_offset = 0;
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index 6dff0e8..3d20ff3 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -51,6 +51,8 @@
         testfile = ::android::base::StringPrintf("%s/testdata/%s", exec_dir.c_str(), tinfo->name());
     }
 
+    void TearDown() override { unlink(testfile.c_str()); }
+
     // name of the file we use for testing
     std::string testfile;
 };
@@ -83,6 +85,31 @@
     EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
 }
 
+TEST_F(FiemapWriterTest, CheckProgress) {
+    std::vector<uint64_t> expected{
+            0,
+            4096,
+    };
+    size_t invocations = 0;
+    auto callback = [&](uint64_t done, uint64_t total) -> bool {
+        EXPECT_LT(invocations, expected.size());
+        EXPECT_EQ(done, expected[invocations]);
+        EXPECT_EQ(total, 4096);
+        invocations++;
+        return true;
+    };
+
+    auto ptr = FiemapWriter::Open(testfile, 4096, true, std::move(callback));
+    EXPECT_NE(ptr, nullptr);
+    EXPECT_EQ(invocations, 2);
+}
+
+TEST_F(FiemapWriterTest, CheckPinning) {
+    auto ptr = FiemapWriter::Open(testfile, 4096);
+    ASSERT_NE(ptr, nullptr);
+    EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));
+}
+
 TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
     FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
     EXPECT_EQ(fptr->size(), 4096);
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
index ae61344..a0085cf 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <functional>
 #include <string>
 #include <vector>
 
@@ -36,9 +37,25 @@
   public:
     // Factory method for FiemapWriter.
     // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
-    // to the given file directly using raw block i/o.
+    // to the given file directly using raw block i/o. The optional progress callback will be
+    // invoked, if create is true, while the file is being initialized. It receives the bytes
+    // written and the number of total bytes. If the callback returns false, the operation will
+    // fail.
     static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
-                                bool create = true);
+                                bool create = true,
+                                std::function<bool(uint64_t, uint64_t)> progress = {});
+
+    // Check that a file still has the same extents since it was last opened with FiemapWriter,
+    // assuming the file was not resized outside of FiemapWriter. Returns false either on error
+    // or if the file was not pinned.
+    //
+    // This will always return true on Ext4. On F2FS, it will return true if either of the
+    // following cases are true:
+    //   - The file was never pinned.
+    //   - The file is pinned and has not been moved by the GC.
+    // Thus, this method should only be called for pinned files (such as those returned by
+    // FiemapWriter::Open).
+    static bool HasPinnedExtents(const std::string& file_path);
 
     // Syncs block device writes.
     bool Flush() const;
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 6f368e4..191e803 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -56,6 +56,11 @@
         "tests/data/*",
     ],
     static_libs: [
+        "libavb",
+        "libavb_host_sysdeps",
+        "libdm",
+        "libfs_avb",
+        "libfstab",
         "libgtest_host",
     ],
     shared_libs: [
@@ -86,8 +91,12 @@
     static_libs: [
         "libfs_avb_test_util",
     ],
+    shared_libs: [
+        "libcrypto",
+    ],
     srcs: [
         "tests/basic_test.cpp",
+        "tests/fs_avb_test.cpp",
     ],
 }
 
@@ -96,10 +105,11 @@
     defaults: ["libfs_avb_host_test_defaults"],
     static_libs: [
         "libfs_avb_test_util",
-        "libfstab",
     ],
     srcs: [
+        "avb_util.cpp",
         "util.cpp",
+        "tests/avb_util_test.cpp",
         "tests/util_test.cpp",
     ],
 }
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index 3b0ef0b..6a3e2c0 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+
 #include <string>
 
 #include <android-base/macros.h>
@@ -186,10 +187,12 @@
             avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
                             AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &avb_slot_data);
 
+    if (!avb_slot_data) return verify_result;
     // Copies avb_slot_data->vbmeta_images[].
     for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
         out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data,
-                                                   avb_slot_data->vbmeta_images[i].vbmeta_size));
+                                                   avb_slot_data->vbmeta_images[i].vbmeta_size,
+                                                   avb_slot_data->vbmeta_images[i].partition_name));
     }
 
     // Free the local resource.
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 0ceb6ee..f57c9d6 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -16,19 +16,67 @@
 
 #include "avb_util.h"
 
+#include <unistd.h>
+
 #include <array>
 #include <sstream>
 
 #include <android-base/file.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
 #include "util.h"
 
+using android::base::StartsWith;
 using android::base::unique_fd;
 
 namespace android {
 namespace fs_mgr {
 
+// Helper functions to print enum class VBMetaVerifyResult.
+const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
+    // clang-format off
+    static const char* const name[] = {
+        "ResultSuccess",
+        "ResultError",
+        "ResultErrorVerification",
+        "ResultUnknown",
+    };
+    // clang-format on
+
+    uint32_t index = static_cast<uint32_t>(result);
+    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+    if (index >= unknown_index) {
+        index = unknown_index;
+    }
+
+    return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
+    os << VBMetaVerifyResultToString(result);
+    return os;
+}
+
+// class VBMetaData
+// ----------------
+std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
+    auto vbmeta_header(std::make_unique<AvbVBMetaImageHeader>());
+
+    if (!vbmeta_header) return nullptr;
+
+    /* Byteswap the header. */
+    avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
+                                               vbmeta_header.get());
+    if (update_vbmeta_size) {
+        vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
+                       vbmeta_header->authentication_data_block_size +
+                       vbmeta_header->auxiliary_data_block_size;
+    }
+
+    return vbmeta_header;
+}
+
 // 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
@@ -173,5 +221,300 @@
     return true;
 }
 
+// Converts a AVB partition_name (without A/B suffix) to a device partition name.
+// e.g.,       "system" => "system_a",
+//       "system_other" => "system_b".
+//
+// If the device is non-A/B, converts it to a partition name without suffix.
+// e.g.,       "system" => "system",
+//       "system_other" => "system".
+std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
+                                         const std::string& ab_suffix,
+                                         const std::string& ab_other_suffix) {
+    bool is_other_slot = false;
+    std::string sanitized_partition_name(avb_partition_name);
+
+    auto other_suffix = sanitized_partition_name.rfind("_other");
+    if (other_suffix != std::string::npos) {
+        sanitized_partition_name.erase(other_suffix);  // converts system_other => system
+        is_other_slot = true;
+    }
+
+    auto append_suffix = is_other_slot ? ab_other_suffix : ab_suffix;
+    return sanitized_partition_name + append_suffix;
+}
+
+off64_t GetTotalSize(int fd) {
+    off64_t saved_current = lseek64(fd, 0, SEEK_CUR);
+    if (saved_current == -1) {
+        PERROR << "Failed to get current position";
+        return -1;
+    }
+
+    // lseek64() returns the resulting offset location from the beginning of the file.
+    off64_t total_size = lseek64(fd, 0, SEEK_END);
+    if (total_size == -1) {
+        PERROR << "Failed to lseek64 to end of the partition";
+        return -1;
+    }
+
+    // Restores the original offset.
+    if (lseek64(fd, saved_current, SEEK_SET) == -1) {
+        PERROR << "Failed to lseek64 to the original offset: " << saved_current;
+    }
+
+    return total_size;
+}
+
+std::unique_ptr<AvbFooter> GetAvbFooter(int fd) {
+    std::array<uint8_t, AVB_FOOTER_SIZE> footer_buf;
+    auto footer(std::make_unique<AvbFooter>());
+
+    off64_t footer_offset = GetTotalSize(fd) - AVB_FOOTER_SIZE;
+
+    ssize_t num_read =
+            TEMP_FAILURE_RETRY(pread64(fd, footer_buf.data(), AVB_FOOTER_SIZE, footer_offset));
+    if (num_read < 0 || num_read != AVB_FOOTER_SIZE) {
+        PERROR << "Failed to read AVB footer";
+        return nullptr;
+    }
+
+    if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf.data(), footer.get())) {
+        PERROR << "AVB footer verification failed.";
+        return nullptr;
+    }
+
+    return footer;
+}
+
+bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob) {
+    if (expected_key_blob.empty()) {  // no expectation of the key, return true.
+        return true;
+    }
+    if (expected_key_blob.size() != length) {
+        return false;
+    }
+    if (0 == memcmp(key, expected_key_blob.data(), length)) {
+        return true;
+    }
+    return false;
+}
+
+VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
+                                         const std::string& expected_public_key_blob) {
+    const uint8_t* pk_data;
+    size_t pk_len;
+    ::AvbVBMetaVerifyResult vbmeta_ret;
+
+    vbmeta_ret = avb_vbmeta_image_verify(vbmeta.data(), vbmeta.size(), &pk_data, &pk_len);
+
+    switch (vbmeta_ret) {
+        case AVB_VBMETA_VERIFY_RESULT_OK:
+            if (pk_data == nullptr || pk_len <= 0) {
+                LERROR << vbmeta.partition()
+                       << ": Error verifying vbmeta image: failed to get public key";
+                return VBMetaVerifyResult::kError;
+            }
+            if (!VerifyPublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
+                LERROR << vbmeta.partition() << ": Error verifying vbmeta image: public key used to"
+                       << " sign data does not match key in chain descriptor";
+                return VBMetaVerifyResult::kErrorVerification;
+            }
+            return VBMetaVerifyResult::kSuccess;
+        case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
+        case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
+        case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
+            LERROR << vbmeta.partition() << ": Error verifying vbmeta image: "
+                   << avb_vbmeta_verify_result_to_string(vbmeta_ret);
+            return VBMetaVerifyResult::kErrorVerification;
+        case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
+            // No way to continue this case.
+            LERROR << vbmeta.partition() << ": Error verifying vbmeta image: invalid vbmeta header";
+            break;
+        case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
+            // No way to continue this case.
+            LERROR << vbmeta.partition()
+                   << ": Error verifying vbmeta image: unsupported AVB version";
+            break;
+        default:
+            LERROR << "Unknown vbmeta image verify return value: " << vbmeta_ret;
+            break;
+    }
+
+    return VBMetaVerifyResult::kError;
+}
+
+std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
+                                             const std::string& expected_public_key_blob,
+                                             VBMetaVerifyResult* out_verify_result) {
+    uint64_t vbmeta_offset = 0;
+    uint64_t vbmeta_size = VBMetaData::kMaxVBMetaSize;
+    bool is_vbmeta_partition = StartsWith(partition_name, "vbmeta");
+
+    if (!is_vbmeta_partition) {
+        std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+        if (!footer) {
+            return nullptr;
+        }
+        vbmeta_offset = footer->vbmeta_offset;
+        vbmeta_size = footer->vbmeta_size;
+    }
+
+    if (vbmeta_size > VBMetaData::kMaxVBMetaSize) {
+        LERROR << "VbMeta size in footer exceeds kMaxVBMetaSize";
+        return nullptr;
+    }
+
+    auto vbmeta = std::make_unique<VBMetaData>(vbmeta_size, partition_name);
+    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, vbmeta->data(), vbmeta_size, vbmeta_offset));
+    // Allows partial read for vbmeta partition, because its vbmeta_size is kMaxVBMetaSize.
+    if (num_read < 0 || (!is_vbmeta_partition && static_cast<uint64_t>(num_read) != vbmeta_size)) {
+        PERROR << partition_name << ": Failed to read vbmeta at offset " << vbmeta_offset
+               << " with size " << vbmeta_size;
+        return nullptr;
+    }
+
+    auto verify_result = VerifyVBMetaSignature(*vbmeta, expected_public_key_blob);
+    if (out_verify_result != nullptr) *out_verify_result = verify_result;
+
+    if (verify_result == VBMetaVerifyResult::kSuccess ||
+        verify_result == VBMetaVerifyResult::kErrorVerification) {
+        return vbmeta;
+    }
+
+    return nullptr;
+}
+
+bool RollbackDetected(const std::string& partition_name ATTRIBUTE_UNUSED,
+                      uint64_t rollback_index ATTRIBUTE_UNUSED) {
+    // TODO(bowgotsai): Support rollback protection.
+    return false;
+}
+
+std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error) {
+    CHECK(fatal_error != nullptr);
+    std::vector<ChainInfo> chain_partitions;
+
+    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) {
+        return {};
+    }
+
+    for (size_t i = 0; i < num_descriptors; i++) {
+        AvbDescriptor desc;
+        if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
+            LERROR << "Descriptor[" << i << "] is invalid in vbmeta: " << vbmeta.partition();
+            *fatal_error = true;
+            return {};
+        }
+        if (desc.tag == AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) {
+            AvbChainPartitionDescriptor chain_desc;
+            if (!avb_chain_partition_descriptor_validate_and_byteswap(
+                        (AvbChainPartitionDescriptor*)descriptors[i], &chain_desc)) {
+                LERROR << "Chain descriptor[" << i
+                       << "] is invalid in vbmeta: " << vbmeta.partition();
+                *fatal_error = true;
+                return {};
+            }
+            const char* chain_partition_name =
+                    ((const char*)descriptors[i]) + sizeof(AvbChainPartitionDescriptor);
+            const char* chain_public_key_blob =
+                    chain_partition_name + chain_desc.partition_name_len;
+            chain_partitions.emplace_back(
+                    std::string(chain_partition_name, chain_desc.partition_name_len),
+                    std::string(chain_public_key_blob, chain_desc.public_key_len));
+        }
+    }
+
+    return chain_partitions;
+}
+
+VBMetaVerifyResult LoadAndVerifyVbmetaImpl(
+        const std::string& partition_name, const std::string& ab_suffix,
+        const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+        bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+        std::function<std::string(const std::string&)> device_path_constructor,
+        bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images) {
+    // Ensures the device path (might be a symlink created by init) is ready to access.
+    auto device_path = device_path_constructor(
+            AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix));
+    if (!WaitForFile(device_path, 1s)) {
+        PERROR << "No such partition: " << device_path;
+        return VBMetaVerifyResult::kError;
+    }
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(device_path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PERROR << "Failed to open: " << device_path;
+        return VBMetaVerifyResult::kError;
+    }
+
+    VBMetaVerifyResult verify_result;
+    std::unique_ptr<VBMetaData> vbmeta =
+            VerifyVBMetaData(fd, partition_name, expected_public_key_blob, &verify_result);
+    if (!vbmeta) {
+        LERROR << partition_name << ": Failed to load vbmeta, result: " << verify_result;
+        return VBMetaVerifyResult::kError;
+    }
+
+    if (!allow_verification_error && verify_result == VBMetaVerifyResult::kErrorVerification) {
+        LERROR << partition_name << ": allow verification error is not allowed";
+        return VBMetaVerifyResult::kError;
+    }
+
+    std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
+            vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+    if (!vbmeta_header) {
+        LERROR << partition_name << ": Failed to get vbmeta header";
+        return VBMetaVerifyResult::kError;
+    }
+
+    if (rollback_protection && RollbackDetected(partition_name, vbmeta_header->rollback_index)) {
+        return VBMetaVerifyResult::kError;
+    }
+
+    // vbmeta flags can only be set by the top-level vbmeta image.
+    if (is_chained_vbmeta && vbmeta_header->flags != 0) {
+        LERROR << partition_name << ": chained vbmeta image has non-zero flags";
+        return VBMetaVerifyResult::kError;
+    }
+
+    if (out_vbmeta_images) {
+        out_vbmeta_images->emplace_back(std::move(*vbmeta));
+    }
+
+    // If verification has been disabled by setting a bit in the image, we're done.
+    if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
+        LWARNING << "VERIFICATION_DISABLED bit is set for partition: " << partition_name;
+        return verify_result;
+    }
+
+    if (load_chained_vbmeta) {
+        bool fatal_error = false;
+        auto chain_partitions = GetChainPartitionInfo(*out_vbmeta_images->rbegin(), &fatal_error);
+        if (fatal_error) {
+            return VBMetaVerifyResult::kError;
+        }
+        for (auto& chain : chain_partitions) {
+            auto sub_ret = LoadAndVerifyVbmetaImpl(
+                    chain.partition_name, ab_suffix, ab_other_suffix, chain.public_key_blob,
+                    allow_verification_error, load_chained_vbmeta, rollback_protection,
+                    device_path_constructor, true, /* is_chained_vbmeta */
+                    out_vbmeta_images);
+            if (sub_ret != VBMetaVerifyResult::kSuccess) {
+                verify_result = sub_ret;  // might be 'ERROR' or 'ERROR VERIFICATION'.
+                if (verify_result == VBMetaVerifyResult::kError) {
+                    return verify_result;  // stop here if we got an 'ERROR'.
+                }
+            }
+        }
+    }
+
+    return verify_result;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index b81e931..babbfef 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -16,9 +16,11 @@
 
 #pragma once
 
+#include <ostream>
 #include <string>
 #include <vector>
 
+#include <fstab/fstab.h>
 #include <libavb/libavb.h>
 #include <libdm/dm.h>
 
@@ -27,6 +29,22 @@
 namespace android {
 namespace fs_mgr {
 
+enum class VBMetaVerifyResult {
+    kSuccess = 0,
+    kError = 1,
+    kErrorVerification = 2,
+};
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);
+
+struct ChainInfo {
+    std::string partition_name;
+    std::string public_key_blob;
+
+    ChainInfo(const std::string& chain_partition_name, const std::string& chain_public_key_blob)
+        : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
+};
+
 // AvbHashtreeDescriptor to dm-verity table setup.
 bool GetHashtreeDescriptor(const std::string& partition_name,
                            const std::vector<VBMetaData>& vbmeta_images,
@@ -41,5 +59,37 @@
                            const std::string& salt, const std::string& root_digest,
                            bool wait_for_verity_dev);
 
+// Maps AVB partition name to a device partition name.
+std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
+                                         const std::string& ab_suffix,
+                                         const std::string& ab_other_suffix);
+
+// AvbFooter and AvbMetaImage maninpulations.
+off64_t GetTotalSize(int fd);
+
+std::unique_ptr<AvbFooter> GetAvbFooter(int fd);
+
+std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
+                                             const std::string& expected_public_key_blob,
+                                             VBMetaVerifyResult* out_verify_result);
+
+VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
+                                         const std::string& expected_public_key_blob);
+
+bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);
+
+// Detects if whether a partition contains a rollback image.
+bool RollbackDetected(const std::string& partition_name, uint64_t rollback_index);
+
+// Extracts chain partition info.
+std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error);
+
+VBMetaVerifyResult LoadAndVerifyVbmetaImpl(
+        const std::string& partition_name, const std::string& ab_suffix,
+        const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+        bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+        std::function<std::string(const std::string&)> device_path_constructor,
+        bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 957aa87..9f8ad53 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -39,6 +39,7 @@
 
 using android::base::Basename;
 using android::base::ParseUint;
+using android::base::ReadFileToString;
 using android::base::StringPrintf;
 
 namespace android {
@@ -59,6 +60,51 @@
     return std::make_pair(total_size, matched);
 }
 
+template <typename Hasher>
+std::pair<std::string, size_t> CalculateVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images) {
+    std::string digest;
+    size_t total_size = 0;
+
+    Hasher hasher;
+    for (const auto& vbmeta : vbmeta_images) {
+        hasher.update(vbmeta.data(), vbmeta.size());
+        total_size += vbmeta.size();
+    }
+
+    // Converts digest bytes to a hex string.
+    digest = BytesToHex(hasher.finalize(), Hasher::DIGEST_SIZE);
+    return std::make_pair(digest, total_size);
+}
+
+// Helper functions to dump enum class AvbHandleStatus.
+const char* AvbHandleStatusToString(AvbHandleStatus status) {
+    // clang-format off
+    static const char* const name[] = {
+        "Success",
+        "Uninitialized",
+        "HashtreeDisabled",
+        "VerificationDisabled",
+        "VerificationError",
+        "Unknown",
+    };
+    // clang-format on
+
+    uint32_t index = static_cast<uint32_t>(status);
+    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+    if (index >= unknown_index) {
+        index = unknown_index;
+    }
+
+    return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {
+    os << AvbHandleStatusToString(status);
+    return os;
+}
+
+// class AvbVerifier
+// -----------------
 // Reads the following values from kernel cmdline and provides the
 // VerifyVbmetaImages() to verify AvbSlotVerifyData.
 //   - androidboot.vbmeta.hash_alg
@@ -74,12 +120,6 @@
     AvbVerifier() = default;
 
   private:
-    enum HashAlgorithm {
-        kInvalid = 0,
-        kSHA256 = 1,
-        kSHA512 = 2,
-    };
-
     HashAlgorithm hash_alg_;
     uint8_t digest_[SHA512_DIGEST_LENGTH];
     size_t vbmeta_size_;
@@ -105,10 +145,10 @@
     fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
     if (hash_alg == "sha256") {
         expected_digest_size = SHA256_DIGEST_LENGTH * 2;
-        avb_verifier->hash_alg_ = kSHA256;
+        avb_verifier->hash_alg_ = HashAlgorithm::kSHA256;
     } else if (hash_alg == "sha512") {
         expected_digest_size = SHA512_DIGEST_LENGTH * 2;
-        avb_verifier->hash_alg_ = kSHA512;
+        avb_verifier->hash_alg_ = HashAlgorithm::kSHA512;
     } else {
         LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
         return nullptr;
@@ -140,10 +180,10 @@
     size_t total_size = 0;
     bool digest_matched = false;
 
-    if (hash_alg_ == kSHA256) {
+    if (hash_alg_ == HashAlgorithm::kSHA256) {
         std::tie(total_size, digest_matched) =
                 VerifyVbmetaDigest<SHA256Hasher>(vbmeta_images, digest_);
-    } else if (hash_alg_ == kSHA512) {
+    } else if (hash_alg_ == HashAlgorithm::kSHA512) {
         std::tie(total_size, digest_matched) =
                 VerifyVbmetaDigest<SHA512Hasher>(vbmeta_images, digest_);
     }
@@ -162,6 +202,98 @@
     return true;
 }
 
+// class AvbHandle
+// ---------------
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
+        const std::string& partition_name, const std::string& ab_suffix,
+        const std::string& ab_other_suffix, const std::string& expected_public_key_path,
+        const HashAlgorithm& hash_algorithm, bool allow_verification_error,
+        bool load_chained_vbmeta, bool rollback_protection,
+        std::function<std::string(const std::string&)> custom_device_path) {
+    AvbUniquePtr avb_handle(new AvbHandle());
+    if (!avb_handle) {
+        LERROR << "Failed to allocate AvbHandle";
+        return nullptr;
+    }
+
+    std::string expected_key_blob;
+    if (!expected_public_key_path.empty()) {
+        if (access(expected_public_key_path.c_str(), F_OK) != 0) {
+            LERROR << "Expected public key path doesn't exist: " << expected_public_key_path;
+            return nullptr;
+        } else if (!ReadFileToString(expected_public_key_path, &expected_key_blob)) {
+            LERROR << "Failed to load: " << expected_public_key_path;
+            return nullptr;
+        }
+    }
+
+    auto android_by_name_symlink = [](const std::string& partition_name_with_ab) {
+        return "/dev/block/by-name/" + partition_name_with_ab;
+    };
+
+    auto device_path = custom_device_path ? custom_device_path : android_by_name_symlink;
+
+    auto verify_result = LoadAndVerifyVbmetaImpl(
+            partition_name, ab_suffix, ab_other_suffix, expected_key_blob, allow_verification_error,
+            load_chained_vbmeta, rollback_protection, device_path, false,
+            /* is_chained_vbmeta */ &avb_handle->vbmeta_images_);
+    switch (verify_result) {
+        case VBMetaVerifyResult::kSuccess:
+            avb_handle->status_ = AvbHandleStatus::kSuccess;
+            break;
+        case VBMetaVerifyResult::kErrorVerification:
+            avb_handle->status_ = AvbHandleStatus::kVerificationError;
+            break;
+        default:
+            LERROR << "LoadAndVerifyVbmetaImpl failed, result: " << verify_result;
+            return nullptr;
+    }
+
+    // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+    avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
+    // Checks any disabled flag is set.
+    std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
+            avb_handle->vbmeta_images_[0].GetVBMetaHeader();
+    bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header->flags &
+                                  AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    bool hashtree_disabled =
+            ((AvbVBMetaImageFlags)vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+    if (verification_disabled) {
+        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+    } else if (hashtree_disabled) {
+        avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
+    }
+
+    // Calculates the summary info for all vbmeta_images_;
+    std::string digest;
+    size_t total_size;
+    if (hash_algorithm == HashAlgorithm::kSHA256) {
+        std::tie(digest, total_size) =
+                CalculateVbmetaDigest<SHA256Hasher>(avb_handle->vbmeta_images_);
+    } else if (hash_algorithm == HashAlgorithm::kSHA512) {
+        std::tie(digest, total_size) =
+                CalculateVbmetaDigest<SHA512Hasher>(avb_handle->vbmeta_images_);
+    } else {
+        LERROR << "Invalid hash algorithm";
+        return nullptr;
+    }
+    avb_handle->vbmeta_info_ = VBMetaInfo(digest, hash_algorithm, total_size);
+
+    LINFO << "Returning avb_handle with status: " << avb_handle->status_;
+    return avb_handle;
+}
+
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
+    // Loads inline vbmeta images, starting from /vbmeta.
+    return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
+                               {} /* expected_public_key, already checked by bootloader */,
+                               HashAlgorithm::kSHA256,
+                               IsDeviceUnlocked(), /* allow_verification_error */
+                               true,               /* load_chained_vbmeta */
+                               false, /* rollback_protection, already checked by bootloader */
+                               nullptr /* custom_device_path */);
+}
 
 AvbUniquePtr AvbHandle::Open() {
     bool is_device_unlocked = IsDeviceUnlocked();
@@ -192,14 +324,14 @@
     //     for more details.
     switch (verify_result) {
         case AVB_SLOT_VERIFY_RESULT_OK:
-            avb_handle->status_ = kAvbHandleSuccess;
+            avb_handle->status_ = AvbHandleStatus::kSuccess;
             break;
         case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
             if (!is_device_unlocked) {
                 LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
                 return nullptr;
             }
-            avb_handle->status_ = kAvbHandleVerificationError;
+            avb_handle->status_ = AvbHandleStatus::kVerificationError;
             break;
         default:
             LERROR << "avb_slot_verify failed, result: " << verify_result;
@@ -220,7 +352,7 @@
                                   AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
 
     if (verification_disabled) {
-        avb_handle->status_ = kAvbHandleVerificationDisabled;
+        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
     } else {
         // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
         std::unique_ptr<AvbVerifier> avb_verifier = AvbVerifier::Create();
@@ -237,7 +369,7 @@
         bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
                                   AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
         if (hashtree_disabled) {
-            avb_handle->status_ = kAvbHandleHashtreeDisabled;
+            avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
         }
     }
 
@@ -246,11 +378,12 @@
 }
 
 AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
-    if (!fstab_entry || status_ == kAvbHandleUninitialized || vbmeta_images_.size() < 1) {
+    if (!fstab_entry || status_ == AvbHandleStatus::kUninitialized || vbmeta_images_.size() < 1) {
         return AvbHashtreeResult::kFail;
     }
 
-    if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
+    if (status_ == AvbHandleStatus::kHashtreeDisabled ||
+        status_ == AvbHandleStatus::kVerificationDisabled) {
         LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
         return AvbHashtreeResult::kDisabled;
     }
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 eca6984..7af3c7e 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <string>
 #include <vector>
@@ -32,23 +33,56 @@
     kDisabled,
 };
 
+enum class HashAlgorithm {
+    kInvalid = 0,
+    kSHA256 = 1,
+    kSHA512 = 2,
+};
+
+enum class AvbHandleStatus {
+    kSuccess = 0,
+    kUninitialized = 1,
+    kHashtreeDisabled = 2,
+    kVerificationDisabled = 3,
+    kVerificationError = 4,
+};
+
+struct VBMetaInfo {
+    std::string digest;
+    HashAlgorithm hash_algorithm;
+    size_t total_size;
+
+    VBMetaInfo() {}
+
+    VBMetaInfo(std::string digest_value, HashAlgorithm algorithm, size_t size)
+        : digest(std::move(digest_value)), hash_algorithm(algorithm), total_size(size) {}
+};
+
 class VBMetaData {
   public:
     // Constructors
     VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
 
-    VBMetaData(const uint8_t* data, size_t size)
-        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]), vbmeta_size_(size) {
+    VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)
+        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+          vbmeta_size_(size),
+          partition_name_(partition_name) {
         // The ownership of data is NOT transferred, i.e., the caller still
         // needs to release the memory as we make a copy here.
         memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));
     }
 
-    explicit VBMetaData(size_t size)
-        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]), vbmeta_size_(size) {}
+    explicit VBMetaData(size_t size, const std::string& partition_name)
+        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+          vbmeta_size_(size),
+          partition_name_(partition_name) {}
+
+    // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to
+    // true to update vbmeta_size_ to the actual size with valid content.
+    std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
 
     // Get methods for each data member.
-    const std::string& device_path() const { return device_path_; }
+    const std::string& partition() const { return partition_name_; }
     uint8_t* data() const { return vbmeta_ptr_.get(); }
     const size_t& size() const { return vbmeta_size_; }
 
@@ -56,9 +90,9 @@
     static const size_t kMaxVBMetaSize = 64 * 1024;
 
   private:
-    std::string device_path_;
     std::unique_ptr<uint8_t[]> vbmeta_ptr_;
     size_t vbmeta_size_;
+    std::string partition_name_;
 };
 
 class FsManagerAvbOps;
@@ -71,7 +105,7 @@
 // descriptors to load verity table into kernel through ioctl.
 class AvbHandle {
   public:
-    // The factory method to return a AvbUniquePtr that holds
+    // The factory methods to return a AvbUniquePtr that holds
     // the verified AVB (external/avb) metadata of all verified partitions
     // in vbmeta_images_.
     //
@@ -79,31 +113,40 @@
     //   - androidboot.vbmeta.{hash_alg, size, digest}.
     //
     // A typical usage will be:
-    //   - AvbUniquePtr handle = AvbHandle::Open();
+    //   - AvbUniquePtr handle = AvbHandle::Open(); or
+    //   - AvbUniquePtr handle = AvbHandle::LoadAndVerifyVbmeta();
     //
     // Possible return values:
     //   - nullptr: any error when reading and verifying the metadata,
     //     e.g., I/O error, digest value mismatch, size mismatch, etc.
     //
-    //   - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
+    //   - a valid unique_ptr with status AvbHandleStatus::HashtreeDisabled:
     //     to support the existing 'adb disable-verity' feature in Android.
     //     It's very helpful for developers to make the filesystem writable to
     //     allow replacing binaries on the device.
     //
-    //   - a valid unique_ptr with status kAvbHandleVerificationDisabled:
+    //   - a valid unique_ptr with status AvbHandleStatus::VerificationDisabled:
     //     to support 'avbctl disable-verification': only the top-level
     //     vbmeta is read, vbmeta structs in other partitions are not processed.
     //     It's needed to bypass AVB when using the generic system.img to run
     //     VTS for project Treble.
     //
-    //   - a valid unique_ptr with status kAvbHandleVerificationError:
+    //   - a valid unique_ptr with status AvbHandleStatus::VerificationError:
     //     there is verification error when libavb loads vbmeta from each
     //     partition. This is only allowed when the device is unlocked.
     //
-    //   - a valid unique_ptr with status kAvbHandleSuccess: the metadata
+    //   - a valid unique_ptr with status AvbHandleStatus::Success: the metadata
     //     is verified and can be trusted.
     //
-    static AvbUniquePtr Open();
+    // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
+    static AvbUniquePtr Open();                 // loads inline vbmeta, via libavb.
+    static AvbUniquePtr LoadAndVerifyVbmeta();  // loads inline vbmeta.
+    static AvbUniquePtr LoadAndVerifyVbmeta(    // loads offline vbmeta.
+            const std::string& partition_name, const std::string& ab_suffix,
+            const std::string& ab_other_suffix, const std::string& expected_public_key,
+            const HashAlgorithm& hash_algorithm, bool allow_verification_error,
+            bool load_chained_vbmeta, bool rollback_protection,
+            std::function<std::string(const std::string&)> custom_device_path = nullptr);
 
     // Sets up dm-verity on the given fstab entry.
     // The 'wait_for_verity_dev' parameter makes this function wait for the
@@ -118,6 +161,8 @@
     AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev);
 
     const std::string& avb_version() const { return avb_version_; }
+    const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }
+    AvbHandleStatus status() const { return status_; }
 
     AvbHandle(const AvbHandle&) = delete;             // no copy
     AvbHandle& operator=(const AvbHandle&) = delete;  // no assignment
@@ -126,17 +171,10 @@
     AvbHandle& operator=(AvbHandle&&) noexcept = delete;  // no move assignment
 
   private:
-    enum AvbHandleStatus {
-        kAvbHandleSuccess = 0,
-        kAvbHandleUninitialized,
-        kAvbHandleHashtreeDisabled,
-        kAvbHandleVerificationDisabled,
-        kAvbHandleVerificationError,
-    };
-
-    AvbHandle() : status_(kAvbHandleUninitialized) {}
+    AvbHandle() : status_(AvbHandleStatus::kUninitialized) {}
 
     std::vector<VBMetaData> vbmeta_images_;
+    VBMetaInfo vbmeta_info_;  // A summary info for vbmeta_images_.
     AvbHandleStatus status_;
     std::string avb_version_;
 };
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
new file mode 100644
index 0000000..26b3294
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -0,0 +1,1088 @@
+/*
+ * 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 <android-base/unique_fd.h>
+#include <base/files/file_util.h>
+#include <base/rand_util.h>
+#include <base/strings/string_util.h>
+
+#include "avb_util.h"
+#include "fs_avb_test_util.h"
+
+// Target classes or functions to test:
+using android::fs_mgr::AvbPartitionToDevicePatition;
+using android::fs_mgr::GetAvbFooter;
+using android::fs_mgr::GetChainPartitionInfo;
+using android::fs_mgr::GetTotalSize;
+using android::fs_mgr::LoadAndVerifyVbmetaImpl;
+using android::fs_mgr::VBMetaData;
+using android::fs_mgr::VBMetaVerifyResult;
+using android::fs_mgr::VerifyPublicKeyBlob;
+using android::fs_mgr::VerifyVBMetaData;
+using android::fs_mgr::VerifyVBMetaSignature;
+
+namespace fs_avb_host_test {
+
+class AvbUtilTest : public BaseFsAvbTest {
+  public:
+    AvbUtilTest(){};
+
+  protected:
+    ~AvbUtilTest(){};
+    // Helper function for VerifyVBMetaSignature test. Modifies vbmeta.data()
+    // in a number of places at |offset| of size |length| and checks that
+    // VerifyVBMetaSignature() returns |expected_result|.
+    bool TestVBMetaModification(VBMetaVerifyResult expected_result, const VBMetaData& vbmeta,
+                                size_t offset, size_t length);
+    // Modifies a random bit for a file, in the range of [offset, offset + length - 1].
+    void ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length);
+
+    // Loads the content of avb_image_path and comparies it with the content of vbmeta.
+    bool CompareVBMeta(const base::FilePath& avb_image_path, const VBMetaData& expected_vbmeta);
+};
+
+TEST_F(AvbUtilTest, AvbPartitionToDevicePatition) {
+    EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", ""));
+    EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", "_b"));
+
+    EXPECT_EQ("system_a", AvbPartitionToDevicePatition("system", "_a", ""));
+    EXPECT_EQ("system_a", AvbPartitionToDevicePatition("system", "_a", "_b"));
+
+    EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "", "_b"));
+    EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "_a", "_b"));
+}
+
+TEST_F(AvbUtilTest, GetFdTotalSize) {
+    // Generates a raw test.img via BaseFsAvbTest.
+    const size_t image_size = 5 * 1024 * 1024;
+    base::FilePath image_path = GenerateImage("test.img", image_size);
+
+    // Checks file size is as expected via base::GetFileSize().
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(image_path, &file_size));
+    EXPECT_EQ(image_size, file_size);
+
+    // Checks file size is expected via libfs_avb internal utils.
+    auto fd = OpenUniqueReadFd(image_path);
+    EXPECT_EQ(image_size, GetTotalSize(fd));
+}
+
+TEST_F(AvbUtilTest, GetFdTotalSizeWithOffset) {
+    // Generates a raw test.img via BaseFsAvbTest.
+    const size_t image_size = 10 * 1024 * 1024;
+    base::FilePath image_path = GenerateImage("test.img", image_size);
+
+    // Checks file size is expected even with a non-zero offset at the beginning.
+    auto fd = OpenUniqueReadFd(image_path);
+    off_t initial_offset = 2019;
+    EXPECT_EQ(initial_offset, lseek(fd, initial_offset, SEEK_SET));
+    EXPECT_EQ(image_size, GetTotalSize(fd));            // checks that total size is still returned.
+    EXPECT_EQ(initial_offset, lseek(fd, 0, SEEK_CUR));  // checks original offset is restored.
+}
+
+TEST_F(AvbUtilTest, GetAvbFooter) {
+    // Generates a raw system.img
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+    EXPECT_NE(0U, system_path.value().size());
+
+    // Checks image size is as expected.
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+    EXPECT_EQ(image_size, file_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\"");
+
+    // Checks partition size is as expected, after adding footer.
+    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+    EXPECT_EQ(partition_size, file_size);
+
+    // Checks avb footer and avb vbmeta.
+    EXPECT_EQ(
+            "Footer version:           1.0\n"
+            "Image size:               15728640 bytes\n"
+            "Original image size:      10485760 bytes\n"
+            "VBMeta offset:            10661888\n"
+            "VBMeta size:              3648 bytes\n"
+            "--\n"
+            "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:            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(system_path));
+
+    // Checks each field from GetAvbFooter(fd).
+    auto fd = OpenUniqueReadFd(system_path);
+    auto footer = GetAvbFooter(fd);
+    EXPECT_NE(nullptr, footer);
+    EXPECT_EQ(10485760, footer->original_image_size);
+    EXPECT_EQ(10661888, footer->vbmeta_offset);
+    EXPECT_EQ(3648, footer->vbmeta_size);
+}
+
+TEST_F(AvbUtilTest, GetAvbFooterErrorVerification) {
+    // Generates a raw system.img
+    const size_t image_size = 5 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+
+    // Checks each field from GetAvbFooter(fd).
+    auto fd = OpenUniqueReadFd(system_path);
+    auto footer = GetAvbFooter(fd);
+    EXPECT_EQ(nullptr, footer);
+}
+
+TEST_F(AvbUtilTest, GetAvbFooterInsufficientSize) {
+    // Generates a raw system.img
+    const size_t image_size = AVB_FOOTER_SIZE - 10;
+    base::FilePath system_path = GenerateImage("system.img", image_size);
+
+    // Checks each field from GetAvbFooter(fd).
+    auto fd = OpenUniqueReadFd(system_path);
+    auto footer = GetAvbFooter(fd);
+    EXPECT_EQ(nullptr, footer);
+}
+
+TEST_F(AvbUtilTest, GetVBMetaHeader) {
+    // 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);
+    // 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.
+    base::FilePath boot_vbmeta = 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"));
+
+    // Creates a VBMetaData with the content from boot-vbmeta.img.
+    std::string content;
+    EXPECT_TRUE(base::ReadFileToString(boot_vbmeta, &content));
+    VBMetaData vbmeta((uint8_t*)content.data(), content.size(), "boot-vbmeta");
+    EXPECT_EQ(content.size(), vbmeta.size());
+
+    // Checks each field returned from GetVBMetaHeader().
+    auto vbmeta_header = vbmeta.GetVBMetaHeader(false /* update_vbmeta_size */);
+    EXPECT_NE(nullptr, vbmeta_header);
+    EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);
+    EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);
+    EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);
+    EXPECT_EQ(0, vbmeta_header->hash_offset);
+    EXPECT_EQ(32, vbmeta_header->hash_size);
+    EXPECT_EQ(32, vbmeta_header->signature_offset);
+    EXPECT_EQ(512, vbmeta_header->signature_size);
+    EXPECT_EQ(176, vbmeta_header->public_key_offset);
+    EXPECT_EQ(1032, vbmeta_header->public_key_size);
+    EXPECT_EQ(0, vbmeta_header->descriptors_offset);
+    EXPECT_EQ(176, vbmeta_header->descriptors_size);
+    EXPECT_EQ(10, vbmeta_header->rollback_index);
+    EXPECT_EQ(0, vbmeta_header->flags);
+    EXPECT_EQ("unit test", std::string((const char*)vbmeta_header->release_string));
+
+    // Appends some garbage to the end of the vbmeta buffer, checks it still can work.
+    std::string padding(2020, 'A');  // Generate a padding with length 2020.
+    std::string content_padding = content + padding;
+    VBMetaData vbmeta_padding((const uint8_t*)content_padding.data(), content_padding.size(),
+                              "boot");
+    EXPECT_EQ(content_padding.size(), vbmeta_padding.size());
+
+    // Checks each field still can be parsed properly, even with garbage padding.
+    vbmeta_header = vbmeta_padding.GetVBMetaHeader(false /* update_vbmeta_size */);
+    EXPECT_NE(nullptr, vbmeta_header);
+    EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);
+    EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);
+    EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);
+    EXPECT_EQ(0, vbmeta_header->hash_offset);
+    EXPECT_EQ(32, vbmeta_header->hash_size);
+    EXPECT_EQ(32, vbmeta_header->signature_offset);
+    EXPECT_EQ(512, vbmeta_header->signature_size);
+    EXPECT_EQ(176, vbmeta_header->public_key_offset);
+    EXPECT_EQ(1032, vbmeta_header->public_key_size);
+    EXPECT_EQ(0, vbmeta_header->descriptors_offset);
+    EXPECT_EQ(176, vbmeta_header->descriptors_size);
+    EXPECT_EQ(10, vbmeta_header->rollback_index);
+    EXPECT_EQ(0, vbmeta_header->flags);
+    EXPECT_EQ("unit test", std::string((const char*)vbmeta_header->release_string));
+
+    // Checks vbmeta size is updated to the actual size without padding.
+    vbmeta_header = vbmeta_padding.GetVBMetaHeader(true /* update_vbmeta_size */);
+    EXPECT_EQ(content_padding.size() - padding.size(), vbmeta_padding.size());
+}
+
+TEST_F(AvbUtilTest, VerifyPublicKeyBlob) {
+    // Generates a raw key.bin
+    const size_t key_size = 2048;
+    base::FilePath key_path = GenerateImage("key.bin", key_size);
+
+    uint8_t key_data[key_size];
+    EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
+
+    std::string expected_key_blob;
+    EXPECT_TRUE(base::ReadFileToString(key_path, &expected_key_blob));
+    EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+
+    key_data[10] ^= 0x80;  // toggles a bit and expects a failure
+    EXPECT_FALSE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+    key_data[10] ^= 0x80;  // toggles the bit again, should pass
+    EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, VerifyEmptyPublicKeyBlob) {
+    // Generates a raw key.bin
+    const size_t key_size = 2048;
+    base::FilePath key_path = GenerateImage("key.bin", key_size);
+
+    uint8_t key_data[key_size];
+    EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
+
+    std::string expected_key_blob = "";  // empty means no expectation, thus return true.
+    EXPECT_TRUE(VerifyPublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignature) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+    auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+                                                    "hashtree", signing_key, "SHA256_RSA4096",
+                                                    10 /* rollback_index */);
+
+    auto expected_public_key = ExtractPublicKeyAvbBlob(signing_key);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, VerifyVBMetaSignature(vbmeta, expected_public_key));
+
+    // Converts the expected key into an 'unexpected' key.
+    expected_public_key[10] ^= 0x80;
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              VerifyVBMetaSignature(vbmeta, expected_public_key));
+}
+
+bool AvbUtilTest::TestVBMetaModification(VBMetaVerifyResult expected_result,
+                                         const VBMetaData& vbmeta, size_t offset, size_t length) {
+    uint8_t* d = reinterpret_cast<uint8_t*>(vbmeta.data());
+    const int kNumCheckIntervals = 8;
+
+    // Tests |kNumCheckIntervals| modifications in the start, middle, and
+    // end of the given sub-array at offset with size.
+    for (int n = 0; n <= kNumCheckIntervals; n++) {
+        size_t o = std::min(length * n / kNumCheckIntervals, length - 1) + offset;
+        d[o] ^= 0x80;
+        VBMetaVerifyResult result = VerifyVBMetaSignature(vbmeta, "" /* expected_public_key */);
+        d[o] ^= 0x80;
+        if (result != expected_result) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureWithModification) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+    auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+                                                    "hashtree", signing_key, "SHA256_RSA4096",
+                                                    10 /* rollback_index */);
+
+    auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t header_block_offset = 0;
+    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+    size_t auxiliary_block_offset =
+            authentication_block_offset + header->authentication_data_block_size;
+
+    // Should detect modifications in the auxiliary data block.
+    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+                                       auxiliary_block_offset, header->auxiliary_data_block_size));
+
+    // Sholud detect modifications in the hash part of authentication data block.
+    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+                                       authentication_block_offset + header->hash_offset,
+                                       header->hash_size));
+
+    // Sholud detect modifications in the signature part of authentication data block.
+    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+                                       authentication_block_offset + header->signature_offset,
+                                       header->signature_size));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureNotSigned) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    auto vbmeta = GenerateImageAndExtractVBMetaData(
+            "system", image_size, partition_size, "hashtree", {} /* avb_signing_key */,
+            "" /* avb_algorithm */, 10 /* rollback_index */);
+
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, VerifyVBMetaSignature(vbmeta, ""));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureInvalidVBMeta) {
+    const size_t buffer_size = 5 * 1024 * 1024;
+    std::vector<uint8_t> vbmeta_buffer(buffer_size);
+    for (size_t n = 0; n < buffer_size; n++) {
+        vbmeta_buffer[n] = uint8_t(n);
+    }
+
+    VBMetaData invalid_vbmeta((const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(),
+                              "invalid_vbmeta");
+    EXPECT_EQ(VBMetaVerifyResult::kError, VerifyVBMetaSignature(invalid_vbmeta, ""));
+}
+
+bool AvbUtilTest::CompareVBMeta(const base::FilePath& avb_image_path,
+                                const VBMetaData& expected_vbmeta) {
+    if (!base::PathExists(avb_image_path)) return false;
+
+    std::string image_file_name = avb_image_path.RemoveExtension().BaseName().value();
+
+    base::FilePath extracted_vbmeta_path;
+    if (base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII)) {
+        extracted_vbmeta_path = avb_image_path;  // no need to extract if it's a vbmeta image.
+    } else {
+        extracted_vbmeta_path = ExtractVBMetaImage(avb_image_path, image_file_name + "-vbmeta.img");
+    }
+
+    // Gets file size of the vbmeta image.
+    int64_t extracted_vbmeta_size;
+    EXPECT_TRUE(base::GetFileSize(extracted_vbmeta_path, &extracted_vbmeta_size));
+
+    // Reads the vbmeta into a vector.
+    std::vector<uint8_t> extracted_vbmeta_content(extracted_vbmeta_size);
+    EXPECT_TRUE(base::ReadFile(extracted_vbmeta_path,
+                               reinterpret_cast<char*>(extracted_vbmeta_content.data()),
+                               extracted_vbmeta_size));
+
+    // Compares extracted_vbmeta_content with the expected_vbmeta.
+    EXPECT_EQ(expected_vbmeta.size(), extracted_vbmeta_size);
+    return memcmp(reinterpret_cast<void*>(extracted_vbmeta_content.data()),
+                  reinterpret_cast<void*>(expected_vbmeta.data()), extracted_vbmeta_size) == 0;
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataWithoutFooter) {
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+    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"));
+
+    android::base::unique_fd fd(open(vbmeta_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(fd > 0);
+
+    VBMetaVerifyResult verify_result;
+    std::unique_ptr<VBMetaData> vbmeta =
+            VerifyVBMetaData(fd, "vbmeta", "" /*expected_public_key_blob */, &verify_result);
+    EXPECT_TRUE(vbmeta != nullptr);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+    // Checkes the returned vbmeta content is the same as that extracted via avbtool.
+    vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataWithFooter) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", 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\"");
+
+    android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(fd > 0);
+
+    VBMetaVerifyResult verify_result;
+    std::unique_ptr<VBMetaData> vbmeta =
+            VerifyVBMetaData(fd, "system", "" /*expected_public_key_blob */, &verify_result);
+    EXPECT_TRUE(vbmeta != nullptr);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+    // Checkes the returned vbmeta content is the same as that extracted via avbtool.
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+// Modifies a random bit for a file, in the range of [offset, offset + length - 1].
+// Length < 0 means only resets previous modification without introducing new modification.
+void AvbUtilTest::ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length) {
+    static int last_modified_location = -1;
+    static std::string last_file_path;
+
+    int64_t file_size;
+    ASSERT_TRUE(base::GetFileSize(file_path, &file_size));
+
+    std::vector<uint8_t> file_content(file_size);
+    ASSERT_TRUE(base::ReadFile(file_path, reinterpret_cast<char*>(file_content.data()), file_size));
+
+    // Resets previous modification for consecutive calls on the same file.
+    if (last_file_path == file_path.value()) {
+        file_content[last_modified_location] ^= 0x80;
+    }
+
+    // Introduces a new modification.
+    if (length > 0) {
+        int modify_location = base::RandInt(offset, offset + length - 1);
+        file_content[modify_location] ^= 0x80;
+        last_file_path = file_path.value();
+        last_modified_location = modify_location;
+    }
+
+    ASSERT_EQ(file_size, static_cast<const size_t>(base::WriteFile(
+                                 file_path, reinterpret_cast<const char*>(file_content.data()),
+                                 file_content.size())));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataError) {
+    const size_t image_size = 10 * 1024 * 1024;
+    const size_t partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", 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\"");
+
+    android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(fd > 0);
+
+    std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+    EXPECT_TRUE(footer != nullptr);
+
+    VBMetaVerifyResult verify_result;
+    std::unique_ptr<VBMetaData> vbmeta =
+            VerifyVBMetaData(fd, "system", "" /*expected_public_key_blob */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+    // Modifies hash and signature, checks there is verification error.
+    auto header = vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t header_block_offset = 0;
+    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+
+    // Modifies the hash.
+    ModifyFile(system_path,
+               footer->vbmeta_offset + authentication_block_offset + header->hash_offset,
+               header->hash_size);
+    android::base::unique_fd hash_modified_fd(
+            open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(hash_modified_fd > 0);
+    // Should return ErrorVerification.
+    vbmeta = VerifyVBMetaData(hash_modified_fd, "system", "" /*expected_public_key_blob */,
+                              &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+    // Modifies the auxiliary data block.
+    size_t auxiliary_block_offset =
+            authentication_block_offset + header->authentication_data_block_size;
+    ModifyFile(system_path, footer->vbmeta_offset + auxiliary_block_offset,
+               header->auxiliary_data_block_size);
+    android::base::unique_fd aux_modified_fd(
+            open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(aux_modified_fd > 0);
+    // Should return ErrorVerification.
+    vbmeta = VerifyVBMetaData(aux_modified_fd, "system", "" /*expected_public_key_blob */,
+                              &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+    // Resets previous modification by setting offset to -1, and checks the verification can pass.
+    ModifyFile(system_path, 0 /* offset */, -1 /* length */);
+    android::base::unique_fd ok_fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_TRUE(ok_fd > 0);
+    // Should return ResultOK..
+    vbmeta = VerifyVBMetaData(ok_fd, "system", "" /*expected_public_key_blob */, &verify_result);
+    EXPECT_NE(nullptr, vbmeta);
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+}
+
+TEST_F(AvbUtilTest, GetChainPartitionInfo) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    // Makes a vbmeta_system.img including the 'system' chained descriptor.
+    GenerateVBMetaImage("vbmeta_system.img", "SHA256_RSA4096", 0,
+                        data_dir_.Append("testkey_rsa4096.pem"),
+                        {},                                  /* include_descriptor_image_paths */
+                        {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+                        "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+                        {},                               /* include_descriptor_image_paths */
+                        {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                         {"vbmeta_system", 2, rsa4096_public_key}},
+                        "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    // Loads the key blobs for comparison.
+    std::string expected_key_blob_2048;
+    EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+    // Checks chain descriptors in vbmeta.img
+    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:          vbmeta_system\n"
+            "      Rollback Index Location: 2\n"
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            InfoImage("vbmeta.img"));
+
+    bool fatal_error = false;
+    auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta.img"), &fatal_error);
+    EXPECT_EQ(2, chained_descriptors.size());  // contains 'boot' and 'vbmeta_system'.
+    EXPECT_EQ(false, fatal_error);
+
+    EXPECT_EQ("boot", chained_descriptors[0].partition_name);
+    EXPECT_EQ(expected_key_blob_2048, chained_descriptors[0].public_key_blob);
+
+    EXPECT_EQ("vbmeta_system", chained_descriptors[1].partition_name);
+    EXPECT_EQ(expected_key_blob_4096, chained_descriptors[1].public_key_blob);
+
+    // Checks chain descriptors in vbmeta_system.img
+    EXPECT_EQ(
+            "Minimum libavb version:   1.0\n"
+            "Header Block:             256 bytes\n"
+            "Authentication Block:     576 bytes\n"
+            "Auxiliary Block:          2176 bytes\n"
+            "Algorithm:                SHA256_RSA4096\n"
+            "Rollback Index:           0\n"
+            "Flags:                    0\n"
+            "Release String:           'unit test'\n"
+            "Descriptors:\n"
+            "    Chain Partition descriptor:\n"
+            "      Partition Name:          system\n"
+            "      Rollback Index Location: 3\n"
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            InfoImage("vbmeta_system.img"));
+
+    chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta_system.img"), &fatal_error);
+    EXPECT_EQ(1, chained_descriptors.size());  // contains 'system' only.
+    EXPECT_EQ(false, fatal_error);
+    EXPECT_EQ("system", chained_descriptors[0].partition_name);
+    EXPECT_EQ(expected_key_blob_4096, chained_descriptors[0].public_key_blob);
+}
+
+TEST_F(AvbUtilTest, GetChainPartitionInfoNone) {
+    // 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"));
+
+    // Checks none of chain descriptors is found.
+    bool fatal_error = false;
+    auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta.img"), &fatal_error);
+    EXPECT_EQ(0, chained_descriptors.size());  // There is no chain descriptors.
+    EXPECT_EQ(false, fatal_error);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImpl) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    // Makes a vbmeta_system.img including the 'system' chained descriptor.
+    auto vbmeta_system_path = GenerateVBMetaImage(
+            "vbmeta_system.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+            {},                                  /* include_descriptor_image_paths */
+            {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+            "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"vbmeta_system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    // Starts to test LoadAndVerifyVbmetaImpl.
+    std::vector<VBMetaData> vbmeta_images;
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                      true /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+    // Skip loading chained vbmeta images.
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                      false /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+    // Only vbmeta is loaded.
+    EXPECT_EQ(1UL, vbmeta_images.size());
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplWithSuffixes) {
+    // Tests the following chained partitions.
+    // vbmeta_a.img
+    // |--> boot_b.img (boot_other)
+    // |--> vbmeta_system_b.img (vbmeta_system_other)
+    //      |--> system_a.img
+
+    // Generates a raw boot_b.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_b.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_a.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_a.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    // Makes a vbmeta_system_b.img including the 'system' chained descriptor.
+    auto vbmeta_system_path = GenerateVBMetaImage(
+            "vbmeta_system_b.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+            {},                                  /* include_descriptor_image_paths */
+            {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+            "--internal_release_string \"unit test\"");
+
+    // Makes a vbmeta_a.img includeing 'boot_other' and 'vbmeta_system_other' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage(
+            "vbmeta_a.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+            {},                                     /* include_descriptor_image_paths */
+            {{"boot_other", 1, rsa2048_public_key}, /* chain_partitions */
+             {"vbmeta_system_other", 2, rsa4096_public_key}},
+            "--internal_release_string \"unit test\"");
+
+    // Starts to test LoadAndVerifyVbmetaImpl with ab_suffix and ab_other_suffix.
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+
+    std::vector<VBMetaData> vbmeta_images;
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                      true /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot_other, vbmeta_system_other and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+    // Skips loading chained vbmeta images.
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                      false /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+    // Only vbmeta is loaded.
+    EXPECT_EQ(1UL, vbmeta_images.size());
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+
+    // Using an invalid suffix for 'other' slot, checks it returns error.
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "_a" /* ab_suffix */,
+                      "_invalid_suffix" /* other_suffix */, "" /* expected_public_key_blob*/,
+                      false /* allow_verification_error */, true /* load_chained_vbmeta */,
+                      true /* rollback_protection */, vbmeta_image_path,
+                      false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplErrorVerification) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+    // Modifies hash, checks there is error if allow_verification_error is false.
+    auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t header_block_offset = 0;
+    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+
+    // Modifies the hash.
+    ModifyFile(vbmeta_path, authentication_block_offset + header->hash_offset, header->hash_size);
+
+    // Starts to test LoadAndVerifyVbmetaImpl.
+    std::vector<VBMetaData> vbmeta_images;
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                      true /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+    // Stops to load vbmeta because the top-level vbmeta has verification error.
+    EXPECT_EQ(0UL, vbmeta_images.size());
+
+    // Tries again with verification error allowed.
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "", /* other_suffix */
+                      "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                      true /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    EXPECT_EQ(3UL, vbmeta_images.size());  // vbmeta, boot, and system
+    // Binary comparison for each vbmeta image.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[2]));
+
+    // Resets the modification of the hash.
+    ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */);
+
+    // Modifies the auxiliary data of system.img
+    auto fd = OpenUniqueReadFd(system_path);
+    auto system_footer = GetAvbFooter(fd);
+    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+    auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+    size_t auxiliary_block_offset =
+            authentication_block_offset + system_header->authentication_data_block_size;
+
+    // Modifies the auxiliary data block.
+    ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,
+               system_header->auxiliary_data_block_size);
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                      "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+                      true /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+    // 'vbmeta', 'boot' but no 'system', because of verification error.
+    EXPECT_EQ(2UL, vbmeta_images.size());
+    // Binary comparison for the loaded 'vbmeta' and 'boot'.
+    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+
+    // Resets the modification of the auxiliary data.
+    ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */);
+
+    // Sets the vbmeta header flags on a chained partition, which introduces an error.
+    ModifyFile(system_path, system_footer->vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags),
+               sizeof(uint32_t));
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                      "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+                      true /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaImplUnexpectedPublicKey) {
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    base::FilePath rsa8192_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+    std::string expected_key_blob_4096;
+    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+    std::string expected_key_blob_8192;
+    EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));
+
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    std::vector<VBMetaData> vbmeta_images;
+    // Uses the correct expected public key.
+    EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                      expected_key_blob_8192, true /* allow_verification_error */,
+                      false /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    // Uses the wrong expected public key with allow_verification_error set to true.
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                      expected_key_blob_4096, true /* allow_verification_error */,
+                      false /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+    // Uses the wrong expected public key with allow_verification_error set to false.
+    vbmeta_images.clear();
+    EXPECT_EQ(VBMetaVerifyResult::kError,
+              LoadAndVerifyVbmetaImpl(
+                      "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+                      expected_key_blob_4096, false /* allow_verification_error */,
+                      false /* load_chained_vbmeta */, true /* rollback_protection */,
+                      vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test.cpp
new file mode 100644
index 0000000..2c819a9
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 <endian.h>
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <fs_avb/fs_avb.h>
+#include <libavb/libavb.h>
+
+#include "fs_avb_test_util.h"
+
+// Target classes or functions to test:
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::HashAlgorithm;
+
+namespace fs_avb_host_test {
+
+class PublicFsAvbTest : public BaseFsAvbTest {
+  public:
+    PublicFsAvbTest(){};
+
+  protected:
+    ~PublicFsAvbTest(){};
+    // Modifies |flags| field in the vbmeta header in an Avb image.
+    // e.g., AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED.
+    void ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path, uint32_t flags);
+};
+
+void PublicFsAvbTest::ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path,
+                                              uint32_t flags) {
+    if (!base::PathExists(vbmeta_image_path)) return;
+
+    // Only support modifying the flags in vbmeta*.img.
+    std::string image_file_name = vbmeta_image_path.RemoveExtension().BaseName().value();
+    ASSERT_TRUE(base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII));
+
+    android::base::unique_fd fd(open(vbmeta_image_path.value().c_str(), O_RDWR | O_CLOEXEC));
+    EXPECT_TRUE(fd > 0);
+
+    auto flags_offset = offsetof(AvbVBMetaImageHeader, flags);
+    uint32_t flags_data = htobe32(flags);
+    EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));
+    EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmeta) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    // Invokes the public API from fs_avb.h.
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            false /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+    // Checks the summary info for all vbmeta images.
+    // Checks the digest matches the value calculated by CalcVBMetaDigest().
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              avb_handle->vbmeta_info().digest);
+    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+
+    // Skip loading chained vbmeta.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            false /* allow_verification_error */, false /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+    EXPECT_EQ("5c31197992b3c72a854ec7dc0eb9609ffebcffab7917ffd381a99ecee328f09c",
+              avb_handle->vbmeta_info().digest);
+    EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);
+    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithModifications) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+    EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+    // Sets AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED in the vbmeta.img.
+    ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            false /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    // Returns a null handler because allow_verification is not True.
+    EXPECT_EQ(nullptr, avb_handle);
+
+    // Try again with allow_verification_error set to true.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            true /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kHashtreeDisabled, avb_handle->status());
+
+    // Checks the summary info for all vbmeta images.
+    // Checks the digest matches the value calculated by CalcVBMetaDigest().
+    EXPECT_EQ("ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ("ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c",
+              avb_handle->vbmeta_info().digest);
+    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+
+    // Sets AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED in the vbmeta.img.
+    ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    // Loads the vbmeta with allow_verification_error set to true.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            true /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kVerificationDisabled, avb_handle->status());
+    // Only the top-level vbmeta.img is loaded, when VERIFICATION_DISABLED is set.
+    // However, CalcVBMetaDigest() reads all vbmeta structs to calculate the digest,
+    // including vbmeta.img, boot.img and syste.img. So we don't compare the digest here.
+    EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);
+
+    // Sets a unknown flag in the vbmeta.imgm and expects to get
+    // AvbHandleStatus::kVerificationError.
+    ModifyVBMetaHeaderFlags(vbmeta_path, 0x10000000);
+    // Loads the vbmeta with allow_verification_error set to true.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+            true /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());
+    // Checks the digest matches the value calculated by CalcVBMetaDigest().
+    EXPECT_EQ("8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1",
+              CalcVBMetaDigest("vbmeta.img", "sha256"));
+    EXPECT_EQ("8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1",
+              avb_handle->vbmeta_info().digest);
+    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithPublicKeys) {
+    // Generates a raw boot.img
+    const size_t boot_image_size = 5 * 1024 * 1024;
+    const size_t boot_partition_size = 10 * 1024 * 1024;
+    base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+    // Adds AVB Hash Footer.
+    AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+                 data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates a raw system.img, use a smaller size to speed-up unit test.
+    const size_t system_image_size = 10 * 1024 * 1024;
+    const size_t system_partition_size = 15 * 1024 * 1024;
+    base::FilePath system_path = GenerateImage("system.img", system_image_size);
+    // Adds AVB Hashtree Footer.
+    AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+                 data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Generates chain partition descriptors.
+    base::FilePath rsa2048_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+    base::FilePath rsa4096_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+    base::FilePath rsa8192_public_key =
+            ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+    auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+                                           data_dir_.Append("testkey_rsa8192.pem"),
+                                           {}, /* include_descriptor_image_paths */
+                                           {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+                                            {"system", 2, rsa4096_public_key}},
+                                           "--internal_release_string \"unit test\"");
+
+    auto vbmeta_image_path = [this](const std::string& partition_name) {
+        return test_dir_.Append(partition_name + ".img").value();
+    };
+    std::vector<VBMetaData> vbmeta_images;
+    // Uses the correct expected public key.
+    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            rsa8192_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,
+            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+    // Uses a non-existed public key.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            "/path/to/non-existed/key", HashAlgorithm::kSHA256, true /* allow_verification_error */,
+            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_EQ(nullptr, avb_handle);
+
+    // Uses an incorrect public key, with allow_verification_error false.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            rsa4096_public_key.value(), HashAlgorithm::kSHA256,
+            false /* allow_verification_error */, true /* load_chained_vbmeta */,
+            true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_EQ(nullptr, avb_handle);
+
+    // Uses an incorrect public key, with allow_verification_error true.
+    avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+            "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+            rsa4096_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,
+            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+    EXPECT_NE(nullptr, avb_handle);
+    EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());
+}
+
+}  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
index 95b17d8..17f4c4e 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
@@ -69,7 +69,7 @@
     return trimmed_digest_data;
 }
 
-void BaseFsAvbTest::GenerateVBMetaImage(
+base::FilePath 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,
@@ -107,17 +107,19 @@
                    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));
+    EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
     vbmeta_image.content.resize(file_size);
-    ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
+    EXPECT_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));
+
+    return vbmeta_images_[file_name].path;  // returns the path.
 }
 
-void BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,
-                                       const std::string& output_file_name,
-                                       const size_t padding_size) {
+base::FilePath 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,
@@ -127,12 +129,15 @@
                    " --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));
+    EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
     vbmeta_image.content.resize(file_size);
-    ASSERT_TRUE(base::ReadFile(vbmeta_image.path,
+    EXPECT_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));
+
+    // Returns the output file path.
+    return vbmeta_images_[output_file_name].path;
 }
 
 // Generates a file with name |file_name| of size |image_size| with
@@ -179,6 +184,49 @@
                    additional_options.c_str());
 }
 
+VBMetaData BaseFsAvbTest::GenerateImageAndExtractVBMetaData(
+        const std::string& partition_name, const size_t image_size, const size_t partition_size,
+        const std::string& footer_type, const base::FilePath& avb_signing_key,
+        const std::string& avb_algorithm, const uint64_t rollback_index) {
+    // Generates a raw image first
+    base::FilePath image_path = GenerateImage(partition_name + ".img", image_size);
+
+    // Appends AVB Hashtree Footer.
+    AddAvbFooter(image_path, footer_type, partition_name, partition_size, avb_algorithm,
+                 rollback_index, avb_signing_key, "d00df00d",
+                 "--internal_release_string \"unit test\"");
+
+    // Extracts vbmeta from the ram image into another *-vbmeta.img.
+    auto vbmeta_image = ExtractVBMetaImage(image_path, partition_name + "-vbmeta.img");
+
+    // Loads *-vbmeta.img into a VBMetaData.
+    std::string vbmeta_buffer;
+    EXPECT_TRUE(base::ReadFileToString(vbmeta_image, &vbmeta_buffer));
+
+    return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};
+}
+
+VBMetaData BaseFsAvbTest::LoadVBMetaData(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;
+
+    // Loads the vbmeta_image into a VBMetaData.
+    std::string vbmeta_buffer;
+    EXPECT_TRUE(base::ReadFileToString(image_path, &vbmeta_buffer));
+
+    std::string partition_name = image_path.RemoveExtension().BaseName().value();
+    return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};
+}
+
+VBMetaData BaseFsAvbTest::ExtractAndLoadVBMetaData(const base::FilePath& image_path,
+                                                   const std::string& output_file_name) {
+    ExtractVBMetaImage(image_path, output_file_name);
+    return LoadVBMetaData(output_file_name);
+}
+
 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(),
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
index f80dc5f..2e46644 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
@@ -16,14 +16,20 @@
 
 #pragma once
 
+#include <fcntl.h>
 #include <inttypes.h>
 #include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/wait.h>
+
 #include <string>
 #include <vector>
 
+#include <android-base/unique_fd.h>
 #include <base/files/file_path.h>
 #include <base/strings/stringprintf.h>
+#include <fs_avb/fs_avb.h>
 #include <gtest/gtest.h>
 
 // Utility macro to run the command expressed by the printf()-style string
@@ -36,6 +42,8 @@
         EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status);                           \
     } while (0);
 
+using android::fs_mgr::VBMetaData;
+
 namespace fs_avb_host_test {
 
 struct VBMetaImage {
@@ -51,6 +59,10 @@
     base::FilePath key_blob_path;
 };
 
+inline android::base::unique_fd OpenUniqueReadFd(const base::FilePath& file_path) {
+    return android::base::unique_fd(open(file_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+}
+
 /* Base-class used for unit test. */
 class BaseFsAvbTest : public ::testing::Test {
   public:
@@ -66,16 +78,18 @@
     // Generates a vbmeta image with |file_name| by avbtool.
     // The generated vbmeta image will be written to disk, see the
     // |vbmeta_images_| variable for its path and the content.
-    void 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 = "");
+    base::FilePath 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 = "");
     // Similar to above, but extracts a vbmeta image from the given image_path.
     // The extracted vbmeta image will be written to disk, with |output_file_name|.
     // See the |vbmeta_images_| variable for its path and the content.
-    void ExtractVBMetaImage(const base::FilePath& image_path, const std::string& output_file_name,
-                            const size_t padding_size = 0);
+    base::FilePath ExtractVBMetaImage(const base::FilePath& image_path,
+                                      const std::string& output_file_name,
+                                      const size_t padding_size = 0);
 
     // Generate a file with name |file_name| of size |image_size| with
     // known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
@@ -86,9 +100,19 @@
     void 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 = "d00df00d",
+                      const base::FilePath& avb_signing_key, const std::string& salt = "d00df00d",
                       const std::string& additional_options = "");
 
+    VBMetaData GenerateImageAndExtractVBMetaData(
+            const std::string& partition_name, const size_t image_size, const size_t partition_size,
+            const std::string& footer_type, const base::FilePath& avb_signing_key,
+            const std::string& avb_algorithm, const uint64_t rollback_index);
+
+    VBMetaData ExtractAndLoadVBMetaData(const base::FilePath& image_path,
+                                        const std::string& output_file_name);
+
+    VBMetaData LoadVBMetaData(const std::string& file_name);
+
     // Returns the output of 'avbtool info_image' for the |image_path|.
     std::string InfoImage(const base::FilePath& image_path);
     // Same as above, but for an internal vbmeta image with |file_name| in |vbmeta_images_|.
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
index 835e8fd..9e37d22 100644
--- a/fs_mgr/libfs_avb/tests/util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <unistd.h>
+
 #include <future>
 #include <string>
 #include <thread>
diff --git a/fs_mgr/libfs_avb/util.cpp b/fs_mgr/libfs_avb/util.cpp
index 17d47d9..9d4f05f 100644
--- a/fs_mgr/libfs_avb/util.cpp
+++ b/fs_mgr/libfs_avb/util.cpp
@@ -17,6 +17,7 @@
 #include "util.h"
 
 #include <sys/ioctl.h>
+
 #include <thread>
 
 #include <android-base/unique_fd.h>
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index b99ff8f..c39fbe7 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -269,6 +269,11 @@
 }
 
 static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+    if (device_info.logical_block_size == 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " logical block size must not be zero.";
+        return false;
+    }
     if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
         LERROR << "Block device " << device_info.partition_name
                << " logical block size must be a multiple of 512.";
@@ -335,7 +340,7 @@
         out.alignment = device_info.alignment;
         out.alignment_offset = device_info.alignment_offset;
         out.size = device_info.size;
-        if (device_info.partition_name.size() >= sizeof(out.partition_name)) {
+        if (device_info.partition_name.size() > sizeof(out.partition_name)) {
             LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
             return false;
         }
@@ -833,9 +838,10 @@
                << block_device.size << ")";
         return false;
     }
-    if (device_info.logical_block_size != geometry_.logical_block_size) {
-        LERROR << "Device logical block size does not match (got " << device_info.logical_block_size
-               << ", expected " << geometry_.logical_block_size << ")";
+    if (geometry_.logical_block_size % device_info.logical_block_size) {
+        LERROR << "Device logical block size is misaligned (block size="
+               << device_info.logical_block_size << ", alignment=" << geometry_.logical_block_size
+               << ")";
         return false;
     }
 
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 7d615a3..8f08169 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -495,6 +495,11 @@
     EXPECT_EQ(new_info.size, 1024 * 1024);
 
     new_info.logical_block_size = 512;
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.logical_block_size, 4096);
+
+    new_info.logical_block_size = 7;
     ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
     ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
     EXPECT_EQ(new_info.logical_block_size, 4096);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 9c5ec5c..8934aaf 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -127,7 +127,7 @@
  * num_entries, and the result must not overflow a 32-bit signed integer.
  */
 typedef struct LpMetadataTableDescriptor {
-    /*  0: Location of the table, relative to the metadata header. */
+    /*  0: Location of the table, relative to end of the metadata header. */
     uint32_t offset;
     /*  4: Number of entries in the table. */
     uint32_t num_entries;
@@ -272,7 +272,7 @@
 
     /* 40: Maximum size in bytes. If 0, the group has no maximum size. */
     uint64_t maximum_size;
-} LpMetadataPartitionGroup;
+} __attribute__((packed)) LpMetadataPartitionGroup;
 
 /* This flag is only intended to be used with super_empty.img and super.img on
  * retrofit devices. If set, the group needs a slot suffix to be interpreted
@@ -323,7 +323,7 @@
 
     /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
     uint32_t flags;
-} LpMetadataBlockDevice;
+} __attribute__((packed)) LpMetadataBlockDevice;
 
 /* This flag is only intended to be used with super_empty.img and super.img on
  * retrofit devices. On these devices there are A and B super partitions, and
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 24c6b2c..dcee6d2 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -256,6 +256,10 @@
             LERROR << "Logical partition has invalid attribute set.";
             return nullptr;
         }
+        if (partition.first_extent_index + partition.num_extents < partition.first_extent_index) {
+            LERROR << "Logical partition first_extent_index + num_extents overflowed.";
+            return nullptr;
+        }
         if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
             LERROR << "Logical partition has invalid extent list.";
             return nullptr;
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 54a1883..bffcb7e 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -373,11 +373,11 @@
         // safety.
         std::string old_blob;
         if (!ValidateAndSerializeMetadata(opener, *backup.get(), slot_suffix, &old_blob)) {
-            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            LERROR << "Error serializing backup metadata to repair corrupted primary";
             return false;
         }
         if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
-            LERROR << "Error writing primary metadata to repair corrupted backup";
+            LERROR << "Error writing backup metadata to repair corrupted primary";
             return false;
         }
     }
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index a849578..ede0122 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -62,16 +62,17 @@
 Returns: true if the command succeeded" ]
 adb_sh() {
   args=
-  for i in ${@}; do
+  for i in "${@}"; do
+    [ -z "${args}" ] || args="${args} "
     if [ X"${i}" != X"${i#\'}" ]; then
-      args="${args} ${i}"
+      args="${args}${i}"
     elif [ X"${i}" != X"${i#* }" ]; then
-      args="${args} '${i}'"
+      args="${args}'${i}'"
     else
-      args="${args} ${i}"
+      args="${args}${i}"
     fi
   done
-  adb shell ${args}
+  adb shell "${args}"
 }
 
 [ "USAGE: adb_date >/dev/stdout
@@ -164,12 +165,26 @@
 
 Returns: true if device in root state" ]
 adb_root() {
+  [ `adb_sh echo '${USER}'` != root ] || return 0
   adb root >/dev/null </dev/null 2>/dev/null
   sleep 2
   adb_wait 2m &&
     [ `adb_sh echo '${USER}'` = root ]
 }
 
+[ "USAGE: adb_unroot
+
+NB: This can be flakey on devices due to USB state
+
+Returns: true if device in un root state" ]
+adb_unroot() {
+  [ `adb_sh echo '${USER}'` = root ] || return 0
+  adb unroot >/dev/null </dev/null 2>/dev/null
+  sleep 2
+  adb_wait 2m &&
+    [ `adb_sh echo '${USER}'` != root ]
+}
+
 [ "USAGE: fastboot_getvar var expected
 
 Returns: true if var output matches expected" ]
@@ -194,6 +209,15 @@
   echo ${O} >&2
 }
 
+[ "USAGE: cleanup
+
+Do nothing: should be redefined when necessary
+
+Returns: cleans up any latent resources, reverses configurations" ]
+cleanup () {
+  :
+}
+
 [ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
 
 If -d, or -t <epoch> argument is supplied, dump logcat.
@@ -212,6 +236,7 @@
     shift 2
   fi
   echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
+  cleanup
   exit 1
 }
 
@@ -285,7 +310,7 @@
     -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\) " \
     -e "^\(bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
     -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-    -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\) "
+    -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
 }
 
 if [ X"-s" = X"${1}" -a -n "${2}" ]; then
@@ -308,6 +333,12 @@
 
 # Do something
 
+D=`get_property ro.serialno`
+[ -n "${D}" ] || D=`get_property ro.boot.serialno`
+[ -z "${D}" ] || ANDROID_SERIAL=${D}
+BUILD_DESCRIPTION=`get_property ro.build.description`
+echo "${BLUE}[     INFO ]${NORMAL} ${ANDROID_SERIAL} ${BUILD_DESCRIPTION}" >&2
+
 echo "${GREEN}[ RUN      ]${NORMAL} Testing kernel support for overlayfs" >&2
 
 overlayfs_supported=true;
@@ -378,10 +409,16 @@
   D=`echo / /
      echo "${D}" | grep -v /dev/root`
 fi
-D=`echo "${D}" | cut -s -d' ' -f1`
+D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+no_dedupe=true
+for d in ${D}; do
+  adb_sh tune2fs -l $d 2>&1 |
+    grep "Filesystem features:.*shared_blocks" >/dev/null &&
+  no_dedupe=false
+done
 D=`adb_sh df -k ${D} </dev/null`
 echo "${D}"
-if [ X"${D}" = X"${D##* 100[%] }" ]; then
+if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
   overlayfs_needed=false
 elif ! ${overlayfs_supported}; then
   die "need overlayfs, but do not have it"
@@ -506,7 +543,9 @@
     echo "${D}" &&
     echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
     die  "overlay takeover after remount"
-  !(adb_sh grep "^overlay " /proc/mounts </dev/null | grep -v "^overlay /vendor/..* overlay ro," | grep " overlay ro,") &&
+  !(adb_sh grep "^overlay " /proc/mounts </dev/null |
+    grep -v "^overlay /\(vendor\|system\)/..* overlay ro," |
+    grep " overlay ro,") &&
     !(adb_sh grep " rw," /proc/mounts </dev/null |
       skip_administrative_mounts data) ||
     die "remount overlayfs missed a spot (ro)"
@@ -521,8 +560,8 @@
 echo "${GREEN}[ RUN      ]${NORMAL} push content to /system and /vendor" >&2
 
 A="Hello World! $(date)"
-echo "${A}" | adb_sh "cat - > /system/hello"
-echo "${A}" | adb_sh "cat - > /vendor/hello"
+echo "${A}" | adb_sh cat - ">/system/hello"
+echo "${A}" | adb_sh cat - ">/vendor/hello"
 B="`adb_cat /system/hello`" ||
   die "sytem hello"
 check_eq "${A}" "${B}" /system before reboot
@@ -543,7 +582,7 @@
     ( echo "${L}" && false ) ||
     die -d "overlay takeover failed after reboot"
 
-  adb_su "sed -n '1,/overlay \\/system/p' /proc/mounts" </dev/null |
+  adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
     skip_administrative_mounts |
     grep -v ' \(squashfs\|ext4\|f2fs\) ' &&
     echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
@@ -556,6 +595,7 @@
 echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
 # Only root can read vendor if sepolicy permissions are as expected
 if ${enforcing}; then
+  adb_unroot
   B="`adb_cat /vendor/hello`" &&
     die "re-read /vendor/hello after reboot w/o root"
   check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
@@ -570,6 +610,7 @@
 echo "${GREEN}[ RUN      ]${NORMAL} flash vendor, confirm its content disappears" >&2
 
 H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
+is_userspace_fastboot=false
 if [ -z "${ANDROID_PRODUCT_OUT}" ]; then
   echo "${ORANGE}[  WARNING ]${NORMAL} build tree not setup, skipping"
 elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
@@ -582,6 +623,8 @@
     fastboot flash vendor ||
     ( fastboot reboot && false) ||
     die "fastboot flash vendor"
+  fastboot_getvar is-userspace yes &&
+    is_userspace_fastboot=true
   if [ -n "${scratch_paritition}" ]; then
     fastboot_getvar partition-type:${scratch_partition} raw ||
       ( fastboot reboot && false) ||
@@ -627,7 +670,12 @@
       echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
       die  "overlay /system takeover after flash vendor"
     echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
-      die  "overlay supposed to be minus /vendor takeover after flash vendor"
+      if ${is_userspace_fastboot}; then
+        die  "overlay supposed to be minus /vendor takeover after flash vendor"
+      else
+        echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing, ignoring a failure"
+        ( die  "overlay supposed to be minus /vendor takeover after flash vendor" )
+      fi
   fi
   B="`adb_cat /system/hello`" ||
     die "re-read /system/hello after flash vendor"
@@ -635,8 +683,17 @@
   adb_root ||
     die "adb root"
   B="`adb_cat /vendor/hello`" &&
-    die "re-read /vendor/hello after flash vendor"
-  check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+    if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
+      die "re-read /vendor/hello after flash vendor"
+    else
+      echo "${ORANGE}[  WARNING ]${NORMAL} user fastboot missing, ignoring a failure"
+      ( die "re-read /vendor/hello after flash vendor" )
+    fi
+  if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
+    check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+  else
+    ( check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor )
+  fi
 fi
 
 echo "${GREEN}[ RUN      ]${NORMAL} remove test content (cleanup)" >&2
@@ -655,17 +712,22 @@
 
 if [ -n "${scratch_partition}" ]; then
 
-  echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition}" >&2
+  echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
 
   adb reboot-fastboot ||
     die "Reboot into fastbootd"
+  cleanup() {
+    rm /tmp/adb-remount-test.img
+  }
   dd if=/dev/zero of=/tmp/adb-remount-test.img bs=4096 count=16 2>/dev/null &&
     fastboot_wait 2m ||
-    ( rm /tmp/adb-remount-test.img && false) ||
     die "reboot into fastboot"
   fastboot flash --force ${scratch_partition} /tmp/adb-remount-test.img
   err=${?}
-  rm /tmp/adb-remount-test.img
+  cleanup
+  cleanup() {
+    :
+  }
   fastboot reboot ||
     die "can not reboot out of fastboot"
   [ 0 -eq ${err} ] ||
@@ -676,6 +738,8 @@
   T=`adb_date`
   D=`adb disable-verity 2>&1`
   err=${?}
+  adb remount ||
+    die "remount failed"
   echo "${D}"
   [ ${err} = 0 ] &&
     [ X"${D}" = X"${D##*setup failed}" ] &&
@@ -684,4 +748,18 @@
     die -t ${T} "setup for overlayfs"
 fi
 
+echo "${GREEN}[ RUN      ]${NORMAL} test raw remount command" >&2
+
+# prerequisite is a prepped device from above
+adb_reboot &&
+  adb_wait 2m ||
+  die "lost device after reboot to ro state"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null &&
+  die "/vendor is not read-only"
+adb_su mount -o rw,remount /vendor ||
+  die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null ||
+  die "/vendor is not read-write"
+echo "${GREEN}[       OK ]${NORMAL} mount -o rw,remount command works" >&2
+
 echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 1922a69..4582401 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -138,38 +138,32 @@
 }
 
 TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
-    auto fstab = fs_mgr_read_fstab("/proc/mounts");
-    ASSERT_NE(fstab, nullptr);
+    Fstab fstab;
+    ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab));
 
     std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "re"),
                                                            endmntent);
     ASSERT_NE(mounts, nullptr);
 
     mntent* mentry;
-    int i = 0;
+    size_t i = 0;
     while ((mentry = getmntent(mounts.get())) != nullptr) {
-        ASSERT_LT(i, fstab->num_entries);
-        auto fsrec = &fstab->recs[i];
+        ASSERT_LT(i, fstab.size());
+        auto& entry = fstab[i];
 
-        std::string mnt_fsname(mentry->mnt_fsname ?: "nullptr");
-        std::string blk_device(fsrec->blk_device ?: "nullptr");
-        EXPECT_EQ(mnt_fsname, blk_device);
-
-        std::string mnt_dir(mentry->mnt_dir ?: "nullptr");
-        std::string mount_point(fsrec->mount_point ?: "nullptr");
-        EXPECT_EQ(mnt_dir, mount_point);
-
-        std::string mnt_type(mentry->mnt_type ?: "nullptr");
-        std::string fs_type(fsrec->fs_type ?: "nullptr");
-        EXPECT_EQ(mnt_type, fs_type);
+        EXPECT_EQ(mentry->mnt_fsname, entry.blk_device);
+        EXPECT_EQ(mentry->mnt_dir, entry.mount_point);
+        EXPECT_EQ(mentry->mnt_type, entry.fs_type);
 
         std::set<std::string> mnt_opts;
-        for (auto& s : android::base::Split(mentry->mnt_opts ?: "nullptr", ",")) {
+        for (auto& s : android::base::Split(mentry->mnt_opts, ",")) {
             mnt_opts.emplace(s);
         }
         std::set<std::string> fs_options;
-        for (auto& s : android::base::Split(fsrec->fs_options ?: "nullptr", ",")) {
-            fs_options.emplace(s);
+        if (!entry.fs_options.empty()) {
+            for (auto& s : android::base::Split(entry.fs_options, ",")) {
+                fs_options.emplace(s);
+            }
         }
         // matches private content in fs_mgr_fstab.c
         static struct flag_list {
@@ -194,14 +188,17 @@
                 {0, 0},
         };
         for (auto f = 0; mount_flags[f].name; ++f) {
-            if (mount_flags[f].flag & fsrec->flags) {
+            if (mount_flags[f].flag & entry.flags) {
                 fs_options.emplace(mount_flags[f].name);
             }
         }
-        if (!(fsrec->flags & MS_RDONLY)) fs_options.emplace("rw");
+        if (!(entry.flags & MS_RDONLY)) {
+            fs_options.emplace("rw");
+        }
         EXPECT_EQ(mnt_opts, fs_options);
         ++i;
     }
+    EXPECT_EQ(i, fstab.size());
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsOptions) {
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 80bf84a..2127b96 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -109,6 +109,7 @@
     libbase \
     libutils \
     libcutils \
+    libprocessgroup \
     liblog \
     libm \
     libc \
diff --git a/init/Android.bp b/init/Android.bp
index c920dc2..9f5d17d 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -19,7 +19,6 @@
     cpp_std: "experimental",
     sanitize: {
         misc_undefined: ["signed-integer-overflow"],
-        address: false,  // TODO(b/120561310): Fix ASAN to work without /proc mounted and re-enable.
     },
     cflags: [
         "-DLOG_UEVENTS=0",
@@ -77,6 +76,7 @@
         "libext4_utils",
         "libfs_mgr",
         "libfscrypt",
+        "libgsi",
         "libhidl-gen-utils",
         "libkeyutils",
         "liblog",
diff --git a/init/Android.mk b/init/Android.mk
index bdd0301..69c63e1 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -91,6 +91,7 @@
     libz \
     libselinux \
     libcap \
+    libgsi \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/README.md b/init/README.md
index 8d47e0a..3a7c71c 100644
--- a/init/README.md
+++ b/init/README.md
@@ -482,9 +482,8 @@
   -f: force installation of the module even if the version of the running kernel
   and the version of the kernel for which the module was compiled do not match.
 
-`load_all_props`
-> Loads properties from /system, /vendor, et cetera.
-  This is included in the default init.rc.
+`load_system_props`
+> (This action is deprecated and no-op.)
 
 `load_persist_props`
 > Loads persistent properties when /data has been decrypted.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 4a66e46..169edbe 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -54,6 +54,7 @@
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
 #include <fscrypt/fscrypt_init_extensions.h>
+#include <libgsi/libgsi.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -520,6 +521,9 @@
         return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
+        if (android::gsi::IsGsiRunning()) {
+            return Error() << "cannot wipe within GSI";
+        }
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
         const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
         return reboot_into_recovery(options);
@@ -972,7 +976,7 @@
 }
 
 static Result<Success> do_load_system_props(const BuiltinArguments& args) {
-    load_system_props();
+    LOG(INFO) << "deprecated action `load_system_props` called.";
     return Success();
 }
 
@@ -1022,7 +1026,8 @@
     }
     service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
-            if (fscrypt_is_native()) {
+            // TODO (b/122850122): support this in gsi
+            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
                 LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
                 if (auto result = reboot_into_recovery(
                             {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
@@ -1093,86 +1098,6 @@
     }
 }
 
-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();
@@ -1211,7 +1136,6 @@
         {"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}}},
@@ -1220,7 +1144,6 @@
         {"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 71fe401..153b857 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -34,6 +34,7 @@
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
+#include <libgsi/libgsi.h>
 #include <liblp/liblp.h>
 
 #include "devices.h"
@@ -72,13 +73,16 @@
     bool InitRequiredDevices();
     bool InitMappedDevice(const std::string& verity_device);
     bool CreateLogicalPartitions();
-    bool MountPartition(FstabEntry* fstab_entry);
+    bool MountPartition(const Fstab::iterator& begin, bool erase_used_fstab_entry,
+                        Fstab::iterator* end = nullptr);
+
     bool MountPartitions();
     bool TrySwitchSystemAsRoot();
     bool TrySkipMountingPartitions();
     bool IsDmLinearEnabled();
     bool GetDmLinearMetadataDevice();
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
+    void UseGsiIfPresent();
 
     ListenerAction UeventCallback(const Uevent& uevent);
 
@@ -207,6 +211,8 @@
     }
 
     required_devices_partition_names_.emplace(super_partition_name_);
+    // When booting from live GSI images, userdata is the super device.
+    required_devices_partition_names_.emplace("userdata");
     return true;
 }
 
@@ -381,65 +387,68 @@
     return true;
 }
 
-bool FirstStageMount::MountPartition(FstabEntry* fstab_entry) {
-    if (fstab_entry->fs_mgr_flags.logical) {
-        if (!fs_mgr_update_logical_partition(fstab_entry)) {
+bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_used_fstab_entry,
+                                     Fstab::iterator* end) {
+    if (begin->fs_mgr_flags.logical) {
+        if (!fs_mgr_update_logical_partition(&(*begin))) {
             return false;
         }
-        if (!InitMappedDevice(fstab_entry->blk_device)) {
+        if (!InitMappedDevice(begin->blk_device)) {
             return false;
         }
     }
-    if (!SetUpDmVerity(fstab_entry)) {
-        PLOG(ERROR) << "Failed to setup verity for '" << fstab_entry->mount_point << "'";
+    if (!SetUpDmVerity(&(*begin))) {
+        PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'";
         return false;
     }
-    if (fs_mgr_do_mount_one(*fstab_entry)) {
-        if (fstab_entry->fs_mgr_flags.formattable) {
-            PLOG(INFO) << "Failed to mount '" << fstab_entry->mount_point << "', "
-                       << "ignoring mount for formattable partition";
-            return true;
+
+    bool mounted = (fs_mgr_do_mount_one(*begin) == 0);
+
+    // Try other mounts with the same mount point.
+    Fstab::iterator current = begin + 1;
+    for (; current != fstab_.end() && current->mount_point == begin->mount_point; current++) {
+        if (!mounted) {
+            // blk_device is already updated to /dev/dm-<N> by SetUpDmVerity() above.
+            // Copy it from the begin iterator.
+            current->blk_device = begin->blk_device;
+            mounted = (fs_mgr_do_mount_one(*current) == 0);
         }
-        PLOG(ERROR) << "Failed to mount '" << fstab_entry->mount_point << "'";
-        return false;
     }
-    return true;
+    if (erase_used_fstab_entry) {
+        current = fstab_.erase(begin, current);
+    }
+    if (end) {
+        *end = current;
+    }
+    return mounted;
 }
 
 // If system is in the fstab then we're not a system-as-root device, and in
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
 bool FirstStageMount::TrySwitchSystemAsRoot() {
+    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/metadata";
+    });
+    if (metadata_partition != fstab_.end()) {
+        if (MountPartition(metadata_partition, true /* erase_used_fstab_entry */)) {
+            UseGsiIfPresent();
+        }
+    }
+
     auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
         return entry.mount_point == "/system";
     });
 
     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;
-        }
-        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";
+    if (MountPartition(system_partition, true /* erase_used_fstab_entry */)) {
+        SwitchRoot("/system");
+    } else {
+        PLOG(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;
 }
 
@@ -476,23 +485,21 @@
 
     if (!TrySkipMountingPartitions()) return false;
 
-    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;
+    for (auto current = fstab_.begin(); current != fstab_.end();) {
+        Fstab::iterator end;
+        if (!MountPartition(current, false, &end)) {
+            if (current->fs_mgr_flags.no_fail) {
+                LOG(INFO) << "Failed to mount " << current->mount_point
+                          << ", ignoring mount for no_fail partition";
+            } else if (current->fs_mgr_flags.formattable) {
+                LOG(INFO) << "Failed to mount " << current->mount_point
+                          << ", ignoring mount for formattable partition";
+            } else {
+                PLOG(ERROR) << "Failed to mount " << current->mount_point;
+                return false;
+            }
         }
+        current = end;
     }
 
     // heads up for instantiating required device(s) for overlayfs logic
@@ -513,6 +520,40 @@
     return true;
 }
 
+void FirstStageMount::UseGsiIfPresent() {
+    std::string metadata_file, error;
+
+    if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) {
+        LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
+        return;
+    }
+
+    auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str());
+    if (!metadata) {
+        LOG(ERROR) << "GSI partition layout could not be read";
+        return;
+    }
+
+    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), "/dev/block/by-name/userdata")) {
+        LOG(ERROR) << "GSI partition layout could not be instantiated";
+        return;
+    }
+
+    if (!android::gsi::MarkSystemAsGsi()) {
+        PLOG(ERROR) << "GSI indicator file could not be written";
+        return;
+    }
+
+    // Replace the existing system fstab entry.
+    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/system";
+    });
+    if (system_partition != fstab_.end()) {
+        fstab_.erase(system_partition);
+    }
+    fstab_.emplace_back(BuildGsiSystemFstabEntry());
+}
+
 bool FirstStageMountVBootV1::GetDmVerityDevices() {
     std::string verity_loc_device;
     need_dm_verity_ = false;
diff --git a/init/init.cpp b/init/init.cpp
index dc46a82..d360fdd 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -451,6 +451,8 @@
     // Also, binder can't be used by recovery.
 #ifndef RECOVERY
     android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
+    android::ProcessState::self()->setCallRestriction(
+            ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY);
 #endif
     return Success();
 }
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 5328869..3199d45 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -699,22 +699,6 @@
     }
 }
 
-void property_load_boot_defaults() {
-    if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
-        // Try recovery path
-        if (!load_properties_from_file("/prop.default", NULL)) {
-            // Try legacy path
-            load_properties_from_file("/default.prop", NULL);
-        }
-    }
-    load_properties_from_file("/product/build.prop", NULL);
-    load_properties_from_file("/product_services/build.prop", NULL);
-    load_properties_from_file("/odm/default.prop", NULL);
-    load_properties_from_file("/vendor/default.prop", NULL);
-
-    update_sys_usb_config();
-}
-
 static void load_override_properties() {
     if (ALLOW_LOCAL_PROP_OVERRIDE) {
         load_properties_from_file("/data/local.prop", NULL);
@@ -779,12 +763,29 @@
     close(fd);
 }
 
-void load_system_props() {
+void property_load_boot_defaults() {
+    // TODO(b/117892318): merge prop.default and build.prop files into one
+    // TODO(b/122864654): read the prop files from all partitions and then
+    // resolve the duplication by their origin so that RO and non-RO properties
+    // have a consistent overriding order.
+    if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
+        // Try recovery path
+        if (!load_properties_from_file("/prop.default", NULL)) {
+            // Try legacy path
+            load_properties_from_file("/default.prop", NULL);
+        }
+    }
+    load_properties_from_file("/product/build.prop", NULL);
+    load_properties_from_file("/product_services/build.prop", NULL);
+    load_properties_from_file("/odm/default.prop", NULL);
+    load_properties_from_file("/vendor/default.prop", NULL);
     load_properties_from_file("/system/build.prop", NULL);
     load_properties_from_file("/odm/build.prop", NULL);
     load_properties_from_file("/vendor/build.prop", NULL);
     load_properties_from_file("/factory/factory.prop", "ro.*");
     load_recovery_id_prop();
+
+    update_sys_usb_config();
 }
 
 static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 45dc6d3..5b90969 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,9 +20,11 @@
 #include <fcntl.h>
 #include <linux/fs.h>
 #include <mntent.h>
+#include <linux/loop.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
+#include <sys/swap.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -55,9 +57,11 @@
 #include "service.h"
 #include "sigchld_handler.h"
 
+using android::base::GetBoolProperty;
 using android::base::Split;
 using android::base::StringPrintf;
 using android::base::Timer;
+using android::base::unique_fd;
 
 namespace android {
 namespace init {
@@ -103,13 +107,17 @@
         int st;
         if (IsF2Fs()) {
             const char* f2fs_argv[] = {
-                "/system/bin/fsck.f2fs", "-f", mnt_fsname_.c_str(),
+                    "/system/bin/fsck.f2fs",
+                    "-a",
+                    mnt_fsname_.c_str(),
             };
             android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG,
                                     true, nullptr, nullptr, 0);
         } else if (IsExt4()) {
             const char* ext4_argv[] = {
-                "/system/bin/e2fsck", "-f", "-y", mnt_fsname_.c_str(),
+                    "/system/bin/e2fsck",
+                    "-y",
+                    mnt_fsname_.c_str(),
             };
             android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG,
                                     true, nullptr, nullptr, 0);
@@ -189,7 +197,7 @@
     return true;
 }
 
-static void DumpUmountDebuggingInfo(bool dump_all) {
+static void DumpUmountDebuggingInfo() {
     int status;
     if (!security_getenforce()) {
         LOG(INFO) << "Run lsof";
@@ -198,10 +206,9 @@
                                 true, nullptr, nullptr, 0);
     }
     FindPartitionsToUmount(nullptr, nullptr, true);
-    if (dump_all) {
-        // dump current tasks, this log can be lengthy, so only dump with dump_all
-        android::base::WriteStringToFile("t", "/proc/sysrq-trigger");
-    }
+    // dump current CPU stack traces and uninterruptible tasks
+    android::base::WriteStringToFile("l", "/proc/sysrq-trigger");
+    android::base::WriteStringToFile("w", "/proc/sysrq-trigger");
 }
 
 static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
@@ -264,11 +271,11 @@
     UmountStat stat = UmountPartitions(timeout - t.duration());
     if (stat != UMOUNT_STAT_SUCCESS) {
         LOG(INFO) << "umount timeout, last resort, kill all and try";
-        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
+        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
         KillAllProcesses();
         // even if it succeeds, still it is timeout and do not run fsck with all processes killed
         UmountStat st = UmountPartitions(0ms);
-        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
+        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
     }
 
     if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
@@ -281,6 +288,48 @@
     return stat;
 }
 
+// zram is able to use backing device on top of a loopback device.
+// In order to unmount /data successfully, we have to kill the loopback device first
+#define ZRAM_DEVICE   "/dev/block/zram0"
+#define ZRAM_RESET    "/sys/block/zram0/reset"
+#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
+static void KillZramBackingDevice() {
+    std::string backing_dev;
+    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return;
+
+    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return;
+
+    // cut the last "\n"
+    backing_dev.erase(backing_dev.length() - 1);
+
+    // shutdown zram handle
+    Timer swap_timer;
+    LOG(INFO) << "swapoff() start...";
+    if (swapoff(ZRAM_DEVICE) == -1) {
+        LOG(ERROR) << "zram_backing_dev: swapoff (" << backing_dev << ")" << " failed";
+        return;
+    }
+    LOG(INFO) << "swapoff() took " << swap_timer;;
+
+    if (!android::base::WriteStringToFile("1", ZRAM_RESET)) {
+        LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
+        return;
+    }
+
+    // clear loopback device
+    unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
+    if (loop.get() < 0) {
+        LOG(ERROR) << "zram_backing_dev: open(" << backing_dev << ")" << " failed";
+        return;
+    }
+
+    if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) {
+        LOG(ERROR) << "zram_backing_dev: loop_clear (" << backing_dev << ")" << " failed";
+        return;
+    }
+    LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully.";
+}
+
 //* Reboot / shutdown the system.
 // cmd ANDROID_RB_* as defined in android_reboot.h
 // reason Reason string like "reboot", "shutdown,userrequested"
@@ -350,9 +399,31 @@
     Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
     Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
     if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
-        // will not check animation class separately
+        bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
+
+        if (do_shutdown_animation) {
+            property_set("service.bootanim.exit", "0");
+            // Could be in the middle of animation. Stop and start so that it can pick
+            // up the right mode.
+            bootAnim->Stop();
+        }
+
         for (const auto& service : ServiceList::GetInstance()) {
-            if (service->classnames().count("animation")) service->SetShutdownCritical();
+            if (service->classnames().count("animation") == 0) {
+                continue;
+            }
+
+            // start all animation classes if stopped.
+            if (do_shutdown_animation) {
+                service->Start().IgnoreError();
+            }
+            service->SetShutdownCritical();  // will not check animation class separately
+        }
+
+        if (do_shutdown_animation) {
+            bootAnim->Start().IgnoreError();
+            surfaceFlinger->SetShutdownCritical();
+            bootAnim->SetShutdownCritical();
         }
     }
 
@@ -423,6 +494,9 @@
         sync();
         LOG(INFO) << "sync() before umount took" << sync_timer;
     }
+    // 5. drop caches and disable zram backing device, if exist
+    KillZramBackingDevice();
+
     UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
     // Follow what linux shutdown is doing: one more sync with little bit delay
     {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index e4da52c..ee302c1 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -34,16 +34,18 @@
 // identical to the system image shipped on a vendor's device.
 
 // The split SEPolicy is loaded as described below:
-// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
-//    Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
-//    were used to compile this precompiled policy.  The system partition contains a similar sha256
-//    of the parts of the SEPolicy that it currently contains.  If these two hashes match, then the
-//    system loads this precompiled_sepolicy directly.
-// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
-//    init needs to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it
-//    is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
-//    and load it.  That function contains even more documentation with the specific implementation
-//    details of how the SEPolicy is compiled if needed.
+// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
+//    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
+//    are the sha256 hashes of the parts of the SEPolicy on /system and /product that were used to
+//    compile this precompiled policy.  The system partition contains a similar sha256 of the parts
+//    of the SEPolicy that it currently contains.  Symmetrically, product paritition contains a
+//    sha256 of its SEPolicy.  System loads this precompiled_sepolicy directly if and only if hashes
+//    for system policy match and hashes for product policy match.
+// 2) If these hashes do not match, then either /system or /product (or both) have been updated out
+//    of sync with /vendor and the init needs to compile the SEPolicy.  /system contains the
+//    SEPolicy compiler, secilc, and it is used by the LoadSplitPolicy() function below to compile
+//    the SEPolicy to a temp directory and load it.  That function contains even more documentation
+//    with the specific implementation details of how the SEPolicy is compiled if needed.
 
 #include "selinux.h"
 
@@ -217,20 +219,35 @@
         return false;
     }
     std::string actual_plat_id;
-    if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+    if (!ReadFirstLine("/system/etc/selinux/plat_sepolicy_and_mapping.sha256", &actual_plat_id)) {
         PLOG(INFO) << "Failed to read "
-                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+                      "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
+        return false;
+    }
+    std::string actual_product_id;
+    if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
+                       &actual_product_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/product/etc/selinux/product_sepolicy_and_mapping.sha256";
         return false;
     }
 
     std::string precompiled_plat_id;
-    std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";
-    if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {
-        PLOG(INFO) << "Failed to read " << precompiled_sha256;
+    std::string precompiled_plat_sha256 = *file + ".plat_sepolicy_and_mapping.sha256";
+    if (!ReadFirstLine(precompiled_plat_sha256.c_str(), &precompiled_plat_id)) {
+        PLOG(INFO) << "Failed to read " << precompiled_plat_sha256;
         file->clear();
         return false;
     }
-    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+    std::string precompiled_product_id;
+    std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256";
+    if (!ReadFirstLine(precompiled_product_sha256.c_str(), &precompiled_product_id)) {
+        PLOG(INFO) << "Failed to read " << precompiled_product_sha256;
+        file->clear();
+        return false;
+    }
+    if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
+        actual_product_id.empty() || actual_product_id != precompiled_product_id) {
         file->clear();
         return false;
     }
@@ -304,13 +321,18 @@
     if (!GetVendorMappingVersion(&vend_plat_vers)) {
         return false;
     }
-    std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+    std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
 
     std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
     if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
         product_policy_cil_file.clear();
     }
 
+    std::string product_mapping_file("/product/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+    if (access(product_mapping_file.c_str(), F_OK) == -1) {
+        product_mapping_file.clear();
+    }
+
     // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
     // nonplat_sepolicy.cil.
     std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
@@ -340,7 +362,7 @@
         "-m", "-M", "true", "-G", "-N",
         // Target the highest policy language version supported by the kernel
         "-c", version_as_string.c_str(),
-        mapping_file.c_str(),
+        plat_mapping_file.c_str(),
         "-o", compiled_sepolicy,
         // We don't care about file_contexts output by the compiler
         "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
@@ -350,6 +372,9 @@
     if (!product_policy_cil_file.empty()) {
         compile_args.push_back(product_policy_cil_file.c_str());
     }
+    if (!product_mapping_file.empty()) {
+        compile_args.push_back(product_mapping_file.c_str());
+    }
     if (!plat_pub_versioned_cil_file.empty()) {
         compile_args.push_back(plat_pub_versioned_cil_file.c_str());
     }
@@ -434,12 +459,6 @@
 
     selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
     selinux_android_restorecon("/dev/device-mapper", 0);
-
-    selinux_android_restorecon("/sbin/mke2fs_static", 0);
-    selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
-
-    selinux_android_restorecon("/sbin/mkfs.f2fs", 0);
-    selinux_android_restorecon("/sbin/sload.f2fs", 0);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/service.cpp b/init/service.cpp
index eec55c3..272809f 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -140,43 +140,6 @@
     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";
@@ -966,14 +929,6 @@
         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;
@@ -990,37 +945,6 @@
             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_
@@ -1028,14 +952,6 @@
             }
         }
 
-        // 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.
@@ -1408,10 +1324,6 @@
     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 676111f..56e75b0 100644
--- a/init/service.h
+++ b/init/service.h
@@ -62,24 +62,6 @@
 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,
@@ -142,7 +124,6 @@
     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);
@@ -151,7 +132,6 @@
     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;
@@ -262,8 +242,6 @@
     std::vector<std::string> args_;
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
-
-    bool pre_apexd_ = false;
 };
 
 class ServiceList {
@@ -306,16 +284,13 @@
     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/libcutils/Android.bp b/libcutils/Android.bp
index 4291212..0dbbc3f 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -66,7 +66,6 @@
         "load_file.cpp",
         "native_handle.cpp",
         "record_stream.cpp",
-        "sched_policy.cpp",
         "sockets.cpp",
         "strdup16to8.cpp",
         "strdup8to16.cpp",
@@ -178,8 +177,12 @@
         "libbase_headers",
         "libcutils_headers",
         "libutils_headers",
+        "libprocessgroup_headers",
     ],
-    export_header_lib_headers: ["libcutils_headers"],
+    export_header_lib_headers: [
+        "libcutils_headers",
+        "libprocessgroup_headers",
+    ],
     local_include_dirs: ["include"],
 
     cflags: [
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 1490fbc..59cbbc5 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -197,6 +197,9 @@
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/run-as" },
+    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
+                                           CAP_MASK_LONG(CAP_SETGID),
+                                              "system/bin/simpleperf_app_runner" },
 
     // Support FIFO scheduling mode in SurfaceFlinger.
     { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index cf91b76..538ff6b 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -17,67 +17,10 @@
 #ifndef __CUTILS_SCHED_POLICY_H
 #define __CUTILS_SCHED_POLICY_H
 
-#include <stdbool.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /*
- * Check if Linux kernel enables CPUSETS feature.
- *
- * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ * For backwards compatibility only
+ * New users should include processgroup/sched_policy.h directly
  */
-extern bool cpusets_enabled();
-
-/*
- * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
- * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
- *
- * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
- */
-extern bool schedboost_enabled();
-
-/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
-typedef enum {
-    SP_DEFAULT = -1,
-    SP_BACKGROUND = 0,
-    SP_FOREGROUND = 1,
-    SP_SYSTEM = 2,  // can't be used with set_sched_policy()
-    SP_AUDIO_APP = 3,
-    SP_AUDIO_SYS = 4,
-    SP_TOP_APP = 5,
-    SP_RT_APP = 6,
-    SP_RESTRICTED = 7,
-    SP_CNT,
-    SP_MAX = SP_CNT - 1,
-    SP_SYSTEM_DEFAULT = SP_FOREGROUND,
-} SchedPolicy;
-
-extern int set_cpuset_policy(int tid, SchedPolicy policy);
-
-/* Assign thread tid to the cgroup associated with the specified policy.
- * If the thread is a thread group leader, that is it's gettid() == getpid(),
- * then the other threads in the same thread group are _not_ affected.
- * On platforms which support gettid(), zero tid means current thread.
- * Return value: 0 for success, or -errno for error.
- */
-extern int set_sched_policy(int tid, SchedPolicy policy);
-
-/* Return the policy associated with the cgroup of thread tid via policy pointer.
- * On platforms which support gettid(), zero tid means current thread.
- * Return value: 0 for success, or -1 for error and set errno.
- */
-extern int get_sched_policy(int tid, SchedPolicy *policy);
-
-/* Return a displayable string corresponding to policy.
- * Return value: non-NULL NUL-terminated name of unspecified length;
- * the caller is responsible for displaying the useful part of the string.
- */
-extern const char *get_sched_policy_name(SchedPolicy policy);
-
-#ifdef __cplusplus
-}
-#endif
+#include <processgroup/sched_policy.h>
 
 #endif /* __CUTILS_SCHED_POLICY_H */ 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 0723612..03edfb5 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -190,7 +190,8 @@
  */
 #define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
 
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
+/* use the ranges below to determine whether a process is isolated */
+#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */
 #define AID_ISOLATED_END 99999   /* end of uids for fully isolated sandboxed processes */
 
 #define AID_USER 100000        /* TODO: switch users over to AID_USER_OFFSET */
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 7884190..72ae559 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -59,6 +59,7 @@
     "libcutils",
     "liblog",
     "libbase",
+    "libprocessgroup",
 ]
 
 cc_test {
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 619a94b..bd7a551 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,30 +15,29 @@
 //
 
 liblog_sources = [
-    "config_read.c",
-    "config_write.c",
-    "log_event_list.c",
-    "log_event_write.c",
-    "log_ratelimit.cpp",
-    "logger_lock.c",
-    "logger_name.c",
-    "logger_read.c",
-    "logger_write.c",
-    "logprint.c",
-    "stderr_write.c",
+    "config_read.cpp",
+    "config_write.cpp",
+    "log_event_list.cpp",
+    "log_event_write.cpp",
+    "logger_lock.cpp",
+    "logger_name.cpp",
+    "logger_read.cpp",
+    "logger_write.cpp",
+    "logprint.cpp",
+    "stderr_write.cpp",
 ]
 liblog_host_sources = [
-    "fake_log_device.c",
-    "fake_writer.c",
+    "fake_log_device.cpp",
+    "fake_writer.cpp",
 ]
 liblog_target_sources = [
     "event_tag_map.cpp",
     "log_time.cpp",
-    "properties.c",
-    "pmsg_reader.c",
-    "pmsg_writer.c",
-    "logd_reader.c",
-    "logd_writer.c",
+    "properties.cpp",
+    "pmsg_reader.cpp",
+    "pmsg_writer.cpp",
+    "logd_reader.cpp",
+    "logd_writer.cpp",
 ]
 
 cc_library_headers {
@@ -74,6 +73,7 @@
             cflags: ["-DFAKE_LOG_DEVICE=1"],
         },
         android: {
+            version_script: "liblog.map.txt",
             srcs: liblog_target_sources,
             // AddressSanitizer runtime library depends on liblog.
             sanitize: {
@@ -86,7 +86,6 @@
             ldflags: ["-Wl,--hash-style=both"],
         },
         windows: {
-            srcs: ["uio.c"],
             enabled: true,
         },
         not_windows: {
diff --git a/liblog/config_read.c b/liblog/config_read.cpp
similarity index 68%
rename from liblog/config_read.c
rename to liblog/config_read.cpp
index 51ffff6..80177a4 100644
--- a/liblog/config_read.c
+++ b/liblog/config_read.cpp
@@ -19,23 +19,21 @@
 #include "config_read.h"
 #include "logger.h"
 
-LIBLOG_HIDDEN struct listnode __android_log_transport_read = {
-  &__android_log_transport_read, &__android_log_transport_read
-};
-LIBLOG_HIDDEN struct listnode __android_log_persist_read = {
-  &__android_log_persist_read, &__android_log_persist_read
-};
+LIBLOG_HIDDEN struct listnode __android_log_transport_read = {&__android_log_transport_read,
+                                                              &__android_log_transport_read};
+LIBLOG_HIDDEN struct listnode __android_log_persist_read = {&__android_log_persist_read,
+                                                            &__android_log_persist_read};
 
-static void __android_log_add_transport(
-    struct listnode* list, struct android_log_transport_read* transport) {
-  size_t i;
+static void __android_log_add_transport(struct listnode* list,
+                                        struct android_log_transport_read* transport) {
+  uint32_t i;
 
   /* Try to keep one functioning transport for each log buffer id */
   for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
     struct android_log_transport_read* transp;
 
     if (list_empty(list)) {
-      if (!transport->available || ((*transport->available)(i) >= 0)) {
+      if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
         list_add_tail(list, &transport->node);
         return;
       }
@@ -44,8 +42,8 @@
         if (!transp->available) {
           return;
         }
-        if (((*transp->available)(i) < 0) &&
-            (!transport->available || ((*transport->available)(i) >= 0))) {
+        if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
+            (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
           list_add_tail(list, &transport->node);
           return;
         }
@@ -56,8 +54,7 @@
 
 LIBLOG_HIDDEN void __android_log_config_read() {
 #if (FAKE_LOG_DEVICE == 0)
-  if ((__android_log_transport == LOGGER_DEFAULT) ||
-      (__android_log_transport & LOGGER_LOGD)) {
+  if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
     extern struct android_log_transport_read logdLoggerRead;
     extern struct android_log_transport_read pmsgLoggerRead;
 
diff --git a/liblog/config_read.h b/liblog/config_read.h
index 7b29fa4..00ea453 100644
--- a/liblog/config_read.h
+++ b/liblog/config_read.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_CONFIG_READ_H__
-#define _LIBLOG_CONFIG_READ_H__
+#pragma once
 
 #include <cutils/list.h>
 
@@ -51,5 +50,3 @@
 LIBLOG_HIDDEN void __android_log_config_read_close();
 
 __END_DECLS
-
-#endif /* _LIBLOG_CONFIG_READ_H__ */
diff --git a/liblog/config_write.c b/liblog/config_write.cpp
similarity index 69%
rename from liblog/config_write.c
rename to liblog/config_write.cpp
index 003ec8f..e65c238 100644
--- a/liblog/config_write.c
+++ b/liblog/config_write.cpp
@@ -19,23 +19,21 @@
 #include "config_write.h"
 #include "logger.h"
 
-LIBLOG_HIDDEN struct listnode __android_log_transport_write = {
-  &__android_log_transport_write, &__android_log_transport_write
-};
-LIBLOG_HIDDEN struct listnode __android_log_persist_write = {
-  &__android_log_persist_write, &__android_log_persist_write
-};
+LIBLOG_HIDDEN struct listnode __android_log_transport_write = {&__android_log_transport_write,
+                                                               &__android_log_transport_write};
+LIBLOG_HIDDEN struct listnode __android_log_persist_write = {&__android_log_persist_write,
+                                                             &__android_log_persist_write};
 
-static void __android_log_add_transport(
-    struct listnode* list, struct android_log_transport_write* transport) {
-  size_t i;
+static void __android_log_add_transport(struct listnode* list,
+                                        struct android_log_transport_write* transport) {
+  uint32_t i;
 
   /* Try to keep one functioning transport for each log buffer id */
   for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
     struct android_log_transport_write* transp;
 
     if (list_empty(list)) {
-      if (!transport->available || ((*transport->available)(i) >= 0)) {
+      if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
         list_add_tail(list, &transport->node);
         return;
       }
@@ -44,8 +42,8 @@
         if (!transp->available) {
           return;
         }
-        if (((*transp->available)(i) < 0) &&
-            (!transport->available || ((*transport->available)(i) >= 0))) {
+        if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
+            (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
           list_add_tail(list, &transport->node);
           return;
         }
@@ -55,20 +53,17 @@
 }
 
 LIBLOG_HIDDEN void __android_log_config_write() {
-  if ((__android_log_transport == LOGGER_DEFAULT) ||
-      (__android_log_transport & LOGGER_LOGD)) {
+  if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
 #if (FAKE_LOG_DEVICE == 0)
     extern struct android_log_transport_write logdLoggerWrite;
     extern struct android_log_transport_write pmsgLoggerWrite;
 
-    __android_log_add_transport(&__android_log_transport_write,
-                                &logdLoggerWrite);
+    __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
     __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
 #else
     extern struct android_log_transport_write fakeLoggerWrite;
 
-    __android_log_add_transport(&__android_log_transport_write,
-                                &fakeLoggerWrite);
+    __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
 #endif
   }
 
@@ -81,8 +76,7 @@
      * Remember we can be called here if we are already initialized.
      */
     if (list_empty(&__android_log_transport_write)) {
-      __android_log_add_transport(&__android_log_transport_write,
-                                  &stderrLoggerWrite);
+      __android_log_add_transport(&__android_log_transport_write, &stderrLoggerWrite);
     } else {
       struct android_log_transport_write* transp;
       write_transport_for_each(transp, &__android_log_transport_write) {
@@ -90,8 +84,7 @@
           return;
         }
       }
-      __android_log_add_transport(&__android_log_persist_write,
-                                  &stderrLoggerWrite);
+      __android_log_add_transport(&__android_log_persist_write, &stderrLoggerWrite);
     }
   }
 }
diff --git a/liblog/config_write.h b/liblog/config_write.h
index db1a083..e3be445 100644
--- a/liblog/config_write.h
+++ b/liblog/config_write.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_CONFIG_WRITE_H__
-#define _LIBLOG_CONFIG_WRITE_H__
+#pragma once
 
 #include <cutils/list.h>
 
@@ -51,5 +50,3 @@
 LIBLOG_HIDDEN void __android_log_config_write_close();
 
 __END_DECLS
-
-#endif /* _LIBLOG_CONFIG_WRITE_H__ */
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.cpp
similarity index 88%
rename from liblog/fake_log_device.c
rename to liblog/fake_log_device.cpp
index 1483c24..5daae41 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.cpp
@@ -17,6 +17,9 @@
  * Intercepts log messages intended for the Android log device.
  * Messages are printed to stderr.
  */
+
+#include "fake_log_device.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -30,9 +33,7 @@
 #include <time.h>
 
 #include <android/log.h>
-#include <log/uio.h>
 
-#include "fake_log_device.h"
 #include "log_portability.h"
 
 #define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
@@ -181,8 +182,7 @@
   logState->debugName[sizeof(logState->debugName) - 1] = '\0';
 
   /* identify binary logs */
-  if (!strcmp(pathName + kDevLogLen, "events") ||
-      !strcmp(pathName + kDevLogLen, "security")) {
+  if (!strcmp(pathName + kDevLogLen, "events") || !strcmp(pathName + kDevLogLen, "security")) {
     logState->isBinary = 1;
   }
 
@@ -204,8 +204,7 @@
       while (isspace(*tags)) tags++;
 
       i = 0;
-      while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
-             i < kMaxTagLen) {
+      while (*tags != '\0' && !isspace(*tags) && *tags != ':' && i < kMaxTagLen) {
         tagName[i++] = *tags++;
       }
       if (i == kMaxTagLen) {
@@ -313,13 +312,11 @@
  */
 static const char* getPriorityString(int priority) {
   /* the first character of each string should be unique */
-  static const char* priorityStrings[] = { "Verbose", "Debug", "Info",
-                                           "Warn",    "Error", "Assert" };
+  static const char* priorityStrings[] = {"Verbose", "Debug", "Info", "Warn", "Error", "Assert"};
   int idx;
 
   idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
-  if (idx < 0 ||
-      idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+  if (idx < 0 || idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
     return "?unknown?";
   return priorityStrings[idx];
 }
@@ -351,8 +348,7 @@
  *
  * Log format parsing taken from the long-dead utils/Log.cpp.
  */
-static void showLog(LogState* state, int logPrio, const char* tag,
-                    const char* msg) {
+static void showLog(LogState* state, int logPrio, const char* tag, const char* msg) {
 #if !defined(_WIN32)
   struct tm tmBuf;
 #endif
@@ -397,19 +393,16 @@
 
   switch (state->outputFormat) {
     case FORMAT_TAG:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
       strcpy(suffixBuf, "\n");
       suffixLen = 1;
       break;
     case FORMAT_PROCESS:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d) ", priChar, pid);
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d) ", priChar, pid);
       suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "  (%s)\n", tag);
       break;
     case FORMAT_THREAD:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d:%5d) ",
-                           priChar, pid, tid);
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d:%5d) ", priChar, pid, tid);
       strcpy(suffixBuf, "\n");
       suffixLen = 1;
       break;
@@ -420,28 +413,24 @@
       suffixLen = 1;
       break;
     case FORMAT_TIME:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "%s %-8s\n\t", timeBuf, tag);
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %-8s\n\t", timeBuf, tag);
       strcpy(suffixBuf, "\n");
       suffixLen = 1;
       break;
     case FORMAT_THREADTIME:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d %5d %c %-8s \n\t",
-                   timeBuf, pid, tid, priChar, tag);
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d %5d %c %-8s \n\t", timeBuf, pid,
+                           tid, priChar, tag);
       strcpy(suffixBuf, "\n");
       suffixLen = 1;
       break;
     case FORMAT_LONG:
-      prefixLen =
-          snprintf(prefixBuf, sizeof(prefixBuf), "[ %s %5d:%5d %c/%-8s ]\n",
-                   timeBuf, pid, tid, priChar, tag);
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "[ %s %5d:%5d %c/%-8s ]\n", timeBuf, pid,
+                           tid, priChar, tag);
       strcpy(suffixBuf, "\n\n");
       suffixLen = 2;
       break;
     default:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
-                           "%c/%-8s(%5d): ", priChar, tag, pid);
+      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s(%5d): ", priChar, tag, pid);
       strcpy(suffixBuf, "\n");
       suffixLen = 1;
       break;
@@ -559,8 +548,7 @@
  *  tag (N bytes -- null-terminated ASCII string)
  *  message (N bytes -- null-terminated ASCII string)
  */
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
-                                    int count) {
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) {
   LogState* state;
 
   /* Make sure that no-one frees the LogState while we're using it.
@@ -572,17 +560,24 @@
   state = fdToLogState(fd);
   if (state == NULL) {
     errno = EBADF;
-    goto error;
+    unlock();
+    return -1;
   }
 
   if (state->isBinary) {
     TRACE("%s: ignoring binary log\n", state->debugName);
-    goto bail;
+    unlock();
+    int len = 0;
+    for (int i = 0; i < count; ++i) {
+      len += vector[i].iov_len;
+    }
+    return len;
   }
 
   if (count != 3) {
     TRACE("%s: writevLog with count=%d not expected\n", state->debugName, count);
-    goto error;
+    unlock();
+    return -1;
   }
 
   /* pull out the three fields */
@@ -598,7 +593,6 @@
       break; /* reached end of configured values */
 
     if (strcmp(state->tagSet[i].tag, tag) == 0) {
-      // TRACE("MATCH tag '%s'\n", tag);
       minPrio = state->tagSet[i].minPriority;
       break;
     }
@@ -606,21 +600,14 @@
 
   if (logPrio >= minPrio) {
     showLog(state, logPrio, tag, msg);
-  } else {
-    // TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
   }
 
-bail:
   unlock();
   int len = 0;
   for (i = 0; i < count; ++i) {
     len += vector[i].iov_len;
   }
   return len;
-
-error:
-  unlock();
-  return -1;
 }
 
 /*
@@ -663,22 +650,16 @@
   return fd;
 }
 
-LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf __unused,
-                                     size_t buf_size __unused) {
+LIBLOG_HIDDEN ssize_t __send_log_msg(char*, size_t) {
   return -ENODEV;
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
-                                                const char* tag __unused,
-                                                int def) {
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char*, int def) {
   int logLevel = def;
   return logLevel >= 0 && prio >= logLevel;
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio,
-                                                    const char* tag __unused,
-                                                    size_t len __unused,
-                                                    int def) {
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
   int logLevel = def;
   return logLevel >= 0 && prio >= logLevel;
 }
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 7b0e745..ef0beb6 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -14,18 +14,25 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_FAKE_LOG_DEVICE_H
-#define _LIBLOG_FAKE_LOG_DEVICE_H
+#pragma once
 
 #include <sys/types.h>
 
 #include "log_portability.h"
+#include "uio.h"
 
 struct iovec;
 
+__BEGIN_DECLS
+
 LIBLOG_HIDDEN int fakeLogOpen(const char* pathName);
 LIBLOG_HIDDEN int fakeLogClose(int fd);
 LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
                                     int count);
 
-#endif  // _LIBLOG_FAKE_LOG_DEVICE_H
+LIBLOG_HIDDEN ssize_t __send_log_msg(char*, size_t);
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char*, int def);
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
+LIBLOG_ABI_PRIVATE int __android_log_is_debuggable();
+
+__END_DECLS
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.cpp
similarity index 82%
rename from liblog/fake_writer.c
rename to liblog/fake_writer.cpp
index 403dc72..46d171b 100644
--- a/liblog/fake_writer.c
+++ b/liblog/fake_writer.cpp
@@ -27,19 +27,18 @@
 
 static int fakeOpen();
 static void fakeClose();
-static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec,
-                     size_t nr);
+static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
 
-static int logFds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 };
+static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
 
 LIBLOG_HIDDEN struct android_log_transport_write fakeLoggerWrite = {
-  .node = { &fakeLoggerWrite.node, &fakeLoggerWrite.node },
-  .context.priv = &logFds,
-  .name = "fake",
-  .available = NULL,
-  .open = fakeOpen,
-  .close = fakeClose,
-  .write = fakeWrite,
+    .node = {&fakeLoggerWrite.node, &fakeLoggerWrite.node},
+    .context.priv = &logFds,
+    .name = "fake",
+    .available = NULL,
+    .open = fakeOpen,
+    .close = fakeClose,
+    .write = fakeWrite,
 };
 
 static int fakeOpen() {
@@ -54,7 +53,7 @@
     if (logFds[i] >= 0) {
       continue;
     }
-    snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
+    snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(static_cast<log_id_t>(i)));
     logFds[i] = fakeLogOpen(buf);
     if (logFds[i] < 0) {
       fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
@@ -72,8 +71,7 @@
   }
 }
 
-static int fakeWrite(log_id_t log_id, struct timespec* ts __unused,
-                     struct iovec* vec, size_t nr) {
+static int fakeWrite(log_id_t log_id, struct timespec*, struct iovec* vec, size_t nr) {
   ssize_t ret;
   size_t i;
   int logFd, len;
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 8dd9157..2687b3a 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
-#define _LIBS_CUTILS_EVENTTAGMAP_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
@@ -69,5 +68,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 3813e6e..5928649 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_H
-#define _LIBS_LOG_LOG_H
+#pragma once
 
 /* Too many in the ecosystem assume these are included */
 #if !defined(_WIN32)
@@ -35,7 +34,6 @@
 #include <log/log_safetynet.h>
 #include <log/log_system.h>
 #include <log/log_time.h>
-#include <log/uio.h> /* helper to define iovec for portability */
 
 #ifdef __cplusplus
 extern "C" {
@@ -152,35 +150,12 @@
 
 #ifdef __linux__
 
-#ifndef __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
 clockid_t android_log_clockid(void);
-#endif
 
 #endif /* __linux__ */
 
 /* --------------------------------------------------------------------- */
 
-#ifndef __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
-#elif __ANDROID_API__ > 18 /* > JellyBean */
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
 /*
  * Release any logger resources (a new log write will immediately re-acquire)
  *
@@ -188,54 +163,6 @@
  * all O_CLOEXEC so wil self clean on exec().
  */
 void __android_log_close(void);
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
-#elif __ANDROID_API__ > 25 /* > OC */
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
-
-/*
- * if last is NULL, caller _must_ provide a consistent value for seconds.
- *
- * Return -1 if we can not acquire a lock, which below will permit the logging,
- * error on allowing a log message through.
- */
-int __android_log_ratelimit(time_t seconds, time_t* last);
-
-/*
- * Usage:
- *
- *   // Global default and state
- *   IF_ALOG_RATELIMIT() {
- *      ALOG*(...);
- *   }
- *
- *   // local state, 10 seconds ratelimit
- *   static time_t local_state;
- *   IF_ALOG_RATELIMIT_LOCAL(10, &local_state) {
- *     ALOG*(...);
- *   }
- */
-
-#define IF_ALOG_RATELIMIT() if (__android_log_ratelimit(0, NULL) > 0)
-#define IF_ALOG_RATELIMIT_LOCAL(seconds, state) \
-  if (__android_log_ratelimit(seconds, state) > 0)
-
-#else
-
-/* No ratelimiting as API unsupported */
-#define IF_ALOG_RATELIMIT() if (1)
-#define IF_ALOG_RATELIMIT_LOCAL(...) if (1)
-
-#endif
 
 #if defined(__clang__)
 #pragma clang diagnostic pop
@@ -244,5 +171,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 1b7c377..636d417 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_EVENT_LIST_H
-#define _LIBS_LOG_EVENT_LIST_H
+#pragma once
 
 #include <errno.h>
 #include <stdint.h>
 
-#if (defined(__cplusplus) && defined(_USING_LIBCXX))
-extern "C++" {
+#ifdef __cplusplus
 #include <string>
-}
 #endif
 
 #include <log/log.h>
@@ -32,18 +29,6 @@
 extern "C" {
 #endif
 
-#ifndef __ANDROID_USE_LIBLOG_EVENT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-#elif __ANDROID_API__ > 23 /* > Marshmallow */
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_EVENT_INTERFACE
-
 /* For manipulating lists of events. */
 
 #define ANDROID_MAX_LIST_NEST_DEPTH 8
@@ -124,8 +109,6 @@
 /* android_log_list C++ helpers */
 extern "C++" {
 class android_log_event_list {
-  friend class __android_log_event_list;
-
  private:
   android_log_context ctx;
   int ret;
@@ -137,10 +120,6 @@
   explicit android_log_event_list(int tag) : ret(0) {
     ctx = create_android_logger(static_cast<uint32_t>(tag));
   }
-  explicit android_log_event_list(log_msg& log_msg) : ret(0) {
-    ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
-                                    log_msg.entry.len - sizeof(uint32_t));
-  }
   ~android_log_event_list() {
     android_log_destroy(&ctx);
   }
@@ -208,14 +187,12 @@
     return *this;
   }
 
-#if defined(_USING_LIBCXX)
   android_log_event_list& operator<<(const std::string& value) {
     int retval =
         android_log_write_string8_len(ctx, value.data(), value.length());
     if (retval < 0) ret = retval;
     return *this;
   }
-#endif
 
   android_log_event_list& operator<<(float value) {
     int retval = android_log_write_float32(ctx, value);
@@ -269,7 +246,6 @@
     return ret >= 0;
   }
 
-#if defined(_USING_LIBCXX)
   bool AppendString(const std::string& value) {
     int retval =
         android_log_write_string8_len(ctx, value.data(), value.length());
@@ -283,7 +259,6 @@
     if (retval < 0) ret = retval;
     return ret;
   }
-#endif
 
   bool AppendFloat(float value) {
     int retval = android_log_write_float32(ctx, value);
@@ -302,22 +277,11 @@
     if (retval < 0) ret = retval;
     return ret >= 0;
   }
-
-  android_log_list_element read() {
-    return android_log_read_next(ctx);
-  }
-  android_log_list_element peek() {
-    return android_log_peek_next(ctx);
-  }
 };
 }
 #endif
 #endif
 
-#endif /* __ANDROID_USE_LIBLOG_EVENT_INTERFACE */
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c44f5a2..c052a50 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_ID_H
-#define _LIBS_LOG_LOG_ID_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
@@ -62,5 +61,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_LOG_ID_H */
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 53653de..64791c2 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_MAIN_H
-#define _LIBS_LOG_LOG_MAIN_H
+#pragma once
 
 #include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
 
 #include <android/log.h>
-#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -349,20 +349,6 @@
  *        over Android.
  */
 
-#ifndef __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 2
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 2
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE
-
 /*
  * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
  * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
@@ -370,12 +356,7 @@
  * any other value.
  */
 int __android_log_is_loggable(int prio, const char* tag, int default_prio);
-
-#if __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE > 1
-#include <sys/types.h>
-
-int __android_log_is_loggable_len(int prio, const char* tag, size_t len,
-                                  int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
 
 #if LOG_NDEBUG /* Production */
 #define android_testLog(prio, tag)                                           \
@@ -387,28 +368,8 @@
                                  ANDROID_LOG_VERBOSE) != 0)
 #endif
 
-#else
-
-#if LOG_NDEBUG /* Production */
-#define android_testLog(prio, tag) \
-  (__android_log_is_loggable(prio, tag, ANDROID_LOG_DEBUG) != 0)
-#else
-#define android_testLog(prio, tag) \
-  (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
-#endif
-
-#endif
-
-#else /* __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE */
-
-#define android_testLog(prio, tag) (1)
-
-#endif /* !__ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE */
-
 #if defined(__clang__)
 #pragma clang diagnostic pop
 #endif
 
 __END_DECLS
-
-#endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 7d398a6..3a8af6d 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -7,29 +7,14 @@
 ** General Public License.
 */
 
-#ifndef _LIBS_LOG_PROPERTIES_H
-#define _LIBS_LOG_PROPERTIES_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#ifndef __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
 int __android_log_is_debuggable();
-#endif
 
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_PROPERTIES_H */
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
index bd629fe..8b8a362 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_RADIO_H
-#define _LIBS_LOG_LOG_RADIO_H
+#pragma once
 
 #include <android/log.h>
 #include <log/log_id.h>
@@ -140,5 +139,3 @@
                                         LOG_TAG, __VA_ARGS__))           \
        : (void)0)
 #endif
-
-#endif /* _LIBS_LOG_LOG_RADIO_H */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 93b9d4e..fdef306 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_READ_H
-#define _LIBS_LOG_LOG_READ_H
+#pragma once
+
+#include <sys/types.h>
 
 /* deal with possible sys/cdefs.h conflict with fcntl.h */
 #ifdef __unused
@@ -47,6 +48,8 @@
  * access to raw information, or parsing is an issue.
  */
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
 /*
  * The userspace structure for version 1 of the logger_entry ABI.
  */
@@ -59,9 +62,7 @@
   int32_t tid;    /* generating process's tid */
   int32_t sec;    /* seconds since Epoch */
   int32_t nsec;   /* nanoseconds */
-#ifndef __cplusplus
   char msg[0]; /* the entry's payload */
-#endif
 };
 #endif
 
@@ -78,9 +79,7 @@
   int32_t sec;       /* seconds since Epoch */
   int32_t nsec;      /* nanoseconds */
   uint32_t euid;     /* effective UID of logger */
-#ifndef __cplusplus
   char msg[0]; /* the entry's payload */
-#endif
 } __attribute__((__packed__));
 #endif
 
@@ -97,9 +96,7 @@
   int32_t sec;       /* seconds since Epoch */
   int32_t nsec;      /* nanoseconds */
   uint32_t lid;      /* log id of the payload */
-#ifndef __cplusplus
   char msg[0]; /* the entry's payload */
-#endif
 } __attribute__((__packed__));
 #endif
 
@@ -117,11 +114,10 @@
   uint32_t nsec;     /* nanoseconds */
   uint32_t lid;      /* log id of the payload, bottom 4 bits currently */
   uint32_t uid;      /* generating process's uid */
-#ifndef __cplusplus
   char msg[0]; /* the entry's payload */
-#endif
 };
 #endif
+#pragma clang diagnostic pop
 
 /*
  * The maximum size of the log entry payload that can be
@@ -197,22 +193,6 @@
 };
 #endif
 
-#ifndef __ANDROID_USE_LIBLOG_READER_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
-#elif __ANDROID_API__ > 23 /* > Marshmallow */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 2
-#elif __ANDROID_API__ > 19 /* > KitKat */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE
-
 struct logger;
 
 log_id_t android_logger_get_id(struct logger* logger);
@@ -225,14 +205,12 @@
 
 struct logger_list;
 
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
 ssize_t android_logger_get_statistics(struct logger_list* logger_list,
                                       char* buf, size_t len);
 ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
                                       char* buf, size_t len);
 int android_logger_set_prune_list(struct logger_list* logger_list, char* buf,
                                   size_t len);
-#endif
 
 #define ANDROID_LOG_RDONLY O_RDONLY
 #define ANDROID_LOG_WRONLY O_WRONLY
@@ -243,13 +221,9 @@
 #else
 #define ANDROID_LOG_NONBLOCK O_NONBLOCK
 #endif
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2
 #define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
 #define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
-#endif
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
 #define ANDROID_LOG_PSTORE 0x80000000
-#endif
 
 struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
                                               pid_t pid);
@@ -268,10 +242,6 @@
                                              unsigned int tail, pid_t pid);
 #define android_logger_list_close android_logger_list_free
 
-#endif /* __ANDROID_USE_LIBLOG_READER_INTERFACE */
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index 07e8c8a..d3e9b19 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -7,8 +7,7 @@
 ** General Public License.
 */
 
-#ifndef _LIBS_LOG_SAFETYNET_H
-#define _LIBS_LOG_SAFETYNET_H
+#pragma once
 
 #include <stdint.h>
 
@@ -16,18 +15,6 @@
 extern "C" {
 #endif
 
-#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-
 #define android_errorWriteLog(tag, subTag) \
   __android_log_error_write(tag, subTag, -1, NULL, 0)
 
@@ -37,10 +24,6 @@
 int __android_log_error_write(int tag, const char* subTag, int32_t uid,
                               const char* data, uint32_t dataLen);
 
-#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_SAFETYNET_H */
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index 3b5ae22..eaec741 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_SYSTEM_H
-#define _LIBS_LOG_LOG_SYSTEM_H
+#pragma once
 
 #include <android/log.h>
 #include <log/log_id.h>
@@ -138,5 +137,3 @@
                                         LOG_TAG, __VA_ARGS__))            \
        : (void)0)
 #endif
-
-#endif /* _LIBS_LOG_LOG_SYSTEM_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 309f5d1..09c9910 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_LOG_LOG_TIME_H
-#define _LIBS_LOG_LOG_TIME_H
+#pragma once
 
 #include <stdint.h>
 #include <time.h>
@@ -34,6 +33,8 @@
 
 #ifdef __cplusplus
 
+extern "C" {
+
 /*
  * NB: we did NOT define a copy constructor. This will result in structure
  * no longer being compatible with pass-by-value which is desired
@@ -41,25 +42,19 @@
  */
 struct log_time {
  public:
-  uint32_t tv_sec; /* good to Feb 5 2106 */
-  uint32_t tv_nsec;
+  uint32_t tv_sec = 0; /* good to Feb 5 2106 */
+  uint32_t tv_nsec = 0;
 
   static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
   static const uint32_t tv_nsec_max = 999999999UL;
+  static const timespec EPOCH;
 
-  log_time(const timespec& T)
-      : tv_sec(static_cast<uint32_t>(T.tv_sec)),
-        tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {
-  }
+  log_time() {}
+  explicit log_time(const timespec& T)
+      : tv_sec(static_cast<uint32_t>(T.tv_sec)), tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {}
   explicit log_time(uint32_t sec, uint32_t nsec = 0)
       : tv_sec(sec), tv_nsec(nsec) {
   }
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-#define __struct_log_time_private_defined
-  static const timespec EPOCH;
-#endif
-  log_time() {
-  }
 #ifdef __linux__
   explicit log_time(clockid_t id) {
     timespec T;
@@ -103,7 +98,6 @@
     return !(*this > T);
   }
 
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
   log_time operator-=(const timespec& T);
   log_time operator-(const timespec& T) const {
     log_time local(*this);
@@ -114,7 +108,6 @@
     log_time local(*this);
     return local += T;
   }
-#endif
 
   /* log_time */
   bool operator==(const log_time& T) const {
@@ -138,7 +131,6 @@
     return !(*this > T);
   }
 
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
   log_time operator-=(const log_time& T);
   log_time operator-(const log_time& T) const {
     log_time local(*this);
@@ -149,7 +141,6 @@
     log_time local(*this);
     return local += T;
   }
-#endif
 
   uint64_t nsec() const {
     return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
@@ -163,13 +154,12 @@
            tv_nsec / (NS_PER_SEC / MS_PER_SEC);
   }
 
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
   static const char default_format[];
 
   /* Add %#q for the fraction of a second to the standard library functions */
   char* strptime(const char* s, const char* format = default_format);
-#endif
 } __attribute__((__packed__));
+}
 
 #else /* __cplusplus */
 
@@ -181,5 +171,3 @@
 #endif /* __cplusplus */
 
 #endif /* __struct_log_time_defined */
-
-#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include/log/log_transport.h b/liblog/include/log/log_transport.h
index 8b02995..b48761a 100644
--- a/liblog/include/log/log_transport.h
+++ b/liblog/include/log/log_transport.h
@@ -7,8 +7,7 @@
 ** General Public License.
 */
 
-#ifndef _LIBS_LOG_TRANSPORT_H
-#define _LIBS_LOG_TRANSPORT_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
@@ -33,5 +32,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* _LIBS_LOG_TRANSPORT_H */
diff --git a/liblog/include/log/logd.h b/liblog/include/log/logd.h
deleted file mode 100644
index 77400ca..0000000
--- a/liblog/include/log/logd.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef _LIBS_LOG_LOGD_H
-#define _LIBS_LOG_LOGD_H
-#include <log/log.h>
-#warning "Deprecated: do not include log/logd.h, use log/log.h instead"
-#endif /*_LIBS_LOG_LOGD_H*/
diff --git a/liblog/include/log/logger.h b/liblog/include/log/logger.h
deleted file mode 100644
index 1bf2d17..0000000
--- a/liblog/include/log/logger.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef _LIBS_LOG_LOGGER_H
-#define _LIBS_LOG_LOGGER_H
-#include <log/log.h>
-#warning "Deprecated: do not include log/logger.h, use log/log.h instead"
-#endif /*_LIBS_LOG_LOGGER_H*/
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
index ca58bc7..8f4b187 100644
--- a/liblog/include/log/logprint.h
+++ b/liblog/include/log/logprint.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LOGPRINT_H
-#define _LOGPRINT_H
+#pragma once
 
 #include <pthread.h>
 
@@ -158,5 +157,3 @@
 #ifdef __cplusplus
 }
 #endif
-
-#endif /*_LOGPRINT_H*/
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index b927b46..5e04148 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -16,8 +16,7 @@
 
 /* This file is used to define the internal protocol for the Android Logger */
 
-#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+#pragma once
 
 /* Android private interfaces */
 
@@ -25,10 +24,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#if (defined(__cplusplus) && defined(_USING_LIBCXX))
-extern "C++" {
+#ifdef __cplusplus
 #include <string>
-}
 #endif
 
 #include <log/log.h>
@@ -153,41 +150,6 @@
 /* Retrieve the composed event buffer */
 int android_log_write_list_buffer(android_log_context ctx, const char** msg);
 
-#ifdef __cplusplus
-#ifdef __class_android_log_event_list_defined
-#ifndef __class_android_log_event_list_private_defined
-#define __class_android_log_event_list_private_defined
-/* android_log_context C++ helpers */
-extern "C++" {
-class __android_log_event_list : public android_log_event_list {
-  __android_log_event_list(const android_log_event_list&) = delete;
-  void operator=(const __android_log_event_list&) = delete;
-
- public:
-  explicit __android_log_event_list(int tag) : android_log_event_list(tag) {
-  }
-  explicit __android_log_event_list(log_msg& log_msg)
-      : android_log_event_list(log_msg) {
-  }
-
-#if defined(_USING_LIBCXX)
-  operator std::string() {
-    if (ret) return std::string("");
-    const char* cp = nullptr;
-    ssize_t len = android_log_write_list_buffer(ctx, &cp);
-    if (len < 0) ret = len;
-    if (!cp || (len <= 0)) return std::string("");
-    return std::string(cp, len);
-  }
-#endif
-};
-}
-#endif
-#endif
-#endif
-
 #if defined(__cplusplus)
 }
 #endif
-
-#endif /* _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ */
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
index 9f74534..1f3dd37 100644
--- a/liblog/include_vndk/log/log_event_list.h
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -27,8 +27,6 @@
 extern "C" {
 #endif
 
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-
 /*
  * The opaque context used to manipulate lists of events.
  */
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 015c9cb..191ef1b 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -59,3 +59,24 @@
     android_log_reset; #vndk
     android_log_parser_reset; #vndk
 };
+
+LIBLOG_PRIVATE {
+  global:
+    __android_log_bswrite;
+    __android_log_btwrite;
+    __android_log_bwrite;
+    __android_log_close;
+    __android_log_pmsg_file_read;
+    __android_log_pmsg_file_write;
+    __android_log_security;
+    __android_log_security_bswrite;
+    __android_logger_get_buffer_size;
+    __android_logger_property_get_bool;
+    android_openEventTagMap;
+    android_log_processBinaryLogBuffer;
+    android_log_processLogBuffer;
+    android_log_read_next;
+    android_log_write_list_buffer;
+    android_lookupEventTagNum;
+    create_android_log_parser;
+};
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.cpp
similarity index 88%
rename from liblog/log_event_list.c
rename to liblog/log_event_list.cpp
index 14002ce..088ea94 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.cpp
@@ -29,21 +29,26 @@
 
 #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
 
-typedef struct {
+enum ReadWriteFlag {
+  kAndroidLoggerRead = 1,
+  kAndroidLoggerWrite = 2,
+};
+
+struct android_log_context_internal {
   uint32_t tag;
-  unsigned pos; /* Read/write position into buffer */
+  unsigned pos;                                    /* Read/write position into buffer */
   unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements   */
   unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1];  /* pos for list counter */
   unsigned list_nest_depth;
   unsigned len; /* Length or raw buffer. */
   bool overflow;
   bool list_stop; /* next call decrement list_nest_depth and issue a stop */
-  enum {
-    kAndroidLoggerRead = 1,
-    kAndroidLoggerWrite = 2,
-  } read_write_flag;
+  ReadWriteFlag read_write_flag;
   uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
-} android_log_context_internal;
+};
+
+// TODO(tomcherry): real C++ structs.
+typedef struct android_log_context_internal android_log_context_internal;
 
 static void init_context(android_log_context_internal* context, uint32_t tag) {
   size_t needed;
@@ -60,8 +65,8 @@
   context->pos += needed;
 }
 
-static void init_parser_context(android_log_context_internal* context,
-                                const char* msg, size_t len) {
+static void init_parser_context(android_log_context_internal* context, const char* msg,
+                                size_t len) {
   len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
   context->len = len;
   memcpy(context->storage, msg, len);
@@ -71,7 +76,8 @@
 LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
   android_log_context_internal* context;
 
-  context = calloc(1, sizeof(android_log_context_internal));
+  context =
+      static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
   if (!context) {
     return NULL;
   }
@@ -80,12 +86,12 @@
   return (android_log_context)context;
 }
 
-LIBLOG_ABI_PUBLIC android_log_context create_android_log_parser(const char* msg,
-                                                                size_t len) {
+LIBLOG_ABI_PUBLIC android_log_context create_android_log_parser(const char* msg, size_t len) {
   android_log_context_internal* context;
   size_t i;
 
-  context = calloc(1, sizeof(android_log_context_internal));
+  context =
+      static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
   if (!context) {
     return NULL;
   }
@@ -123,8 +129,8 @@
   return 0;
 }
 
-LIBLOG_ABI_PUBLIC int android_log_parser_reset(android_log_context ctx,
-                                               const char* msg, size_t len) {
+LIBLOG_ABI_PUBLIC int android_log_parser_reset(android_log_context ctx, const char* msg,
+                                               size_t len) {
   android_log_context_internal* context;
 
   context = (android_log_context_internal*)ctx;
@@ -138,7 +144,6 @@
   return 0;
 }
 
-
 LIBLOG_ABI_PUBLIC int android_log_write_list_begin(android_log_context ctx) {
   size_t needed;
   android_log_context_internal* context;
@@ -180,8 +185,7 @@
   buf[3] = (val >> 24) & 0xFF;
 }
 
-LIBLOG_ABI_PUBLIC int android_log_write_int32(android_log_context ctx,
-                                              int32_t value) {
+LIBLOG_ABI_PUBLIC int android_log_write_int32(android_log_context ctx, int32_t value) {
   size_t needed;
   android_log_context_internal* context;
 
@@ -215,8 +219,7 @@
   buf[7] = (val >> 56) & 0xFF;
 }
 
-LIBLOG_ABI_PUBLIC int android_log_write_int64(android_log_context ctx,
-                                              int64_t value) {
+LIBLOG_ABI_PUBLIC int android_log_write_int64(android_log_context ctx, int64_t value) {
   size_t needed;
   android_log_context_internal* context;
 
@@ -239,8 +242,7 @@
   return 0;
 }
 
-LIBLOG_ABI_PUBLIC int android_log_write_string8_len(android_log_context ctx,
-                                                    const char* value,
+LIBLOG_ABI_PUBLIC int android_log_write_string8_len(android_log_context ctx, const char* value,
                                                     size_t maxlen) {
   size_t needed;
   ssize_t len;
@@ -276,13 +278,11 @@
   return len;
 }
 
-LIBLOG_ABI_PUBLIC int android_log_write_string8(android_log_context ctx,
-                                                const char* value) {
+LIBLOG_ABI_PUBLIC int android_log_write_string8(android_log_context ctx, const char* value) {
   return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
 }
 
-LIBLOG_ABI_PUBLIC int android_log_write_float32(android_log_context ctx,
-                                                float value) {
+LIBLOG_ABI_PUBLIC int android_log_write_float32(android_log_context ctx, float value) {
   size_t needed;
   uint32_t ivalue;
   android_log_context_internal* context;
@@ -337,8 +337,7 @@
 /*
  * Logs the list of elements to the event log.
  */
-LIBLOG_ABI_PUBLIC int android_log_write_list(android_log_context ctx,
-                                             log_id_t id) {
+LIBLOG_ABI_PUBLIC int android_log_write_list(android_log_context ctx, log_id_t id) {
   android_log_context_internal* context;
   const char* msg;
   ssize_t len;
@@ -368,13 +367,11 @@
   }
   return (id == LOG_ID_EVENTS)
              ? __android_log_bwrite(context->tag, msg, len)
-             : ((id == LOG_ID_STATS)
-                    ? __android_log_stats_bwrite(context->tag, msg, len)
-                    : __android_log_security_bwrite(context->tag, msg, len));
+             : ((id == LOG_ID_STATS) ? __android_log_stats_bwrite(context->tag, msg, len)
+                                     : __android_log_security_bwrite(context->tag, msg, len));
 }
 
-LIBLOG_ABI_PRIVATE int android_log_write_list_buffer(android_log_context ctx,
-                                                     const char** buffer) {
+LIBLOG_ABI_PRIVATE int android_log_write_list_buffer(android_log_context ctx, const char** buffer) {
   android_log_context_internal* context;
   const char* msg;
   ssize_t len;
@@ -428,8 +425,7 @@
  * this and continues to call this function, the behavior is undefined
  * (although it won't crash).
  */
-static android_log_list_element android_log_read_next_internal(
-    android_log_context ctx, int peek) {
+static android_log_list_element android_log_read_next_internal(android_log_context ctx, int peek) {
   android_log_list_element elem;
   unsigned pos;
   android_log_context_internal* context;
@@ -444,9 +440,9 @@
       (context->count[context->list_nest_depth] >=
        (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
     elem.type = EVENT_TYPE_UNKNOWN;
-    if (context && (context->list_stop ||
-                    ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
-                     !context->count[context->list_nest_depth]))) {
+    if (context &&
+        (context->list_stop || ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+                                !context->count[context->list_nest_depth]))) {
       elem.type = EVENT_TYPE_LIST_STOP;
     }
     elem.complete = true;
@@ -460,9 +456,8 @@
   pos = context->pos;
   if (context->list_stop) {
     elem.type = EVENT_TYPE_LIST_STOP;
-    elem.complete = !context->count[0] &&
-                    (!context->list_nest_depth ||
-                     ((context->list_nest_depth == 1) && !context->count[1]));
+    elem.complete = !context->count[0] && (!context->list_nest_depth ||
+                                           ((context->list_nest_depth == 1) && !context->count[1]));
     if (!peek) {
       /* Suck in superfluous stop */
       if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
@@ -485,7 +480,7 @@
     return elem;
   }
 
-  elem.type = context->storage[pos++];
+  elem.type = static_cast<AndroidEventLogType>(context->storage[pos++]);
   switch ((int)elem.type) {
     case EVENT_TYPE_FLOAT:
     /* Rely on union to translate elem.data.int32 into elem.data.float32 */
@@ -598,12 +593,10 @@
   }
 }
 
-LIBLOG_ABI_PUBLIC android_log_list_element
-android_log_read_next(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC android_log_list_element android_log_read_next(android_log_context ctx) {
   return android_log_read_next_internal(ctx, 0);
 }
 
-LIBLOG_ABI_PUBLIC android_log_list_element
-android_log_peek_next(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC android_log_list_element android_log_peek_next(android_log_context ctx) {
   return android_log_read_next_internal(ctx, 1);
 }
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.cpp
similarity index 89%
rename from liblog/log_event_write.c
rename to liblog/log_event_write.cpp
index 45a6f37..e644a3b 100644
--- a/liblog/log_event_write.c
+++ b/liblog/log_event_write.cpp
@@ -24,9 +24,8 @@
 
 #define MAX_SUBTAG_LEN 32
 
-LIBLOG_ABI_PUBLIC int __android_log_error_write(int tag, const char* subTag,
-                                                int32_t uid, const char* data,
-                                                uint32_t dataLen) {
+LIBLOG_ABI_PUBLIC int __android_log_error_write(int tag, const char* subTag, int32_t uid,
+                                                const char* data, uint32_t dataLen) {
   int ret = -EINVAL;
 
   if (subTag && (data || !dataLen)) {
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
index 88805c7..b9fb1d2 100644
--- a/liblog/log_portability.h
+++ b/liblog/log_portability.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_PORTABILITY_H__
-#define _LIBLOG_PORTABILITY_H__
+#pragma once
 
 #include <sys/cdefs.h>
 #include <unistd.h>
@@ -46,7 +45,7 @@
 #if defined(_WIN32)
 #define LIBLOG_WEAK static /* Accept that it is totally private */
 #else
-#define LIBLOG_WEAK __attribute__((weak, visibility("default")))
+#define LIBLOG_WEAK extern "C" __attribute__((weak, visibility("default")))
 #endif
 
 /* possible missing definitions in sys/cdefs.h */
@@ -62,11 +61,6 @@
 #endif
 #endif
 
-/* Unused argument. For C code only, remove symbol name for C++ */
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
 /* possible missing definitions in unistd.h */
 
 #ifndef TEMP_FAILURE_RETRY
@@ -80,5 +74,3 @@
     _rc;                                   \
   })
 #endif
-
-#endif /* _LIBLOG_PORTABILITY_H__ */
diff --git a/liblog/log_ratelimit.cpp b/liblog/log_ratelimit.cpp
deleted file mode 100644
index 33770dd..0000000
--- a/liblog/log_ratelimit.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-** Copyright 2016, 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 <pthread.h>
-#include <time.h>
-
-#include <log/log.h>
-
-#include "log_portability.h"
-
-// Global default if 'last' argument in __android_log_ratelimit is NULL
-static time_t g_last_clock;
-// Global above can not deal well with callers playing games with the
-// seconds argument, so we will also hold on to the maximum value
-// ever provided and use that to gain consistency.  If the caller
-// provides their own 'last' argument, then they can play such games
-// of varying the 'seconds' argument to their pleasure.
-static time_t g_last_seconds;
-static const time_t last_seconds_default = 10;
-static const time_t last_seconds_max = 24 * 60 * 60;  // maximum of a day
-static const time_t last_seconds_min = 2;             // granularity
-// Lock to protect last_clock and last_seconds, but also 'last'
-// argument (not NULL) as supplied to __android_log_ratelimit.
-static pthread_mutex_t lock_ratelimit = PTHREAD_MUTEX_INITIALIZER;
-
-// if last is NULL, caller _must_ provide a consistent value for
-// seconds, otherwise we will take the maximum ever issued and hold
-// on to that.  Preserves value of non-zero errno.  Return -1 if we
-// can not acquire a lock, 0 if we are not to log a message, and 1
-// if we are ok to log a message.  Caller should check > 0 for true.
-LIBLOG_ABI_PUBLIC int __android_log_ratelimit(time_t seconds, time_t* last) {
-  int save_errno = errno;
-
-  // Two reasons for trylock failure:
-  //   1. In a signal handler. Must prevent deadlock
-  //   2. Too many threads calling __android_log_ratelimit.
-  //      Bonus to not print if they race here because that
-  //      dovetails the goal of ratelimiting. One may print
-  //      and the others will wait their turn ...
-  if (pthread_mutex_trylock(&lock_ratelimit)) {
-    if (save_errno) errno = save_errno;
-    return -1;
-  }
-
-  if (seconds == 0) {
-    seconds = last_seconds_default;
-  } else if (seconds < last_seconds_min) {
-    seconds = last_seconds_min;
-  } else if (seconds > last_seconds_max) {
-    seconds = last_seconds_max;
-  }
-
-  if (!last) {
-    if (g_last_seconds > seconds) {
-      seconds = g_last_seconds;
-    } else if (g_last_seconds < seconds) {
-      g_last_seconds = seconds;
-    }
-    last = &g_last_clock;
-  }
-
-  time_t now = time(NULL);
-  if ((now == (time_t)-1) || ((*last + seconds) > now)) {
-    pthread_mutex_unlock(&lock_ratelimit);
-    if (save_errno) errno = save_errno;
-    return 0;
-  }
-  *last = now;
-  pthread_mutex_unlock(&lock_ratelimit);
-  if (save_errno) errno = save_errno;
-  return 1;
-}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index ae376be..77bb94f 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -137,7 +137,7 @@
 LIBLOG_ABI_PRIVATE log_time log_time::operator-=(const timespec& T) {
   // No concept of negative time, clamp to EPOCH
   if (*this <= T) {
-    return *this = EPOCH;
+    return *this = log_time(EPOCH);
   }
 
   if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
@@ -165,7 +165,7 @@
 LIBLOG_ABI_PRIVATE log_time log_time::operator-=(const log_time& T) {
   // No concept of negative time, clamp to EPOCH
   if (*this <= T) {
-    return *this = EPOCH;
+    return *this = log_time(EPOCH);
   }
 
   if (this->tv_nsec < T.tv_nsec) {
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.cpp
similarity index 86%
rename from liblog/logd_reader.c
rename to liblog/logd_reader.cpp
index 603ba24..05bbcbc 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.cpp
@@ -47,8 +47,7 @@
 static int logdVersion(struct android_log_logger* logger,
                        struct android_log_transport_context* transp);
 static int logdRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp,
-                    struct log_msg* log_msg);
+                    struct android_log_transport_context* transp, struct log_msg* log_msg);
 static int logdPoll(struct android_log_logger_list* logger_list,
                     struct android_log_transport_context* transp);
 static void logdClose(struct android_log_logger_list* logger_list,
@@ -56,37 +55,33 @@
 static int logdClear(struct android_log_logger* logger,
                      struct android_log_transport_context* transp);
 static ssize_t logdSetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp,
-                           size_t size);
+                           struct android_log_transport_context* transp, size_t size);
 static ssize_t logdGetSize(struct android_log_logger* logger,
                            struct android_log_transport_context* transp);
 static ssize_t logdGetReadableSize(struct android_log_logger* logger,
                                    struct android_log_transport_context* transp);
 static ssize_t logdGetPrune(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp,
-                            char* buf, size_t len);
+                            struct android_log_transport_context* transp, char* buf, size_t len);
 static ssize_t logdSetPrune(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp,
-                            char* buf, size_t len);
+                            struct android_log_transport_context* transp, char* buf, size_t len);
 static ssize_t logdGetStats(struct android_log_logger_list* logger,
-                            struct android_log_transport_context* transp,
-                            char* buf, size_t len);
+                            struct android_log_transport_context* transp, char* buf, size_t len);
 
 LIBLOG_HIDDEN struct android_log_transport_read logdLoggerRead = {
-  .node = { &logdLoggerRead.node, &logdLoggerRead.node },
-  .name = "logd",
-  .available = logdAvailable,
-  .version = logdVersion,
-  .read = logdRead,
-  .poll = logdPoll,
-  .close = logdClose,
-  .clear = logdClear,
-  .getSize = logdGetSize,
-  .setSize = logdSetSize,
-  .getReadableSize = logdGetReadableSize,
-  .getPrune = logdGetPrune,
-  .setPrune = logdSetPrune,
-  .getStats = logdGetStats,
+    .node = {&logdLoggerRead.node, &logdLoggerRead.node},
+    .name = "logd",
+    .available = logdAvailable,
+    .version = logdVersion,
+    .read = logdRead,
+    .poll = logdPoll,
+    .close = logdClose,
+    .clear = logdClear,
+    .getSize = logdGetSize,
+    .setSize = logdSetSize,
+    .getReadableSize = logdGetReadableSize,
+    .getPrune = logdGetPrune,
+    .setPrune = logdSetPrune,
+    .getStats = logdGetStats,
 };
 
 static int logdAvailable(log_id_t logId) {
@@ -109,8 +104,7 @@
 
 #if defined(_WIN32)
 
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId,
-                                    int type) {
+LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId, int type) {
   errno = ENOSYS;
   return -ENOSYS;
 }
@@ -131,8 +125,7 @@
 
 /* Documented in header file. */
 LIBLOG_WEAK int socket_make_sockaddr_un(const char* name, int namespaceId,
-                                        struct sockaddr_un* p_addr,
-                                        socklen_t* alen) {
+                                        struct sockaddr_un* p_addr, socklen_t* alen) {
   memset(p_addr, 0, sizeof(*p_addr));
   size_t namelen;
 
@@ -158,8 +151,7 @@
 
       namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
       /* unix_path_max appears to be missing on linux */
-      if (namelen >
-          sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
+      if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
         goto error;
       }
 
@@ -171,8 +163,7 @@
     case ANDROID_SOCKET_NAMESPACE_RESERVED:
       namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
       /* unix_path_max appears to be missing on linux */
-      if (namelen >
-          sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
+      if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
         goto error;
       }
 
@@ -183,8 +174,7 @@
     case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
       namelen = strlen(name);
       /* unix_path_max appears to be missing on linux */
-      if (namelen >
-          sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
+      if (namelen > sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
         goto error;
       }
 
@@ -210,8 +200,8 @@
  *
  * Used by AndroidSocketImpl
  */
-LIBLOG_WEAK int socket_local_client_connect(int fd, const char* name,
-                                            int namespaceId, int type __unused) {
+LIBLOG_WEAK int socket_local_client_connect(int fd, const char* name, int namespaceId,
+                                            int type __unused) {
   struct sockaddr_un addr;
   socklen_t alen;
   int err;
@@ -236,8 +226,7 @@
  * connect to peer named "name"
  * returns fd or -1 on error
  */
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId,
-                                    int type) {
+LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId, int type) {
   int s;
 
   s = socket(AF_LOCAL, type, 0);
@@ -255,14 +244,13 @@
 /* End of ../libcutils/socket_local_client.c */
 
 /* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg,
-                            char* buf, size_t buf_size) {
+static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg, char* buf,
+                            size_t buf_size) {
   ssize_t ret;
   size_t len;
   char* cp;
   int errno_save = 0;
-  int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                 SOCK_STREAM);
+  int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
   if (sock < 0) {
     return sock;
   }
@@ -342,8 +330,7 @@
                      struct android_log_transport_context* transp __unused) {
   char buf[512];
 
-  return check_log_success(buf,
-                           send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+  return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf)));
 }
 
 /* returns the total size of the log's ring buffer */
@@ -364,8 +351,7 @@
 }
 
 static ssize_t logdSetSize(struct android_log_logger* logger,
-                           struct android_log_transport_context* transp __unused,
-                           size_t size) {
+                           struct android_log_transport_context* transp __unused, size_t size) {
   char buf[512];
 
   snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
@@ -378,8 +364,7 @@
  * log consumed)
  */
 static ssize_t logdGetReadableSize(struct android_log_logger* logger,
-                                   struct android_log_transport_context* transp
-                                       __unused) {
+                                   struct android_log_transport_context* transp __unused) {
   char buf[512];
 
   ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
@@ -407,8 +392,8 @@
  * returns statistics
  */
 static ssize_t logdGetStats(struct android_log_logger_list* logger_list,
-                            struct android_log_transport_context* transp __unused,
-                            char* buf, size_t len) {
+                            struct android_log_transport_context* transp __unused, char* buf,
+                            size_t len) {
   struct android_log_logger* logger;
   char* cp = buf;
   size_t remaining = len;
@@ -434,14 +419,14 @@
 }
 
 static ssize_t logdGetPrune(struct android_log_logger_list* logger_list __unused,
-                            struct android_log_transport_context* transp __unused,
-                            char* buf, size_t len) {
+                            struct android_log_transport_context* transp __unused, char* buf,
+                            size_t len) {
   return send_log_msg(NULL, "getPruneList", buf, len);
 }
 
 static ssize_t logdSetPrune(struct android_log_logger_list* logger_list __unused,
-                            struct android_log_transport_context* transp __unused,
-                            char* buf, size_t len) {
+                            struct android_log_transport_context* transp __unused, char* buf,
+                            size_t len) {
   const char cmd[] = "setPruneList ";
   const size_t cmdlen = sizeof(cmd) - 1;
 
@@ -455,8 +440,7 @@
   return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
 }
 
-static void caught_signal(int signum __unused) {
-}
+static void caught_signal(int signum __unused) {}
 
 static int logdOpen(struct android_log_logger_list* logger_list,
                     struct android_log_transport_context* transp) {
@@ -476,12 +460,10 @@
     return sock;
   }
 
-  sock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                             SOCK_SEQPACKET);
+  sock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
   if (sock == 0) {
     /* Guarantee not file descriptor zero */
-    int newsock = socket_local_client(
-        "logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+    int newsock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
     close(sock);
     sock = newsock;
   }
@@ -492,8 +474,7 @@
     return sock;
   }
 
-  strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose"
-                                                            : "stream");
+  strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
   cp = buffer + strlen(buffer);
 
   strcpy(cp, " lids");
@@ -518,14 +499,13 @@
   if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
     if (logger_list->mode & ANDROID_LOG_WRAP) {
       // ToDo: alternate API to allow timeout to be adjusted.
-      ret = snprintf(cp, remaining, " timeout=%u",
-                     ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
+      ret = snprintf(cp, remaining, " timeout=%u", ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
       ret = min(ret, remaining);
       remaining -= ret;
       cp += ret;
     }
-    ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
-                   logger_list->start.tv_sec, logger_list->start.tv_nsec);
+    ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, logger_list->start.tv_sec,
+                   logger_list->start.tv_nsec);
     ret = min(ret, remaining);
     remaining -= ret;
     cp += ret;
@@ -576,8 +556,7 @@
 
 /* Read from the selected logs */
 static int logdRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp,
-                    struct log_msg* log_msg) {
+                    struct android_log_transport_context* transp, struct log_msg* log_msg) {
   int ret, e;
   struct sigaction ignore;
   struct sigaction old_sigaction;
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 8ebb1ae..0bba7cf 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_LOGD_READER_H__
-#define _LIBLOG_LOGD_READER_H__
+#pragma once
 
 #include <unistd.h>
 
@@ -26,5 +25,3 @@
 LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf, size_t buf_size);
 
 __END_DECLS
-
-#endif /* _LIBLOG_LOGD_READER_H__ */
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.cpp
similarity index 85%
rename from liblog/logd_writer.c
rename to liblog/logd_writer.cpp
index e0e3eca..ed906b3 100644
--- a/liblog/logd_writer.c
+++ b/liblog/logd_writer.cpp
@@ -38,6 +38,7 @@
 #include "config_write.h"
 #include "log_portability.h"
 #include "logger.h"
+#include "uio.h"
 
 /* branchless on many architectures. */
 #define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
@@ -45,17 +46,16 @@
 static int logdAvailable(log_id_t LogId);
 static int logdOpen();
 static void logdClose();
-static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                     size_t nr);
+static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
 
 LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
-  .node = { &logdLoggerWrite.node, &logdLoggerWrite.node },
-  .context.sock = -EBADF,
-  .name = "logd",
-  .available = logdAvailable,
-  .open = logdOpen,
-  .close = logdClose,
-  .write = logdWrite,
+    .node = {&logdLoggerWrite.node, &logdLoggerWrite.node},
+    .context.sock = -EBADF,
+    .name = "logd",
+    .available = logdAvailable,
+    .open = logdOpen,
+    .close = logdClose,
+    .write = logdWrite,
 };
 
 /* log_init_lock assumed */
@@ -64,8 +64,7 @@
 
   i = atomic_load(&logdLoggerWrite.context.sock);
   if (i < 0) {
-    int sock = TEMP_FAILURE_RETRY(
-        socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+    int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
     if (sock < 0) {
       ret = -errno;
     } else {
@@ -74,15 +73,15 @@
       un.sun_family = AF_UNIX;
       strcpy(un.sun_path, "/dev/socket/logdw");
 
-      if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un,
-                                     sizeof(struct sockaddr_un))) < 0) {
+      if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) <
+          0) {
         ret = -errno;
         switch (ret) {
           case -ENOTCONN:
           case -ECONNREFUSED:
           case -ENOENT:
             i = atomic_exchange(&logdLoggerWrite.context.sock, ret);
-          /* FALLTHRU */
+            [[fallthrough]];
           default:
             break;
         }
@@ -124,16 +123,15 @@
   return 1;
 }
 
-static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                     size_t nr) {
+static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
   ssize_t ret;
   int sock;
   static const unsigned headerLength = 1;
   struct iovec newVec[nr + headerLength];
   android_log_header_t header;
   size_t i, payloadSize;
-  static atomic_int_fast32_t dropped;
-  static atomic_int_fast32_t droppedSecurity;
+  static atomic_int dropped;
+  static atomic_int droppedSecurity;
 
   sock = atomic_load(&logdLoggerWrite.context.sock);
   if (sock < 0) switch (sock) {
@@ -181,8 +179,7 @@
   newVec[0].iov_len = sizeof(header);
 
   if (sock >= 0) {
-    int32_t snapshot =
-        atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
+    int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
     if (snapshot) {
       android_log_event_int_t buffer;
 
@@ -196,14 +193,12 @@
 
       ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
       if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
-        atomic_fetch_add_explicit(&droppedSecurity, snapshot,
-                                  memory_order_relaxed);
+        atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed);
       }
     }
     snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
-    if (snapshot &&
-        __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog",
-                                      strlen("liblog"), ANDROID_LOG_VERBOSE)) {
+    if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
+                                                  ANDROID_LOG_VERBOSE)) {
       android_log_event_int_t buffer;
 
       header.id = LOG_ID_EVENTS;
@@ -267,12 +262,11 @@
         return ret;
       }
 
-      ret = TEMP_FAILURE_RETRY(
-          writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
+      ret = TEMP_FAILURE_RETRY(writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
       if (ret < 0) {
         ret = -errno;
       }
-    /* FALLTHRU */
+      [[fallthrough]];
     default:
       break;
   }
diff --git a/liblog/logger.h b/liblog/logger.h
index af83228..b2479d2 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -14,22 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_LOGGER_H__
-#define _LIBLOG_LOGGER_H__
+#pragma once
 
 #include <stdatomic.h>
 #include <stdbool.h>
 
 #include <cutils/list.h>
 #include <log/log.h>
-#include <log/uio.h>
 
 #include "log_portability.h"
+#include "uio.h"
 
 __BEGIN_DECLS
 
 /* Union, sock or fd of zero is not allowed unless static initialized */
-union android_log_context {
+union android_log_context_union {
   void* priv;
   atomic_int sock;
   atomic_int fd;
@@ -41,7 +40,7 @@
   struct listnode node;
   const char* name;                  /* human name to describe the transport */
   unsigned logMask;                  /* mask cache of available() success */
-  union android_log_context context; /* Initialized by static allocation */
+  union android_log_context_union context; /* Initialized by static allocation */
 
   int (*available)(log_id_t logId); /* Does not cause resources to be taken */
   int (*open)();   /* can be called multiple times, reusing current resources */
@@ -115,7 +114,7 @@
 
 struct android_log_transport_context {
   struct listnode node;
-  union android_log_context context; /* zero init per-transport context */
+  union android_log_context_union context; /* zero init per-transport context */
   struct android_log_logger_list* parent;
 
   struct android_log_transport_read* transport;
@@ -161,8 +160,6 @@
 LIBLOG_HIDDEN int __android_log_trylock();
 LIBLOG_HIDDEN void __android_log_unlock();
 
-LIBLOG_HIDDEN int __android_log_transport;
+extern LIBLOG_HIDDEN int __android_log_transport;
 
 __END_DECLS
-
-#endif /* _LIBLOG_LOGGER_H__ */
diff --git a/liblog/logger_lock.c b/liblog/logger_lock.cpp
similarity index 100%
rename from liblog/logger_lock.c
rename to liblog/logger_lock.cpp
diff --git a/liblog/logger_name.c b/liblog/logger_name.cpp
similarity index 79%
rename from liblog/logger_name.c
rename to liblog/logger_name.cpp
index 479bbfe..c6f3cb7 100644
--- a/liblog/logger_name.c
+++ b/liblog/logger_name.cpp
@@ -15,6 +15,7 @@
 */
 
 #include <string.h>
+#include <type_traits>
 
 #include <log/log.h>
 
@@ -22,7 +23,7 @@
 
 /* In the future, we would like to make this list extensible */
 static const char* LOG_NAME[LOG_ID_MAX] = {
-      /* clang-format off */
+    /* clang-format off */
   [LOG_ID_MAIN] = "main",
   [LOG_ID_RADIO] = "radio",
   [LOG_ID_EVENTS] = "events",
@@ -31,7 +32,7 @@
   [LOG_ID_STATS] = "stats",
   [LOG_ID_SECURITY] = "security",
   [LOG_ID_KERNEL] = "kernel",
-  /* clang-format on */
+    /* clang-format on */
 };
 
 LIBLOG_ABI_PUBLIC const char* android_log_id_to_name(log_id_t log_id) {
@@ -41,12 +42,15 @@
   return LOG_NAME[log_id];
 }
 
+static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value,
+              "log_id_t must be an unsigned int");
+
 LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char* logName) {
   const char* b;
-  int ret;
+  unsigned int ret;
 
   if (!logName) {
-    return -1; /* NB: log_id_t is unsigned */
+    return static_cast<log_id_t>(0xFFFFFFFF);
   }
   b = strrchr(logName, '/');
   if (!b) {
@@ -58,8 +62,8 @@
   for (ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
     const char* l = LOG_NAME[ret];
     if (l && !strcmp(b, l)) {
-      return ret;
+      return static_cast<log_id_t>(ret);
     }
   }
-  return -1; /* should never happen */
+  return static_cast<log_id_t>(0xFFFFFFFF); /* should never happen */
 }
diff --git a/liblog/logger_read.c b/liblog/logger_read.cpp
similarity index 76%
rename from liblog/logger_read.c
rename to liblog/logger_read.cpp
index 29ebaf7..e429c36 100644
--- a/liblog/logger_read.c
+++ b/liblog/logger_read.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include "log/log_read.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
@@ -34,8 +36,7 @@
 /* android_logger_alloc unimplemented, no use case */
 /* android_logger_free not exported */
 static void android_logger_free(struct logger* logger) {
-  struct android_log_logger* logger_internal =
-      (struct android_log_logger*)logger;
+  struct android_log_logger* logger_internal = (struct android_log_logger*)logger;
 
   if (!logger_internal) {
     return;
@@ -71,15 +72,13 @@
 
   __android_log_lock();
   /* mini __write_to_log_initialize() to populate transports */
-  if (list_empty(&__android_log_transport_read) &&
-      list_empty(&__android_log_persist_read)) {
+  if (list_empty(&__android_log_transport_read) && list_empty(&__android_log_persist_read)) {
     __android_log_config_read();
   }
   __android_log_unlock();
 
-  node = (logger_list->mode & ANDROID_LOG_PSTORE)
-             ? &__android_log_persist_read
-             : &__android_log_transport_read;
+  node = (logger_list->mode & ANDROID_LOG_PSTORE) ? &__android_log_persist_read
+                                                  : &__android_log_transport_read;
 
   read_transport_for_each(transport, node) {
     struct android_log_transport_context* transp;
@@ -92,15 +91,14 @@
       if ((logId == LOG_ID_SECURITY) && (__android_log_uid() != AID_SYSTEM)) {
         continue;
       }
-      if (transport->read &&
-          (!transport->available || (transport->available(logId) >= 0))) {
+      if (transport->read && (!transport->available || (transport->available(logId) >= 0))) {
         logMask |= 1 << logId;
       }
     }
     if (!logMask) {
       continue;
     }
-    transp = calloc(1, sizeof(*transp));
+    transp = static_cast<android_log_transport_context*>(calloc(1, sizeof(*transp)));
     if (!transp) {
       return -ENOMEM;
     }
@@ -116,31 +114,29 @@
   return 0;
 }
 
-#define LOGGER_FUNCTION(logger, def, func, args...)                   \
-  ssize_t ret = -EINVAL;                                              \
-  struct android_log_transport_context* transp;                       \
-  struct android_log_logger* logger_internal =                        \
-      (struct android_log_logger*)(logger);                           \
-                                                                      \
-  if (!logger_internal) {                                             \
-    return ret;                                                       \
-  }                                                                   \
-  ret = init_transport_context(logger_internal->parent);              \
-  if (ret < 0) {                                                      \
-    return ret;                                                       \
-  }                                                                   \
-                                                                      \
-  ret = (def);                                                        \
-  transport_context_for_each(transp, logger_internal->parent) {       \
-    if ((transp->logMask & (1 << logger_internal->logId)) &&          \
-        transp->transport && transp->transport->func) {               \
-      ssize_t retval =                                                \
-          (transp->transport->func)(logger_internal, transp, ##args); \
-      if ((ret >= 0) || (ret == (def))) {                             \
-        ret = retval;                                                 \
-      }                                                               \
-    }                                                                 \
-  }                                                                   \
+#define LOGGER_FUNCTION(logger, def, func, args...)                                  \
+  ssize_t ret = -EINVAL;                                                             \
+  struct android_log_transport_context* transp;                                      \
+  struct android_log_logger* logger_internal = (struct android_log_logger*)(logger); \
+                                                                                     \
+  if (!logger_internal) {                                                            \
+    return ret;                                                                      \
+  }                                                                                  \
+  ret = init_transport_context(logger_internal->parent);                             \
+  if (ret < 0) {                                                                     \
+    return ret;                                                                      \
+  }                                                                                  \
+                                                                                     \
+  ret = (def);                                                                       \
+  transport_context_for_each(transp, logger_internal->parent) {                      \
+    if ((transp->logMask & (1 << logger_internal->logId)) && transp->transport &&    \
+        transp->transport->func) {                                                   \
+      ssize_t retval = (transp->transport->func)(logger_internal, transp, ##args);   \
+      if ((ret >= 0) || (ret == (def))) {                                            \
+        ret = retval;                                                                \
+      }                                                                              \
+    }                                                                                \
+  }                                                                                  \
   return ret
 
 LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger* logger) {
@@ -152,8 +148,7 @@
   LOGGER_FUNCTION(logger, -ENODEV, getSize);
 }
 
-LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger* logger,
-                                                  unsigned long size) {
+LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger* logger, unsigned long size) {
   LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
 }
 
@@ -161,8 +156,7 @@
  * returns the readable size of the log's ring buffer (that is, amount of the
  * log consumed)
  */
-LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(
-    struct logger* logger) {
+LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(struct logger* logger) {
   LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
 }
 
@@ -173,51 +167,50 @@
   LOGGER_FUNCTION(logger, 4, version);
 }
 
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)              \
-  struct android_log_transport_context* transp;                            \
-  struct android_log_logger_list* logger_list_internal =                   \
-      (struct android_log_logger_list*)(logger_list);                      \
-                                                                           \
-  ssize_t ret = init_transport_context(logger_list_internal);              \
-  if (ret < 0) {                                                           \
-    return ret;                                                            \
-  }                                                                        \
-                                                                           \
-  ret = (def);                                                             \
-  transport_context_for_each(transp, logger_list_internal) {               \
-    if (transp->transport && (transp->transport->func)) {                  \
-      ssize_t retval =                                                     \
-          (transp->transport->func)(logger_list_internal, transp, ##args); \
-      if ((ret >= 0) || (ret == (def))) {                                  \
-        ret = retval;                                                      \
-      }                                                                    \
-    }                                                                      \
-  }                                                                        \
+#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                           \
+  struct android_log_transport_context* transp;                                         \
+  struct android_log_logger_list* logger_list_internal =                                \
+      (struct android_log_logger_list*)(logger_list);                                   \
+                                                                                        \
+  ssize_t ret = init_transport_context(logger_list_internal);                           \
+  if (ret < 0) {                                                                        \
+    return ret;                                                                         \
+  }                                                                                     \
+                                                                                        \
+  ret = (def);                                                                          \
+  transport_context_for_each(transp, logger_list_internal) {                            \
+    if (transp->transport && (transp->transport->func)) {                               \
+      ssize_t retval = (transp->transport->func)(logger_list_internal, transp, ##args); \
+      if ((ret >= 0) || (ret == (def))) {                                               \
+        ret = retval;                                                                   \
+      }                                                                                 \
+    }                                                                                   \
+  }                                                                                     \
   return ret
 
 /*
  * returns statistics
  */
-LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(
-    struct logger_list* logger_list, char* buf, size_t len) {
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf,
+                                                        size_t len) {
   LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
 }
 
-LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(
-    struct logger_list* logger_list, char* buf, size_t len) {
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf,
+                                                        size_t len) {
   LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
 }
 
-LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(
-    struct logger_list* logger_list, char* buf, size_t len) {
+LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(struct logger_list* logger_list, char* buf,
+                                                    size_t len) {
   LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
 }
 
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc(
-    int mode, unsigned int tail, pid_t pid) {
+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;
 
-  logger_list = calloc(1, sizeof(*logger_list));
+  logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
   if (!logger_list) {
     return NULL;
   }
@@ -231,11 +224,11 @@
   return (struct logger_list*)logger_list;
 }
 
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc_time(
-    int mode, log_time start, pid_t pid) {
+LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc_time(int mode, log_time start,
+                                                                     pid_t pid) {
   struct android_log_logger_list* logger_list;
 
-  logger_list = calloc(1, sizeof(*logger_list));
+  logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
   if (!logger_list) {
     return NULL;
   }
@@ -253,8 +246,8 @@
 /* android_logger_list_unregister unimplemented, no use case */
 
 /* Open the named log and add it to the logger list */
-LIBLOG_ABI_PUBLIC struct logger* android_logger_open(
-    struct logger_list* logger_list, log_id_t logId) {
+LIBLOG_ABI_PUBLIC struct logger* android_logger_open(struct logger_list* logger_list,
+                                                     log_id_t logId) {
   struct android_log_logger_list* logger_list_internal =
       (struct android_log_logger_list*)logger_list;
   struct android_log_logger* logger;
@@ -269,7 +262,7 @@
     }
   }
 
-  logger = calloc(1, sizeof(*logger));
+  logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
   if (!logger) {
     goto err;
   }
@@ -296,8 +289,8 @@
 }
 
 /* Open the single named log and make it part of a new logger list */
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_open(
-    log_id_t logId, int mode, unsigned int tail, pid_t pid) {
+LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_open(log_id_t logId, int mode,
+                                                               unsigned int tail, pid_t pid) {
   struct logger_list* logger_list = android_logger_list_alloc(mode, tail, pid);
 
   if (!logger_list) {
@@ -364,8 +357,8 @@
   }
 
   /* at least one transport */
-  transp = node_to_item(logger_list_internal->transport.next,
-                        struct android_log_transport_context, node);
+  transp = node_to_item(logger_list_internal->transport.next, struct android_log_transport_context,
+                        node);
 
   /* more than one transport? */
   if (transp->node.next != &logger_list_internal->transport) {
@@ -386,11 +379,9 @@
             retval = transp->ret = 0;
           } else if ((logger_list_internal->mode & ANDROID_LOG_NONBLOCK) ||
                      !transp->transport->poll) {
-            retval = android_transport_read(logger_list_internal, transp,
-                                            &transp->logMsg);
+            retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
           } else {
-            int pollval =
-                (*transp->transport->poll)(logger_list_internal, transp);
+            int pollval = (*transp->transport->poll)(logger_list_internal, transp);
             if (pollval <= 0) {
               sched_yield();
               pollval = (*transp->transport->poll)(logger_list_internal, transp);
@@ -402,8 +393,7 @@
               }
               retval = transp->ret = pollval;
             } else if (pollval > 0) {
-              retval = android_transport_read(logger_list_internal, transp,
-                                              &transp->logMsg);
+              retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
             }
           }
         }
@@ -416,11 +406,9 @@
               (oldest->logMsg.entry.nsec > transp->logMsg.entry.nsec)))) {
           oldest = transp;
         }
-        transp = node_to_item(transp->node.next,
-                              struct android_log_transport_context, node);
+        transp = node_to_item(transp->node.next, struct android_log_transport_context, node);
       } while (transp != node_to_item(&logger_list_internal->transport,
-                                      struct android_log_transport_context,
-                                      node));
+                                      struct android_log_transport_context, node));
       if (!oldest && (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
         return (ret < 0) ? ret : -EAGAIN;
       }
@@ -434,10 +422,10 @@
     ret = oldest->ret;
     if (ret < oldest->logMsg.entry.hdr_size) {
       // zero truncated header fields.
-      memset(log_msg, 0,
-             (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg)
-                  ? sizeof(oldest->logMsg)
-                  : oldest->logMsg.entry.hdr_size));
+      memset(
+          log_msg, 0,
+          (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg) ? sizeof(oldest->logMsg)
+                                                                  : oldest->logMsg.entry.hdr_size));
     }
     memcpy(log_msg, &oldest->logMsg, ret);
     oldest->logMsg.entry.len = 0; /* Mark it as copied */
@@ -471,8 +459,7 @@
 
   while (!list_empty(&logger_list_internal->logger)) {
     struct listnode* node = list_head(&logger_list_internal->logger);
-    struct android_log_logger* logger =
-        node_to_item(node, struct android_log_logger, node);
+    struct android_log_logger* logger = node_to_item(node, struct android_log_logger, node);
     android_logger_free((struct logger*)logger);
   }
 
diff --git a/liblog/logger_write.c b/liblog/logger_write.cpp
similarity index 88%
rename from liblog/logger_write.c
rename to liblog/logger_write.cpp
index 6dcda9b..af8cb2d 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.cpp
@@ -33,23 +33,19 @@
 #include "config_write.h"
 #include "log_portability.h"
 #include "logger.h"
+#include "uio.h"
 
 #define LOG_BUF_SIZE 1024
 
 static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
-static int (*write_to_log)(log_id_t, struct iovec* vec,
-                           size_t nr) = __write_to_log_init;
+static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init;
 
 /*
  * This is used by the C++ code to decide if it should write logs through
  * the C code.  Basically, if /dev/socket/logd is available, we're running in
  * the simulator rather than a desktop tool and want to use the device.
  */
-static enum {
-  kLogUninitialized,
-  kLogNotAvailable,
-  kLogAvailable
-} g_log_status = kLogUninitialized;
+static enum { kLogUninitialized, kLogNotAvailable, kLogAvailable } g_log_status = kLogUninitialized;
 
 static int check_log_uid_permissions() {
 #if defined(__ANDROID__)
@@ -70,7 +66,7 @@
           if (num_groups <= 0) {
             return -EPERM;
           }
-          groups = calloc(num_groups, sizeof(gid_t));
+          groups = static_cast<gid_t*>(calloc(num_groups, sizeof(gid_t)));
           if (!groups) {
             return -ENOMEM;
           }
@@ -93,9 +89,8 @@
   return 0;
 }
 
-static void __android_log_cache_available(
-    struct android_log_transport_write* node) {
-  size_t i;
+static void __android_log_cache_available(struct android_log_transport_write* node) {
+  uint32_t i;
 
   if (node->logMask) {
     return;
@@ -104,13 +99,13 @@
   for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
     if (node->write && (i != LOG_ID_KERNEL) &&
         ((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) &&
-        (!node->available || ((*node->available)(i) >= 0))) {
+        (!node->available || ((*node->available)(static_cast<log_id_t>(i)) >= 0))) {
       node->logMask |= 1 << i;
     }
   }
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_dev_available() {
+LIBLOG_ABI_PUBLIC extern "C" int __android_log_dev_available() {
   struct android_log_transport_write* node;
 
   if (list_empty(&__android_log_transport_write)) {
@@ -308,10 +303,9 @@
       }
     }
     if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
-      tag = android_lookupEventTag_len(m, &len, get4LE(vec[0].iov_base));
+      tag = android_lookupEventTag_len(m, &len, get4LE(static_cast<uint8_t*>(vec[0].iov_base)));
     }
-    ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len,
-                                        ANDROID_LOG_VERBOSE);
+    ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len, ANDROID_LOG_VERBOSE);
     if (f) { /* local copy marked for close */
       android_closeEventTagMap(f);
     }
@@ -322,7 +316,7 @@
   } else {
     /* Validate the incoming tag, tag content can not split across iovec */
     char prio = ANDROID_LOG_VERBOSE;
-    const char* tag = vec[0].iov_base;
+    const char* tag = static_cast<const char*>(vec[0].iov_base);
     size_t len = vec[0].iov_len;
     if (!tag) {
       len = 0;
@@ -408,13 +402,12 @@
   return ret;
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char* tag,
-                                          const char* msg) {
+LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char* tag, const char* msg) {
   return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
-                                              const char* tag, const char* msg) {
+LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio, const char* tag,
+                                              const char* msg) {
   struct iovec vec[3];
   char tmp_tag[32];
 
@@ -457,7 +450,7 @@
         bufID = LOG_ID_RADIO;
         snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
         tag = tmp_tag;
-      /* FALLTHRU */
+        [[fallthrough]];
       default:
         break;
     }
@@ -476,11 +469,10 @@
   vec[2].iov_base = (void*)msg;
   vec[2].iov_len = strlen(msg) + 1;
 
-  return write_to_log(bufID, vec, 3);
+  return write_to_log(static_cast<log_id_t>(bufID), vec, 3);
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char* tag,
-                                           const char* fmt, va_list ap) {
+LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
   char buf[LOG_BUF_SIZE];
 
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
@@ -488,8 +480,7 @@
   return __android_log_write(prio, tag, buf);
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char* tag,
-                                          const char* fmt, ...) {
+LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
   va_list ap;
   char buf[LOG_BUF_SIZE];
 
@@ -500,8 +491,7 @@
   return __android_log_write(prio, tag, buf);
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
-                                              const char* tag, const char* fmt,
+LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt,
                                               ...) {
   va_list ap;
   char buf[LOG_BUF_SIZE];
@@ -513,8 +503,8 @@
   return __android_log_buf_write(bufID, prio, tag, buf);
 }
 
-LIBLOG_ABI_PUBLIC void __android_log_assert(const char* cond, const char* tag,
-                                            const char* fmt, ...) {
+LIBLOG_ABI_PUBLIC void __android_log_assert(const char* cond, const char* tag, const char* fmt,
+                                            ...) {
   char buf[LOG_BUF_SIZE];
 
   if (fmt) {
@@ -535,18 +525,15 @@
 
   // Log assertion failures to stderr for the benefit of "adb shell" users
   // and gtests (http://b/23675822).
-  struct iovec iov[2] = {
-    { buf, strlen(buf) }, { (char*)"\n", 1 },
-  };
-  TEMP_FAILURE_RETRY(writev(2, iov, 2));
+  TEMP_FAILURE_RETRY(write(2, buf, strlen(buf)));
+  TEMP_FAILURE_RETRY(write(2, "\n", 1));
 
   __android_log_write(ANDROID_LOG_FATAL, tag, buf);
   abort(); /* abort so we have a chance to debug the situation */
            /* NOTREACHED */
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag, const void* payload,
-                                           size_t len) {
+LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -557,9 +544,7 @@
   return write_to_log(LOG_ID_EVENTS, vec, 2);
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_stats_bwrite(int32_t tag,
-                                                 const void* payload,
-                                                 size_t len) {
+LIBLOG_ABI_PUBLIC int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len) {
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -570,9 +555,7 @@
   return write_to_log(LOG_ID_STATS, vec, 2);
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
-                                                    const void* payload,
-                                                    size_t len) {
+LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len) {
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -588,8 +571,8 @@
  * for the general case where we're generating lists of stuff, but very
  * handy if we just want to dump an integer into the log.
  */
-LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
-                                            const void* payload, size_t len) {
+LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type, const void* payload,
+                                            size_t len) {
   struct iovec vec[3];
 
   vec[0].iov_base = &tag;
@@ -627,8 +610,7 @@
  * Like __android_log_security_bwrite, but used for writing strings to the
  * security log.
  */
-LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
-                                                     const char* payload) {
+LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag, const char* payload) {
   struct iovec vec[4];
   char type = EVENT_TYPE_STRING;
   uint32_t len = strlen(payload);
@@ -695,8 +677,7 @@
 
     write_to_log = __write_to_log_init;
     /* generically we only expect these two values for write_to_log */
-  } else if ((write_to_log != __write_to_log_init) &&
-             (write_to_log != __write_to_log_daemon)) {
+  } else if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
     write_to_log = __write_to_log_init;
   }
 
@@ -716,8 +697,7 @@
   } else {
     __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)) {
+    if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
       ret = -EINVAL;
     }
   }
diff --git a/liblog/logprint.c b/liblog/logprint.cpp
similarity index 90%
rename from liblog/logprint.c
rename to liblog/logprint.cpp
index 7937cb1..798b089 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.cpp
@@ -15,7 +15,6 @@
 ** limitations under the License.
 */
 
-#define _GNU_SOURCE /* for asprintf */
 #ifndef __MINGW32__
 #define HAVE_STRSEP
 #endif
@@ -154,7 +153,7 @@
     case ANDROID_LOG_DEFAULT:
     case ANDROID_LOG_UNKNOWN:
     default:                  return '?';
-    /* clang-format on */
+      /* clang-format on */
   }
 }
 
@@ -172,16 +171,14 @@
     case ANDROID_LOG_DEFAULT:
     case ANDROID_LOG_UNKNOWN:
     default:                  return ANDROID_COLOR_DEFAULT;
-    /* clang-format on */
+      /* clang-format on */
   }
 }
 
-static android_LogPriority filterPriForTag(AndroidLogFormat* p_format,
-                                           const char* tag) {
+static android_LogPriority filterPriForTag(AndroidLogFormat* p_format, const char* tag) {
   FilterInfo* p_curFilter;
 
-  for (p_curFilter = p_format->filters; p_curFilter != NULL;
-       p_curFilter = p_curFilter->p_next) {
+  for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next) {
     if (0 == strcmp(tag, p_curFilter->mTag)) {
       if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
         return p_format->global_pri;
@@ -198,8 +195,7 @@
  * returns 1 if this log line should be printed based on its priority
  * and tag, and 0 if it should not
  */
-LIBLOG_ABI_PUBLIC int android_log_shouldPrintLine(AndroidLogFormat* p_format,
-                                                  const char* tag,
+LIBLOG_ABI_PUBLIC int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
                                                   android_LogPriority pri) {
   return pri >= filterPriForTag(p_format, tag);
 }
@@ -207,7 +203,7 @@
 LIBLOG_ABI_PUBLIC AndroidLogFormat* android_log_format_new() {
   AndroidLogFormat* p_ret;
 
-  p_ret = calloc(1, sizeof(AndroidLogFormat));
+  p_ret = static_cast<AndroidLogFormat*>(calloc(1, sizeof(AndroidLogFormat)));
 
   p_ret->global_pri = ANDROID_LOG_VERBOSE;
   p_ret->format = FORMAT_BRIEF;
@@ -302,8 +298,7 @@
 /**
  * Returns FORMAT_OFF on invalid string
  */
-LIBLOG_ABI_PUBLIC AndroidLogPrintFormat
-android_log_formatFromString(const char* formatString) {
+LIBLOG_ABI_PUBLIC AndroidLogPrintFormat android_log_formatFromString(const char* formatString) {
   static AndroidLogPrintFormat format;
 
   /* clang-format off */
@@ -326,7 +321,7 @@
   else if (!strcmp(formatString, "monotonic")) format = FORMAT_MODIFIER_MONOTONIC;
   else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
   else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
-  /* clang-format on */
+    /* clang-format on */
 
 #ifndef __MINGW32__
   else {
@@ -344,9 +339,8 @@
      */
     tzset();
     if (!tzname[0] ||
-        ((!strcmp(tzname[0], utc) || !strcmp(tzname[0], gmt)) /* error? */
-         && strcasecmp(formatString, utc) &&
-         strcasecmp(formatString, gmt))) { /* ok */
+        ((!strcmp(tzname[0], utc) || !strcmp(tzname[0], gmt))                  /* error? */
+         && strcasecmp(formatString, utc) && strcasecmp(formatString, gmt))) { /* ok */
       if (cp) {
         setenv(tz, cp, 1);
       } else {
@@ -581,7 +575,7 @@
     msg[msgEnd] = '\0';
   }
 
-  entry->priority = msg[0];
+  entry->priority = static_cast<android_LogPriority>(msg[0]);
   entry->tag = msg + 1;
   entry->tagLen = msgStart - 1;
   entry->message = msg + msgStart;
@@ -643,9 +637,8 @@
   TYPE_MONOTONIC = 's'
 };
 
-static int android_log_printBinaryEvent(const unsigned char** pEventData,
-                                        size_t* pEventDataLen, char** pOutBuf,
-                                        size_t* pOutBufLen, const char** fmtStr,
+static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen,
+                                        char** pOutBuf, size_t* pOutBufLen, const char** fmtStr,
                                         size_t* fmtLen) {
   const unsigned char* eventData = *pEventData;
   size_t eventDataLen = *pEventDataLen;
@@ -729,13 +722,10 @@
     }
 
     if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
-      static const unsigned char typeTable[] = {
-        EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING, EVENT_TYPE_LIST,
-        EVENT_TYPE_FLOAT
-      };
+      static const unsigned char typeTable[] = {EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING,
+                                                EVENT_TYPE_LIST, EVENT_TYPE_FLOAT};
 
-      if ((*cp >= '1') &&
-          (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
+      if ((*cp >= '1') && (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
           (type != typeTable[(size_t)(*cp - '1')]))
         len = 0;
 
@@ -858,8 +848,8 @@
         outBufLen--;
 
         for (i = 0; i < count; i++) {
-          result = android_log_printBinaryEvent(
-              &eventData, &eventDataLen, &outBuf, &outBufLen, fmtStr, fmtLen);
+          result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
+                                                fmtStr, fmtLen);
           if (result != 0) goto bail;
 
           if (i < (count - 1)) {
@@ -889,24 +879,21 @@
         case TYPE_BYTES:
           if ((lval != 0) && ((lval % 1024) == 0)) {
             /* repaint with multiplier */
-            static const char suffixTable[] = { 'K', 'M', 'G', 'T' };
+            static const char suffixTable[] = {'K', 'M', 'G', 'T'};
             size_t idx = 0;
             outBuf -= outCount;
             outBufLen += outCount;
             do {
               lval /= 1024;
               if ((lval % 1024) != 0) break;
-            } while (++idx <
-                     ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
-            outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval,
-                                suffixTable[idx]);
+            } while (++idx < ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
+            outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval, suffixTable[idx]);
           } else {
             outCount = snprintf(outBuf, outBufLen, "B");
           }
           break;
         case TYPE_MILLISECONDS:
-          if (((lval <= -1000) || (1000 <= lval)) &&
-              (outBufLen || (outBuf[-1] == '0'))) {
+          if (((lval <= -1000) || (1000 <= lval)) && (outBufLen || (outBuf[-1] == '0'))) {
             /* repaint as (fractional) seconds, possibly saving space */
             if (outBufLen) outBuf[0] = outBuf[-1];
             outBuf[-1] = outBuf[-2];
@@ -943,22 +930,19 @@
           }
           if (val >= minute) {
             if (val >= hour) {
-              outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":",
-                                  (val / hour) % (day / hour));
+              outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":", (val / hour) % (day / hour));
               if (outCount >= outBufLen) break;
               outBuf += outCount;
               outBufLen -= outCount;
             }
             outCount =
-                snprintf(outBuf, outBufLen,
-                         (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+                snprintf(outBuf, outBufLen, (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
                          (val / minute) % (hour / minute));
             if (outCount >= outBufLen) break;
             outBuf += outCount;
             outBufLen -= outCount;
           }
-          outCount = snprintf(outBuf, outBufLen,
-                              (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+          outCount = snprintf(outBuf, outBufLen, (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
                               val % minute);
         } break;
         case TYPE_ALLOCATIONS:
@@ -1015,7 +999,7 @@
  */
 LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
     struct logger_entry* buf, AndroidLogEntry* entry,
-    const EventTagMap* map __unused, /* only on !__ANDROID__ */
+    [[maybe_unused]] const EventTagMap* map, /* only on !__ANDROID__ */
     char* messageBuf, int messageBufLen) {
   size_t inCount;
   uint32_t tagIndex;
@@ -1100,8 +1084,8 @@
   int result = 0;
 
   if ((inCount > 0) || fmtLen) {
-    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
-                                          &outRemaining, &fmtStr, &fmtLen);
+    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, &fmtStr,
+                                          &fmtLen);
   }
   if ((result == 1) && fmtStr) {
     /* We overflowed :-(, let's repaint the line w/o format dressings */
@@ -1112,8 +1096,7 @@
     eventData += 4;
     outBuf = messageBuf;
     outRemaining = messageBufLen - 1;
-    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
-                                          &outRemaining, NULL, NULL);
+    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, NULL, NULL);
   }
   if (result < 0) {
     fprintf(stderr, "Binary log entry conversion failed\n");
@@ -1187,8 +1170,7 @@
   }
 
   for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
-       num_to_read < 5 && (first_char & mask);
-       num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+       num_to_read < 5 && (first_char & mask); num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
     if (num_to_read > len) {
       return -1;
     }
@@ -1226,8 +1208,7 @@
     len = utf8_character_length(message, len);
 
     if (len < 0) {
-      snprintf(buf, sizeof(buf),
-               ((messageLen > 1) && isdigit(message[1])) ? "\\%03o" : "\\%o",
+      snprintf(buf, sizeof(buf), ((messageLen > 1) && isdigit(message[1])) ? "\\%03o" : "\\%o",
                *message & 0377);
       len = 1;
     } else {
@@ -1281,8 +1262,7 @@
   return p;
 }
 
-static struct timespec* sumTimespec(struct timespec* left,
-                                    struct timespec* right) {
+static struct timespec* sumTimespec(struct timespec* left, struct timespec* right) {
   left->tv_nsec += right->tv_nsec;
   left->tv_sec += right->tv_sec;
   if (left->tv_nsec >= (long)NS_PER_SEC) {
@@ -1292,8 +1272,7 @@
   return left;
 }
 
-static struct timespec* subTimespec(struct timespec* result,
-                                    struct timespec* left,
+static struct timespec* subTimespec(struct timespec* result, struct timespec* left,
                                     struct timespec* right) {
   result->tv_nsec = left->tv_nsec - right->tv_nsec;
   result->tv_sec = left->tv_sec - right->tv_sec;
@@ -1309,8 +1288,7 @@
 }
 
 #ifdef __ANDROID__
-static void convertMonotonic(struct timespec* result,
-                             const AndroidLogEntry* entry) {
+static void convertMonotonic(struct timespec* result, const AndroidLogEntry* entry) {
   struct listnode* node;
   struct conversionList {
     struct listnode node; /* first */
@@ -1322,8 +1300,8 @@
   /* If we do not have a conversion list, build one up */
   if (list_empty(&convertHead)) {
     bool suspended_pending = false;
-    struct timespec suspended_monotonic = { 0, 0 };
-    struct timespec suspended_diff = { 0, 0 };
+    struct timespec suspended_monotonic = {0, 0};
+    struct timespec suspended_diff = {0, 0};
 
     /*
      * Read dmesg for _some_ synchronization markers and insert
@@ -1413,15 +1391,14 @@
           } else {
             unsetenv(tz);
           }
-          list = calloc(1, sizeof(struct conversionList));
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
           list_init(&list->node);
           list->time = time;
           subTimespec(&list->convert, &time, &monotonic);
           list_add_tail(&convertHead, &list->node);
         }
         if (suspended_pending && !list_empty(&convertHead)) {
-          list = node_to_item(list_tail(&convertHead), struct conversionList,
-                              node);
+          list = node_to_item(list_tail(&convertHead), struct conversionList, node);
           if (subTimespec(&time, subTimespec(&time, &list->time, &list->convert),
                           &suspended_monotonic)
                   ->tv_sec > 0) {
@@ -1434,13 +1411,13 @@
           time = suspended_monotonic;
           sumTimespec(&time, &convert);
           /* breakpoint just before sleep */
-          list = calloc(1, sizeof(struct conversionList));
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
           list_init(&list->node);
           list->time = time;
           list->convert = convert;
           list_add_tail(&convertHead, &list->node);
           /* breakpoint just after sleep */
-          list = calloc(1, sizeof(struct conversionList));
+          list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
           list_init(&list->node);
           list->time = time;
           sumTimespec(&list->time, &suspended_diff);
@@ -1453,7 +1430,7 @@
       pclose(p);
     }
     /* last entry is our current time conversion */
-    list = calloc(1, sizeof(struct conversionList));
+    list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
     list_init(&list->node);
     clock_gettime(CLOCK_REALTIME, &list->time);
     clock_gettime(CLOCK_MONOTONIC, &convert);
@@ -1469,7 +1446,7 @@
       time = suspended_monotonic;
       sumTimespec(&time, &convert);
       /* breakpoint just after sleep */
-      list = calloc(1, sizeof(struct conversionList));
+      list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
       list_init(&list->node);
       list->time = time;
       sumTimespec(&list->time, &suspended_diff);
@@ -1477,7 +1454,7 @@
       sumTimespec(&list->convert, &suspended_diff);
       list_add_head(&convertHead, &list->node);
       /* breakpoint just before sleep */
-      list = calloc(1, sizeof(struct conversionList));
+      list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
       list_init(&list->node);
       list->time = time;
       list->convert = convert;
@@ -1548,8 +1525,7 @@
  * Returns NULL on malloc error
  */
 
-LIBLOG_ABI_PUBLIC char* android_log_formatLogLine(AndroidLogFormat* p_format,
-                                                  char* defaultBuffer,
+LIBLOG_ABI_PUBLIC char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
                                                   size_t defaultBufferSize,
                                                   const AndroidLogEntry* entry,
                                                   size_t* p_outLength) {
@@ -1600,26 +1576,23 @@
   }
   if (p_format->epoch_output || p_format->monotonic_output) {
     ptm = NULL;
-    snprintf(timeBuf, sizeof(timeBuf),
-             p_format->monotonic_output ? "%6lld" : "%19lld", (long long)now);
+    snprintf(timeBuf, sizeof(timeBuf), p_format->monotonic_output ? "%6lld" : "%19lld",
+             (long long)now);
   } else {
 #if !defined(_WIN32)
     ptm = localtime_r(&now, &tmBuf);
 #else
     ptm = localtime(&now);
 #endif
-    strftime(timeBuf, sizeof(timeBuf),
-             &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
+    strftime(timeBuf, sizeof(timeBuf), &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
   }
   len = strlen(timeBuf);
   if (p_format->nsec_time_output) {
     len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%09ld", nsec);
   } else if (p_format->usec_time_output) {
-    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld",
-                    nsec / US_PER_NSEC);
+    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld", nsec / US_PER_NSEC);
   } else {
-    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld",
-                    nsec / MS_PER_NSEC);
+    len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld", nsec / MS_PER_NSEC);
   }
   if (p_format->zone_output && ptm) {
     strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
@@ -1629,8 +1602,8 @@
    * Construct a buffer containing the log header and log message.
    */
   if (p_format->colored_output) {
-    prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
-                         colorFromPri(entry->priority));
+    prefixLen =
+        snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", colorFromPri(entry->priority));
     prefixLen = MIN(prefixLen, sizeof(prefixBuf));
 
     const char suffixContents[] = "\x1B[0m";
@@ -1649,8 +1622,7 @@
 #if !defined(__MINGW32__)
 #if (FAKE_LOG_DEVICE == 0)
 #ifndef __BIONIC__
-#warning \
-    "This code assumes that getpwuid is thread safe, only true with Bionic!"
+#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
 #endif
 #endif
       struct passwd* pwd = getpwuid(entry->uid);
@@ -1669,21 +1641,21 @@
 
   switch (p_format->format) {
     case FORMAT_TAG:
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%c/%-8.*s: ", priChar, (int)entry->tagLen, entry->tag);
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8.*s: ", priChar,
+                     (int)entry->tagLen, entry->tag);
       strcpy(suffixBuf + suffixLen, "\n");
       ++suffixLen;
       break;
     case FORMAT_PROCESS:
-      len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen,
-                     "  (%.*s)\n", (int)entry->tagLen, entry->tag);
+      len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen, "  (%.*s)\n",
+                     (int)entry->tagLen, entry->tag);
       suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%c(%s%5d) ", priChar, uid, entry->pid);
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d) ", priChar,
+                     uid, entry->pid);
       break;
     case FORMAT_THREAD:
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%c(%s%5d:%5d) ", priChar, uid, entry->pid, entry->tid);
+      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d:%5d) ",
+                     priChar, uid, entry->pid, entry->tid);
       strcpy(suffixBuf + suffixLen, "\n");
       ++suffixLen;
       break;
@@ -1695,8 +1667,8 @@
       break;
     case FORMAT_TIME:
       len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar,
-                     (int)entry->tagLen, entry->tag, uid, entry->pid);
+                     "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar, (int)entry->tagLen, entry->tag, uid,
+                     entry->pid);
       strcpy(suffixBuf + suffixLen, "\n");
       ++suffixLen;
       break;
@@ -1706,24 +1678,24 @@
         *ret = ' ';
       }
       len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid,
-                     entry->tid, priChar, (int)entry->tagLen, entry->tag);
+                     "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid, entry->tid, priChar,
+                     (int)entry->tagLen, entry->tag);
       strcpy(suffixBuf + suffixLen, "\n");
       ++suffixLen;
       break;
     case FORMAT_LONG:
       len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid,
-                     entry->tid, priChar, (int)entry->tagLen, entry->tag);
+                     "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid, entry->tid, priChar,
+                     (int)entry->tagLen, entry->tag);
       strcpy(suffixBuf + suffixLen, "\n\n");
       suffixLen += 2;
       prefixSuffixIsHeaderFooter = 1;
       break;
     case FORMAT_BRIEF:
     default:
-      len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
-                     "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen,
-                     entry->tag, uid, entry->pid);
+      len =
+          snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+                   "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen, entry->tag, uid, entry->pid);
       strcpy(suffixBuf + suffixLen, "\n");
       ++suffixLen;
       break;
@@ -1847,16 +1819,15 @@
  * Returns count bytes written
  */
 
-LIBLOG_ABI_PUBLIC int android_log_printLogLine(AndroidLogFormat* p_format,
-                                               int fd,
+LIBLOG_ABI_PUBLIC int android_log_printLogLine(AndroidLogFormat* p_format, int fd,
                                                const AndroidLogEntry* entry) {
   int ret;
   char defaultBuffer[512];
   char* outBuffer = NULL;
   size_t totalLen;
 
-  outBuffer = android_log_formatLogLine(
-      p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
+  outBuffer =
+      android_log_formatLogLine(p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
 
   if (!outBuffer) return -1;
 
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.cpp
similarity index 84%
rename from liblog/pmsg_reader.c
rename to liblog/pmsg_reader.cpp
index bf0e4fe..7bc6e4a 100644
--- a/liblog/pmsg_reader.c
+++ b/liblog/pmsg_reader.cpp
@@ -32,28 +32,27 @@
 static int pmsgVersion(struct android_log_logger* logger,
                        struct android_log_transport_context* transp);
 static int pmsgRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp,
-                    struct log_msg* log_msg);
+                    struct android_log_transport_context* transp, struct log_msg* log_msg);
 static void pmsgClose(struct android_log_logger_list* logger_list,
                       struct android_log_transport_context* transp);
 static int pmsgClear(struct android_log_logger* logger,
                      struct android_log_transport_context* transp);
 
 LIBLOG_HIDDEN struct android_log_transport_read pmsgLoggerRead = {
-  .node = { &pmsgLoggerRead.node, &pmsgLoggerRead.node },
-  .name = "pmsg",
-  .available = pmsgAvailable,
-  .version = pmsgVersion,
-  .read = pmsgRead,
-  .poll = NULL,
-  .close = pmsgClose,
-  .clear = pmsgClear,
-  .setSize = NULL,
-  .getSize = NULL,
-  .getReadableSize = NULL,
-  .getPrune = NULL,
-  .setPrune = NULL,
-  .getStats = NULL,
+    .node = {&pmsgLoggerRead.node, &pmsgLoggerRead.node},
+    .name = "pmsg",
+    .available = pmsgAvailable,
+    .version = pmsgVersion,
+    .read = pmsgRead,
+    .poll = NULL,
+    .close = pmsgClose,
+    .clear = pmsgClear,
+    .setSize = NULL,
+    .getSize = NULL,
+    .getReadableSize = NULL,
+    .getPrune = NULL,
+    .setPrune = NULL,
+    .getStats = NULL,
 };
 
 static int pmsgAvailable(log_id_t logId) {
@@ -68,8 +67,7 @@
 
 /* Determine the credentials of the caller */
 static bool uid_has_log_permission(uid_t uid) {
-  return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) ||
-         (uid == AID_LOGD);
+  return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) || (uid == AID_LOGD);
 }
 
 static uid_t get_best_effective_uid() {
@@ -130,8 +128,7 @@
 }
 
 static int pmsgRead(struct android_log_logger_list* logger_list,
-                    struct android_log_transport_context* transp,
-                    struct log_msg* log_msg) {
+                    struct android_log_transport_context* transp, struct log_msg* log_msg) {
   ssize_t ret;
   off_t current, next;
   uid_t uid;
@@ -174,8 +171,7 @@
       if (fd <= 0) {
         return -EBADF;
       }
-      ret = TEMP_FAILURE_RETRY(
-          read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
+      ret = TEMP_FAILURE_RETRY(read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
       if (ret < 0) {
         return -errno;
       }
@@ -185,11 +181,10 @@
       return preread_count ? -EIO : -EAGAIN;
     }
     if ((buf.p.magic != LOGGER_MAGIC) || (buf.p.len <= sizeof(buf)) ||
-        (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) ||
-        (buf.l.id >= LOG_ID_MAX) || (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
+        (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) || (buf.l.id >= LOG_ID_MAX) ||
+        (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
         ((buf.l.id != LOG_ID_EVENTS) && (buf.l.id != LOG_ID_SECURITY) &&
-         ((buf.prio == ANDROID_LOG_UNKNOWN) ||
-          (buf.prio == ANDROID_LOG_DEFAULT) ||
+         ((buf.prio == ANDROID_LOG_UNKNOWN) || (buf.prio == ANDROID_LOG_DEFAULT) ||
           (buf.prio >= ANDROID_LOG_SILENT)))) {
       do {
         memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
@@ -213,8 +208,7 @@
         if (fd <= 0) {
           return -EBADF;
         }
-        ret = TEMP_FAILURE_RETRY(
-            read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
+        ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
         if (ret < 0) {
           return -errno;
         }
@@ -250,8 +244,7 @@
     if (fd <= 0) {
       return -EBADF;
     }
-    next = TEMP_FAILURE_RETRY(
-        lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
+    next = TEMP_FAILURE_RETRY(lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
     if (next < 0) {
       return -errno;
     }
@@ -277,9 +270,10 @@
   return result;
 }
 
-LIBLOG_ABI_PRIVATE ssize_t
-__android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
-                             __android_log_pmsg_file_read_fn fn, void* arg) {
+LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio,
+                                                        const char* prefix,
+                                                        __android_log_pmsg_file_read_fn fn,
+                                                        void* arg) {
   ssize_t ret;
   struct android_log_logger_list logger_list;
   struct android_log_transport_context transp;
@@ -312,14 +306,12 @@
   memset(&logger_list, 0, sizeof(logger_list));
   memset(&transp, 0, sizeof(transp));
 
-  logger_list.mode =
-      ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
+  logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
   transp.logMask = (unsigned)-1;
   if (logId != LOG_ID_ANY) {
     transp.logMask = (1 << logId);
   }
-  transp.logMask &=
-      ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+  transp.logMask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
   if (!transp.logMask) {
     return -EINVAL;
   }
@@ -346,15 +338,13 @@
 
   /* Read the file content */
   while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
-    char* cp;
-    size_t hdr_size = transp.logMsg.entry.hdr_size
-                          ? transp.logMsg.entry.hdr_size
-                          : sizeof(transp.logMsg.entry_v1);
+    const char* cp;
+    size_t hdr_size = transp.logMsg.entry.hdr_size ? transp.logMsg.entry.hdr_size
+                                                   : sizeof(transp.logMsg.entry_v1);
     char* msg = (char*)&transp.logMsg + hdr_size;
-    char* split = NULL;
+    const char* split = NULL;
 
-    if ((hdr_size < sizeof(transp.logMsg.entry_v1)) ||
-        (hdr_size > sizeof(transp.logMsg.entry))) {
+    if ((hdr_size < sizeof(transp.logMsg.entry_v1)) || (hdr_size > sizeof(transp.logMsg.entry))) {
       continue;
     }
     /* Check for invalid sequence number */
@@ -366,8 +356,7 @@
 
     /* Determine if it has <dirbase>:<filebase> format for tag */
     len = transp.logMsg.entry.len - sizeof(prio);
-    for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len;
-         ++cp) {
+    for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
       if (*cp == ':') {
         if (split) {
           break;
@@ -395,13 +384,12 @@
         continue;
       }
       offset = split - prefix;
-      if ((msg[offset + sizeof(prio)] != ':') ||
-          strncmp(msg + sizeof(prio), prefix, offset)) {
+      if ((msg[offset + sizeof(prio)] != ':') || strncmp(msg + sizeof(prio), prefix, offset)) {
         continue;
       }
       ++offset;
-      if ((prefix_len > offset) && strncmp(&msg[offset + sizeof(prio)],
-                                           split + 1, prefix_len - offset)) {
+      if ((prefix_len > offset) &&
+          strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) {
         continue;
       }
     }
@@ -413,8 +401,8 @@
     /* check if there is an existing entry */
     list_for_each(node, &name_list) {
       names = node_to_item(node, struct names, node);
-      if (!strcmp(names->name, msg + sizeof(prio)) &&
-          (names->id == transp.logMsg.entry.lid) && (names->prio == *msg)) {
+      if (!strcmp(names->name, msg + sizeof(prio)) && (names->id == transp.logMsg.entry.lid) &&
+          (names->prio == *msg)) {
         break;
       }
     }
@@ -425,13 +413,13 @@
       unsigned long long nl;
 
       len = strlen(msg + sizeof(prio)) + 1;
-      names = calloc(1, sizeof(*names) + len);
+      names = static_cast<struct names*>(calloc(1, sizeof(*names) + len));
       if (!names) {
         ret = -ENOMEM;
         break;
       }
       strcpy(names->name, msg + sizeof(prio));
-      names->id = transp.logMsg.entry.lid;
+      names->id = static_cast<log_id_t>(transp.logMsg.entry.lid);
       names->prio = *msg;
       list_init(&names->content);
       /*
@@ -491,19 +479,17 @@
     }
 
     /* Add content */
-    content =
-        calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len);
+    content = static_cast<struct content*>(
+        calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len));
     if (!content) {
       ret = -ENOMEM;
       break;
     }
-    memcpy(&content->entry, &transp.logMsg.entry,
-           hdr_size + transp.logMsg.entry.len);
+    memcpy(&content->entry, &transp.logMsg.entry, hdr_size + transp.logMsg.entry.len);
 
     /* Insert in sequence number sorted order, to ease reconstruction */
     list_for_each_reverse(node, &names->content) {
-      if ((node_to_item(node, struct content, node))->entry.nsec <
-          transp.logMsg.entry.nsec) {
+      if ((node_to_item(node, struct content, node))->entry.nsec < transp.logMsg.entry.nsec) {
         break;
       }
     }
@@ -536,7 +522,7 @@
       }
 
       if (!buf) {
-        buf = malloc(sizeof(char));
+        buf = static_cast<char*>(malloc(sizeof(char)));
         if (!buf) {
           ret = -ENOMEM;
           list_remove(content_node);
@@ -549,7 +535,7 @@
       /* Missing sequence numbers */
       while (sequence < content->entry.nsec) {
         /* plus space for enforced nul */
-        buf = realloc_or_free(buf, len + sizeof(char) + sizeof(char));
+        buf = static_cast<char*>(realloc_or_free(buf, len + sizeof(char) + sizeof(char)));
         if (!buf) {
           break;
         }
@@ -564,16 +550,14 @@
         continue;
       }
       /* plus space for enforced nul */
-      buf = realloc_or_free(buf, len + add_len + sizeof(char));
+      buf = static_cast<char*>(realloc_or_free(buf, len + add_len + sizeof(char)));
       if (!buf) {
         ret = -ENOMEM;
         list_remove(content_node);
         free(content);
         continue;
       }
-      memcpy(buf + len,
-             (char*)&content->entry + content->entry.hdr_size + tag_len +
-                 sizeof(prio),
+      memcpy(buf + len, (char*)&content->entry + content->entry.hdr_size + tag_len + sizeof(prio),
              add_len);
       len += add_len;
       buf[len] = '\0'; /* enforce trailing hidden nul */
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.cpp
similarity index 88%
rename from liblog/pmsg_writer.c
rename to liblog/pmsg_writer.cpp
index dc42856..b2fc6d0 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.cpp
@@ -33,21 +33,21 @@
 #include "config_write.h"
 #include "log_portability.h"
 #include "logger.h"
+#include "uio.h"
 
 static int pmsgOpen();
 static void pmsgClose();
 static int pmsgAvailable(log_id_t logId);
-static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                     size_t nr);
+static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
 
 LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
-  .node = { &pmsgLoggerWrite.node, &pmsgLoggerWrite.node },
-  .context.fd = -1,
-  .name = "pmsg",
-  .available = pmsgAvailable,
-  .open = pmsgOpen,
-  .close = pmsgClose,
-  .write = pmsgWrite,
+    .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
+    .context.fd = -1,
+    .name = "pmsg",
+    .available = pmsgAvailable,
+    .open = pmsgOpen,
+    .close = pmsgClose,
+    .write = pmsgWrite,
 };
 
 static int pmsgOpen() {
@@ -76,8 +76,7 @@
   if (logId > LOG_ID_SECURITY) {
     return -EINVAL;
   }
-  if ((logId != LOG_ID_SECURITY) && (logId != LOG_ID_EVENTS) &&
-      !__android_log_is_debuggable()) {
+  if ((logId != LOG_ID_SECURITY) && (logId != LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
     return -EINVAL;
   }
   if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
@@ -96,8 +95,7 @@
   return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
 }
 
-static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                     size_t nr) {
+static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
   static const unsigned headerLength = 2;
   struct iovec newVec[nr + headerLength];
   android_log_header_t header;
@@ -110,7 +108,7 @@
       return -EINVAL;
     }
 
-    if (SNET_EVENT_LOG_TAG != get4LE(vec[0].iov_base)) {
+    if (SNET_EVENT_LOG_TAG != get4LE(static_cast<uint8_t*>(vec[0].iov_base))) {
       return -EPERM;
     }
   }
@@ -169,8 +167,7 @@
   }
   pmsgHeader.len += payloadSize;
 
-  ret = TEMP_FAILURE_RETRY(
-      writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i));
+  ret = TEMP_FAILURE_RETRY(writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i));
   if (ret < 0) {
     ret = errno ? -errno : -ENOTCONN;
   }
@@ -203,10 +200,8 @@
 }
 
 /* Write a buffer as filename references (tag = <basedir>:<basename>) */
-LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write(log_id_t logId,
-                                                         char prio,
-                                                         const char* filename,
-                                                         const char* buf,
+LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio,
+                                                         const char* filename, const char* buf,
                                                          size_t len) {
   bool weOpened;
   size_t length, packet_len;
@@ -249,13 +244,11 @@
   vec[1].iov_len = length;
 
   weOpened = false;
-  for (ts.tv_nsec = 0, length = len; length;
-       ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
+  for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
     ssize_t ret;
     size_t transfer;
 
-    if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
-        ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+    if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
       len -= length;
       break;
     }
diff --git a/liblog/properties.c b/liblog/properties.cpp
similarity index 86%
rename from liblog/properties.c
rename to liblog/properties.cpp
index 11be827..764877e 100644
--- a/liblog/properties.c
+++ b/liblog/properties.cpp
@@ -14,9 +14,10 @@
 ** limitations under the License.
 */
 
+#include <log/log_properties.h>
+
 #include <ctype.h>
 #include <pthread.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
@@ -160,11 +161,11 @@
       }
       if (!last_tag || !last_tag[0]) {
         if (!last_tag) {
-          last_tag = calloc(1, len + 1);
+          last_tag = static_cast<char*>(calloc(1, len + 1));
           last_tag_len = 0;
           if (last_tag) last_tag_len = len + 1;
         } else if (len >= last_tag_len) {
-          last_tag = realloc(last_tag, len + 1);
+          last_tag = static_cast<char*>(realloc(last_tag, len + 1));
           last_tag_len = 0;
           if (last_tag) last_tag_len = len + 1;
         }
@@ -258,22 +259,19 @@
     case 'A': return ANDROID_LOG_FATAL;
     case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
     case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
-    /* clang-format on */
+      /* clang-format on */
   }
   return default_prio;
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio, const char* tag,
-                                                    size_t len,
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio, const char* tag, size_t len,
                                                     int default_prio) {
   int logLevel = __android_log_level(tag, len, default_prio);
   return logLevel >= 0 && prio >= logLevel;
 }
 
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char* tag,
-                                                int default_prio) {
-  int logLevel =
-      __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
+  int logLevel = __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
   return logLevel >= 0 && prio >= logLevel;
 }
 
@@ -286,7 +284,7 @@
   if (tag_cache.c) { /* ro property does not change after set */
     ret = tag_cache.c == '1';
   } else if (lock()) {
-    struct cache_char temp_cache = { { NULL, -1 }, '\0' };
+    struct cache_char temp_cache = {{NULL, 0xFFFFFFFF}, '\0'};
     refresh_cache(&temp_cache, key);
     ret = temp_cache.c == '1';
   } else {
@@ -332,8 +330,7 @@
     return self->evaluate(self);
   }
 
-  change_detected = check_cache(&self->cache_persist.cache) ||
-                    check_cache(&self->cache_ro.cache);
+  change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
   current_serial = __system_property_area_serial();
   if (current_serial != self->serial) {
     change_detected = 1;
@@ -365,15 +362,12 @@
  * to handle developer requirements.
  */
 LIBLOG_ABI_PUBLIC clockid_t android_log_clockid() {
-  static struct cache2_char clockid = {
-    PTHREAD_MUTEX_INITIALIZER, 0,
-    "persist.logd.timestamp",  { { NULL, -1 }, '\0' },
-    "ro.logd.timestamp",       { { NULL, -1 }, '\0' },
-    evaluate_persist_ro
-  };
+  static struct cache2_char clockid = {PTHREAD_MUTEX_INITIALIZER, 0,
+                                       "persist.logd.timestamp",  {{NULL, 0xFFFFFFFF}, '\0'},
+                                       "ro.logd.timestamp",       {{NULL, 0xFFFFFFFF}, '\0'},
+                                       evaluate_persist_ro};
 
-  return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC
-                                                    : CLOCK_REALTIME;
+  return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
 }
 
 /*
@@ -388,11 +382,10 @@
 
 LIBLOG_ABI_PUBLIC int __android_log_security() {
   static struct cache2_char security = {
-    PTHREAD_MUTEX_INITIALIZER, 0,
-    "persist.logd.security",   { { NULL, -1 }, BOOLEAN_FALSE },
-    "ro.device_owner",         { { NULL, -1 }, BOOLEAN_FALSE },
-    evaluate_security
-  };
+      PTHREAD_MUTEX_INITIALIZER, 0,
+      "persist.logd.security",   {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+      "ro.device_owner",         {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+      evaluate_security};
 
   return do_cache2_char(&security);
 }
@@ -423,8 +416,7 @@
   char property[PROP_VALUE_MAX];
 };
 
-static void refresh_cache_property(struct cache_property* cache,
-                                   const char* key) {
+static void refresh_cache_property(struct cache_property* cache, const char* key) {
   if (!cache->cache.pinfo) {
     cache->cache.pinfo = __system_property_find(key);
     if (!cache->cache.pinfo) {
@@ -436,19 +428,18 @@
 }
 
 /* get boolean with the logger twist that supports eng adjustments */
-LIBLOG_ABI_PRIVATE bool __android_logger_property_get_bool(const char* key,
-                                                           int flag) {
-  struct cache_property property = { { NULL, -1 }, { 0 } };
+LIBLOG_ABI_PRIVATE bool __android_logger_property_get_bool(const char* key, int flag) {
+  struct cache_property property = {{NULL, 0xFFFFFFFF}, {0}};
   if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
     char newkey[strlen("persist.") + strlen(key) + 1];
     snprintf(newkey, sizeof(newkey), "ro.%s", key);
     refresh_cache_property(&property, newkey);
     property.cache.pinfo = NULL;
-    property.cache.serial = -1;
+    property.cache.serial = 0xFFFFFFFF;
     snprintf(newkey, sizeof(newkey), "persist.%s", key);
     refresh_cache_property(&property, newkey);
     property.cache.pinfo = NULL;
-    property.cache.serial = -1;
+    property.cache.serial = 0xFFFFFFFF;
   }
 
   refresh_cache_property(&property, key);
@@ -477,8 +468,7 @@
   }
 
   if ((flag & BOOL_DEFAULT_FLAG_SVELTE) &&
-      __android_logger_property_get_bool("ro.config.low_ram",
-                                         BOOL_DEFAULT_FALSE)) {
+      __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)) {
     return false;
   }
   if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
@@ -531,8 +521,7 @@
   unsigned long (*const evaluate)(const struct cache2_property_size* self);
 };
 
-static inline unsigned long do_cache2_property_size(
-    struct cache2_property_size* self) {
+static inline unsigned long do_cache2_property_size(struct cache2_property_size* self) {
   uint32_t current_serial;
   int change_detected;
   unsigned long v;
@@ -542,8 +531,7 @@
     return self->evaluate(self);
   }
 
-  change_detected = check_cache(&self->cache_persist.cache) ||
-                    check_cache(&self->cache_ro.cache);
+  change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
   current_serial = __system_property_area_serial();
   if (current_serial != self->serial) {
     change_detected = 1;
@@ -560,8 +548,7 @@
   return v;
 }
 
-static unsigned long property_get_size_from_cache(
-    const struct cache_property* cache) {
+static unsigned long property_get_size_from_cache(const struct cache_property* cache) {
   char* cp;
   unsigned long value = strtoul(cache->property, &cp, 10);
 
@@ -569,11 +556,11 @@
     case 'm':
     case 'M':
       value *= 1024;
-    /* FALLTHRU */
+      [[fallthrough]];
     case 'k':
     case 'K':
       value *= 1024;
-    /* FALLTHRU */
+      [[fallthrough]];
     case '\0':
       break;
 
@@ -588,8 +575,7 @@
   return value;
 }
 
-static unsigned long evaluate_property_get_size(
-    const struct cache2_property_size* self) {
+static unsigned long evaluate_property_get_size(const struct cache2_property_size* self) {
   unsigned long size = property_get_size_from_cache(&self->cache_persist);
   if (size) {
     return size;
@@ -601,37 +587,35 @@
   static const char global_tunable[] = "persist.logd.size"; /* Settings App */
   static const char global_default[] = "ro.logd.size";      /* BoardConfig.mk */
   static struct cache2_property_size global = {
-    /* clang-format off */
+      /* clang-format off */
     PTHREAD_MUTEX_INITIALIZER, 0,
-    global_tunable, { { NULL, -1 }, {} },
-    global_default, { { NULL, -1 }, {} },
+    global_tunable, { { NULL, 0xFFFFFFFF }, {} },
+    global_default, { { NULL, 0xFFFFFFFF }, {} },
     evaluate_property_get_size
-    /* clang-format on */
+      /* clang-format on */
   };
   char key_persist[strlen(global_tunable) + strlen(".security") + 1];
   char key_ro[strlen(global_default) + strlen(".security") + 1];
   struct cache2_property_size local = {
-    /* clang-format off */
+      /* clang-format off */
     PTHREAD_MUTEX_INITIALIZER, 0,
-    key_persist, { { NULL, -1 }, {} },
-    key_ro,      { { NULL, -1 }, {} },
+    key_persist, { { NULL, 0xFFFFFFFF }, {} },
+    key_ro,      { { NULL, 0xFFFFFFFF }, {} },
     evaluate_property_get_size
-    /* clang-format on */
+      /* clang-format on */
   };
   unsigned long property_size, default_size;
 
   default_size = do_cache2_property_size(&global);
   if (!default_size) {
-    default_size = __android_logger_property_get_bool("ro.config.low_ram",
-                                                      BOOL_DEFAULT_FALSE)
+    default_size = __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)
                        ? LOG_BUFFER_MIN_SIZE /* 64K  */
                        : LOG_BUFFER_SIZE;    /* 256K */
   }
 
   snprintf(key_persist, sizeof(key_persist), "%s.%s", global_tunable,
            android_log_id_to_name(logId));
-  snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default,
-           android_log_id_to_name(logId));
+  snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default, android_log_id_to_name(logId));
   property_size = do_cache2_property_size(&local);
 
   if (!property_size) {
diff --git a/liblog/stderr_write.c b/liblog/stderr_write.cpp
similarity index 90%
rename from liblog/stderr_write.c
rename to liblog/stderr_write.cpp
index dbe5309..28195aa 100644
--- a/liblog/stderr_write.c
+++ b/liblog/stderr_write.cpp
@@ -37,16 +37,15 @@
 #include <log/event_tag_map.h>
 #include <log/log.h>
 #include <log/logprint.h>
-#include <log/uio.h>
 
 #include "log_portability.h"
 #include "logger.h"
+#include "uio.h"
 
 static int stderrOpen();
 static void stderrClose();
 static int stderrAvailable(log_id_t logId);
-static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                       size_t nr);
+static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
 
 struct stderrContext {
   AndroidLogFormat* logformat;
@@ -56,13 +55,13 @@
 };
 
 LIBLOG_HIDDEN struct android_log_transport_write stderrLoggerWrite = {
-  .node = { &stderrLoggerWrite.node, &stderrLoggerWrite.node },
-  .context.priv = NULL,
-  .name = "stderr",
-  .available = stderrAvailable,
-  .open = stderrOpen,
-  .close = stderrClose,
-  .write = stderrWrite,
+    .node = {&stderrLoggerWrite.node, &stderrLoggerWrite.node},
+    .context.priv = NULL,
+    .name = "stderr",
+    .available = stderrAvailable,
+    .open = stderrOpen,
+    .close = stderrClose,
+    .write = stderrWrite,
 };
 
 static int stderrOpen() {
@@ -78,7 +77,7 @@
     return fileno(stderr);
   }
 
-  ctx = calloc(1, sizeof(struct stderrContext));
+  ctx = static_cast<stderrContext*>(calloc(1, sizeof(stderrContext)));
   if (!ctx) {
     return -ENOMEM;
   }
@@ -123,7 +122,7 @@
 }
 
 static void stderrClose() {
-  struct stderrContext* ctx = stderrLoggerWrite.context.priv;
+  stderrContext* ctx = static_cast<stderrContext*>(stderrLoggerWrite.context.priv);
 
   if (ctx) {
     stderrLoggerWrite.context.priv = NULL;
@@ -147,14 +146,13 @@
   return 1;
 }
 
-static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
-                       size_t nr) {
+static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
   struct log_msg log_msg;
   AndroidLogEntry entry;
   char binaryMsgBuf[1024];
   int err;
   size_t i;
-  struct stderrContext* ctx = stderrLoggerWrite.context.priv;
+  stderrContext* ctx = static_cast<stderrContext*>(stderrLoggerWrite.context.priv);
 
   if (!ctx) return -EBADF;
   if (!vec || !nr) return -EINVAL;
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 2c47fd6..4ab2acb 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -29,10 +29,10 @@
         "-fno-builtin",
     ],
     shared_libs: [
-        "liblog",
         "libm",
         "libbase",
     ],
+    static_libs: ["liblog"],
     srcs: ["liblog_benchmark.cpp"],
 }
 
@@ -63,10 +63,10 @@
         "log_wrap_test.cpp",
     ],
     shared_libs: [
-        "liblog",
         "libcutils",
         "libbase",
     ],
+    static_libs: ["liblog"],
 }
 
 // Build tests for the device (with .so). Run with:
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index c2f3f83..c8ac321 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -21,6 +21,7 @@
 #include <sys/socket.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
 #include <unordered_set>
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 2d0fc9b..83f0fa9 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -3160,41 +3160,6 @@
 }
 #endif  // USING_LOGGER_DEFAULT
 
-#ifdef USING_LOGGER_DEFAULT  // Do not retest ratelimit
-TEST(liblog, __android_log_ratelimit) {
-  time_t state = 0;
-
-  errno = 42;
-  // Prime
-  __android_log_ratelimit(3, &state);
-  EXPECT_EQ(errno, 42);
-  // Check
-  EXPECT_FALSE(__android_log_ratelimit(3, &state));
-  sleep(1);
-  EXPECT_FALSE(__android_log_ratelimit(3, &state));
-  sleep(4);
-  EXPECT_TRUE(__android_log_ratelimit(3, &state));
-  sleep(5);
-  EXPECT_TRUE(__android_log_ratelimit(3, &state));
-
-  // API checks
-  IF_ALOG_RATELIMIT_LOCAL(3, &state) {
-    EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT_LOCAL(3, &state)");
-  }
-
-  IF_ALOG_RATELIMIT() {
-    ;
-  }
-  else {
-    EXPECT_TRUE(0 == "IF_ALOG_RATELIMIT()");
-  }
-  IF_ALOG_RATELIMIT() {
-    EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT()");
-  }
-  // Do not test default seconds, to allow liblog to tune freely
-}
-#endif  // USING_LOGGER_DEFAULT
-
 #ifdef USING_LOGGER_DEFAULT  // Do not retest event mapping functionality
 TEST(liblog, android_lookupEventTagNum) {
 #ifdef __ANDROID__
diff --git a/liblog/tests/log_time_test.cpp b/liblog/tests/log_time_test.cpp
index 0ae1d18..47fe594 100644
--- a/liblog/tests/log_time_test.cpp
+++ b/liblog/tests/log_time_test.cpp
@@ -21,12 +21,6 @@
 #include <log/log_time.h>
 
 TEST(liblog, log_time) {
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-  log_time(CLOCK_MONOTONIC);
-
-  EXPECT_EQ(log_time, log_time::EPOCH);
-#endif
-
   struct timespec ts;
   clock_gettime(CLOCK_MONOTONIC, &ts);
   log_time tl(ts);
diff --git a/liblog/uio.c b/liblog/uio.c
deleted file mode 100644
index e127202..0000000
--- a/liblog/uio.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2007-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if defined(_WIN32)
-
-#include <unistd.h>
-
-#include <log/uio.h>
-
-#include "log_portability.h"
-
-LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec* vecs, int count) {
-  int total = 0;
-
-  for (; count > 0; count--, vecs++) {
-    char* buf = vecs->iov_base;
-    int len = vecs->iov_len;
-
-    while (len > 0) {
-      int ret = read(fd, buf, len);
-      if (ret < 0) {
-        if (total == 0) total = -1;
-        goto Exit;
-      }
-      if (ret == 0) goto Exit;
-
-      total += ret;
-      buf += ret;
-      len -= ret;
-    }
-  }
-Exit:
-  return total;
-}
-
-LIBLOG_ABI_PUBLIC int writev(int fd, const struct iovec* vecs, int count) {
-  int total = 0;
-
-  for (; count > 0; count--, vecs++) {
-    const char* buf = vecs->iov_base;
-    int len = vecs->iov_len;
-
-    while (len > 0) {
-      int ret = write(fd, buf, len);
-      if (ret < 0) {
-        if (total == 0) total = -1;
-        goto Exit;
-      }
-      if (ret == 0) goto Exit;
-
-      total += ret;
-      buf += ret;
-      len -= ret;
-    }
-  }
-Exit:
-  return total;
-}
-
-#endif
diff --git a/liblog/include/log/uio.h b/liblog/uio.h
similarity index 62%
rename from liblog/include/log/uio.h
rename to liblog/uio.h
index a492bae..c85893c 100644
--- a/liblog/include/log/uio.h
+++ b/liblog/uio.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2014 The Android Open Source Project
+ * 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.
@@ -14,37 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef _LIBS_CUTILS_UIO_H
-#define _LIBS_CUTILS_UIO_H
+#pragma once
 
-#if !defined(_WIN32)
-
-#include <sys/uio.h>
-
-#else
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// Implementation of sys/uio.h for Win32.
-//
-
+#if defined(_WIN32)
 #include <stddef.h>
-
 struct iovec {
   void* iov_base;
   size_t iov_len;
 };
-
-extern int readv(int fd, struct iovec* vecs, int count);
-extern int writev(int fd, const struct iovec* vecs, int count);
-
-#ifdef __cplusplus
-}
+#else
+#include <sys/uio.h>
 #endif
-
-#endif
-
-#endif /* _LIBS_UTILS_UIO_H */
diff --git a/libmeminfo/include/meminfo/meminfo.h b/libmeminfo/include/meminfo/meminfo.h
index 5ee32d4..2fc7867 100644
--- a/libmeminfo/include/meminfo/meminfo.h
+++ b/libmeminfo/include/meminfo/meminfo.h
@@ -72,15 +72,10 @@
         : start(s), end(e), offset(off), flags(f), name(n) {}
     ~Vma() = default;
 
-    void clear() {
-        memset(&usage, 0, sizeof(usage));
-        memset(&wss, 0, sizeof(wss));
-    }
+    void clear() { memset(&usage, 0, sizeof(usage)); }
 
     // Memory usage of this mapping.
     MemUsage usage;
-    // Working set within this mapping.
-    MemUsage wss;
 };
 
 }  // namespace meminfo
diff --git a/libmeminfo/include/meminfo/pageacct.h b/libmeminfo/include/meminfo/pageacct.h
index 8ddaef2..8483d84 100644
--- a/libmeminfo/include/meminfo/pageacct.h
+++ b/libmeminfo/include/meminfo/pageacct.h
@@ -65,5 +65,17 @@
     ::android::base::unique_fd pageidle_fd_;
 };
 
+// Returns if the page present bit is set in the value
+// passed in.
+bool page_present(uint64_t pagemap_val);
+
+// Returns if the page swapped bit is set in the value
+// passed in.
+bool page_swapped(uint64_t pagemap_val);
+
+// Returns the page frame number (physical page) from
+// pagemap value
+uint64_t page_pfn(uint64_t pagemap_val);
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index 0bfd80f..1fb4151 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -40,6 +40,11 @@
     const MemUsage& Usage();
     const MemUsage& Wss();
 
+    // Same as Maps() except, only valid for reading working set using CONFIG_IDLE_PAGE_TRACKING
+    // support in kernel. If the kernel support doesn't exist, the function will return an empty
+    // vector.
+    const std::vector<Vma>& MapsWithPageIdle();
+
     // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
     // constant reference to the vma vector after the collection is done.
     //
@@ -64,15 +69,27 @@
     //   private_dirty
     //   SwapPss
     // All other fields of MemUsage are zeroed.
-    bool SmapsOrRollup(bool use_rollup, MemUsage* stats) const;
+    bool SmapsOrRollup(MemUsage* stats) const;
+
+    // Used to parse either of /proc/<pid>/{smaps, smaps_rollup} and record the process's
+    // Pss.
+    // Returns 'true' on success and the value of Pss in the out parameter.
+    bool SmapsOrRollupPss(uint64_t* pss) const;
 
     const std::vector<uint16_t>& SwapOffsets();
 
+    // Reads /proc/<pid>/pagemap for this process for each page within
+    // the 'vma' and stores that in 'pagemap'. It is assumed that the 'vma'
+    // is obtained by calling Maps() or 'ForEachVma' for the same object. No special checks
+    // are made to see if 'vma' is *valid*.
+    // Returns false if anything goes wrong, 'true' otherwise.
+    bool PageMap(const Vma& vma, std::vector<uint64_t>* pagemap);
+
     ~ProcMemInfo() = default;
 
   private:
-    bool ReadMaps(bool get_wss);
-    bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss);
+    bool ReadMaps(bool get_wss, bool use_pageidle = false);
+    bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle);
 
     pid_t pid_;
     bool get_wss_;
@@ -82,7 +99,6 @@
     std::vector<Vma> maps_;
 
     MemUsage usage_;
-    MemUsage wss_;
     std::vector<uint16_t> swap_offsets_;
 };
 
@@ -90,10 +106,21 @@
 // same format as /proc/<pid>/smaps. Returns 'false' if the file is malformed.
 bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback);
 
+// Returns if the kernel supports /proc/<pid>/smaps_rollup. Assumes that the
+// calling process has access to the /proc/<pid>/smaps_rollup.
+// Returns 'false' if the calling process has no permission to read the file if it exists
+// of if the file doesn't exist.
+bool IsSmapsRollupSupported(pid_t pid);
+
 // Same as ProcMemInfo::SmapsOrRollup but reads the statistics directly
 // from a file. The file MUST be in the same format as /proc/<pid>/smaps
 // or /proc/<pid>/smaps_rollup
 bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats);
 
+// Same as ProcMemInfo::SmapsOrRollupPss but reads the statistics directly
+// from a file and returns total Pss in kB. The file MUST be in the same format
+// as /proc/<pid>/smaps or /proc/<pid>/smaps_rollup
+bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss);
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
index 450a469..8388920 100644
--- a/libmeminfo/include/meminfo/sysmeminfo.h
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -58,7 +58,7 @@
     // in vmalloc area by the kernel.
     // Note that this deliberately ignores binder buffers. They are _always_
     // mapped in a process and are counted for in each process.
-    uint64_t ReadVmallocInfo(const std::string& path = "/proc/vmallocinfo");
+    uint64_t ReadVmallocInfo();
 
     // getters
     uint64_t mem_total_kb() { return mem_in_kb_[kMemTotal]; }
@@ -84,5 +84,10 @@
                      std::function<void(const std::string&, uint64_t)> store_val);
 };
 
+// Parse /proc/vmallocinfo and return total physical memory mapped
+// in vmalloc area by the kernel. Note that this deliberately ignores binder buffers. They are
+// _always_ mapped in a process and are counted for in each process.
+uint64_t ReadVmallocInfo(const std::string& path = "/proc/vmallocinfo");
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/libmeminfo/libmeminfo_benchmark.cpp b/libmeminfo/libmeminfo_benchmark.cpp
index 81fc3b3..d88919b 100644
--- a/libmeminfo/libmeminfo_benchmark.cpp
+++ b/libmeminfo/libmeminfo_benchmark.cpp
@@ -455,8 +455,7 @@
     std::string vmallocinfo =
             ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
     for (auto _ : state) {
-        SysMemInfo smi;
-        CHECK_EQ(smi.ReadVmallocInfo(vmallocinfo), 29884416);
+        CHECK_EQ(::android::meminfo::ReadVmallocInfo(vmallocinfo), 29884416);
     }
 }
 BENCHMARK(BM_VmallocInfo_new);
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 20ed3bf..ccc40d1 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -121,6 +121,23 @@
     EXPECT_EQ(proc_usage.swap / getpagesize(), swap_offsets.size());
 }
 
+TEST_F(ValidateProcMemInfo, TestPageMap) {
+    std::vector<uint64_t> pagemap;
+
+    auto vma_callback = [&](const Vma& vma) {
+        uint64_t* pmap_out;
+        size_t len;
+        ASSERT_EQ(0, pm_process_pagemap_range(proc, vma.start, vma.end, &pmap_out, &len));
+        ASSERT_TRUE(proc_mem->PageMap(vma, &pagemap));
+
+        EXPECT_EQ(len, ((vma.end - vma.start) / getpagesize()));
+        for (size_t i = 0; i < len; i++) {
+            EXPECT_EQ(pmap_out[i], pagemap[i]);
+        }
+    };
+    ASSERT_TRUE(proc_mem->ForEachVma(vma_callback));
+}
+
 class ValidateProcMemInfoWss : public ::testing::Test {
   protected:
     void SetUp() override {
@@ -284,13 +301,13 @@
     EXPECT_EQ(swap_offsets.size(), 0);
 }
 
-TEST(TestProcMemInfo, SmapsOrRollupReturn) {
-    // if /proc/<pid>/smaps_rollup file exists, .SmapsRollup() must return true;
-    // false otherwise
+TEST(TestProcMemInfo, IsSmapsSupportedTest) {
     std::string path = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid);
-    ProcMemInfo proc_mem(pid);
-    MemUsage stats;
-    EXPECT_EQ(!access(path.c_str(), F_OK), proc_mem.SmapsOrRollup(true, &stats));
+    bool supported = IsSmapsRollupSupported(pid);
+    EXPECT_EQ(!access(path.c_str(), F_OK | R_OK), supported);
+    // Second call must return what the first one returned regardless of the pid parameter.
+    // So, deliberately pass invalid pid.
+    EXPECT_EQ(supported, IsSmapsRollupSupported(-1));
 }
 
 TEST(TestProcMemInfo, SmapsOrRollupTest) {
@@ -365,6 +382,50 @@
     EXPECT_EQ(stats.swap_pss, 70);
 }
 
+TEST(TestProcMemInfo, SmapsOrRollupPssRollupTest) {
+    // This is a made up smaps for the test
+    std::string smaps =
+            R"smaps(12c00000-13440000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
+Name:           [anon:dalvik-main space (region space)]
+Size:               8448 kB
+KernelPageSize:        4 kB
+MMUPageSize:           4 kB
+Rss:                2652 kB
+Pss:                2652 kB
+Shared_Clean:        840 kB
+Shared_Dirty:         40 kB
+Private_Clean:        84 kB
+Private_Dirty:      2652 kB
+Referenced:         2652 kB
+Anonymous:          2652 kB
+AnonHugePages:         0 kB
+ShmemPmdMapped:        0 kB
+Shared_Hugetlb:        0 kB
+Private_Hugetlb:       0 kB
+Swap:                102 kB
+SwapPss:              70 kB
+Locked:             2652 kB
+VmFlags: rd wr mr mw me ac 
+)smaps";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(smaps, tf.fd));
+
+    uint64_t pss;
+    ASSERT_EQ(SmapsOrRollupPssFromFile(tf.path, &pss), true);
+    EXPECT_EQ(pss, 2652);
+}
+
+TEST(TestProcMemInfo, SmapsOrRollupPssSmapsTest) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+
+    uint64_t pss;
+    ASSERT_EQ(SmapsOrRollupPssFromFile(path, &pss), true);
+    EXPECT_EQ(pss, 19119);
+}
+
 TEST(TestProcMemInfo, ForEachVmaFromFileTest) {
     std::string exec_dir = ::android::base::GetExecutableDirectory();
     std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
@@ -374,6 +435,9 @@
     auto collect_vmas = [&](const Vma& v) { vmas.push_back(v); };
     ASSERT_TRUE(ForEachVmaFromFile(path, collect_vmas));
 
+    // We should get a total of 6 vmas
+    ASSERT_EQ(vmas.size(), 6);
+
     // Expect values to be equal to what we have in testdata1/smaps_short
     // Check for sizes first
     ASSERT_EQ(vmas[0].usage.vss, 32768);
@@ -468,6 +532,8 @@
     auto vmas = proc_mem.Smaps(path);
 
     ASSERT_FALSE(vmas.empty());
+    // We should get a total of 6 vmas
+    ASSERT_EQ(vmas.size(), 6);
 
     // Expect values to be equal to what we have in testdata1/smaps_short
     // Check for sizes first
@@ -795,8 +861,7 @@
     ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
     std::string file = std::string(tf.path);
 
-    SysMemInfo smi;
-    EXPECT_EQ(smi.ReadVmallocInfo(file), 0);
+    EXPECT_EQ(ReadVmallocInfo(file), 0);
 }
 
 TEST(SysMemInfoParser, TestVmallocInfoKernel) {
@@ -808,8 +873,7 @@
     ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
     std::string file = std::string(tf.path);
 
-    SysMemInfo smi;
-    EXPECT_EQ(smi.ReadVmallocInfo(file), getpagesize());
+    EXPECT_EQ(ReadVmallocInfo(file), getpagesize());
 }
 
 TEST(SysMemInfoParser, TestVmallocInfoModule) {
@@ -821,8 +885,7 @@
     ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
     std::string file = std::string(tf.path);
 
-    SysMemInfo smi;
-    EXPECT_EQ(smi.ReadVmallocInfo(file), 6 * getpagesize());
+    EXPECT_EQ(ReadVmallocInfo(file), 6 * getpagesize());
 }
 
 TEST(SysMemInfoParser, TestVmallocInfoAll) {
@@ -839,8 +902,7 @@
     ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
     std::string file = std::string(tf.path);
 
-    SysMemInfo smi;
-    EXPECT_EQ(smi.ReadVmallocInfo(file), 7 * getpagesize());
+    EXPECT_EQ(ReadVmallocInfo(file), 7 * getpagesize());
 }
 
 int main(int argc, char** argv) {
diff --git a/libmeminfo/pageacct.cpp b/libmeminfo/pageacct.cpp
index 887a74d..0a26c08 100644
--- a/libmeminfo/pageacct.cpp
+++ b/libmeminfo/pageacct.cpp
@@ -138,5 +138,18 @@
     return !!(idle_bits & (1ULL << (pfn % 64)));
 }
 
+// Public methods
+bool page_present(uint64_t pagemap_val) {
+    return PAGE_PRESENT(pagemap_val);
+}
+
+bool page_swapped(uint64_t pagemap_val) {
+    return PAGE_SWAPPED(pagemap_val);
+}
+
+uint64_t page_pfn(uint64_t pagemap_val) {
+    return PAGE_PFN(pagemap_val);
+}
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index 1f8db1a..069b6b3 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <fstream>
 #include <iostream>
 #include <memory>
@@ -120,6 +121,14 @@
     return maps_;
 }
 
+const std::vector<Vma>& ProcMemInfo::MapsWithPageIdle() {
+    if (maps_.empty() && !ReadMaps(get_wss_, true)) {
+        LOG(ERROR) << "Failed to read maps with page idle for Process " << pid_;
+    }
+
+    return maps_;
+}
+
 const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
     if (!maps_.empty()) {
         return maps_;
@@ -157,14 +166,14 @@
     if (!get_wss_) {
         LOG(WARNING) << "Trying to read process working set for " << pid_
                      << " using invalid object";
-        return wss_;
+        return usage_;
     }
 
     if (maps_.empty() && !ReadMaps(get_wss_)) {
         LOG(ERROR) << "Failed to get working set for Process " << pid_;
     }
 
-    return wss_;
+    return usage_;
 }
 
 bool ProcMemInfo::ForEachVma(const VmaCallback& callback) {
@@ -172,11 +181,17 @@
     return ForEachVmaFromFile(path, callback);
 }
 
-bool ProcMemInfo::SmapsOrRollup(bool use_rollup, MemUsage* stats) const {
-    std::string path = ::android::base::StringPrintf("/proc/%d/%s", pid_,
-                                                     use_rollup ? "smaps_rollup" : "smaps");
+bool ProcMemInfo::SmapsOrRollup(MemUsage* stats) const {
+    std::string path = ::android::base::StringPrintf(
+            "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
     return SmapsOrRollupFromFile(path, stats);
-};
+}
+
+bool ProcMemInfo::SmapsOrRollupPss(uint64_t* pss) const {
+    std::string path = ::android::base::StringPrintf(
+            "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
+    return SmapsOrRollupPssFromFile(path, pss);
+}
 
 const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() {
     if (get_wss_) {
@@ -192,7 +207,34 @@
     return swap_offsets_;
 }
 
-bool ProcMemInfo::ReadMaps(bool get_wss) {
+bool ProcMemInfo::PageMap(const Vma& vma, std::vector<uint64_t>* pagemap) {
+    pagemap->clear();
+    std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
+    ::android::base::unique_fd pagemap_fd(
+            TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (pagemap_fd < 0) {
+        PLOG(ERROR) << "Failed to open " << pagemap_file;
+        return false;
+    }
+
+    uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
+    pagemap->reserve(nr_pages);
+
+    uint64_t idx = vma.start / getpagesize();
+    uint64_t last = idx + nr_pages;
+    uint64_t val;
+    for (; idx < last; idx++) {
+        if (pread64(pagemap_fd, &val, sizeof(uint64_t), idx * sizeof(uint64_t)) < 0) {
+            PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+            return false;
+        }
+        pagemap->emplace_back(val);
+    }
+
+    return true;
+}
+
+bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle) {
     // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
     // running for the lifetime of the system can recycle the objects and don't have to
     // unnecessarily retain and update this object in memory (which can get significantly large).
@@ -222,23 +264,19 @@
     }
 
     for (auto& vma : maps_) {
-        if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss)) {
+        if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss, use_pageidle)) {
             LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
                        << vma.end << "]";
             maps_.clear();
             return false;
         }
-        if (get_wss) {
-            add_mem_usage(&wss_, vma.wss);
-        } else {
-            add_mem_usage(&usage_, vma.usage);
-        }
+        add_mem_usage(&usage_, vma.usage);
     }
 
     return true;
 }
 
-bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
+bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) {
     PageAcct& pinfo = PageAcct::Instance();
     uint64_t pagesz = getpagesize();
     uint64_t num_pages = (vma.end - vma.start) / pagesz;
@@ -251,6 +289,13 @@
         return false;
     }
 
+    if (get_wss && use_pageidle) {
+        if (!pinfo.InitPageAcct(true)) {
+            LOG(ERROR) << "Failed to init idle page accounting";
+            return false;
+        }
+    }
+
     std::unique_ptr<uint64_t[]> pg_flags(new uint64_t[num_pages]);
     std::unique_ptr<uint64_t[]> pg_counts(new uint64_t[num_pages]);
     for (uint64_t i = 0; i < num_pages; ++i) {
@@ -293,38 +338,28 @@
         bool is_private = (pg_counts[i] == 1);
         // Working set
         if (get_wss) {
-            bool is_referenced = !!(pg_flags[i] & (1 << KPF_REFERENCED));
+            bool is_referenced = use_pageidle ? (pinfo.IsPageIdle(page_frame) == 1)
+                                              : !!(pg_flags[i] & (1 << KPF_REFERENCED));
             if (!is_referenced) {
                 continue;
             }
             // This effectively makes vss = rss for the working set is requested.
             // The libpagemap implementation returns vss > rss for
             // working set, which doesn't make sense.
-            vma.wss.vss += pagesz;
-            vma.wss.rss += pagesz;
-            vma.wss.uss += is_private ? pagesz : 0;
-            vma.wss.pss += pagesz / pg_counts[i];
-            if (is_private) {
-                vma.wss.private_dirty += is_dirty ? pagesz : 0;
-                vma.wss.private_clean += is_dirty ? 0 : pagesz;
-            } else {
-                vma.wss.shared_dirty += is_dirty ? pagesz : 0;
-                vma.wss.shared_clean += is_dirty ? 0 : pagesz;
-            }
+            vma.usage.vss += pagesz;
+        }
+
+        vma.usage.rss += pagesz;
+        vma.usage.uss += is_private ? pagesz : 0;
+        vma.usage.pss += pagesz / pg_counts[i];
+        if (is_private) {
+            vma.usage.private_dirty += is_dirty ? pagesz : 0;
+            vma.usage.private_clean += is_dirty ? 0 : pagesz;
         } else {
-            vma.usage.rss += pagesz;
-            vma.usage.uss += is_private ? pagesz : 0;
-            vma.usage.pss += pagesz / pg_counts[i];
-            if (is_private) {
-                vma.usage.private_dirty += is_dirty ? pagesz : 0;
-                vma.usage.private_clean += is_dirty ? 0 : pagesz;
-            } else {
-                vma.usage.shared_dirty += is_dirty ? pagesz : 0;
-                vma.usage.shared_clean += is_dirty ? 0 : pagesz;
-            }
+            vma.usage.shared_dirty += is_dirty ? pagesz : 0;
+            vma.usage.shared_clean += is_dirty ? 0 : pagesz;
         }
     }
-
     return true;
 }
 
@@ -338,8 +373,9 @@
     char* line = nullptr;
     bool parsing_vma = false;
     ssize_t line_len;
+    size_t line_alloc = 0;
     Vma vma;
-    while ((line_len = getline(&line, 0, fp.get())) > 0) {
+    while ((line_len = getline(&line, &line_alloc, fp.get())) > 0) {
         // Make sure the line buffer terminates like a C string for ReadMapFile
         line[line_len] = '\0';
 
@@ -382,15 +418,41 @@
     return true;
 }
 
+enum smaps_rollup_support { UNTRIED, SUPPORTED, UNSUPPORTED };
+
+static std::atomic<smaps_rollup_support> g_rollup_support = UNTRIED;
+
+bool IsSmapsRollupSupported(pid_t pid) {
+    // Similar to OpenSmapsOrRollup checks from android_os_Debug.cpp, except
+    // the method only checks if rollup is supported and returns the status
+    // right away.
+    enum smaps_rollup_support rollup_support = g_rollup_support.load(std::memory_order_relaxed);
+    if (rollup_support != UNTRIED) {
+        return rollup_support == SUPPORTED;
+    }
+    std::string rollup_file = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid);
+    if (access(rollup_file.c_str(), F_OK | R_OK)) {
+        // No check for errno = ENOENT necessary here. The caller MUST fallback to
+        // using /proc/<pid>/smaps instead anyway.
+        g_rollup_support.store(UNSUPPORTED, std::memory_order_relaxed);
+        return false;
+    }
+
+    g_rollup_support.store(SUPPORTED, std::memory_order_relaxed);
+    LOG(INFO) << "Using smaps_rollup for pss collection";
+    return true;
+}
+
 bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats) {
     auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (fp == nullptr) {
         return false;
     }
 
-    char line[1024];
+    char* line = nullptr;
+    size_t line_alloc = 0;
     stats->clear();
-    while (fgets(line, sizeof(line), fp.get()) != nullptr) {
+    while (getline(&line, &line_alloc, fp.get()) > 0) {
         switch (line[0]) {
             case 'P':
                 if (strncmp(line, "Pss:", 4) == 0) {
@@ -423,6 +485,28 @@
         }
     }
 
+    // free getline() managed buffer
+    free(line);
+    return true;
+}
+
+bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss) {
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return false;
+    }
+    *pss = 0;
+    char* line = nullptr;
+    size_t line_alloc = 0;
+    while (getline(&line, &line_alloc, fp.get()) > 0) {
+        uint64_t v;
+        if (sscanf(line, "Pss: %" SCNu64 " kB", &v) == 1) {
+            *pss += v;
+        }
+    }
+
+    // free getline() managed buffer
+    free(line);
     return true;
 }
 
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
index 8fd18d0..5cfa6c3 100644
--- a/libmeminfo/sysmeminfo.cpp
+++ b/libmeminfo/sysmeminfo.cpp
@@ -79,36 +79,8 @@
     });
 }
 
-uint64_t SysMemInfo::ReadVmallocInfo(const std::string& path) {
-    uint64_t vmalloc_total = 0;
-    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
-    if (fp == nullptr) {
-        return vmalloc_total;
-    }
-
-    char line[1024];
-    while (fgets(line, 1024, fp.get()) != nullptr) {
-        // We are looking for lines like
-        // 0x0000000000000000-0x0000000000000000   12288 drm_property_create_blob+0x44/0xec pages=2
-        // vmalloc 0x0000000000000000-0x0000000000000000    8192
-        // wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc Notice that if the caller is
-        // coming from a module, the kernel prints and extra "[module_name]" after the address and
-        // the symbol of the call site. This means we can't use the old sscanf() method of getting
-        // the # of pages.
-        char* p_start = strstr(line, "pages=");
-        if (p_start == nullptr) {
-            // we didn't find anything
-            continue;
-        }
-
-        p_start = strtok(p_start, " ");
-        long nr_pages;
-        if (sscanf(p_start, "pages=%ld", &nr_pages) == 1) {
-            vmalloc_total += (nr_pages * getpagesize());
-        }
-    }
-
-    return vmalloc_total;
+uint64_t SysMemInfo::ReadVmallocInfo() {
+    return ::android::meminfo::ReadVmallocInfo();
 }
 
 // TODO: Delete this function if it can't match up with the c-like implementation below.
@@ -263,5 +235,41 @@
     return false;
 }
 
+// Public methods
+uint64_t ReadVmallocInfo(const std::string& path) {
+    uint64_t vmalloc_total = 0;
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (fp == nullptr) {
+        return vmalloc_total;
+    }
+
+    char* line = nullptr;
+    size_t line_alloc = 0;
+    while (getline(&line, &line_alloc, fp.get()) > 0) {
+        // We are looking for lines like
+        //
+        // 0x0000000000000000-0x0000000000000000   12288 drm_property_create_blob+0x44/0xec pages=2 vmalloc
+        // 0x0000000000000000-0x0000000000000000    8192 wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc
+        //
+        // Notice that if the caller is coming from a module, the kernel prints and extra
+        // "[module_name]" after the address and the symbol of the call site. This means we can't
+        // use the old sscanf() method of getting the # of pages.
+        char* p_start = strstr(line, "pages=");
+        if (p_start == nullptr) {
+            // we didn't find anything
+            continue;
+        }
+
+        uint64_t nr_pages;
+        if (sscanf(p_start, "pages=%" SCNu64 "", &nr_pages) == 1) {
+            vmalloc_total += (nr_pages * getpagesize());
+        }
+    }
+
+    free(line);
+
+    return vmalloc_total;
+}
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index c852bbb..2e89c41 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 cc_binary {
-    name: "librank2",
+    name: "librank",
     cflags: [
         "-Wall",
         "-Werror",
@@ -27,7 +27,7 @@
 }
 
 cc_binary {
-    name: "procmem2",
+    name: "procmem",
     cflags: [
         "-Wall",
         "-Werror",
@@ -41,7 +41,7 @@
 }
 
 cc_binary {
-    name: "procrank2",
+    name: "procrank",
     cflags: [
         "-Wall",
         "-Werror",
@@ -55,7 +55,7 @@
 }
 
 cc_binary {
-    name: "showmap2",
+    name: "showmap",
     cflags: [
         "-Wall",
         "-Werror",
@@ -67,3 +67,17 @@
         "libmeminfo",
     ],
 }
+
+cc_binary {
+    name: "wsstop",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    srcs: ["wsstop.cpp"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libmeminfo",
+    ],
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
index b9b174d..47881ed 100644
--- a/libmeminfo/tools/procmem.cpp
+++ b/libmeminfo/tools/procmem.cpp
@@ -98,7 +98,7 @@
     std::stringstream ss;
     print_header(ss);
     for (auto& vma : maps) {
-        const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
+        const MemUsage& vma_stats = vma.usage;
         if (hide_zeroes && vma_stats.rss == 0) {
             continue;
         }
@@ -116,14 +116,14 @@
 int main(int argc, char* argv[]) {
     int opt;
     auto pss_sort = [](const Vma& a, const Vma& b) {
-        uint64_t pss_a = show_wss ? a.wss.pss : a.usage.pss;
-        uint64_t pss_b = show_wss ? b.wss.pss : b.usage.pss;
+        uint64_t pss_a = a.usage.pss;
+        uint64_t pss_b = b.usage.pss;
         return pss_a > pss_b;
     };
 
     auto uss_sort = [](const Vma& a, const Vma& b) {
-        uint64_t uss_a = show_wss ? a.wss.uss : a.usage.uss;
-        uint64_t uss_b = show_wss ? b.wss.uss : b.usage.uss;
+        uint64_t uss_a = a.usage.uss;
+        uint64_t uss_b = b.usage.uss;
         return uss_a > uss_b;
     };
 
@@ -182,7 +182,7 @@
     }
 
     ProcMemInfo proc(pid, show_wss);
-    const MemUsage& proc_stats = show_wss ? proc.Wss() : proc.Usage();
+    const MemUsage& proc_stats = proc.Usage();
     std::vector<Vma> maps(proc.Maps());
     if (sort_func != nullptr) {
         std::sort(maps.begin(), maps.end(), sort_func);
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
index a751722..21a684c 100644
--- a/libmeminfo/tools/procrank.cpp
+++ b/libmeminfo/tools/procrank.cpp
@@ -465,7 +465,7 @@
         }
 
         // Skip processes with no memory mappings
-        uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss;
+        uint64_t vss = proc.Usage().vss;
         if (vss == 0) return true;
 
         // collect swap_offset counts from all processes in 1st pass
diff --git a/libmeminfo/tools/wsstop.cpp b/libmeminfo/tools/wsstop.cpp
new file mode 100644
index 0000000..368d04e
--- /dev/null
+++ b/libmeminfo/tools/wsstop.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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 <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+
+// Global options
+static int32_t g_delay = 0;
+static int32_t g_total = 2;
+static pid_t g_pid = -1;
+
+[[noreturn]] static void usage(int exit_status) {
+    fprintf(stderr,
+            "%s [-d DELAY_BETWEEN_EACH_SAMPLE] [-n REFRESH_TOTAL] PID\n"
+            "-d\tdelay between each working set sample (default 0)\n"
+            "-n\ttotal number of refreshes before we exit (default 2)\n",
+            getprogname());
+
+    exit(exit_status);
+}
+
+static void print_header() {
+    const char* addr1 = "           start              end ";
+    const char* addr2 = "            addr             addr ";
+
+    printf("%s  virtual                        shared    shared   private   private\n", addr1);
+    printf("%s     size       RSS       PSS     clean     dirty     clean     dirty      swap   "
+           "swapPSS",
+           addr2);
+    printf(" object\n");
+}
+
+static void print_divider() {
+    printf("---------------- ---------------- ");
+    printf("--------- --------- --------- --------- --------- --------- --------- --------- "
+           "--------- ");
+    printf("------------------------------\n");
+}
+
+static void print_vma(const Vma& v) {
+    printf("%16" PRIx64 " %16" PRIx64 " ", v.start, v.end);
+    printf("%8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64
+           "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K ",
+           v.usage.vss / 1024, v.usage.rss / 1024, v.usage.pss / 1024, v.usage.shared_clean / 1024,
+           v.usage.shared_dirty / 1024, v.usage.private_clean / 1024, v.usage.private_dirty / 1024,
+           v.usage.swap / 1024, v.usage.swap_pss / 1024);
+    printf("%s\n", v.name.c_str());
+}
+
+static bool same_vma(const Vma& cur, const Vma& last) {
+    return (cur.start == last.start && cur.end == last.end && cur.name == last.name &&
+            cur.flags == last.flags && cur.offset == last.offset);
+}
+
+static Vma diff_vma_params(const Vma& cur, const Vma& last) {
+    Vma res;
+    res.usage.shared_clean = cur.usage.shared_clean > last.usage.shared_clean
+                                     ? cur.usage.shared_clean - last.usage.shared_clean
+                                     : 0;
+    res.usage.shared_dirty = cur.usage.shared_dirty > last.usage.shared_dirty
+                                     ? cur.usage.shared_dirty - last.usage.shared_dirty
+                                     : 0;
+    res.usage.private_clean = cur.usage.private_clean > last.usage.private_clean
+                                      ? cur.usage.private_clean - last.usage.private_clean
+                                      : 0;
+    res.usage.private_dirty = cur.usage.private_dirty > last.usage.private_dirty
+                                      ? cur.usage.private_dirty - last.usage.private_dirty
+                                      : 0;
+
+    res.usage.rss = cur.usage.rss > last.usage.rss ? cur.usage.rss - last.usage.rss : 0;
+    res.usage.pss = cur.usage.pss > last.usage.pss ? cur.usage.pss - last.usage.pss : 0;
+    res.usage.uss = cur.usage.uss > last.usage.uss ? cur.usage.uss - last.usage.uss : 0;
+    res.usage.swap = cur.usage.swap > last.usage.swap ? cur.usage.swap - last.usage.swap : 0;
+    res.usage.swap_pss =
+            cur.usage.swap_pss > last.usage.swap_pss ? cur.usage.swap_pss - last.usage.swap_pss : 0;
+
+    // set vma properties to the same as the current one.
+    res.start = cur.start;
+    res.end = cur.end;
+    res.offset = cur.offset;
+    res.flags = cur.flags;
+    res.name = cur.name;
+    return res;
+}
+
+static void diff_workingset(std::vector<Vma>& wss, std::vector<Vma>& old, std::vector<Vma>* res) {
+    res->clear();
+    auto vma_sorter = [](const Vma& a, const Vma& b) { return a.start < b.start; };
+    std::sort(wss.begin(), wss.end(), vma_sorter);
+    std::sort(old.begin(), old.end(), vma_sorter);
+    if (old.empty()) {
+        *res = wss;
+        return;
+    }
+
+    for (auto& i : wss) {
+        bool found_same_vma = false;
+        // TODO: This is highly inefficient, fix it if it takes
+        // too long. Worst case will be system_server
+        for (auto& j : old) {
+            if (same_vma(i, j)) {
+                res->emplace_back(diff_vma_params(i, j));
+                found_same_vma = true;
+                break;
+            }
+        }
+
+        if (!found_same_vma) {
+            res->emplace_back(i);
+        }
+    }
+
+    std::sort(res->begin(), res->end(), vma_sorter);
+    return;
+}
+
+static int workingset() {
+    std::vector<Vma> last_wss = {};
+    std::vector<Vma> diff_wss = {};
+    uint32_t nr_refresh = 0;
+
+    while (true) {
+        std::unique_ptr<ProcMemInfo> proc_mem = std::make_unique<ProcMemInfo>(g_pid, true);
+        std::vector<Vma> wss = proc_mem->MapsWithPageIdle();
+
+        diff_workingset(wss, last_wss, &diff_wss);
+        diff_wss.erase(std::remove_if(diff_wss.begin(), diff_wss.end(),
+                                      [](const auto& v) { return v.usage.rss == 0; }),
+                       diff_wss.end());
+        if ((nr_refresh % 5) == 0) {
+            print_header();
+            print_divider();
+        }
+
+        for (const auto& v : diff_wss) {
+            print_vma(v);
+        }
+
+        nr_refresh++;
+        if (nr_refresh == g_total) {
+            break;
+        }
+
+        last_wss = wss;
+        sleep(g_delay);
+        print_divider();
+    }
+
+    return 0;
+}
+
+int main(int argc, char* argv[]) {
+    struct option longopts[] = {
+            {"help", no_argument, nullptr, 'h'},
+            {0, 0, nullptr, 0},
+    };
+
+    int opt;
+    while ((opt = getopt_long(argc, argv, "d:n:h", longopts, nullptr)) != -1) {
+        switch (opt) {
+            case 'd':
+                g_delay = atoi(optarg);
+                break;
+            case 'n':
+                g_total = atoi(optarg);
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    if ((argc - 1) < optind) {
+        fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
+        usage(EXIT_FAILURE);
+    }
+
+    g_pid = atoi(argv[optind]);
+    if (g_pid <= 0) {
+        fprintf(stderr, "Invalid process id %s\n", argv[optind]);
+        usage(EXIT_FAILURE);
+    }
+
+    if (!::android::meminfo::PageAcct::KernelHasPageIdle()) {
+        fprintf(stderr, "Missing support for Idle page tracking in the kernel\n");
+        return 0;
+    }
+
+    return workingset();
+}
diff --git a/libmemtrack/.clang-format b/libmemtrack/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmemtrack/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 0955633..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -28,10 +28,11 @@
 
 cc_binary {
     name: "memtrack_test",
-    srcs: ["memtrack_test.c"],
+    srcs: ["memtrack_test.cpp"],
+    static_libs: ["libc++fs"],
     shared_libs: [
+        "libbase",
         "libmemtrack",
-        "libpagemap",
     ],
     cflags: [
         "-Wall",
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
deleted file mode 100644
index 77c935e..0000000
--- a/libmemtrack/memtrack_test.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2013 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <memtrack/memtrack.h>
-
-#include <pagemap/pagemap.h>
-
-#define DIV_ROUND_UP(x,y) (((x) + (y) - 1) / (y))
-
-static int getprocname(pid_t pid, char *buf, int len) {
-    char *filename;
-    FILE *f;
-    int rc = 0;
-    static const char* unknown_cmdline = "<unknown>";
-
-    if (len <= 0) {
-        return -1;
-    }
-
-    if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) {
-        rc = 1;
-        goto exit;
-    }
-
-    f = fopen(filename, "r");
-    if (f == NULL) {
-        rc = 2;
-        goto releasefilename;
-    }
-
-    if (fgets(buf, len, f) == NULL) {
-        rc = 3;
-        goto closefile;
-    }
-
-closefile:
-    (void) fclose(f);
-releasefilename:
-    free(filename);
-exit:
-    if (rc != 0) {
-        /*
-         * The process went away before we could read its process name. Try
-         * to give the user "<unknown>" here, but otherwise they get to look
-         * at a blank.
-         */
-        if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
-            rc = 4;
-        }
-    }
-
-    return rc;
-}
-
-int main(int argc, char *argv[])
-{
-    int ret;
-    pm_kernel_t *ker;
-    size_t num_procs;
-    pid_t *pids;
-    struct memtrack_proc *p;
-    size_t i;
-
-    (void)argc;
-    (void)argv;
-
-    ret = pm_kernel_create(&ker);
-    if (ret) {
-        fprintf(stderr, "Error creating kernel interface -- "
-                        "does this kernel have pagemap?\n");
-        exit(EXIT_FAILURE);
-    }
-
-    ret = pm_kernel_pids(ker, &pids, &num_procs);
-    if (ret) {
-        fprintf(stderr, "Error listing processes.\n");
-        exit(EXIT_FAILURE);
-    }
-
-    p = memtrack_proc_new();
-    if (ret) {
-        fprintf(stderr, "failed to create memtrack process handle\n");
-        exit(EXIT_FAILURE);
-    }
-
-    for (i = 0; i < num_procs; i++) {
-        pid_t pid = pids[i];
-        char cmdline[256];
-        size_t v1;
-        size_t v2;
-        size_t v3;
-        size_t v4;
-        size_t v5;
-        size_t v6;
-
-        getprocname(pid, cmdline, (int)sizeof(cmdline));
-
-        ret = memtrack_proc_get(p, pid);
-        if (ret) {
-            fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n",
-                    pid, strerror(-ret), ret);
-            continue;
-        }
-
-        v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
-        v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
-        v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
-        v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
-        v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
-        v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
-
-        if (v1 | v2 | v3 | v4 | v5 | v6) {
-            printf("%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid,
-                   v1, v2, v3, v4, v5, v6, cmdline);
-        }
-    }
-
-    memtrack_proc_destroy(p);
-
-    return 0;
-}
diff --git a/libmemtrack/memtrack_test.cpp b/libmemtrack/memtrack_test.cpp
new file mode 100644
index 0000000..aeeaf24
--- /dev/null
+++ b/libmemtrack/memtrack_test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <filesystem>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <memtrack/memtrack.h>
+
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+
+static void getprocname(pid_t pid, std::string* name) {
+    std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+    if (!::android::base::ReadFileToString(fname, name)) {
+        fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+        *name = "<unknown>";
+    }
+}
+
+int main(int /* argc */, char** /* argv */) {
+    int ret;
+    struct memtrack_proc* p;
+    std::vector<pid_t> pids;
+
+    p = memtrack_proc_new();
+    if (p == nullptr) {
+        fprintf(stderr, "failed to create memtrack process handle\n");
+        exit(EXIT_FAILURE);
+    }
+
+    for (auto& de : std::filesystem::directory_iterator("/proc")) {
+        if (!std::filesystem::is_directory(de.status())) {
+            continue;
+        }
+
+        pid_t pid;
+        if (!::android::base::ParseInt(de.path().filename().string(), &pid)) {
+            continue;
+        }
+        pids.emplace_back(pid);
+    }
+
+    for (auto& pid : pids) {
+        size_t v1;
+        size_t v2;
+        size_t v3;
+        size_t v4;
+        size_t v5;
+        size_t v6;
+        std::string cmdline;
+
+        getprocname(pid, &cmdline);
+
+        ret = memtrack_proc_get(p, pid);
+        if (ret) {
+            fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n", pid, strerror(-ret),
+                    ret);
+            continue;
+        }
+
+        v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
+        v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
+        v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
+        v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
+        v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
+        v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
+
+        if (v1 | v2 | v3 | v4 | v5 | v6) {
+            fprintf(stdout, "%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid, v1, v2, v3, v4, v5, v6,
+                    cmdline.c_str());
+        }
+    }
+
+    memtrack_proc_destroy(p);
+
+    return ret;
+}
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 59cd033..c54570e 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -17,6 +17,15 @@
         "liblog",
     ],
 
+    target: {
+        android: {
+            version_script: "libnativebridge.map.txt",
+        },
+        linux: {
+            version_script: "libnativebridge.map.txt",
+        },
+    },
+
     stubs: {
         symbol_file: "libnativebridge.map.txt",
         versions: ["1"],
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
index f2cc942..6f0824b 100644
--- a/libnativebridge/OWNERS
+++ b/libnativebridge/OWNERS
@@ -1 +1,2 @@
 dimitry@google.com
+eaeltsin@google.com
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 17983bc..2802d36 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -24,3 +24,10 @@
         "vndksp.libraries.txt",
     ],
 }
+
+cc_library_headers {
+    name: "libnativeloader-dummy-headers",
+
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
index 5c28a9f..f735653 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1,2 +1,6 @@
 dimitry@google.com
 jiyong@google.com
+ngeoffray@google.com
+oth@google.com
+mast@google.com
+rpl@google.com
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index 43c9329..2d6ce85 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -18,6 +18,7 @@
 #define __ANDROID_DLEXT_NAMESPACES_H__
 
 #include <android/dlext.h>
+#include <stdbool.h>
 
 __BEGIN_DECLS
 
@@ -84,12 +85,9 @@
  * If a library or any of its dependencies are outside of the permitted_when_isolated_path
  * and search_path, and it is not part of the public namespace dlopen will fail.
  */
-extern struct android_namespace_t* android_create_namespace(const char* name,
-                                                            const char* ld_library_path,
-                                                            const char* default_library_path,
-                                                            uint64_t type,
-                                                            const char* permitted_when_isolated_path,
-                                                            android_namespace_t* parent);
+extern struct android_namespace_t* android_create_namespace(
+    const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+    const char* permitted_when_isolated_path, struct android_namespace_t* parent);
 
 /*
  * Creates a link between namespaces. Every link has list of sonames of
@@ -107,24 +105,11 @@
  *      step will not go deeper into linked namespaces for this library but
  *      will do so for DT_NEEDED libraries.
  */
-extern bool android_link_namespaces(android_namespace_t* from,
-                                    android_namespace_t* to,
+extern bool android_link_namespaces(struct android_namespace_t* from,
+                                    struct android_namespace_t* to,
                                     const char* shared_libs_sonames);
 
-/*
- * Get the default library search path.
- * The path will be copied into buffer, which must have space for at least
- * buffer_size chars. Elements are separated with ':', and the path will always
- * be null-terminated.
- *
- * If buffer_size is too small to hold the entire default search path and the
- * null terminator, this function will abort. There is currently no way to find
- * out what the required buffer size is. At the time of this writing, PATH_MAX
- * is sufficient and used by all callers of this function.
- */
-extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
-
-extern android_namespace_t* android_get_exported_namespace(const char* name);
+extern struct android_namespace_t* android_get_exported_namespace(const char* name);
 
 __END_DECLS
 
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index c1d9d2a..260f655 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -17,14 +17,21 @@
 #ifndef NATIVE_LOADER_H_
 #define NATIVE_LOADER_H_
 
-#include "jni.h"
+#include <stdbool.h>
 #include <stdint.h>
-#include <string>
+#include "jni.h"
 #if defined(__ANDROID__)
 #include <android/dlext.h>
 #endif
 
+#ifdef __cplusplus
 namespace android {
+extern "C" {
+#endif  // __cplusplus
+
+// README: the char** error message parameter being passed
+// to the methods below need to be freed through calling NativeLoaderFreeErrorMessage.
+// It's the caller's responsibility to call that method.
 
 __attribute__((visibility("default")))
 void InitializeNativeLoader();
@@ -38,42 +45,39 @@
                                    jstring library_path,
                                    jstring permitted_path);
 
-__attribute__((visibility("default")))
-void* OpenNativeLibrary(JNIEnv* env,
-                        int32_t target_sdk_version,
-                        const char* path,
-                        jobject class_loader,
-                        jstring library_path,
-                        bool* needs_native_bridge,
-                        std::string* error_msg);
+__attribute__((visibility("default"))) void* OpenNativeLibrary(
+    JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
+    const char* caller_location, jstring library_path, bool* needs_native_bridge, char** error_msg);
 
 __attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle,
                                                                const bool needs_native_bridge,
-                                                               std::string* error_msg);
+                                                               char** error_msg);
+
+__attribute__((visibility("default"))) void NativeLoaderFreeErrorMessage(char* msg);
 
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
 // there is no namespace associated with the class_loader.
 // TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
-__attribute__((visibility("default")))
-android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
-// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
-class NativeLoaderNamespace;
-__attribute__((visibility("default")))
-NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(
+__attribute__((visibility("default"))) struct android_namespace_t* FindNamespaceByClassLoader(
     JNIEnv* env, jobject class_loader);
+// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
+struct NativeLoaderNamespace;
+__attribute__((visibility("default"))) struct NativeLoaderNamespace*
+FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
 // Load library.  Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does
 // not require access to JNIEnv either.
-__attribute__((visibility("default")))
-void* OpenNativeLibrary(NativeLoaderNamespace* ns,
-                        const char* path,
-                        bool* needs_native_bridge,
-                        std::string* error_msg);
+__attribute__((visibility("default"))) void* OpenNativeLibraryInNamespace(
+    struct NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
+    char** error_msg);
 #endif
 
 __attribute__((visibility("default")))
 void ResetNativeLoader();
 
-};  // namespace android
+#ifdef __cplusplus
+}  // extern "C"
+}  // namespace android
+#endif  // __cplusplus
 
 #endif  // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/libnativeloader.map.txt b/libnativeloader/libnativeloader.map.txt
new file mode 100644
index 0000000..40c30bd
--- /dev/null
+++ b/libnativeloader/libnativeloader.map.txt
@@ -0,0 +1,31 @@
+#
+# 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): Prune these uses once the runtime APEX is complete.
+LIBNATIVELOADER_1 {
+  global:
+    OpenNativeLibrary;
+    InitializeNativeLoader;
+    ResetNativeLoader;
+    CloseNativeLibrary;
+    OpenNativeLibraryInNamespace;
+    FindNamespaceByClassLoader;
+    FindNativeLoaderNamespaceByClassLoader;
+    CreateClassLoaderNamespace;
+    NativeLoaderFreeErrorMessage;
+  local:
+    *;
+};
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index de7ea08..ad967db 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -52,7 +52,7 @@
 namespace android {
 
 #if defined(__ANDROID__)
-class NativeLoaderNamespace {
+struct NativeLoaderNamespace {
  public:
   NativeLoaderNamespace()
       : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
@@ -119,6 +119,8 @@
 // This list includes all directories app is allowed to access this way.
 static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
 
+static constexpr const char* kApexPath = "/apex/";
+
 static bool is_debuggable() {
   char debuggable[PROP_VALUE_MAX];
   property_get("ro.debuggable", debuggable, "0");
@@ -151,14 +153,9 @@
  public:
   LibraryNamespaces() : initialized_(false) { }
 
-  NativeLoaderNamespace* Create(JNIEnv* env,
-                                uint32_t target_sdk_version,
-                                jobject class_loader,
-                                bool is_shared,
-                                bool is_for_vendor,
-                                jstring java_library_path,
-                                jstring java_permitted_path,
-                                std::string* error_msg) {
+  NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
+                                bool is_shared, bool is_for_vendor, jstring java_library_path,
+                                jstring java_permitted_path, std::string* error_msg) {
     std::string library_path; // empty string by default.
 
     if (java_library_path != nullptr) {
@@ -628,20 +625,54 @@
   return nullptr;
 }
 
-void* OpenNativeLibrary(JNIEnv* env,
-                        int32_t target_sdk_version,
-                        const char* path,
-                        jobject class_loader,
-                        jstring library_path,
-                        bool* needs_native_bridge,
-                        std::string* error_msg) {
+#if defined(__ANDROID__)
+static android_namespace_t* FindExportedNamespace(const char* caller_location) {
+  std::string location = caller_location;
+  // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
+  // /apex/com.android...modulename/...
+  //
+  // And we extract from it 'modulename', which is the name of the linker namespace.
+  if (android::base::StartsWith(location, kApexPath)) {
+    size_t slash_index = location.find_first_of('/', strlen(kApexPath));
+    LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
+                        "Error finding namespace of apex: no slash in path %s", caller_location);
+    size_t dot_index = location.find_last_of('.', slash_index);
+    LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
+                        "Error finding namespace of apex: no dot in apex name %s", caller_location);
+    std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+    android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
+    LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
+                        "Error finding namespace of apex: no namespace called %s", name.c_str());
+    return boot_namespace;
+  }
+  return nullptr;
+}
+#endif
+
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+                        jobject class_loader, const char* caller_location, jstring library_path,
+                        bool* needs_native_bridge, char** error_msg) {
 #if defined(__ANDROID__)
   UNUSED(target_sdk_version);
   if (class_loader == nullptr) {
     *needs_native_bridge = false;
+    if (caller_location != nullptr) {
+      android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
+      if (boot_namespace != nullptr) {
+        const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE,
+            .library_namespace = boot_namespace,
+        };
+        void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
+        if (handle == nullptr) {
+          *error_msg = strdup(dlerror());
+        }
+        return handle;
+      }
+    }
     void* handle = dlopen(path, RTLD_NOW);
     if (handle == nullptr) {
-      *error_msg = dlerror();
+      *error_msg = strdup(dlerror());
     }
     return handle;
   }
@@ -652,21 +683,18 @@
   if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
     // This is the case where the classloader was not created by ApplicationLoaders
     // In this case we create an isolated not-shared namespace for it.
-    if ((ns = g_namespaces->Create(env,
-                                   target_sdk_version,
-                                   class_loader,
-                                   false /* is_shared */,
-                                   false /* is_for_vendor */,
-                                   library_path,
-                                   nullptr,
-                                   error_msg)) == nullptr) {
+    std::string create_error_msg;
+    if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */,
+                                   false /* is_for_vendor */, library_path, nullptr,
+                                   &create_error_msg)) == nullptr) {
+      *error_msg = strdup(create_error_msg.c_str());
       return nullptr;
     }
   }
 
-  return OpenNativeLibrary(ns, path, needs_native_bridge, error_msg);
+  return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
 #else
-  UNUSED(env, target_sdk_version, class_loader);
+  UNUSED(env, target_sdk_version, class_loader, caller_location);
 
   // Do some best effort to emulate library-path support. It will not
   // work for dependencies.
@@ -705,35 +733,40 @@
       if (handle != nullptr) {
         return handle;
       }
-      *error_msg = NativeBridgeGetError();
+      *error_msg = strdup(NativeBridgeGetError());
     } else {
-      *error_msg = dlerror();
+      *error_msg = strdup(dlerror());
     }
   }
   return nullptr;
 #endif
 }
 
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, std::string* error_msg) {
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
   bool success;
   if (needs_native_bridge) {
     success = (NativeBridgeUnloadLibrary(handle) == 0);
     if (!success) {
-      *error_msg = NativeBridgeGetError();
+      *error_msg = strdup(NativeBridgeGetError());
     }
   } else {
     success = (dlclose(handle) == 0);
     if (!success) {
-      *error_msg = dlerror();
+      *error_msg = strdup(dlerror());
     }
   }
 
   return success;
 }
 
+void NativeLoaderFreeErrorMessage(char* msg) {
+  // The error messages get allocated through strdup, so we must call free on them.
+  free(msg);
+}
+
 #if defined(__ANDROID__)
-void* OpenNativeLibrary(NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
-                        std::string* error_msg) {
+void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
+                                   bool* needs_native_bridge, char** error_msg) {
   if (ns->is_android_namespace()) {
     android_dlextinfo extinfo;
     extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
@@ -741,14 +774,14 @@
 
     void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
     if (handle == nullptr) {
-      *error_msg = dlerror();
+      *error_msg = strdup(dlerror());
     }
     *needs_native_bridge = false;
     return handle;
   } else {
     void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
     if (handle == nullptr) {
-      *error_msg = NativeBridgeGetError();
+      *error_msg = strdup(NativeBridgeGetError());
     }
     *needs_native_bridge = true;
     return handle;
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index d528f30..1464e39 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -69,3 +69,14 @@
         "libbase",
     ],
 }
+
+// Build the test for the C API.
+cc_test {
+    name: "libnativeloader-api-tests",
+    host_supported: true,
+    test_per_src: true,
+    srcs: [
+        "api_test.c",
+    ],
+    header_libs: ["libnativeloader-dummy-headers"],
+}
diff --git a/libnativeloader/test/api_test.c b/libnativeloader/test/api_test.c
new file mode 100644
index 0000000..e7025fd
--- /dev/null
+++ b/libnativeloader/test/api_test.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 "nativeloader/native_loader.h"
+
+int main(int argc, char** argv) {
+  (void)argc;
+  (void)argv;
+  return 0;
+}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index c38279d..d04a79a 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,10 +1,45 @@
+cc_library_headers {
+    name: "libprocessgroup_headers",
+    vendor_available: true,
+    recovery_available: true,
+    host_supported: true,
+    export_include_dirs: ["include"],
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
 cc_library {
-    srcs: ["processgroup.cpp"],
+    srcs: [
+        "processgroup.cpp",
+        "sched_policy.cpp",
+    ],
     name: "libprocessgroup",
     host_supported: true,
     recovery_available: true,
-    shared_libs: ["libbase"],
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    // for cutils/android_filesystem_config.h
+    header_libs: [
+        "libcutils_headers",
+        "libprocessgroup_headers",
+    ],
     export_include_dirs: ["include"],
+    export_header_lib_headers: [
+        "libprocessgroup_headers",
+    ],
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
new file mode 100644
index 0000000..79a32fd
--- /dev/null
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Check if Linux kernel enables CPUSETS feature.
+ *
+ * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ */
+extern bool cpusets_enabled();
+
+/*
+ * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
+ * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
+ *
+ * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
+ */
+extern bool schedboost_enabled();
+
+/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
+typedef enum {
+    SP_DEFAULT = -1,
+    SP_BACKGROUND = 0,
+    SP_FOREGROUND = 1,
+    SP_SYSTEM = 2,  // can't be used with set_sched_policy()
+    SP_AUDIO_APP = 3,
+    SP_AUDIO_SYS = 4,
+    SP_TOP_APP = 5,
+    SP_RT_APP = 6,
+    SP_RESTRICTED = 7,
+    SP_CNT,
+    SP_MAX = SP_CNT - 1,
+    SP_SYSTEM_DEFAULT = SP_FOREGROUND,
+} SchedPolicy;
+
+extern int set_cpuset_policy(int tid, SchedPolicy policy);
+
+/* Assign thread tid to the cgroup associated with the specified policy.
+ * If the thread is a thread group leader, that is it's gettid() == getpid(),
+ * then the other threads in the same thread group are _not_ affected.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -errno for error.
+ */
+extern int set_sched_policy(int tid, SchedPolicy policy);
+
+/* Return the policy associated with the cgroup of thread tid via policy pointer.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -1 for error and set errno.
+ */
+extern int get_sched_policy(int tid, SchedPolicy *policy);
+
+/* Return a displayable string corresponding to policy.
+ * Return value: non-NULL NUL-terminated name of unspecified length;
+ * the caller is responsible for displaying the useful part of the string.
+ */
+extern const char *get_sched_policy_name(SchedPolicy policy);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 9df8dd9..8d2ac3d 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -42,7 +42,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <private/android_filesystem_config.h>
+#include <cutils/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
diff --git a/libcutils/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
similarity index 99%
rename from libcutils/sched_policy.cpp
rename to libprocessgroup/sched_policy.cpp
index 3fa548f..f95d7e4 100644
--- a/libcutils/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -14,7 +14,7 @@
 ** limitations under the License.
 */
 
-#include <cutils/sched_policy.h>
+#include <processgroup/sched_policy.h>
 
 #define LOG_TAG "SchedPolicy"
 
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 8ad339f..2ec4754 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -67,3 +67,18 @@
 
     cflags: ["-Werror"],
 }
+
+python_binary_host {
+    name: "simg_dump.py",
+    main: "simg_dump.py",
+    srcs: ["simg_dump.py"],
+    version: {
+        py2: {
+            embedded_launcher: true,
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
deleted file mode 100644
index 05e68bc..0000000
--- a/libsparse/Android.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2010 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := simg_dump.py
-LOCAL_SRC_FILES := simg_dump.py
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-LOCAL_CFLAGS := -Werror
-include $(BUILD_PREBUILT)
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index 162d1cf..b5bc5af 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -51,10 +51,6 @@
     explicit stats_event_list(int tag) : ret(0) {
         ctx = create_android_logger(static_cast<uint32_t>(tag));
     }
-    explicit stats_event_list(log_msg& log_msg) : ret(0) {
-        ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
-                                        log_msg.entry.len - sizeof(uint32_t));
-    }
     ~stats_event_list() { android_log_destroy(&ctx); }
 
     int close() {
@@ -251,9 +247,6 @@
         }
         return ret >= 0;
     }
-
-    android_log_list_element read() { return android_log_read_next(ctx); }
-    android_log_list_element peek() { return android_log_peek_next(ctx); }
 };
 
 #endif
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index f00fc2d..f5be95c 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/uio.h>
 #include <sys/un.h>
 #include <time.h>
 #include <unistd.h>
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index e267f58..d1b8271 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -127,6 +127,10 @@
         },
     },
 
+    whole_static_libs: [
+        "libdemangle"
+    ],
+
     static_libs: [
         "libprocinfo",
     ],
@@ -187,8 +191,10 @@
         "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoCreateMemoryTest.cpp",
+        "tests/MapInfoGetBuildIDTest.cpp",
         "tests/MapInfoGetElfTest.cpp",
         "tests/MapInfoGetLoadBiasTest.cpp",
+        "tests/MapInfoTest.cpp",
         "tests/MapsTest.cpp",
         "tests/MemoryBufferTest.cpp",
         "tests/MemoryCacheTest.cpp",
@@ -338,6 +344,7 @@
     ],
 
     shared_libs: [
+        "libbase",
         "libunwindstack",
     ],
 }
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index 5bc60b9..393eb3e 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -32,8 +32,1451 @@
 
 namespace unwindstack {
 
+enum DwarfOpHandleFunc : uint8_t {
+  OP_ILLEGAL = 0,
+  OP_DEREF,
+  OP_DEREF_SIZE,
+  OP_PUSH,
+  OP_DUP,
+  OP_DROP,
+  OP_OVER,
+  OP_PICK,
+  OP_SWAP,
+  OP_ROT,
+  OP_ABS,
+  OP_AND,
+  OP_DIV,
+  OP_MINUS,
+  OP_MOD,
+  OP_MUL,
+  OP_NEG,
+  OP_NOT,
+  OP_OR,
+  OP_PLUS,
+  OP_PLUS_UCONST,
+  OP_SHL,
+  OP_SHR,
+  OP_SHRA,
+  OP_XOR,
+  OP_BRA,
+  OP_EQ,
+  OP_GE,
+  OP_GT,
+  OP_LE,
+  OP_LT,
+  OP_NE,
+  OP_SKIP,
+  OP_LIT,
+  OP_REG,
+  OP_REGX,
+  OP_BREG,
+  OP_BREGX,
+  OP_NOP,
+  OP_NOT_IMPLEMENTED,
+};
+
+struct OpCallback {
+  // It may seem tempting to "clean this up" by replacing "const char[26]" with
+  // "const char*", but doing so would place the entire callback table in
+  // .data.rel.ro section, instead of .rodata section, and thus increase
+  // dirty memory usage.  Libunwindstack is used by the linker and therefore
+  // loaded for every running process, so every bit of memory counts.
+  // Unlike C standard, C++ standard guarantees this array is big enough to
+  // store the names, or else we would get a compilation error.
+  const char name[26];
+
+  // Similarily for this field, we do NOT want to directly store function
+  // pointers here. Not only would that cause the callback table to be placed
+  // in .data.rel.ro section, but it would be duplicated for each AddressType.
+  // Instead, we use DwarfOpHandleFunc enum to decouple the callback table from
+  // the function pointers.
+  DwarfOpHandleFunc handle_func;
+
+  uint8_t num_required_stack_values;
+  uint8_t num_operands;
+  uint8_t operands[2];
+};
+
+constexpr static OpCallback kCallbackTable[256] = {
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x00 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x01 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x02 illegal op
+    {
+        // 0x03 DW_OP_addr
+        "DW_OP_addr",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_absptr},
+    },
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x04 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x05 illegal op
+    {
+        // 0x06 DW_OP_deref
+        "DW_OP_deref",
+        OP_DEREF,
+        1,
+        0,
+        {},
+    },
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0x07 illegal op
+    {
+        // 0x08 DW_OP_const1u
+        "DW_OP_const1u",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_udata1},
+    },
+    {
+        // 0x09 DW_OP_const1s
+        "DW_OP_const1s",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sdata1},
+    },
+    {
+        // 0x0a DW_OP_const2u
+        "DW_OP_const2u",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_udata2},
+    },
+    {
+        // 0x0b DW_OP_const2s
+        "DW_OP_const2s",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sdata2},
+    },
+    {
+        // 0x0c DW_OP_const4u
+        "DW_OP_const4u",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_udata4},
+    },
+    {
+        // 0x0d DW_OP_const4s
+        "DW_OP_const4s",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sdata4},
+    },
+    {
+        // 0x0e DW_OP_const8u
+        "DW_OP_const8u",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_udata8},
+    },
+    {
+        // 0x0f DW_OP_const8s
+        "DW_OP_const8s",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sdata8},
+    },
+    {
+        // 0x10 DW_OP_constu
+        "DW_OP_constu",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x11 DW_OP_consts
+        "DW_OP_consts",
+        OP_PUSH,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x12 DW_OP_dup
+        "DW_OP_dup",
+        OP_DUP,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x13 DW_OP_drop
+        "DW_OP_drop",
+        OP_DROP,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x14 DW_OP_over
+        "DW_OP_over",
+        OP_OVER,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x15 DW_OP_pick
+        "DW_OP_pick",
+        OP_PICK,
+        0,
+        1,
+        {DW_EH_PE_udata1},
+    },
+    {
+        // 0x16 DW_OP_swap
+        "DW_OP_swap",
+        OP_SWAP,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x17 DW_OP_rot
+        "DW_OP_rot",
+        OP_ROT,
+        3,
+        0,
+        {},
+    },
+    {
+        // 0x18 DW_OP_xderef
+        "DW_OP_xderef",
+        OP_NOT_IMPLEMENTED,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x19 DW_OP_abs
+        "DW_OP_abs",
+        OP_ABS,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x1a DW_OP_and
+        "DW_OP_and",
+        OP_AND,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1b DW_OP_div
+        "DW_OP_div",
+        OP_DIV,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1c DW_OP_minus
+        "DW_OP_minus",
+        OP_MINUS,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1d DW_OP_mod
+        "DW_OP_mod",
+        OP_MOD,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1e DW_OP_mul
+        "DW_OP_mul",
+        OP_MUL,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x1f DW_OP_neg
+        "DW_OP_neg",
+        OP_NEG,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x20 DW_OP_not
+        "DW_OP_not",
+        OP_NOT,
+        1,
+        0,
+        {},
+    },
+    {
+        // 0x21 DW_OP_or
+        "DW_OP_or",
+        OP_OR,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x22 DW_OP_plus
+        "DW_OP_plus",
+        OP_PLUS,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x23 DW_OP_plus_uconst
+        "DW_OP_plus_uconst",
+        OP_PLUS_UCONST,
+        1,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x24 DW_OP_shl
+        "DW_OP_shl",
+        OP_SHL,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x25 DW_OP_shr
+        "DW_OP_shr",
+        OP_SHR,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x26 DW_OP_shra
+        "DW_OP_shra",
+        OP_SHRA,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x27 DW_OP_xor
+        "DW_OP_xor",
+        OP_XOR,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x28 DW_OP_bra
+        "DW_OP_bra",
+        OP_BRA,
+        1,
+        1,
+        {DW_EH_PE_sdata2},
+    },
+    {
+        // 0x29 DW_OP_eq
+        "DW_OP_eq",
+        OP_EQ,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2a DW_OP_ge
+        "DW_OP_ge",
+        OP_GE,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2b DW_OP_gt
+        "DW_OP_gt",
+        OP_GT,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2c DW_OP_le
+        "DW_OP_le",
+        OP_LE,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2d DW_OP_lt
+        "DW_OP_lt",
+        OP_LT,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2e DW_OP_ne
+        "DW_OP_ne",
+        OP_NE,
+        2,
+        0,
+        {},
+    },
+    {
+        // 0x2f DW_OP_skip
+        "DW_OP_skip",
+        OP_SKIP,
+        0,
+        1,
+        {DW_EH_PE_sdata2},
+    },
+    {
+        // 0x30 DW_OP_lit0
+        "DW_OP_lit0",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x31 DW_OP_lit1
+        "DW_OP_lit1",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x32 DW_OP_lit2
+        "DW_OP_lit2",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x33 DW_OP_lit3
+        "DW_OP_lit3",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x34 DW_OP_lit4
+        "DW_OP_lit4",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x35 DW_OP_lit5
+        "DW_OP_lit5",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x36 DW_OP_lit6
+        "DW_OP_lit6",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x37 DW_OP_lit7
+        "DW_OP_lit7",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x38 DW_OP_lit8
+        "DW_OP_lit8",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x39 DW_OP_lit9
+        "DW_OP_lit9",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3a DW_OP_lit10
+        "DW_OP_lit10",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3b DW_OP_lit11
+        "DW_OP_lit11",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3c DW_OP_lit12
+        "DW_OP_lit12",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3d DW_OP_lit13
+        "DW_OP_lit13",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3e DW_OP_lit14
+        "DW_OP_lit14",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x3f DW_OP_lit15
+        "DW_OP_lit15",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x40 DW_OP_lit16
+        "DW_OP_lit16",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x41 DW_OP_lit17
+        "DW_OP_lit17",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x42 DW_OP_lit18
+        "DW_OP_lit18",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x43 DW_OP_lit19
+        "DW_OP_lit19",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x44 DW_OP_lit20
+        "DW_OP_lit20",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x45 DW_OP_lit21
+        "DW_OP_lit21",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x46 DW_OP_lit22
+        "DW_OP_lit22",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x47 DW_OP_lit23
+        "DW_OP_lit23",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x48 DW_OP_lit24
+        "DW_OP_lit24",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x49 DW_OP_lit25
+        "DW_OP_lit25",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4a DW_OP_lit26
+        "DW_OP_lit26",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4b DW_OP_lit27
+        "DW_OP_lit27",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4c DW_OP_lit28
+        "DW_OP_lit28",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4d DW_OP_lit29
+        "DW_OP_lit29",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4e DW_OP_lit30
+        "DW_OP_lit30",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x4f DW_OP_lit31
+        "DW_OP_lit31",
+        OP_LIT,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x50 DW_OP_reg0
+        "DW_OP_reg0",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x51 DW_OP_reg1
+        "DW_OP_reg1",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x52 DW_OP_reg2
+        "DW_OP_reg2",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x53 DW_OP_reg3
+        "DW_OP_reg3",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x54 DW_OP_reg4
+        "DW_OP_reg4",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x55 DW_OP_reg5
+        "DW_OP_reg5",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x56 DW_OP_reg6
+        "DW_OP_reg6",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x57 DW_OP_reg7
+        "DW_OP_reg7",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x58 DW_OP_reg8
+        "DW_OP_reg8",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x59 DW_OP_reg9
+        "DW_OP_reg9",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5a DW_OP_reg10
+        "DW_OP_reg10",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5b DW_OP_reg11
+        "DW_OP_reg11",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5c DW_OP_reg12
+        "DW_OP_reg12",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5d DW_OP_reg13
+        "DW_OP_reg13",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5e DW_OP_reg14
+        "DW_OP_reg14",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x5f DW_OP_reg15
+        "DW_OP_reg15",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x60 DW_OP_reg16
+        "DW_OP_reg16",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x61 DW_OP_reg17
+        "DW_OP_reg17",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x62 DW_OP_reg18
+        "DW_OP_reg18",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x63 DW_OP_reg19
+        "DW_OP_reg19",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x64 DW_OP_reg20
+        "DW_OP_reg20",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x65 DW_OP_reg21
+        "DW_OP_reg21",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x66 DW_OP_reg22
+        "DW_OP_reg22",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x67 DW_OP_reg23
+        "DW_OP_reg23",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x68 DW_OP_reg24
+        "DW_OP_reg24",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x69 DW_OP_reg25
+        "DW_OP_reg25",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6a DW_OP_reg26
+        "DW_OP_reg26",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6b DW_OP_reg27
+        "DW_OP_reg27",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6c DW_OP_reg28
+        "DW_OP_reg28",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6d DW_OP_reg29
+        "DW_OP_reg29",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6e DW_OP_reg30
+        "DW_OP_reg30",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x6f DW_OP_reg31
+        "DW_OP_reg31",
+        OP_REG,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x70 DW_OP_breg0
+        "DW_OP_breg0",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x71 DW_OP_breg1
+        "DW_OP_breg1",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x72 DW_OP_breg2
+        "DW_OP_breg2",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x73 DW_OP_breg3
+        "DW_OP_breg3",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x74 DW_OP_breg4
+        "DW_OP_breg4",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x75 DW_OP_breg5
+        "DW_OP_breg5",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x76 DW_OP_breg6
+        "DW_OP_breg6",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x77 DW_OP_breg7
+        "DW_OP_breg7",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x78 DW_OP_breg8
+        "DW_OP_breg8",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x79 DW_OP_breg9
+        "DW_OP_breg9",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7a DW_OP_breg10
+        "DW_OP_breg10",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7b DW_OP_breg11
+        "DW_OP_breg11",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7c DW_OP_breg12
+        "DW_OP_breg12",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7d DW_OP_breg13
+        "DW_OP_breg13",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7e DW_OP_breg14
+        "DW_OP_breg14",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x7f DW_OP_breg15
+        "DW_OP_breg15",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x80 DW_OP_breg16
+        "DW_OP_breg16",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x81 DW_OP_breg17
+        "DW_OP_breg17",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x82 DW_OP_breg18
+        "DW_OP_breg18",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x83 DW_OP_breg19
+        "DW_OP_breg19",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x84 DW_OP_breg20
+        "DW_OP_breg20",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x85 DW_OP_breg21
+        "DW_OP_breg21",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x86 DW_OP_breg22
+        "DW_OP_breg22",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x87 DW_OP_breg23
+        "DW_OP_breg23",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x88 DW_OP_breg24
+        "DW_OP_breg24",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x89 DW_OP_breg25
+        "DW_OP_breg25",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8a DW_OP_breg26
+        "DW_OP_breg26",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8b DW_OP_breg27
+        "DW_OP_breg27",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8c DW_OP_breg28
+        "DW_OP_breg28",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8d DW_OP_breg29
+        "DW_OP_breg29",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8e DW_OP_breg30
+        "DW_OP_breg30",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x8f DW_OP_breg31
+        "DW_OP_breg31",
+        OP_BREG,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x90 DW_OP_regx
+        "DW_OP_regx",
+        OP_REGX,
+        0,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x91 DW_OP_fbreg
+        "DW_OP_fbreg",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_sleb128},
+    },
+    {
+        // 0x92 DW_OP_bregx
+        "DW_OP_bregx",
+        OP_BREGX,
+        0,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+    },
+    {
+        // 0x93 DW_OP_piece
+        "DW_OP_piece",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x94 DW_OP_deref_size
+        "DW_OP_deref_size",
+        OP_DEREF_SIZE,
+        1,
+        1,
+        {DW_EH_PE_udata1},
+    },
+    {
+        // 0x95 DW_OP_xderef_size
+        "DW_OP_xderef_size",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_udata1},
+    },
+    {
+        // 0x96 DW_OP_nop
+        "DW_OP_nop",
+        OP_NOP,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x97 DW_OP_push_object_address
+        "DW_OP_push_object_address",
+        OP_NOT_IMPLEMENTED,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x98 DW_OP_call2
+        "DW_OP_call2",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_udata2},
+    },
+    {
+        // 0x99 DW_OP_call4
+        "DW_OP_call4",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_udata4},
+    },
+    {
+        // 0x9a DW_OP_call_ref
+        "DW_OP_call_ref",
+        OP_NOT_IMPLEMENTED,
+        0,
+        0,  // Has a different sized operand (4 bytes or 8 bytes).
+        {},
+    },
+    {
+        // 0x9b DW_OP_form_tls_address
+        "DW_OP_form_tls_address",
+        OP_NOT_IMPLEMENTED,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x9c DW_OP_call_frame_cfa
+        "DW_OP_call_frame_cfa",
+        OP_NOT_IMPLEMENTED,
+        0,
+        0,
+        {},
+    },
+    {
+        // 0x9d DW_OP_bit_piece
+        "DW_OP_bit_piece",
+        OP_NOT_IMPLEMENTED,
+        0,
+        2,
+        {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+    },
+    {
+        // 0x9e DW_OP_implicit_value
+        "DW_OP_implicit_value",
+        OP_NOT_IMPLEMENTED,
+        0,
+        1,
+        {DW_EH_PE_uleb128},
+    },
+    {
+        // 0x9f DW_OP_stack_value
+        "DW_OP_stack_value",
+        OP_NOT_IMPLEMENTED,
+        1,
+        0,
+        {},
+    },
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xa9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xaa illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xab illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xac illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xad illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xae illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xaf illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xb9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xba illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbc illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbd illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbe illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xbf illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xc9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xca illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xcb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xcc illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xcd illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xce illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xcf illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xd9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xda illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xdb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xdc illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xdd illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xde illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xdf illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe0 DW_OP_lo_user
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xe9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xea illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xeb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xec illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xed illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xee illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xef illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf0 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf1 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf2 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf3 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf4 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf5 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf6 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf7 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf8 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xf9 illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfa illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfb illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfc illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfd illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xfe illegal op
+    {"", OP_ILLEGAL, 0, 0, {}},  // 0xff DW_OP_hi_user
+};
+
 template <typename AddressType>
-constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
+const typename DwarfOp<AddressType>::OpHandleFuncPtr DwarfOp<AddressType>::kOpHandleFuncList[] = {
+    [OP_ILLEGAL] = nullptr,
+    [OP_DEREF] = &DwarfOp<AddressType>::op_deref,
+    [OP_DEREF_SIZE] = &DwarfOp<AddressType>::op_deref_size,
+    [OP_PUSH] = &DwarfOp<AddressType>::op_push,
+    [OP_DUP] = &DwarfOp<AddressType>::op_dup,
+    [OP_DROP] = &DwarfOp<AddressType>::op_drop,
+    [OP_OVER] = &DwarfOp<AddressType>::op_over,
+    [OP_PICK] = &DwarfOp<AddressType>::op_pick,
+    [OP_SWAP] = &DwarfOp<AddressType>::op_swap,
+    [OP_ROT] = &DwarfOp<AddressType>::op_rot,
+    [OP_ABS] = &DwarfOp<AddressType>::op_abs,
+    [OP_AND] = &DwarfOp<AddressType>::op_and,
+    [OP_DIV] = &DwarfOp<AddressType>::op_div,
+    [OP_MINUS] = &DwarfOp<AddressType>::op_minus,
+    [OP_MOD] = &DwarfOp<AddressType>::op_mod,
+    [OP_MUL] = &DwarfOp<AddressType>::op_mul,
+    [OP_NEG] = &DwarfOp<AddressType>::op_neg,
+    [OP_NOT] = &DwarfOp<AddressType>::op_not,
+    [OP_OR] = &DwarfOp<AddressType>::op_or,
+    [OP_PLUS] = &DwarfOp<AddressType>::op_plus,
+    [OP_PLUS_UCONST] = &DwarfOp<AddressType>::op_plus_uconst,
+    [OP_SHL] = &DwarfOp<AddressType>::op_shl,
+    [OP_SHR] = &DwarfOp<AddressType>::op_shr,
+    [OP_SHRA] = &DwarfOp<AddressType>::op_shra,
+    [OP_XOR] = &DwarfOp<AddressType>::op_xor,
+    [OP_BRA] = &DwarfOp<AddressType>::op_bra,
+    [OP_EQ] = &DwarfOp<AddressType>::op_eq,
+    [OP_GE] = &DwarfOp<AddressType>::op_ge,
+    [OP_GT] = &DwarfOp<AddressType>::op_gt,
+    [OP_LE] = &DwarfOp<AddressType>::op_le,
+    [OP_LT] = &DwarfOp<AddressType>::op_lt,
+    [OP_NE] = &DwarfOp<AddressType>::op_ne,
+    [OP_SKIP] = &DwarfOp<AddressType>::op_skip,
+    [OP_LIT] = &DwarfOp<AddressType>::op_lit,
+    [OP_REG] = &DwarfOp<AddressType>::op_reg,
+    [OP_REGX] = &DwarfOp<AddressType>::op_regx,
+    [OP_BREG] = &DwarfOp<AddressType>::op_breg,
+    [OP_BREGX] = &DwarfOp<AddressType>::op_bregx,
+    [OP_NOP] = &DwarfOp<AddressType>::op_nop,
+    [OP_NOT_IMPLEMENTED] = &DwarfOp<AddressType>::op_not_implemented,
+};
 
 template <typename AddressType>
 bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end) {
@@ -97,12 +1540,13 @@
   }
 
   const auto* op = &kCallbackTable[cur_op_];
-  const auto handle_func = op->handle_func;
-  if (handle_func == nullptr) {
+  if (op->handle_func == OP_ILLEGAL) {
     last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
 
+  const auto handle_func = kOpHandleFuncList[op->handle_func];
+
   // Make sure that the required number of stack elements is available.
   if (stack_.size() < op->num_required_stack_values) {
     last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
@@ -135,7 +1579,7 @@
     std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
     std::string log_string;
     const auto* op = &kCallbackTable[cur_op];
-    if (op->handle_func == nullptr) {
+    if (op->handle_func == OP_ILLEGAL) {
       log_string = "Illegal";
     } else {
       log_string = op->name;
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index 4c69b3d..ac9fd2d 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -42,14 +42,6 @@
   // Signed version of AddressType
   typedef typename std::make_signed<AddressType>::type SignedType;
 
-  struct OpCallback {
-    const char* name;
-    bool (DwarfOp::*handle_func)();
-    uint8_t num_required_stack_values;
-    uint8_t num_operands;
-    uint8_t operands[2];
-  };
-
  public:
   DwarfOp(DwarfMemory* memory, Memory* regular_memory)
       : memory_(memory), regular_memory_(regular_memory) {}
@@ -143,1342 +135,8 @@
   bool op_nop();
   bool op_not_implemented();
 
-  constexpr static OpCallback kCallbackTable[256] = {
-      {nullptr, nullptr, 0, 0, {}},  // 0x00 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0x01 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0x02 illegal op
-      {
-          // 0x03 DW_OP_addr
-          "DW_OP_addr",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_absptr},
-      },
-      {nullptr, nullptr, 0, 0, {}},  // 0x04 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0x05 illegal op
-      {
-          // 0x06 DW_OP_deref
-          "DW_OP_deref",
-          &DwarfOp::op_deref,
-          1,
-          0,
-          {},
-      },
-      {nullptr, nullptr, 0, 0, {}},  // 0x07 illegal op
-      {
-          // 0x08 DW_OP_const1u
-          "DW_OP_const1u",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_udata1},
-      },
-      {
-          // 0x09 DW_OP_const1s
-          "DW_OP_const1s",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sdata1},
-      },
-      {
-          // 0x0a DW_OP_const2u
-          "DW_OP_const2u",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_udata2},
-      },
-      {
-          // 0x0b DW_OP_const2s
-          "DW_OP_const2s",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sdata2},
-      },
-      {
-          // 0x0c DW_OP_const4u
-          "DW_OP_const4u",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_udata4},
-      },
-      {
-          // 0x0d DW_OP_const4s
-          "DW_OP_const4s",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sdata4},
-      },
-      {
-          // 0x0e DW_OP_const8u
-          "DW_OP_const8u",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_udata8},
-      },
-      {
-          // 0x0f DW_OP_const8s
-          "DW_OP_const8s",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sdata8},
-      },
-      {
-          // 0x10 DW_OP_constu
-          "DW_OP_constu",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x11 DW_OP_consts
-          "DW_OP_consts",
-          &DwarfOp::op_push,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x12 DW_OP_dup
-          "DW_OP_dup",
-          &DwarfOp::op_dup,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x13 DW_OP_drop
-          "DW_OP_drop",
-          &DwarfOp::op_drop,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x14 DW_OP_over
-          "DW_OP_over",
-          &DwarfOp::op_over,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x15 DW_OP_pick
-          "DW_OP_pick",
-          &DwarfOp::op_pick,
-          0,
-          1,
-          {DW_EH_PE_udata1},
-      },
-      {
-          // 0x16 DW_OP_swap
-          "DW_OP_swap",
-          &DwarfOp::op_swap,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x17 DW_OP_rot
-          "DW_OP_rot",
-          &DwarfOp::op_rot,
-          3,
-          0,
-          {},
-      },
-      {
-          // 0x18 DW_OP_xderef
-          "DW_OP_xderef",
-          &DwarfOp::op_not_implemented,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x19 DW_OP_abs
-          "DW_OP_abs",
-          &DwarfOp::op_abs,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x1a DW_OP_and
-          "DW_OP_and",
-          &DwarfOp::op_and,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1b DW_OP_div
-          "DW_OP_div",
-          &DwarfOp::op_div,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1c DW_OP_minus
-          "DW_OP_minus",
-          &DwarfOp::op_minus,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1d DW_OP_mod
-          "DW_OP_mod",
-          &DwarfOp::op_mod,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1e DW_OP_mul
-          "DW_OP_mul",
-          &DwarfOp::op_mul,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x1f DW_OP_neg
-          "DW_OP_neg",
-          &DwarfOp::op_neg,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x20 DW_OP_not
-          "DW_OP_not",
-          &DwarfOp::op_not,
-          1,
-          0,
-          {},
-      },
-      {
-          // 0x21 DW_OP_or
-          "DW_OP_or",
-          &DwarfOp::op_or,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x22 DW_OP_plus
-          "DW_OP_plus",
-          &DwarfOp::op_plus,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x23 DW_OP_plus_uconst
-          "DW_OP_plus_uconst",
-          &DwarfOp::op_plus_uconst,
-          1,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x24 DW_OP_shl
-          "DW_OP_shl",
-          &DwarfOp::op_shl,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x25 DW_OP_shr
-          "DW_OP_shr",
-          &DwarfOp::op_shr,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x26 DW_OP_shra
-          "DW_OP_shra",
-          &DwarfOp::op_shra,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x27 DW_OP_xor
-          "DW_OP_xor",
-          &DwarfOp::op_xor,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x28 DW_OP_bra
-          "DW_OP_bra",
-          &DwarfOp::op_bra,
-          1,
-          1,
-          {DW_EH_PE_sdata2},
-      },
-      {
-          // 0x29 DW_OP_eq
-          "DW_OP_eq",
-          &DwarfOp::op_eq,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2a DW_OP_ge
-          "DW_OP_ge",
-          &DwarfOp::op_ge,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2b DW_OP_gt
-          "DW_OP_gt",
-          &DwarfOp::op_gt,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2c DW_OP_le
-          "DW_OP_le",
-          &DwarfOp::op_le,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2d DW_OP_lt
-          "DW_OP_lt",
-          &DwarfOp::op_lt,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2e DW_OP_ne
-          "DW_OP_ne",
-          &DwarfOp::op_ne,
-          2,
-          0,
-          {},
-      },
-      {
-          // 0x2f DW_OP_skip
-          "DW_OP_skip",
-          &DwarfOp::op_skip,
-          0,
-          1,
-          {DW_EH_PE_sdata2},
-      },
-      {
-          // 0x30 DW_OP_lit0
-          "DW_OP_lit0",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x31 DW_OP_lit1
-          "DW_OP_lit1",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x32 DW_OP_lit2
-          "DW_OP_lit2",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x33 DW_OP_lit3
-          "DW_OP_lit3",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x34 DW_OP_lit4
-          "DW_OP_lit4",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x35 DW_OP_lit5
-          "DW_OP_lit5",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x36 DW_OP_lit6
-          "DW_OP_lit6",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x37 DW_OP_lit7
-          "DW_OP_lit7",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x38 DW_OP_lit8
-          "DW_OP_lit8",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x39 DW_OP_lit9
-          "DW_OP_lit9",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3a DW_OP_lit10
-          "DW_OP_lit10",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3b DW_OP_lit11
-          "DW_OP_lit11",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3c DW_OP_lit12
-          "DW_OP_lit12",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3d DW_OP_lit13
-          "DW_OP_lit13",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3e DW_OP_lit14
-          "DW_OP_lit14",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x3f DW_OP_lit15
-          "DW_OP_lit15",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x40 DW_OP_lit16
-          "DW_OP_lit16",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x41 DW_OP_lit17
-          "DW_OP_lit17",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x42 DW_OP_lit18
-          "DW_OP_lit18",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x43 DW_OP_lit19
-          "DW_OP_lit19",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x44 DW_OP_lit20
-          "DW_OP_lit20",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x45 DW_OP_lit21
-          "DW_OP_lit21",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x46 DW_OP_lit22
-          "DW_OP_lit22",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x47 DW_OP_lit23
-          "DW_OP_lit23",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x48 DW_OP_lit24
-          "DW_OP_lit24",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x49 DW_OP_lit25
-          "DW_OP_lit25",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4a DW_OP_lit26
-          "DW_OP_lit26",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4b DW_OP_lit27
-          "DW_OP_lit27",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4c DW_OP_lit28
-          "DW_OP_lit28",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4d DW_OP_lit29
-          "DW_OP_lit29",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4e DW_OP_lit30
-          "DW_OP_lit30",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x4f DW_OP_lit31
-          "DW_OP_lit31",
-          &DwarfOp::op_lit,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x50 DW_OP_reg0
-          "DW_OP_reg0",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x51 DW_OP_reg1
-          "DW_OP_reg1",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x52 DW_OP_reg2
-          "DW_OP_reg2",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x53 DW_OP_reg3
-          "DW_OP_reg3",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x54 DW_OP_reg4
-          "DW_OP_reg4",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x55 DW_OP_reg5
-          "DW_OP_reg5",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x56 DW_OP_reg6
-          "DW_OP_reg6",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x57 DW_OP_reg7
-          "DW_OP_reg7",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x58 DW_OP_reg8
-          "DW_OP_reg8",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x59 DW_OP_reg9
-          "DW_OP_reg9",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5a DW_OP_reg10
-          "DW_OP_reg10",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5b DW_OP_reg11
-          "DW_OP_reg11",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5c DW_OP_reg12
-          "DW_OP_reg12",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5d DW_OP_reg13
-          "DW_OP_reg13",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5e DW_OP_reg14
-          "DW_OP_reg14",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x5f DW_OP_reg15
-          "DW_OP_reg15",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x60 DW_OP_reg16
-          "DW_OP_reg16",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x61 DW_OP_reg17
-          "DW_OP_reg17",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x62 DW_OP_reg18
-          "DW_OP_reg18",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x63 DW_OP_reg19
-          "DW_OP_reg19",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x64 DW_OP_reg20
-          "DW_OP_reg20",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x65 DW_OP_reg21
-          "DW_OP_reg21",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x66 DW_OP_reg22
-          "DW_OP_reg22",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x67 DW_OP_reg23
-          "DW_OP_reg23",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x68 DW_OP_reg24
-          "DW_OP_reg24",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x69 DW_OP_reg25
-          "DW_OP_reg25",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6a DW_OP_reg26
-          "DW_OP_reg26",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6b DW_OP_reg27
-          "DW_OP_reg27",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6c DW_OP_reg28
-          "DW_OP_reg28",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6d DW_OP_reg29
-          "DW_OP_reg29",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6e DW_OP_reg30
-          "DW_OP_reg30",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x6f DW_OP_reg31
-          "DW_OP_reg31",
-          &DwarfOp::op_reg,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x70 DW_OP_breg0
-          "DW_OP_breg0",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x71 DW_OP_breg1
-          "DW_OP_breg1",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x72 DW_OP_breg2
-          "DW_OP_breg2",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x73 DW_OP_breg3
-          "DW_OP_breg3",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x74 DW_OP_breg4
-          "DW_OP_breg4",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x75 DW_OP_breg5
-          "DW_OP_breg5",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x76 DW_OP_breg6
-          "DW_OP_breg6",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x77 DW_OP_breg7
-          "DW_OP_breg7",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x78 DW_OP_breg8
-          "DW_OP_breg8",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x79 DW_OP_breg9
-          "DW_OP_breg9",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7a DW_OP_breg10
-          "DW_OP_breg10",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7b DW_OP_breg11
-          "DW_OP_breg11",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7c DW_OP_breg12
-          "DW_OP_breg12",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7d DW_OP_breg13
-          "DW_OP_breg13",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7e DW_OP_breg14
-          "DW_OP_breg14",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x7f DW_OP_breg15
-          "DW_OP_breg15",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x80 DW_OP_breg16
-          "DW_OP_breg16",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x81 DW_OP_breg17
-          "DW_OP_breg17",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x82 DW_OP_breg18
-          "DW_OP_breg18",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x83 DW_OP_breg19
-          "DW_OP_breg19",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x84 DW_OP_breg20
-          "DW_OP_breg20",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x85 DW_OP_breg21
-          "DW_OP_breg21",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x86 DW_OP_breg22
-          "DW_OP_breg22",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x87 DW_OP_breg23
-          "DW_OP_breg23",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x88 DW_OP_breg24
-          "DW_OP_breg24",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x89 DW_OP_breg25
-          "DW_OP_breg25",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8a DW_OP_breg26
-          "DW_OP_breg26",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8b DW_OP_breg27
-          "DW_OP_breg27",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8c DW_OP_breg28
-          "DW_OP_breg28",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8d DW_OP_breg29
-          "DW_OP_breg29",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8e DW_OP_breg30
-          "DW_OP_breg30",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x8f DW_OP_breg31
-          "DW_OP_breg31",
-          &DwarfOp::op_breg,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x90 DW_OP_regx
-          "DW_OP_regx",
-          &DwarfOp::op_regx,
-          0,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x91 DW_OP_fbreg
-          "DW_OP_fbreg",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_sleb128},
-      },
-      {
-          // 0x92 DW_OP_bregx
-          "DW_OP_bregx",
-          &DwarfOp::op_bregx,
-          0,
-          2,
-          {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
-      },
-      {
-          // 0x93 DW_OP_piece
-          "DW_OP_piece",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x94 DW_OP_deref_size
-          "DW_OP_deref_size",
-          &DwarfOp::op_deref_size,
-          1,
-          1,
-          {DW_EH_PE_udata1},
-      },
-      {
-          // 0x95 DW_OP_xderef_size
-          "DW_OP_xderef_size",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_udata1},
-      },
-      {
-          // 0x96 DW_OP_nop
-          "DW_OP_nop",
-          &DwarfOp::op_nop,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x97 DW_OP_push_object_address
-          "DW_OP_push_object_address",
-          &DwarfOp::op_not_implemented,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x98 DW_OP_call2
-          "DW_OP_call2",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_udata2},
-      },
-      {
-          // 0x99 DW_OP_call4
-          "DW_OP_call4",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_udata4},
-      },
-      {
-          // 0x9a DW_OP_call_ref
-          "DW_OP_call_ref",
-          &DwarfOp::op_not_implemented,
-          0,
-          0,  // Has a different sized operand (4 bytes or 8 bytes).
-          {},
-      },
-      {
-          // 0x9b DW_OP_form_tls_address
-          "DW_OP_form_tls_address",
-          &DwarfOp::op_not_implemented,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x9c DW_OP_call_frame_cfa
-          "DW_OP_call_frame_cfa",
-          &DwarfOp::op_not_implemented,
-          0,
-          0,
-          {},
-      },
-      {
-          // 0x9d DW_OP_bit_piece
-          "DW_OP_bit_piece",
-          &DwarfOp::op_not_implemented,
-          0,
-          2,
-          {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
-      },
-      {
-          // 0x9e DW_OP_implicit_value
-          "DW_OP_implicit_value",
-          &DwarfOp::op_not_implemented,
-          0,
-          1,
-          {DW_EH_PE_uleb128},
-      },
-      {
-          // 0x9f DW_OP_stack_value
-          "DW_OP_stack_value",
-          &DwarfOp::op_not_implemented,
-          1,
-          0,
-          {},
-      },
-      {nullptr, nullptr, 0, 0, {}},  // 0xa0 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa1 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa2 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa3 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa4 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa5 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa6 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa7 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa8 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xa9 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xaa illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xab illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xac illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xad illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xae illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xaf illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb0 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb1 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb2 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb3 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb4 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb5 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb6 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb7 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb8 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xb9 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xba illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xbb illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xbc illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xbd illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xbe illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xbf illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc0 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc1 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc2 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc3 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc4 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc5 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc6 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc7 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc8 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xc9 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xca illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xcb illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xcc illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xcd illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xce illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xcf illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd0 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd1 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd2 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd3 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd4 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd5 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd6 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd7 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd8 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xd9 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xda illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xdb illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xdc illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xdd illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xde illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xdf illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe0 DW_OP_lo_user
-      {nullptr, nullptr, 0, 0, {}},  // 0xe1 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe2 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe3 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe4 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe5 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe6 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe7 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe8 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xe9 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xea illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xeb illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xec illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xed illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xee illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xef illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf0 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf1 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf2 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf3 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf4 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf5 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf6 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf7 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf8 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xf9 illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xfa illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xfb illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xfc illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xfd illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xfe illegal op
-      {nullptr, nullptr, 0, 0, {}},  // 0xff DW_OP_hi_user
-  };
+  using OpHandleFuncPtr = bool (DwarfOp::*)();
+  static const OpHandleFuncPtr kOpHandleFuncList[];
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 5b586a2..2f5eed9 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -140,8 +140,11 @@
   return true;
 }
 
-bool Elf::GetBuildID(std::string* build_id) {
-  return valid_ && interface_->GetBuildID(build_id);
+std::string Elf::GetBuildID() {
+  if (!valid_) {
+    return "";
+  }
+  return interface_->GetBuildID();
 }
 
 void Elf::GetLastError(ErrorData* data) {
@@ -384,4 +387,22 @@
   return false;
 }
 
+std::string Elf::GetBuildID(Memory* memory) {
+  if (!IsValidElf(memory)) {
+    return "";
+  }
+
+  uint8_t class_type;
+  if (!memory->Read(EI_CLASS, &class_type, 1)) {
+    return "";
+  }
+
+  if (class_type == ELFCLASS32) {
+    return ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(memory);
+  } else if (class_type == ELFCLASS64) {
+    return ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(memory);
+  }
+  return "";
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index d0af94a..c1b98d9 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -177,14 +177,14 @@
 template <typename EhdrType, typename PhdrType>
 uint64_t ElfInterface::GetLoadBias(Memory* memory) {
   EhdrType ehdr;
-  if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+  if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
     return false;
   }
 
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
-    if (!memory->Read(offset, &phdr, sizeof(phdr))) {
+    if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
       return 0;
     }
     if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
@@ -238,31 +238,31 @@
 }
 
 template <typename NhdrType>
-bool ElfInterface::ReadBuildID(std::string* build_id) {
+std::string ElfInterface::ReadBuildID() {
   // Ensure there is no overflow in any of the calulations below.
   uint64_t tmp;
   if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
-    return false;
+    return "";
   }
 
   uint64_t offset = 0;
   while (offset < gnu_build_id_size_) {
     if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
-      return false;
+      return "";
     }
     NhdrType hdr;
     if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
-      return false;
+      return "";
     }
     offset += sizeof(hdr);
 
     if (gnu_build_id_size_ - offset < hdr.n_namesz) {
-      return false;
+      return "";
     }
     if (hdr.n_namesz > 0) {
       std::string name(hdr.n_namesz, '\0');
       if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
-        return false;
+        return "";
       }
 
       // Trim trailing \0 as GNU is stored as a C string in the ELF file.
@@ -273,18 +273,20 @@
       offset += (hdr.n_namesz + 3) & ~3;
 
       if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
-        if (gnu_build_id_size_ - offset < hdr.n_descsz) {
-          return false;
+        if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+          return "";
         }
-        build_id->resize(hdr.n_descsz);
-        return memory_->ReadFully(gnu_build_id_offset_ + offset, &(*build_id)[0],
-                                  hdr.n_descsz);
+        std::string build_id(hdr.n_descsz - 1, '\0');
+        if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
+          return build_id;
+        }
+        return "";
       }
     }
     // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
     offset += (hdr.n_descsz + 3) & ~3;
   }
-  return false;
+  return "";
 }
 
 template <typename EhdrType, typename ShdrType>
@@ -308,7 +310,7 @@
   // Skip the first header, it's always going to be NULL.
   offset += ehdr.e_shentsize;
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
-    if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+    if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
       return;
     }
 
@@ -320,7 +322,7 @@
         continue;
       }
       uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
-      if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
+      if (!memory_->ReadFully(str_offset, &str_shdr, sizeof(str_shdr))) {
         continue;
       }
       if (str_shdr.sh_type != SHT_STRTAB) {
@@ -536,6 +538,103 @@
   *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
 }
 
+template <typename EhdrType, typename ShdrType>
+bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
+  EhdrType ehdr;
+  if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+    return false;
+  }
+
+  uint64_t offset = ehdr.e_shoff;
+  uint64_t sec_offset;
+  uint64_t sec_size;
+  ShdrType shdr;
+  if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+    return false;
+  }
+
+  uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
+  if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
+    return false;
+  }
+  sec_offset = shdr.sh_offset;
+  sec_size = shdr.sh_size;
+
+  // Skip the first header, it's always going to be NULL.
+  offset += ehdr.e_shentsize;
+  for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
+    if (!memory->ReadFully(offset, &shdr, sizeof(shdr))) {
+      return false;
+    }
+    std::string name;
+    if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
+        memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+      *build_id_offset = shdr.sh_offset;
+      *build_id_size = shdr.sh_size;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+template <typename EhdrType, typename ShdrType, typename NhdrType>
+std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
+  uint64_t note_offset;
+  uint64_t note_size;
+  if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, &note_offset, &note_size)) {
+    return "";
+  }
+
+  // Ensure there is no overflow in any of the calculations below.
+  uint64_t tmp;
+  if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
+    return "";
+  }
+
+  uint64_t offset = 0;
+  while (offset < note_size) {
+    if (note_size - offset < sizeof(NhdrType)) {
+      return "";
+    }
+    NhdrType hdr;
+    if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
+      return "";
+    }
+    offset += sizeof(hdr);
+
+    if (note_size - offset < hdr.n_namesz) {
+      return "";
+    }
+    if (hdr.n_namesz > 0) {
+      std::string name(hdr.n_namesz, '\0');
+      if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
+        return "";
+      }
+
+      // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+      if (name.back() == '\0') name.resize(name.size() - 1);
+
+      // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+      offset += (hdr.n_namesz + 3) & ~3;
+
+      if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+        if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+          return "";
+        }
+        std::string build_id(hdr.n_descsz - 1, '\0');
+        if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
+          return build_id;
+        }
+        return "";
+      }
+    }
+    // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+    offset += (hdr.n_descsz + 3) & ~3;
+  }
+  return "";
+}
+
 // Instantiate all of the needed template functions.
 template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
 template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
@@ -551,8 +650,8 @@
 template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
 template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
 
-template bool ElfInterface::ReadBuildID<Elf32_Nhdr>(std::string*);
-template bool ElfInterface::ReadBuildID<Elf64_Nhdr>(std::string*);
+template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
+template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
 
 template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
 template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
@@ -571,4 +670,9 @@
 template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
 template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
 
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
+    Memory*);
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
+    Memory*);
+
 }  // namespace unwindstack
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 39a09cf..f319971 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -146,6 +146,10 @@
     }
   }
 
+  if (process_memory == nullptr) {
+    return nullptr;
+  }
+
   // Need to verify that this elf is valid. It's possible that
   // only part of the elf file to be mapped into memory is in the executable
   // map. In this case, there will be another read-only map that includes the
@@ -221,6 +225,19 @@
   return elf.get();
 }
 
+bool MapInfo::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+  {
+    // Make sure no other thread is trying to update this elf object.
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (elf == nullptr) {
+      return false;
+    }
+  }
+  // No longer need the lock, once the elf object is created, it is not deleted
+  // until this object is deleted.
+  return elf->GetFunctionName(addr, name, func_offset);
+}
+
 uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
   uint64_t cur_load_bias = load_bias.load();
   if (cur_load_bias != static_cast<uint64_t>(-1)) {
@@ -250,4 +267,48 @@
   return cur_load_bias;
 }
 
+MapInfo::~MapInfo() {
+  uintptr_t id = build_id.load();
+  if (id != 0) {
+    delete reinterpret_cast<std::string*>(id);
+  }
+}
+
+std::string MapInfo::GetBuildID() {
+  uintptr_t id = build_id.load();
+  if (build_id != 0) {
+    return *reinterpret_cast<std::string*>(id);
+  }
+
+  // No need to lock, at worst if multiple threads do this at the same
+  // time it should be detected and only one thread should win and
+  // save the data.
+  std::unique_ptr<std::string> cur_build_id(new std::string);
+
+  // Now need to see if the elf object exists.
+  // Make sure no other thread is trying to add the elf to this map.
+  mutex_.lock();
+  Elf* elf_obj = elf.get();
+  mutex_.unlock();
+  if (elf_obj != nullptr) {
+    *cur_build_id = elf_obj->GetBuildID();
+  } else {
+    // This will only work if we can get the file associated with this memory.
+    // If this is only available in memory, then the section name information
+    // is not present and we will not be able to find the build id info.
+    std::unique_ptr<Memory> memory(GetFileMemory());
+    if (memory != nullptr) {
+      *cur_build_id = Elf::GetBuildID(memory.get());
+    }
+  }
+
+  id = reinterpret_cast<uintptr_t>(cur_build_id.get());
+  uintptr_t expected_id = 0;
+  if (build_id.compare_exchange_weak(expected_id, id)) {
+    // Value saved, so make sure the memory is not freed.
+    cur_build_id.release();
+  }
+  return *reinterpret_cast<std::string*>(id);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 14a4e31..f330fe0 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -135,7 +135,7 @@
   Memory* elf_memory = elf->memory();
   // Read from elf memory since it is usually more expensive to read from
   // process memory.
-  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
     return false;
   }
 
@@ -160,7 +160,7 @@
 
   // read sc_pc and sc_regs[32] from stack
   uint64_t values[MIPS_REG_LAST];
-  if (!process_memory->Read(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
+  if (!process_memory->ReadFully(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
     return false;
   }
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 8133639..2734cf8 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -26,10 +26,13 @@
 
 #include <android-base/stringprintf.h>
 
+#include <demangle.h>
+
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
 #include <unwindstack/Unwinder.h>
 
 #if !defined(NO_LIBDEXFILE_SUPPORT)
@@ -141,7 +144,6 @@
 
   bool return_address_attempt = false;
   bool adjust_pc = false;
-  std::unique_ptr<JitDebug> jit_debug;
   for (; frames_.size() < max_frames_;) {
     uint64_t cur_pc = regs_->pc();
     uint64_t cur_sp = regs_->sp();
@@ -245,7 +247,7 @@
         // or the pc in the first frame is in a valid map.
         // This allows for a case where the code jumps into the middle of
         // nowhere, but there is no other unwind information after that.
-        if (frames_.size() != 2 || maps_->Find(frames_[0].pc) != nullptr) {
+        if (frames_.size() > 2 || (frames_.size() > 0 && maps_->Find(frames_[0].pc) != nullptr)) {
           // Remove the speculative frame.
           frames_.pop_back();
         }
@@ -306,7 +308,7 @@
   }
 
   if (!frame.function_name.empty()) {
-    data += " (" + frame.function_name;
+    data += " (" + demangle(frame.function_name.c_str());
     if (frame.function_offset != 0) {
       data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
     }
@@ -327,4 +329,29 @@
 }
 #endif
 
+bool UnwinderFromPid::Init(ArchEnum arch) {
+  if (pid_ == getpid()) {
+    maps_ptr_.reset(new LocalMaps());
+  } else {
+    maps_ptr_.reset(new RemoteMaps(pid_));
+  }
+  if (!maps_ptr_->Parse()) {
+    return false;
+  }
+  maps_ = maps_ptr_.get();
+
+  process_memory_ = Memory::CreateProcessMemoryCached(pid_);
+
+  jit_debug_ptr_.reset(new JitDebug(process_memory_));
+  jit_debug_ = jit_debug_ptr_.get();
+  SetJitDebug(jit_debug_, arch);
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  dex_files_ptr_.reset(new DexFiles(process_memory_));
+  dex_files_ = dex_files_ptr_.get();
+  SetDexFiles(dex_files_, arch);
+#endif
+
+  return true;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
index db0fb54..8caecc7 100644
--- a/libunwindstack/benchmarks/unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -20,6 +20,9 @@
 
 #include <benchmark/benchmark.h>
 
+#include <android-base/strings.h>
+
+#include <unwindstack/Elf.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -80,4 +83,63 @@
 }
 BENCHMARK(BM_cached_unwind);
 
+static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
+                       unwindstack::MapInfo** build_id_map_info) {
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+    return;
+  }
+
+  // Find the libc.so share library and use that for benchmark purposes.
+  *build_id_map_info = nullptr;
+  for (unwindstack::MapInfo* map_info : maps) {
+    if (map_info->offset == 0 && map_info->GetBuildID() != "") {
+      *build_id_map_info = map_info;
+      break;
+    }
+  }
+
+  if (*build_id_map_info == nullptr) {
+    state.SkipWithError("Failed to find a map with a BuildID.");
+  }
+}
+
+static void BM_get_build_id_from_elf(benchmark::State& state) {
+  unwindstack::LocalMaps maps;
+  unwindstack::MapInfo* build_id_map_info;
+  Initialize(state, maps, &build_id_map_info);
+
+  unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
+                                                    unwindstack::Regs::CurrentArch());
+  if (!elf->valid()) {
+    state.SkipWithError("Cannot get valid elf from map.");
+  }
+
+  for (auto _ : state) {
+    uintptr_t id = build_id_map_info->build_id;
+    if (id != 0) {
+      delete reinterpret_cast<std::string*>(id);
+      build_id_map_info->build_id = 0;
+    }
+    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+  }
+}
+BENCHMARK(BM_get_build_id_from_elf);
+
+static void BM_get_build_id_from_file(benchmark::State& state) {
+  unwindstack::LocalMaps maps;
+  unwindstack::MapInfo* build_id_map_info;
+  Initialize(state, maps, &build_id_map_info);
+
+  for (auto _ : state) {
+    uintptr_t id = build_id_map_info->build_id;
+    if (id != 0) {
+      delete reinterpret_cast<std::string*>(id);
+      build_id_map_info->build_id = 0;
+    }
+    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+  }
+}
+BENCHMARK(BM_get_build_id_from_file);
+
 BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index 0336173..67a9640 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -40,7 +40,7 @@
  public:
   explicit DexFiles(std::shared_ptr<Memory>& memory);
   DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
-  ~DexFiles();
+  virtual ~DexFiles();
 
   DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
 
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 27f7201..00a249f 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -65,8 +65,6 @@
 
   bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
 
-  bool GetBuildID(std::string* build_id);
-
   uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
 
   bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
@@ -74,6 +72,8 @@
 
   ElfInterface* CreateInterfaceFromMemory(Memory* memory);
 
+  std::string GetBuildID();
+
   uint64_t GetLoadBias() { return load_bias_; }
 
   bool IsValidPc(uint64_t pc);
@@ -102,6 +102,8 @@
 
   static uint64_t GetLoadBias(Memory* memory);
 
+  static std::string GetBuildID(Memory* memory);
+
   static void SetCachingEnabled(bool enable);
   static bool CachingEnabled() { return cache_enabled_; }
 
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 52992d9..d41bb13 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -62,7 +62,7 @@
 
   virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
 
-  virtual bool GetBuildID(std::string* build_id) = 0;
+  virtual std::string GetBuildID() = 0;
 
   virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
@@ -100,6 +100,9 @@
   template <typename EhdrType, typename PhdrType>
   static uint64_t GetLoadBias(Memory* memory);
 
+  template <typename EhdrType, typename ShdrType, typename NhdrType>
+  static std::string ReadBuildIDFromMemory(Memory* memory);
+
  protected:
   template <typename AddressType>
   void InitHeadersWithTemplate(uint64_t load_bias);
@@ -128,7 +131,7 @@
   static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
 
   template <typename NhdrType>
-  bool ReadBuildID(std::string* build_id);
+  std::string ReadBuildID();
 
   Memory* memory_;
   std::unordered_map<uint64_t, LoadInfo> pt_loads_;
@@ -192,9 +195,7 @@
     return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
   }
 
-  bool GetBuildID(std::string* build_id) {
-    return ElfInterface::ReadBuildID<Elf32_Nhdr>(build_id);
-  }
+  std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf32_Nhdr>(); }
 
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
@@ -226,9 +227,7 @@
     return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
   }
 
-  bool GetBuildID(std::string* build_id) {
-    return ElfInterface::ReadBuildID<Elf64_Nhdr>(build_id);
-  }
+  std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf64_Nhdr>(); }
 
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index f64b04f..8b7b4b5 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -38,7 +38,7 @@
  public:
   explicit JitDebug(std::shared_ptr<Memory>& memory);
   JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
-  ~JitDebug();
+  virtual ~JitDebug();
 
   Elf* GetElf(Maps* maps, uint64_t pc);
 
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 5e3d6f6..5143ff1 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -38,7 +38,8 @@
         flags(flags),
         name(name),
         prev_map(map_info),
-        load_bias(static_cast<uint64_t>(-1)) {}
+        load_bias(static_cast<uint64_t>(-1)),
+        build_id(0) {}
   MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
           const std::string& name)
       : start(start),
@@ -47,8 +48,9 @@
         flags(flags),
         name(name),
         prev_map(map_info),
-        load_bias(static_cast<uint64_t>(-1)) {}
-  ~MapInfo() = default;
+        load_bias(static_cast<uint64_t>(-1)),
+        build_id(0) {}
+  ~MapInfo();
 
   uint64_t start = 0;
   uint64_t end = 0;
@@ -68,6 +70,11 @@
 
   std::atomic_uint64_t load_bias;
 
+  // This is a pointer to a new'd std::string.
+  // Using an atomic value means that we don't need to lock and will
+  // make it easier to move to a fine grained lock in the future.
+  std::atomic_uintptr_t build_id;
+
   // This function guarantees it will never return nullptr.
   Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
 
@@ -75,6 +82,10 @@
 
   Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
 
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
+
+  std::string GetBuildID();
+
  private:
   MapInfo(const MapInfo&) = delete;
   void operator=(const MapInfo&) = delete;
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index d7bbd9d..f4788d7 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,7 +24,9 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/DexFiles.h>
 #include <unwindstack/Error.h>
+#include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -32,9 +34,7 @@
 namespace unwindstack {
 
 // Forward declarations.
-class DexFiles;
 class Elf;
-class JitDebug;
 enum ArchEnum : uint8_t;
 
 struct FrameData {
@@ -67,7 +67,12 @@
       : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
     frames_.reserve(max_frames);
   }
-  ~Unwinder() = default;
+  Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
+      : max_frames_(max_frames), maps_(maps), process_memory_(process_memory) {
+    frames_.reserve(max_frames);
+  }
+
+  virtual ~Unwinder() = default;
 
   void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
               const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
@@ -81,6 +86,10 @@
 
   void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
 
+  void SetRegs(Regs* regs) { regs_ = regs; }
+  Maps* GetMaps() { return maps_; }
+  std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
+
   // Disabling the resolving of names results in the function name being
   // set to an empty string and the function offset being set to zero.
   void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
@@ -92,7 +101,9 @@
   ErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
 
- private:
+ protected:
+  Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
+
   void FillInDexFrame();
   void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
                    uint64_t pc_adjustment);
@@ -110,6 +121,22 @@
   ErrorData last_error_;
 };
 
+class UnwinderFromPid : public Unwinder {
+ public:
+  UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
+  virtual ~UnwinderFromPid() = default;
+
+  bool Init(ArchEnum arch);
+
+ private:
+  pid_t pid_;
+  std::unique_ptr<Maps> maps_ptr_;
+  std::unique_ptr<JitDebug> jit_debug_ptr_;
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  std::unique_ptr<DexFiles> dex_files_ptr_;
+#endif
+};
+
 }  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_UNWINDER_H
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index c2bd0f6..946bc3c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -72,9 +72,7 @@
 
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
   bool GetGlobalVariable(const std::string&, uint64_t*) override;
-  bool GetBuildID(std::string*) override {
-    return false;
-  }
+  std::string GetBuildID() override { return fake_build_id_; }
 
   bool Step(uint64_t, Regs*, Memory*, bool*) override;
 
@@ -82,6 +80,9 @@
     globals_[global] = offset;
   }
 
+  void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
+  void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
+
   static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
   static void FakePushStepData(const StepData data) { steps_.push_back(data); }
 
@@ -96,6 +97,7 @@
 
  private:
   std::unordered_map<std::string, uint64_t> globals_;
+  std::string fake_build_id_;
 
   static std::deque<FunctionData> functions_;
   static std::deque<StepData> steps_;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 6023dc4..7239749 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -1228,9 +1228,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_TRUE(elf->GetBuildID(&build_id));
-  EXPECT_STREQ(build_id.c_str(), "BUILDID");
+  ASSERT_EQ("BUILDID", elf->GetBuildID());
 }
 
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
@@ -1292,9 +1290,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_TRUE(elf->GetBuildID(&build_id));
-  EXPECT_STREQ(build_id.c_str(), "BUILDID");
+  ASSERT_EQ("BUILDID", elf->GetBuildID());
 }
 
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
@@ -1346,8 +1342,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_FALSE(elf->GetBuildID(&build_id));
+  ASSERT_EQ("", elf->GetBuildID());
 }
 
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
@@ -1399,8 +1394,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_FALSE(elf->GetBuildID(&build_id));
+  ASSERT_EQ("", elf->GetBuildID());
 }
 
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
@@ -1452,8 +1446,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_FALSE(elf->GetBuildID(&build_id));
+  ASSERT_EQ("", elf->GetBuildID());
 }
 
 TEST_F(ElfInterfaceTest, build_id32) {
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index f7689ce..1ff2306 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -311,7 +311,7 @@
   void InitHeaders(uint64_t) override {}
   bool GetSoname(std::string*) override { return false; }
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
-  bool GetBuildID(std::string*) override { return false; }
+  std::string GetBuildID() override { return ""; }
 
   MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
   MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
new file mode 100644
index 0000000..3b89c59
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 <elf.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/test_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetBuildIDTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    tf_.reset(new TemporaryFile);
+
+    memory_ = new MemoryFake;
+    elf_ = new ElfFake(new MemoryFake);
+    elf_interface_ = new ElfInterfaceFake(memory_);
+    elf_->FakeSetInterface(elf_interface_);
+    elf_container_.reset(elf_);
+    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+  }
+
+  void MultipleThreadTest(std::string expected_build_id);
+
+  MemoryFake* memory_;
+  ElfFake* elf_;
+  ElfInterfaceFake* elf_interface_;
+  std::unique_ptr<ElfFake> elf_container_;
+  std::unique_ptr<MapInfo> map_info_;
+  std::unique_ptr<TemporaryFile> tf_;
+};
+
+TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+
+  EXPECT_EQ("", info.GetBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_elf) {
+  map_info_->elf.reset(elf_container_.release());
+  elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+  EXPECT_EQ("FAKE_BUILD_ID", map_info_->GetBuildID());
+}
+
+void MapInfoGetBuildIDTest::MultipleThreadTest(std::string expected_build_id) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  std::string build_id_values[kNumConcurrentThreads];
+  std::vector<std::thread*> threads;
+
+  std::atomic_bool wait;
+  wait = true;
+  // Create all of the threads and have them do the GetLoadBias at the same time
+  // to make it likely that a race will occur.
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    std::thread* thread = new std::thread([i, this, &wait, &build_id_values]() {
+      while (wait)
+        ;
+      build_id_values[i] = map_info_->GetBuildID();
+    });
+    threads.push_back(thread);
+  }
+
+  // Set them all going and wait for the threads to finish.
+  wait = false;
+  for (auto thread : threads) {
+    thread->join();
+    delete thread;
+  }
+
+  // Now verify that all of the elf files are exactly the same and valid.
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    EXPECT_EQ(expected_build_id, build_id_values[i]) << "Thread " << i << " mismatched.";
+  }
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists) {
+  map_info_->elf.reset(elf_container_.release());
+  elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+  MultipleThreadTest("FAKE_BUILD_ID");
+}
+
+static void InitElfData(int fd) {
+  Elf32_Ehdr ehdr;
+  TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr);
+  ehdr.e_shstrndx = 2;
+  off_t offset = 0;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(ehdr)), write(fd, &ehdr, sizeof(ehdr)));
+
+  char note_section[128];
+  Elf32_Nhdr note_header = {};
+  note_header.n_namesz = 4;   // "GNU"
+  note_header.n_descsz = 12;  // "ELF_BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  memcpy(&note_section[note_offset], "ELF_BUILDID", sizeof("ELF_BUILDID"));
+  note_offset += sizeof("ELF_BUILDID");
+
+  Elf32_Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_section);
+  offset += ehdr.e_shoff + sizeof(shdr);
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  offset += sizeof(shdr);
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+  offset = 0xf500;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(".note.gnu.build-id")),
+            write(fd, ".note.gnu.build-id", sizeof(".note.gnu.build-id")));
+
+  offset = 0xb000;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(note_section)),
+            write(fd, note_section, sizeof(note_section)));
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_memory) {
+  InitElfData(tf_->fd);
+
+  EXPECT_EQ("ELF_BUILDID", map_info_->GetBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists_in_memory) {
+  InitElfData(tf_->fd);
+
+  MultipleThreadTest("ELF_BUILDID");
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 99f8fa3..d7b8485 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -88,6 +88,12 @@
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
+
+  // Now verify that an empty process memory returns an invalid elf object.
+  info.elf.reset();
+  elf = info.GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 0000000..e2cbb98
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "ElfFake.h"
+
+namespace unwindstack {
+
+TEST(MapInfoTest, maps_constructor_const_char) {
+  MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
+  MapInfo map_info(&prev_map, 1, 2, 3, 4, "map");
+
+  EXPECT_EQ(&prev_map, map_info.prev_map);
+  EXPECT_EQ(1UL, map_info.start);
+  EXPECT_EQ(2UL, map_info.end);
+  EXPECT_EQ(3UL, map_info.offset);
+  EXPECT_EQ(4UL, map_info.flags);
+  EXPECT_EQ("map", map_info.name);
+  EXPECT_EQ(static_cast<uint64_t>(-1), map_info.load_bias);
+  EXPECT_EQ(0UL, map_info.elf_offset);
+  EXPECT_TRUE(map_info.elf.get() == nullptr);
+}
+
+TEST(MapInfoTest, maps_constructor_string) {
+  std::string name("string_map");
+  MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
+  MapInfo map_info(&prev_map, 1, 2, 3, 4, name);
+
+  EXPECT_EQ(&prev_map, map_info.prev_map);
+  EXPECT_EQ(1UL, map_info.start);
+  EXPECT_EQ(2UL, map_info.end);
+  EXPECT_EQ(3UL, map_info.offset);
+  EXPECT_EQ(4UL, map_info.flags);
+  EXPECT_EQ("string_map", map_info.name);
+  EXPECT_EQ(static_cast<uint64_t>(-1), map_info.load_bias);
+  EXPECT_EQ(0UL, map_info.elf_offset);
+  EXPECT_TRUE(map_info.elf.get() == nullptr);
+}
+
+TEST(MapInfoTest, get_function_name) {
+  ElfFake* elf = new ElfFake(nullptr);
+  ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+  elf->FakeSetInterface(interface);
+  interface->FakePushFunctionData(FunctionData("function", 1000));
+
+  MapInfo map_info(nullptr, 1, 2, 3, 4, "");
+  map_info.elf.reset(elf);
+
+  std::string name;
+  uint64_t offset;
+  ASSERT_TRUE(map_info.GetFunctionName(1000, &name, &offset));
+  EXPECT_EQ("function", name);
+  EXPECT_EQ(1000UL, offset);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index aab9ec2..0588a84 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -210,8 +210,8 @@
   ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
       "  #00 pc 0001a9f8  libc.so (abort+64)\n"
-      "  #01 pc 00006a1b  libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
-      "  #02 pc 00007441  libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
+      "  #01 pc 00006a1b  libbase.so (android::base::DefaultAborter(char const*)+6)\n"
+      "  #02 pc 00007441  libbase.so (android::base::LogMessage::~LogMessage()+748)\n"
       "  #03 pc 00015147  /does/not/exist/libhidlbase.so\n",
       frame_info);
   EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
@@ -234,9 +234,10 @@
   ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
       "  #00 pc 0006dc49  libandroid_runtime.so "
-      "(_ZN7android14AndroidRuntime15javaThreadShellEPv+80)\n"
+      "(android::AndroidRuntime::javaThreadShell(void*)+80)\n"
       "  #01 pc 0006dce5  libandroid_runtime.so "
-      "(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
+      "(android::AndroidRuntime::javaCreateThreadEtc(int (*)(void*), void*, char const*, int, "
+      "unsigned int, void**))\n",
       frame_info);
   EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
   EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
@@ -257,10 +258,10 @@
       "  #01 pc 000000000042a078  libunwindstack_test (SignalMiddleFunction+8)\n"
       "  #02 pc 000000000042a08c  libunwindstack_test (SignalOuterFunction+8)\n"
       "  #03 pc 000000000042d8fc  libunwindstack_test "
-      "(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n"
+      "(unwindstack::RemoteThroughSignal(int, unsigned int)+20)\n"
       "  #04 pc 000000000042d8d8  libunwindstack_test "
-      "(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
-      "  #05 pc 0000000000455d70  libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
+      "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+32)\n"
+      "  #05 pc 0000000000455d70  libunwindstack_test (testing::Test::Run()+392)\n",
       frame_info);
   EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
   EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
@@ -296,54 +297,57 @@
   std::string frame_info(DumpFrames(unwinder));
   ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
   EXPECT_EQ(
-      "  #00 pc 00068fb8  libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
+      "  #00 pc 00068fb8  libarttestd.so (art::CauseSegfault()+72)\n"
       "  #01 pc 00067f00  libarttestd.so (Java_Main_unwindInProcess+10032)\n"
       "  #02 pc 000021a8  137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
       "boolean)+136)\n"
       "  #03 pc 0000fe80  anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
       "  #04 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #05 pc 00146ab5  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #06 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #07 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #08 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+234)\n"
       "  #09 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #10 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
       "  #11 pc 0000fe03  anonymous:ee74c000 (int Main.compare(Main, Main)+51)\n"
       "  #12 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #13 pc 00146ab5  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #14 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #15 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #16 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+234)\n"
       "  #17 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #18 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
       "  #19 pc 0000fd3b  anonymous:ee74c000 (int Main.compare(java.lang.Object, "
       "java.lang.Object)+107)\n"
       "  #20 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #21 pc 00146ab5  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #22 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #23 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #24 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+234)\n"
       "  #25 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #26 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
       "  #27 pc 0000fbdb  anonymous:ee74c000 (int "
@@ -351,81 +355,86 @@
       "java.util.Comparator)+331)\n"
       "  #28 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
       "  #29 pc 00146acb  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+907)\n"
       "  #30 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #31 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #32 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+234)\n"
       "  #33 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #34 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
       "  #35 pc 0000f624  anonymous:ee74c000 (boolean Main.foo()+164)\n"
       "  #36 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #37 pc 00146ab5  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #38 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #39 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #40 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+234)\n"
       "  #41 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #42 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
       "  #43 pc 0000eedb  anonymous:ee74c000 (void Main.runPrimary()+59)\n"
       "  #44 pc 006ad4d2  libartd.so (art_quick_invoke_stub+338)\n"
       "  #45 pc 00146ab5  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+885)\n"
       "  #46 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #47 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #48 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+234)\n"
       "  #49 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #50 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
       "  #51 pc 0000ac21  anonymous:ee74c000 (void Main.main(java.lang.String[])+97)\n"
       "  #52 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
       "  #53 pc 00146acb  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+907)\n"
       "  #54 pc 0039cf0d  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
       "  #55 pc 00392552  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+354)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+354)\n"
       "  #56 pc 0039399a  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+234)\n"
       "  #57 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
       "  #58 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
       "  #59 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
       "  #60 pc 00146acb  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+907)\n"
       "  #61 pc 005aac95  libartd.so "
-      "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
-      "8ArgArrayEPNS_6JValueEPKc+85)\n"
+      "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
+      "art::ArgArray*, art::JValue*, char const*)+85)\n"
       "  #62 pc 005aab5a  libartd.so "
-      "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
-      "jmethodIDPc+362)\n"
+      "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
+      "_jmethodID*, char*)+362)\n"
       "  #63 pc 0048a3dd  libartd.so "
-      "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n"
+      "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+125)\n"
       "  #64 pc 0018448c  libartd.so "
-      "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_"
-      "9Primitive4TypeENS_10InvokeTypeE+1964)\n"
+      "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, char*, "
+      "art::Primitive::Type, art::InvokeType)+1964)\n"
       "  #65 pc 0017cf06  libartd.so "
-      "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n"
+      "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+70)\n"
       "  #66 pc 00001d8c  dalvikvm32 "
-      "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n"
+      "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+60)\n"
       "  #67 pc 00001a80  dalvikvm32 (main+1312)\n"
       "  #68 pc 00018275  libc.so\n",
       frame_info);
@@ -597,32 +606,34 @@
       "  #03 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
       "  #04 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
       "  #05 pc 000bf7a9  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #06 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #07 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #08 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+128)\n"
       "  #09 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
       "  #10 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
       "  #11 pc 00011c31  anonymous:e2796000 (int Main.compare(Main, Main)+64)\n"
       "  #12 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
       "  #13 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
       "  #14 pc 000bf7a9  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #15 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #16 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #17 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+128)\n"
       "  #18 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
       "  #19 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
       "  #20 pc 00011b77  anonymous:e2796000 (int Main.compare(java.lang.Object, "
@@ -630,16 +641,17 @@
       "  #21 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
       "  #22 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
       "  #23 pc 000bf7a9  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #24 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #25 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #26 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+128)\n"
       "  #27 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
       "  #28 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
       "  #29 pc 00011a29  anonymous:e2796000 (int "
@@ -648,85 +660,90 @@
       "  #30 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
       "  #31 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
       "  #32 pc 000bf7bb  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+882)\n"
       "  #33 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #34 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #35 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+128)\n"
       "  #36 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
       "  #37 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
       "  #38 pc 0001139b  anonymous:e2796000 (boolean Main.foo()+178)\n"
       "  #39 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
       "  #40 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
       "  #41 pc 000bf7a9  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #42 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #43 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #44 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+128)\n"
       "  #45 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
       "  #46 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
       "  #47 pc 00010aa7  anonymous:e2796000 (void Main.runPrimary()+70)\n"
       "  #48 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
       "  #49 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
       "  #50 pc 000bf7a9  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+864)\n"
       "  #51 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #52 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #53 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+128)\n"
       "  #54 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
       "  #55 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
       "  #56 pc 0000ba99  anonymous:e2796000 (void Main.main(java.lang.String[])+144)\n"
       "  #57 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
       "  #58 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
       "  #59 pc 000bf7bb  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+882)\n"
       "  #60 pc 00247833  libartd.so "
-      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
-      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+      "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
       "  #61 pc 0022e935  libartd.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+244)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+244)\n"
       "  #62 pc 0022f71d  libartd.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+128)\n"
       "  #63 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
       "  #64 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
       "  #65 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
       "  #66 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
       "  #67 pc 000bf7bb  libartd.so "
-      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+      "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+      "const*)+882)\n"
       "  #68 pc 003b292d  libartd.so "
-      "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
-      "8ArgArrayEPNS_6JValueEPKc+52)\n"
+      "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
+      "art::ArgArray*, art::JValue*, char const*)+52)\n"
       "  #69 pc 003b26c3  libartd.so "
-      "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
-      "jmethodIDSt9__va_list+210)\n"
+      "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
+      "_jmethodID*, std::__va_list)+210)\n"
       "  #70 pc 00308411  libartd.so "
-      "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+76)\n"
+      "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+76)\n"
       "  #71 pc 000e6a9f  libartd.so "
-      "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDSt9__va_listNS_"
-      "9Primitive4TypeENS_10InvokeTypeE+1486)\n"
+      "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, "
+      "std::__va_list, art::Primitive::Type, art::InvokeType)+1486)\n"
       "  #72 pc 000e19b9  libartd.so "
-      "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+40)\n"
+      "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+40)\n"
       "  #73 pc 0000159f  dalvikvm32 "
-      "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+30)\n"
+      "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+30)\n"
       "  #74 pc 00001349  dalvikvm32 (main+896)\n"
       "  #75 pc 000850c9  libc.so\n",
       frame_info);
@@ -997,41 +1014,44 @@
       "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
       "  #02 pc 004135bb  libart.so (art_quick_osr_stub+42)\n"
       "  #03 pc 002657a5  libart.so "
-      "(_ZN3art3jit3Jit25MaybeDoOnStackReplacementEPNS_6ThreadEPNS_9ArtMethodEjiPNS_6JValueE+876)\n"
+      "(art::jit::Jit::MaybeDoOnStackReplacement(art::Thread*, art::ArtMethod*, unsigned int, int, "
+      "art::JValue*)+876)\n"
       "  #04 pc 004021a7  libart.so (MterpMaybeDoOnStackReplacement+86)\n"
       "  #05 pc 00412474  libart.so (ExecuteMterpImpl+66164)\n"
       "  #06 pc cd8365b0  <unknown>\n"  // symbol in dex file
       "  #07 pc 001d7f1b  libart.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+374)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+374)\n"
       "  #08 pc 001dc593  libart.so "
-      "(_ZN3art11interpreter33ArtInterpreterToInterpreterBridgeEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameEPNS_6JValueE+154)\n"
+      "(art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, "
+      "art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+154)\n"
       "  #09 pc 001f4d01  libart.so "
-      "(_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_"
-      "11InstructionEtPNS_6JValueE+732)\n"
+      "(bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, "
+      "art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+732)\n"
       "  #10 pc 003fe427  libart.so (MterpInvokeInterface+1354)\n"
       "  #11 pc 00405b94  libart.so (ExecuteMterpImpl+14740)\n"
       "  #12 pc 7004873e  <unknown>\n"  // symbol in dex file
       "  #13 pc 001d7f1b  libart.so "
-      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
-      "6JValueEb+374)\n"
+      "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+      "art::ShadowFrame&, art::JValue, bool)+374)\n"
       "  #14 pc 001dc4d5  libart.so "
-      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
-      "20CodeItemDataAccessorEPNS_11ShadowFrameE+92)\n"
+      "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+      "const&, art::ShadowFrame*)+92)\n"
       "  #15 pc 003f25ab  libart.so (artQuickToInterpreterBridge+970)\n"
       "  #16 pc 00417aff  libart.so (art_quick_to_interpreter_bridge+30)\n"
       "  #17 pc 00413575  libart.so (art_quick_invoke_stub_internal+68)\n"
       "  #18 pc 00418531  libart.so (art_quick_invoke_stub+236)\n"
-      "  #19 pc 000b468d  libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+136)\n"
+      "  #19 pc 000b468d  libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned "
+      "int, art::JValue*, char const*)+136)\n"
       "  #20 pc 00362f49  libart.so "
-      "(_ZN3art12_GLOBAL__N_118InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_"
-      "9ArtMethodEPNS0_8ArgArrayEPNS_6JValueEPKc+52)\n"
+      "(art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable "
+      "const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char "
+      "const*)+52)\n"
       "  #21 pc 00363cd9  libart.so "
-      "(_ZN3art35InvokeVirtualOrInterfaceWithJValuesERKNS_33ScopedObjectAccessAlreadyRunnableEP8_"
-      "jobjectP10_jmethodIDP6jvalue+332)\n"
-      "  #22 pc 003851dd  libart.so (_ZN3art6Thread14CreateCallbackEPv+868)\n"
-      "  #23 pc 00062925  libc.so (_ZL15__pthread_startPv+22)\n"
+      "(art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, "
+      "_jobject*, _jmethodID*, jvalue*)+332)\n"
+      "  #22 pc 003851dd  libart.so (art::Thread::CreateCallback(void*)+868)\n"
+      "  #23 pc 00062925  libc.so (__pthread_start(void*)+22)\n"
       "  #24 pc 0001de39  libc.so (__start_thread+24)\n",
       frame_info);
   EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
@@ -1107,8 +1127,8 @@
       "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
       "  #02 pc 004135bb  libart.so (art_quick_osr_stub+42)\n"
 
-      "  #03 pc 003851dd  libart.so (_ZN3art6Thread14CreateCallbackEPv+868)\n"
-      "  #04 pc 00062925  libc.so (_ZL15__pthread_startPv+22)\n"
+      "  #03 pc 003851dd  libart.so (art::Thread::CreateCallback(void*)+868)\n"
+      "  #04 pc 00062925  libc.so (__pthread_start(void*)+22)\n"
       "  #05 pc 0001de39  libc.so (__start_thread+24)\n",
       frame_info);
 
@@ -1139,22 +1159,22 @@
       "  #01 pc 0032bfeb  libunwindstack_test (SignalMiddleFunction+2)\n"
       "  #02 pc 0032bff3  libunwindstack_test (SignalOuterFunction+2)\n"
       "  #03 pc 0032fed3  libunwindstack_test "
-      "(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
+      "(unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)\n"
       "  #04 pc 00026528  libc.so\n"
       "  #05 pc 00000000  <unknown>\n"
       "  #06 pc 0032c2d9  libunwindstack_test (InnerFunction+736)\n"
       "  #07 pc 0032cc4f  libunwindstack_test (MiddleFunction+42)\n"
       "  #08 pc 0032cc81  libunwindstack_test (OuterFunction+42)\n"
       "  #09 pc 0032e547  libunwindstack_test "
-      "(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
+      "(unwindstack::RemoteThroughSignal(int, unsigned int)+270)\n"
       "  #10 pc 0032ed99  libunwindstack_test "
-      "(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
-      "  #11 pc 00354453  libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
-      "  #12 pc 00354de7  libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
-      "  #13 pc 00355105  libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
+      "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+16)\n"
+      "  #11 pc 00354453  libunwindstack_test (testing::Test::Run()+154)\n"
+      "  #12 pc 00354de7  libunwindstack_test (testing::TestInfo::Run()+194)\n"
+      "  #13 pc 00355105  libunwindstack_test (testing::TestCase::Run()+180)\n"
       "  #14 pc 0035a215  libunwindstack_test "
-      "(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
-      "  #15 pc 00359f4f  libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
+      "(testing::internal::UnitTestImpl::RunAllTests()+664)\n"
+      "  #15 pc 00359f4f  libunwindstack_test (testing::UnitTest::Run()+110)\n"
       "  #16 pc 0034d3db  libunwindstack_test (main+38)\n"
       "  #17 pc 00092c0d  libc.so (__libc_init+48)\n"
       "  #18 pc 0004202f  libunwindstack_test (_start_main+38)\n",
@@ -1213,9 +1233,9 @@
   EXPECT_EQ(
       "  #00 pc 0005138c  libc.so (__ioctl+8)\n"
       "  #01 pc 0002140f  libc.so (ioctl+30)\n"
-      "  #02 pc 00039535  libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+204)\n"
-      "  #03 pc 00039633  libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+10)\n"
-      "  #04 pc 00039b57  libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+38)\n"
+      "  #02 pc 00039535  libbinder.so (android::IPCThreadState::talkWithDriver(bool)+204)\n"
+      "  #03 pc 00039633  libbinder.so (android::IPCThreadState::getAndExecuteCommand()+10)\n"
+      "  #04 pc 00039b57  libbinder.so (android::IPCThreadState::joinThreadPool(bool)+38)\n"
       "  #05 pc 00000c21  mediaserver (main+104)\n"
       "  #06 pc 00084b89  libc.so (__libc_init+48)\n"
       "  #07 pc 00000b77  mediaserver (_start_main+38)\n",
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index ea992c7..c747eab 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -44,6 +44,14 @@
 
 namespace unwindstack {
 
+enum TestTypeEnum : uint8_t {
+  TEST_TYPE_LOCAL_UNWINDER = 0,
+  TEST_TYPE_LOCAL_UNWINDER_FROM_PID,
+  TEST_TYPE_LOCAL_WAIT_FOR_FINISH,
+  TEST_TYPE_REMOTE,
+  TEST_TYPE_REMOTE_WITH_INVALID_CALL,
+};
+
 static std::atomic_bool g_ready;
 static volatile bool g_ready_for_remote;
 static volatile bool g_signal_ready_for_remote;
@@ -72,7 +80,10 @@
 
 extern "C" void SignalInnerFunction() {
   g_signal_ready_for_remote = true;
-  while (!g_finish.load()) {
+  // Avoid any function calls because not every instruction will be
+  // unwindable.
+  // This method of looping is only used when testing a remote unwind.
+  while (true) {
   }
 }
 
@@ -88,10 +99,10 @@
   SignalOuterFunction();
 }
 
-static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder& unwinder) {
+static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder* unwinder) {
   std::string unwind;
-  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
-    unwind += unwinder.FormatFrame(i) + '\n';
+  for (size_t i = 0; i < unwinder->NumFrames(); i++) {
+    unwind += unwinder->FormatFrame(i) + '\n';
   }
 
   return std::string(
@@ -100,14 +111,10 @@
          function_names.front() + "\n" + "Unwind data:\n" + unwind;
 }
 
-static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
-                         std::vector<const char*> expected_function_names) {
-  auto process_memory(Memory::CreateProcessMemory(pid));
+static void VerifyUnwind(Unwinder* unwinder, std::vector<const char*> expected_function_names) {
+  unwinder->Unwind();
 
-  Unwinder unwinder(512, maps, regs, process_memory);
-  unwinder.Unwind();
-
-  for (auto& frame : unwinder.frames()) {
+  for (auto& frame : unwinder->frames()) {
     if (frame.function_name == expected_function_names.back()) {
       expected_function_names.pop_back();
       if (expected_function_names.empty()) {
@@ -119,35 +126,63 @@
   ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
 }
 
+static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
+                         std::vector<const char*> expected_function_names) {
+  auto process_memory(Memory::CreateProcessMemory(pid));
+
+  Unwinder unwinder(512, maps, regs, process_memory);
+  VerifyUnwind(&unwinder, expected_function_names);
+}
+
 // This test assumes that this code is compiled with optimizations turned
 // off. If this doesn't happen, then all of the calls will be optimized
 // away.
-extern "C" void InnerFunction(bool local, bool trigger_invalid_call) {
-  if (local) {
-    LocalMaps maps;
-    ASSERT_TRUE(maps.Parse());
-    std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
-    RegsGetLocal(regs.get());
-
-    VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
-  } else {
+extern "C" void InnerFunction(TestTypeEnum test_type) {
+  if (test_type == TEST_TYPE_LOCAL_WAIT_FOR_FINISH) {
+    while (!g_finish.load()) {
+    }
+    return;
+  }
+  if (test_type == TEST_TYPE_REMOTE || test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
     g_ready_for_remote = true;
     g_ready = true;
-    if (trigger_invalid_call) {
+    if (test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
       void (*crash_func)() = nullptr;
       crash_func();
     }
-    while (!g_finish.load()) {
+    // Avoid any function calls because not every instruction will be
+    // unwindable.
+    // This method of looping is only used when testing a remote unwind.
+    while (true) {
     }
+    return;
   }
+
+  std::unique_ptr<Unwinder> unwinder;
+  std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+  RegsGetLocal(regs.get());
+  std::unique_ptr<Maps> maps;
+
+  if (test_type == TEST_TYPE_LOCAL_UNWINDER) {
+    maps.reset(new LocalMaps());
+    ASSERT_TRUE(maps->Parse());
+    auto process_memory(Memory::CreateProcessMemory(getpid()));
+    unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
+  } else {
+    UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
+    ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
+    unwinder_from_pid->SetRegs(regs.get());
+    unwinder.reset(unwinder_from_pid);
+  }
+  VerifyUnwind(unwinder.get(), kFunctionOrder);
 }
 
-extern "C" void MiddleFunction(bool local, bool trigger_invalid_call) {
-  InnerFunction(local, trigger_invalid_call);
+extern "C" void MiddleFunction(TestTypeEnum test_type) {
+  InnerFunction(test_type);
 }
 
-extern "C" void OuterFunction(bool local, bool trigger_invalid_call) {
-  MiddleFunction(local, trigger_invalid_call);
+extern "C" void OuterFunction(TestTypeEnum test_type) {
+  MiddleFunction(test_type);
 }
 
 class UnwindTest : public ::testing::Test {
@@ -156,7 +191,11 @@
 };
 
 TEST_F(UnwindTest, local) {
-  OuterFunction(true, false);
+  OuterFunction(TEST_TYPE_LOCAL_UNWINDER);
+}
+
+TEST_F(UnwindTest, local_use_from_pid) {
+  OuterFunction(TEST_TYPE_LOCAL_UNWINDER_FROM_PID);
 }
 
 void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
@@ -191,7 +230,7 @@
 TEST_F(UnwindTest, remote) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    OuterFunction(false, false);
+    OuterFunction(TEST_TYPE_REMOTE);
     exit(0);
   }
   ASSERT_NE(-1, pid);
@@ -212,11 +251,39 @@
       << "ptrace detach failed with unexpected error: " << strerror(errno);
 }
 
+TEST_F(UnwindTest, unwind_from_pid_remote) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    OuterFunction(TEST_TYPE_REMOTE);
+    exit(0);
+  }
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  bool completed;
+  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+  ASSERT_TRUE(regs.get() != nullptr);
+
+  UnwinderFromPid unwinder(512, pid);
+  ASSERT_TRUE(unwinder.Init(regs->Arch()));
+  unwinder.SetRegs(regs.get());
+
+  VerifyUnwind(&unwinder, kFunctionOrder);
+
+  // Verify that calling the same object works again.
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+      << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
 TEST_F(UnwindTest, from_context) {
   std::atomic_int tid(0);
   std::thread thread([&]() {
     tid = syscall(__NR_gettid);
-    OuterFunction(false, false);
+    OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
   });
 
   struct sigaction act, oldact;
@@ -266,7 +333,7 @@
     act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
     ASSERT_EQ(0, sigaction(signal, &act, &oldact));
 
-    OuterFunction(false, signal == SIGSEGV);
+    OuterFunction(signal != SIGSEGV ? TEST_TYPE_REMOTE : TEST_TYPE_REMOTE_WITH_INVALID_CALL);
     exit(0);
   }
   ASSERT_NE(-1, pid);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 1fdeee5..d88531f 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -749,6 +749,23 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
+// Verify that a speculative frame does not cause a crash when it wasn't
+// really added due to a filter.
+TEST_F(UnwinderTest, speculative_frame_check_with_no_frames) {
+  regs_.set_pc(0x23000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetReturnAddress(0x23100);
+  regs_.FakeSetReturnAddressValid(true);
+
+  Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+
+  std::vector<std::string> skip_names{"libanother.so"};
+  unwinder.Unwind(&skip_names);
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(0U, unwinder.NumFrames());
+}
+
 // Verify that an unwind stops when a frame is in given suffix.
 TEST_F(UnwinderTest, map_ignore_suffixes) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
@@ -1080,6 +1097,12 @@
             Unwinder::FormatFrame(frame, false));
   EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true));
 
+  // Verify the function name is demangled.
+  frame.function_name = "_ZN4funcEv";
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (func())",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (func())", Unwinder::FormatFrame(frame, true));
+
   frame.function_name = "";
   EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so", Unwinder::FormatFrame(frame, false));
   EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so", Unwinder::FormatFrame(frame, true));
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index e729453..1812e50 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -57,12 +57,6 @@
 }
 
 void DoUnwind(pid_t pid) {
-  unwindstack::RemoteMaps remote_maps(pid);
-  if (!remote_maps.Parse()) {
-    printf("Failed to parse map data.\n");
-    return;
-  }
-
   unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
   if (regs == nullptr) {
     printf("Unable to get remote reg data\n");
@@ -95,15 +89,13 @@
   }
   printf("\n");
 
-  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
-  unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+  unwindstack::UnwinderFromPid unwinder(1024, pid);
+  if (!unwinder.Init(regs->Arch())) {
+    printf("Failed to init unwinder object.\n");
+    return;
+  }
 
-  unwindstack::JitDebug jit_debug(process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs->Arch());
-
-  unwindstack::DexFiles dex_files(process_memory);
-  unwinder.SetDexFiles(&dex_files, regs->Arch());
-
+  unwinder.SetRegs(regs);
   unwinder.Unwind();
 
   // Print the frames.
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 5ae8874..4f67d67 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -240,12 +240,6 @@
     return 1;
   }
 
-  unwindstack::RemoteMaps maps(pid);
-  if (!maps.Parse()) {
-    printf("Unable to parse maps.\n");
-    return 1;
-  }
-
   // Save the current state of the registers.
   if (!SaveRegs(regs)) {
     return 1;
@@ -253,35 +247,38 @@
 
   // Do an unwind so we know how much of the stack to save, and what
   // elf files are involved.
+  unwindstack::UnwinderFromPid unwinder(1024, pid);
+  if (!unwinder.Init(regs->Arch())) {
+    printf("Unable to init unwinder object.\n");
+    return 1;
+  }
+  unwinder.SetRegs(regs);
   uint64_t sp = regs->sp();
-  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
-  unwindstack::JitDebug jit_debug(process_memory);
-  unwindstack::Unwinder unwinder(1024, &maps, regs, process_memory);
-  unwinder.SetJitDebug(&jit_debug, regs->Arch());
   unwinder.Unwind();
 
   std::unordered_map<uint64_t, map_info_t> maps_by_start;
   std::vector<std::pair<uint64_t, uint64_t>> stacks;
+  unwindstack::Maps* maps = unwinder.GetMaps();
   uint64_t sp_map_start = 0;
-  unwindstack::MapInfo* map_info = maps.Find(sp);
+  unwindstack::MapInfo* map_info = maps->Find(sp);
   if (map_info != nullptr) {
     stacks.emplace_back(std::make_pair(sp, map_info->end));
     sp_map_start = map_info->start;
   }
 
   for (const auto& frame : unwinder.frames()) {
-    map_info = maps.Find(frame.sp);
+    map_info = maps->Find(frame.sp);
     if (map_info != nullptr && sp_map_start != map_info->start) {
       stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
       sp_map_start = map_info->start;
     }
 
     if (maps_by_start.count(frame.map_start) == 0) {
-      map_info = maps.Find(frame.map_start);
+      map_info = maps->Find(frame.map_start);
 
       auto info = FillInAndGetMapInfo(maps_by_start, map_info);
       bool file_copied = false;
-      SaveMapInformation(process_memory, info, &file_copied);
+      SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
 
       // If you are using a a linker that creates two maps (one read-only, one
       // read-executable), it's necessary to capture the previous map
@@ -291,7 +288,7 @@
           prev_map->flags == PROT_READ && map_info->name == prev_map->name &&
           maps_by_start.count(prev_map->start) == 0) {
         info = FillInAndGetMapInfo(maps_by_start, prev_map);
-        SaveMapInformation(process_memory, info, &file_copied);
+        SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
       }
     }
   }
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 3f2dfb0..3f5b88b 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -123,8 +123,8 @@
     printf("Soname: %s\n", soname.c_str());
   }
 
-  std::string build_id;
-  if (elf.GetBuildID(&build_id)) {
+  std::string build_id = elf.GetBuildID();
+  if (!build_id.empty()) {
     printf("Build ID: ");
     for (size_t i = 0; i < build_id.size(); ++i) {
       printf("%02hhx", build_id[i]);
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 3e8417e..fb7ca32 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -22,11 +22,13 @@
         "liblog_headers",
         "libsystem_headers",
         "libcutils_headers",
+        "libprocessgroup_headers",
     ],
     export_header_lib_headers: [
         "liblog_headers",
         "libsystem_headers",
         "libcutils_headers",
+        "libprocessgroup_headers",
     ],
     export_include_dirs: ["include"],
 
@@ -82,6 +84,7 @@
 
             shared_libs: [
                 "libcutils",
+                "libprocessgroup",
                 "libdl",
                 "libvndksupport",
             ],
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 64bc402..31ca138 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -36,7 +36,7 @@
 
 #include <utils/Log.h>
 
-#include <cutils/sched_policy.h>
+#include <processgroup/sched_policy.h>
 
 #if defined(__ANDROID__)
 # define __android_unused
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 5f0a51f..24a745a 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -275,25 +275,6 @@
   return ss-s;
 }
 
-
-char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n)
-{
-  char16_t *q = dst;
-  const char16_t *p = src;
-  char ch;
-
-  while (n) {
-    n--;
-    *q++ = ch = *p++;
-    if ( !ch )
-      break;
-  }
-
-  *q = 0;
-
-  return dst;
-}
-
 size_t strnlen16(const char16_t *s, size_t maxlen)
 {
   const char16_t *ss = s;
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index 61a1b4f..a2aaa47 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -28,7 +28,6 @@
 size_t strlen16(const char16_t *);
 size_t strnlen16(const char16_t *, size_t);
 char16_t *strcpy16(char16_t *, const char16_t *);
-char16_t *strncpy16(char16_t *, const char16_t *, size_t);
 char16_t *strstr16(const char16_t*, const char16_t*);
 
 // Version of comparison that supports embedded NULs.
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 903d0e2..f9ed57c 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -5,6 +5,7 @@
     shared_libs: [
         "libcutils",
         "liblog",
+        "libprocessgroup",
     ],
     static_libs: [
         "libstatslogc",
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 7794f81..ca78c38 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "lowmemorykiller"
 
+#include <dirent.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <pwd.h>
@@ -28,18 +29,22 @@
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <sys/mman.h>
+#include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/sysinfo.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
 #include <cutils/properties.h>
+#include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <lmkd.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
 #include <log/log_time.h>
+#include <system/thread_defs.h>
 
 #ifdef LMKD_LOG_STATS
 #include "statslog.h"
@@ -1261,6 +1266,41 @@
     return maxprocp;
 }
 
+static void set_process_group_and_prio(int pid, SchedPolicy sp, int prio) {
+    DIR* d;
+    char proc_path[PATH_MAX];
+    struct dirent* de;
+
+    snprintf(proc_path, sizeof(proc_path), "/proc/%d/task", pid);
+    if (!(d = opendir(proc_path))) {
+        ALOGW("Failed to open %s; errno=%d: process pid(%d) might have died", proc_path, errno,
+              pid);
+        return;
+    }
+
+    while ((de = readdir(d))) {
+        int t_pid;
+
+        if (de->d_name[0] == '.') continue;
+        t_pid = atoi(de->d_name);
+
+        if (!t_pid) {
+            ALOGW("Failed to get t_pid for '%s' of pid(%d)", de->d_name, pid);
+            continue;
+        }
+
+        if (setpriority(PRIO_PROCESS, t_pid, prio) && errno != ESRCH) {
+            ALOGW("Unable to raise priority of killing t_pid (%d): errno=%d", t_pid, errno);
+        }
+
+        if (set_cpuset_policy(t_pid, sp)) {
+            ALOGW("Failed to set_cpuset_policy on pid(%d) t_pid(%d) to %d", pid, t_pid, (int)sp);
+            continue;
+        }
+    }
+    closedir(d);
+}
+
 static int last_killed_pid = -1;
 
 /* Kill one process specified by procp.  Returns the size of the process killed */
@@ -1301,6 +1341,9 @@
 
     /* CAP_KILL required */
     r = kill(pid, SIGKILL);
+
+    set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
+
     inc_killcnt(procp->oomadj);
     ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
         taskname, pid, uid, procp->oomadj, tasksize * page_k);
diff --git a/lmkd/tests/Android.bp b/lmkd/tests/Android.bp
index cbf44e9..4e845fd 100644
--- a/lmkd/tests/Android.bp
+++ b/lmkd/tests/Android.bp
@@ -18,6 +18,7 @@
     shared_libs: [
         "libbase",
         "liblog",
+        "libcutils",
     ],
 
     static_libs: [
diff --git a/logcat/Android.bp b/logcat/Android.bp
index b0563a6..5030b15 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -24,10 +24,10 @@
     ],
     shared_libs: [
         "libbase",
-        "libcutils",
-        "liblog",
         "libpcrecpp",
+        "libprocessgroup",
     ],
+    static_libs: ["liblog"],
     logtags: ["event.logtags"],
 }
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 87bc6ae..15e07fe 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -49,11 +49,11 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
+#include <processgroup/sched_policy.h>
 #include <system/thread_defs.h>
 
 #include <pcrecpp.h>
diff --git a/logcat/tests/Android.bp b/logcat/tests/Android.bp
index e1f4d6f..ab84150 100644
--- a/logcat/tests/Android.bp
+++ b/logcat/tests/Android.bp
@@ -48,10 +48,8 @@
 cc_test {
     name: "logcat-unit-tests",
     defaults: ["logcat-tests-defaults"],
-    shared_libs: [
-        "liblog",
-        "libbase",
-    ],
+    shared_libs: ["libbase"],
+    static_libs: ["liblog"],
     srcs: [
         "logcat_test.cpp",
         "logcatd_test.cpp",
diff --git a/logd/.clang-format b/logd/.clang-format
deleted file mode 100644
index 393c309..0000000
--- a/logd/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/logd/.clang-format b/logd/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/logd/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/logd/Android.bp b/logd/Android.bp
index 5c79976..360f2fe 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -63,16 +63,26 @@
 
     srcs: ["main.cpp"],
 
-    static_libs: ["liblogd"],
+    static_libs: [
+        "liblog",
+        "liblogd",
+    ],
 
     shared_libs: [
         "libsysutils",
-        "liblog",
         "libcutils",
         "libbase",
         "libpackagelistparser",
+        "libprocessgroup",
         "libcap",
     ],
 
     cflags: ["-Werror"],
 }
+
+
+prebuilt_etc {
+    name: "logtagd.rc",
+    src: "logtagd.rc",
+    sub_dir: "init",
+}
diff --git a/logd/Android.mk b/logd/Android.mk
deleted file mode 100644
index b3ce560..0000000
--- a/logd/Android.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logtagd.rc
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
-
-include $(BUILD_PREBUILT)
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 18d5287..a21555c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -111,7 +111,7 @@
 }
 
 std::map<std::string, std::string> LogAudit::populateDenialMap() {
-    std::ifstream bug_file("/system/etc/selinux/selinux_denial_metadata");
+    std::ifstream bug_file("/vendor/etc/selinux/selinux_denial_metadata");
     std::string line;
     // allocate a map for the static map pointer in auditParse to keep track of,
     // this function only runs once
@@ -300,7 +300,7 @@
         return 0;
     }
 
-    log_time now;
+    log_time now(log_time::EPOCH);
 
     static const char audit_str[] = " audit(";
     char* timeptr = strstr(str, audit_str);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index fbdbf79..9cbc7c4 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -399,7 +399,7 @@
                         ((*it)->getLogId() != LOG_ID_KERNEL))) {
         mLogElements.push_back(elem);
     } else {
-        log_time end = log_time::EPOCH;
+        log_time end(log_time::EPOCH);
         bool end_set = false;
         bool end_always = false;
 
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 513c0c3..edd326a 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -197,10 +197,9 @@
     // NOTREACHED
 }
 
-log_time LogKlog::correction =
-    (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
-        ? log_time::EPOCH
-        : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
+log_time LogKlog::correction = (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
+                                       ? log_time(log_time::EPOCH)
+                                       : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
 
 LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
                  bool auditd)
@@ -268,7 +267,7 @@
     static const char real_format[] = "%Y-%m-%d %H:%M:%S.%09q UTC";
     if (len < (ssize_t)(strlen(real_format) + 5)) return;
 
-    log_time real;
+    log_time real(log_time::EPOCH);
     const char* ep = real.strptime(real_string, real_format);
     if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
         return;
@@ -282,20 +281,20 @@
     tm.tm_isdst = -1;
     localtime_r(&now, &tm);
     if ((tm.tm_gmtoff < 0) && ((-tm.tm_gmtoff) > (long)real.tv_sec)) {
-        real = log_time::EPOCH;
+        real = log_time(log_time::EPOCH);
     } else {
         real.tv_sec += tm.tm_gmtoff;
     }
     if (monotonic > real) {
-        correction = log_time::EPOCH;
+        correction = log_time(log_time::EPOCH);
     } else {
         correction = real - monotonic;
     }
 }
 
-void LogKlog::sniffTime(log_time& now, const char*& buf, ssize_t len,
-                        bool reverse) {
-    if (len <= 0) return;
+log_time LogKlog::sniffTime(const char*& buf, ssize_t len, bool reverse) {
+    log_time now(log_time::EPOCH);
+    if (len <= 0) return now;
 
     const char* cp = nullptr;
     if ((len > 10) && (*buf == '[')) {
@@ -310,7 +309,7 @@
         }
         buf = cp;
 
-        if (isMonotonic()) return;
+        if (isMonotonic()) return now;
 
         const char* b;
         if (((b = android::strnstr(cp, len, suspendStr))) &&
@@ -329,11 +328,11 @@
             //     trigger a check for ntp-induced or hardware clock drift.
             log_time real(CLOCK_REALTIME);
             log_time mono(CLOCK_MONOTONIC);
-            correction = (real < mono) ? log_time::EPOCH : (real - mono);
+            correction = (real < mono) ? log_time(log_time::EPOCH) : (real - mono);
         } else if (((b = android::strnstr(cp, len, suspendedStr))) &&
                    (((b += strlen(suspendStr)) - cp) < len)) {
             len -= b - cp;
-            log_time real;
+            log_time real(log_time::EPOCH);
             char* endp;
             real.tv_sec = strtol(b, &endp, 10);
             if ((*endp == '.') && ((endp - b) < len)) {
@@ -345,7 +344,7 @@
                 }
                 if (reverse) {
                     if (real > correction) {
-                        correction = log_time::EPOCH;
+                        correction = log_time(log_time::EPOCH);
                     } else {
                         correction -= real;
                     }
@@ -363,6 +362,7 @@
             now = log_time(CLOCK_REALTIME);
         }
     }
+    return now;
 }
 
 pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
@@ -451,8 +451,7 @@
     }
     parseKernelPrio(cp, len - (cp - buf));
 
-    log_time now;
-    sniffTime(now, cp, len - (cp - buf), true);
+    log_time now = sniffTime(cp, len - (cp - buf), true);
 
     const char* suspended = android::strnstr(buf, len, suspendedStr);
     if (!suspended || (suspended > cp)) {
@@ -468,7 +467,7 @@
     }
     parseKernelPrio(cp, len - (cp - buf));
 
-    sniffTime(now, cp, len - (cp - buf), true);
+    sniffTime(cp, len - (cp - buf), true);
 }
 
 // Convert kernel log priority number into an Android Logger priority number
@@ -548,8 +547,7 @@
     const char* p = buf;
     int pri = parseKernelPrio(p, len);
 
-    log_time now;
-    sniffTime(now, p, len - (p - buf), false);
+    log_time now = sniffTime(p, len - (p - buf), false);
 
     // sniff for start marker
     const char* start = android::strnstr(p, len - (p - buf), klogdStr);
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index bb92dd2..6bfd6a8 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -55,11 +55,10 @@
     }
 
    protected:
-    void sniffTime(log_time& now, const char*& buf, ssize_t len, bool reverse);
-    pid_t sniffPid(const char*& buf, ssize_t len);
-    void calculateCorrection(const log_time& monotonic, const char* real_string,
-                             ssize_t len);
-    virtual bool onDataAvailable(SocketClient* cli);
+     log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
+     pid_t sniffPid(const char*& buf, ssize_t len);
+     void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
+     virtual bool onDataAvailable(SocketClient* cli);
 };
 
 #endif
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 1ab9dd1..f19e7b0 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -30,6 +30,7 @@
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <log/log_event_list.h>
 #include <log/log_properties.h>
@@ -38,6 +39,8 @@
 #include "LogTags.h"
 #include "LogUtils.h"
 
+using android::base::make_scope_guard;
+
 static LogTags* logtags;
 
 const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
@@ -316,27 +319,29 @@
         std::string Format;
         android_log_list_element elem;
         {
-            android_log_event_list ctx(log_msg);
-            elem = ctx.read();
+            auto ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+                                                 log_msg.entry.len - sizeof(uint32_t));
+            auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+            elem = android_log_read_next(ctx);
             if (elem.type != EVENT_TYPE_LIST) {
                 continue;
             }
-            elem = ctx.read();
+            elem = android_log_read_next(ctx);
             if (elem.type != EVENT_TYPE_INT) {
                 continue;
             }
             Tag = elem.data.int32;
-            elem = ctx.read();
+            elem = android_log_read_next(ctx);
             if (elem.type != EVENT_TYPE_STRING) {
                 continue;
             }
             Name = std::string(elem.data.string, elem.len);
-            elem = ctx.read();
+            elem = android_log_read_next(ctx);
             if (elem.type != EVENT_TYPE_STRING) {
                 continue;
             }
             Format = std::string(elem.data.string, elem.len);
-            elem = ctx.read();
+            elem = android_log_read_next(ctx);
         }
         if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
 
@@ -524,10 +529,22 @@
     tag2format_const_iterator iform = tag2format.find(tag);
     std::string Format = (iform != tag2format.end()) ? iform->second : "";
 
-    __android_log_event_list ctx(TAG_DEF_LOG_TAG);
-    ctx << tag << Name << Format;
-    std::string buffer(ctx);
-    if (buffer.length() <= 0) return;  // unlikely
+    auto ctx = create_android_logger(TAG_DEF_LOG_TAG);
+    auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+    if (android_log_write_int32(ctx, static_cast<int32_t>(tag) < 0) ||
+        android_log_write_string8_len(ctx, Name.c_str(), Name.size()) < 0 ||
+        android_log_write_string8_len(ctx, Format.c_str(), Format.size()) < 0) {
+        return;
+    }
+
+    const char* cp = nullptr;
+    ssize_t len = android_log_write_list_buffer(ctx, &cp);
+
+    if (len <= 0 || cp == nullptr) {
+        return;
+    }
+
+    std::string buffer(cp, len);
 
     /*
      *  struct {
diff --git a/logd/main.cpp b/logd/main.cpp
index 8c38d9a..fd3cdf8 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -38,12 +38,12 @@
 #include <android-base/macros.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/properties.h>
-#include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
 #include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
+#include <processgroup/sched_policy.h>
 #include <utils/threads.h>
 
 #include "CommandListener.h"
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
index f15beb2..83a194f 100644
--- a/logd/tests/Android.bp
+++ b/logd/tests/Android.bp
@@ -38,9 +38,9 @@
     shared_libs: [
         "libbase",
         "libcutils",
-        "liblog",
         "libselinux",
     ],
+    static_libs: ["liblog"],
 }
 
 // Build tests for the logger. Run with:
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index 4432f9e..9ee7869 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -115,7 +115,7 @@
     uint32_t header_size;
 } __attribute__((packed));
 
-/* When the boot image header has a version of 1, the structure of the boot
+/* When the boot image header has a version of 2, the structure of the boot
  * image is as follows:
  *
  * +---------------------+
@@ -129,17 +129,21 @@
  * +---------------------+
  * | recovery dtbo/acpio | p pages
  * +---------------------+
+ * | dtb                 | q pages
+ * +---------------------+
+
  * n = (kernel_size + page_size - 1) / page_size
  * m = (ramdisk_size + page_size - 1) / page_size
  * o = (second_size + page_size - 1) / page_size
  * p = (recovery_dtbo_size + page_size - 1) / page_size
+ * q = (dtb_size + page_size - 1) / page_size
  *
  * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
+ * 1. kernel, ramdisk and DTB are required (size != 0)
  * 2. recovery_dtbo/recovery_acpio is required for recovery.img in non-A/B
  *    devices(recovery_dtbo_size != 0)
  * 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second) at
+ * 4. load each element (kernel, ramdisk, second, dtb) at
  *    the specified physical address (kernel_addr, etc)
  * 5. If booting to recovery mode in a non-A/B device, extract recovery
  *    dtbo/acpio and apply the correct set of overlays on the base device tree
@@ -150,3 +154,7 @@
  * 8. if second_size != 0: jump to second_addr
  *    else: jump to kernel_addr
  */
+struct boot_img_hdr_v2 : public boot_img_hdr_v1 {
+    uint32_t dtb_size; /* size in bytes for DTB image */
+    uint64_t dtb_addr; /* physical load address for DTB image */
+} __attribute__((packed));
diff --git a/mkbootimg/mkbootimg.py b/mkbootimg/mkbootimg.py
index 2eb2bab..92b11a5 100644
--- a/mkbootimg/mkbootimg.py
+++ b/mkbootimg/mkbootimg.py
@@ -62,7 +62,13 @@
 
 
 def write_header(args):
+    BOOT_IMAGE_HEADER_V1_SIZE = 1648
+    BOOT_IMAGE_HEADER_V2_SIZE = 1660
     BOOT_MAGIC = 'ANDROID!'.encode()
+
+    if (args.header_version > 2):
+        raise ValueError('Boot header version %d not supported' % args.header_version)
+
     args.output.write(pack('8s', BOOT_MAGIC))
     args.output.write(pack('10I',
         filesize(args.kernel),                          # size in bytes
@@ -85,6 +91,8 @@
 
     if args.header_version > 0:
         update_sha(sha, args.recovery_dtbo)
+    if args.header_version > 1:
+        update_sha(sha, args.dtb)
 
     img_id = pack('32s', sha.digest())
 
@@ -97,8 +105,16 @@
             args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
         else:
             args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
-        args.output.write(pack('I', args.output.tell() + 4))         # size of boot header
 
+    # Populate boot image header size for header versions 1 and 2.
+    if args.header_version == 1:
+        args.output.write(pack('I', BOOT_IMAGE_HEADER_V1_SIZE))
+    elif args.header_version == 2:
+        args.output.write(pack('I', BOOT_IMAGE_HEADER_V2_SIZE))
+
+    if args.header_version > 1:
+        args.output.write(pack('I', filesize(args.dtb)))   # size in bytes
+        args.output.write(pack('Q', args.base + args.dtb_offset)) # dtb physical load address
     pad_file(args.output, args.pagesize)
     return img_id
 
@@ -161,6 +177,7 @@
                         required=True)
     parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
     parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
+    parser.add_argument('--dtb', help='path to dtb', type=FileType('rb'))
     recovery_dtbo_group = parser.add_mutually_exclusive_group()
     recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
     recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
@@ -172,6 +189,8 @@
     parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
     parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
                         default=0x00f00000)
+    parser.add_argument('--dtb_offset', help='dtb offset', type=parse_int, default=0x01f00000)
+
     parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
                         default=0)
     parser.add_argument('--os_patch_level', help='operating system patch level',
@@ -196,6 +215,8 @@
 
     if args.header_version > 0:
         write_padded_file(args.output, args.recovery_dtbo, args.pagesize)
+    if args.header_version > 1:
+        write_padded_file(args.output, args.dtb, args.pagesize)
 
 def main():
     args = parse_cmdline()
diff --git a/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
old mode 100644
new mode 100755
index c37acd5..6b5d5d0
--- a/mkbootimg/unpack_bootimg.py
+++ b/mkbootimg/unpack_bootimg.py
@@ -15,7 +15,7 @@
 
 """unpacks the bootimage.
 
-Extracts the kernel, ramdisk, second bootloader and recovery dtbo images.
+Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images.
 """
 
 from __future__ import print_function
@@ -82,6 +82,14 @@
         print('boot header size: %s' % boot_header_size)
     else:
         recovery_dtbo_size = 0
+    if version > 1:
+        dtb_size = unpack('I', args.boot_img.read(4))[0]
+        print('dtb size: %s' % dtb_size)
+        dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
+        print('dtb address: %s' % dtb_load_address)
+    else:
+        dtb_size = 0
+
 
     # The first page contains the boot header
     num_header_pages = 1
@@ -103,6 +111,15 @@
     if recovery_dtbo_size > 0:
         image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
                                 'recovery_dtbo'))
+    if dtb_size > 0:
+        num_second_pages = get_number_of_pages(second_size, page_size)
+        num_recovery_dtbo_pages = get_number_of_pages(recovery_dtbo_size, page_size)
+        dtb_offset = page_size * (
+            num_header_pages + num_kernel_pages + num_ramdisk_pages + num_second_pages +
+            num_recovery_dtbo_pages
+        )
+
+        image_info_list.append((dtb_offset, dtb_size, 'dtb'))
 
     for image_info in image_info_list:
         extract_image(image_info[0], image_info[1], args.boot_img,
@@ -113,7 +130,7 @@
     """parse command line arguments"""
     parser = ArgumentParser(
         description='Unpacks boot.img/recovery.img, extracts the kernel,'
-        'ramdisk, second bootloader and recovery dtbo')
+        'ramdisk, second bootloader, recovery dtbo and dtb')
     parser.add_argument(
         '--boot_img',
         help='path to boot image',
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 6637deb..5c57d69 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -13,5 +13,6 @@
     ],
     stl: "none",
     system_shared_libs: [],
+    header_libs: ["libc_headers"],
     export_include_dirs: ["include"],
 }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index dbf8b9e..5a6f41b 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -22,7 +22,6 @@
 LOCAL_MODULE := init-debug.rc
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
 
 # Start of runtime APEX compatibility.
@@ -274,14 +273,11 @@
 include $(LOCAL_PATH)/update_and_install_ld_config.mk
 endef
 
-# For VNDK snapshot versions prior to 28, ld.config.txt is installed from the
-# prebuilt under /prebuilts/vndk
 vndk_snapshots := $(wildcard prebuilts/vndk/*)
 supported_vndk_snapshot_versions := \
-  $(strip $(foreach ver,$(patsubst prebuilts/vndk/v%,%,$(vndk_snapshots)),\
-    $(if $(call math_gt_or_eq,$(ver),28),$(ver),)))
-$(eval $(foreach ver,$(supported_vndk_snapshot_versions),\
-  $(call build_versioned_ld_config,$(ver))))
+  $(strip $(patsubst prebuilts/vndk/v%,%,$(vndk_snapshots)))
+$(foreach ver,$(supported_vndk_snapshot_versions),\
+  $(eval $(call build_versioned_ld_config,$(ver))))
 
 vndk_snapshots :=
 supported_vndk_snapshot_versions :=
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 461184a..6d9d810 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -32,6 +32,73 @@
 namespace.default.asan.search.paths +=           /odm/${LIB}
 
 ###############################################################################
+# APEX related namespaces.
+###############################################################################
+
+additional.namespaces = runtime,conscrypt,media
+namespace.default.asan.permitted.paths += /apex/com.android.resolv/${LIB}
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime
+namespace.default.asan.links = runtime
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
+namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.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
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = default
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+
+###############################################################################
 # Namespace config for binaries under /postinstall.
 # Only one default namespace is defined and it has no directories other than
 # /system/lib and /product/lib in the search paths. This is because linker
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 2dda648..05f75bf 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -28,7 +28,7 @@
 dir.postinstall = /postinstall
 
 [system]
-additional.namespaces = runtime,sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -109,7 +109,11 @@
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.default.links = runtime
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
 namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.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
@@ -123,12 +127,47 @@
 
 # Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
 # when it exists.
 namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = default
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+
+###############################################################################
 # "sphal" namespace
 #
 # SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
@@ -280,7 +319,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = system,vndk
+additional.namespaces = runtime,system,vndk
 
 ###############################################################################
 # "default" namespace
@@ -291,6 +330,9 @@
 # partition (VNDK and LLNDK libraries) are not loaded here but from the
 # separate namespace 'system'. The delegation to the system namespace is done
 # via the 'namespace.default.link.system.shared_libs' property below.
+#
+# '#VNDK27#' TAG is only for building ld.config.27.txt for backward
+# compatibility. (TODO:b/123390078) Move them to a separate file.
 ###############################################################################
 namespace.default.isolated = true
 namespace.default.visible = true
@@ -300,11 +342,17 @@
 
 namespace.default.permitted.paths  = /odm
 namespace.default.permitted.paths += /vendor
+#VNDK27#namespace.default.search.paths += /vendor/${LIB}/hw
+#VNDK27#namespace.default.search.paths += /vendor/${LIB}/egl
 
 namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
+#VNDK27#namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/hw
+#VNDK27#namespace.default.asan.search.paths +=           /vendor/${LIB}/hw
+#VNDK27#namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/egl
+#VNDK27#namespace.default.asan.search.paths +=           /vendor/${LIB}/egl
 
 namespace.default.asan.permitted.paths  = /data/asan/odm
 namespace.default.asan.permitted.paths +=           /odm
@@ -317,6 +365,20 @@
 namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
 
 ###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = system
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.system.allow_all_shared_libs = true
+
+###############################################################################
 # "vndk" namespace
 #
 # This namespace is where VNDK and VNDK-SP libraries are loaded for
@@ -373,6 +435,9 @@
 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 = libdexfile_external.so
+
 ###############################################################################
 # Namespace config for binaries under /postinstall.
 # Only default namespace is defined and default has no directories
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 33b4698..335369e 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -28,7 +28,7 @@
 dir.postinstall = /postinstall
 
 [system]
-additional.namespaces = runtime,sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -55,9 +55,14 @@
 namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the platform namespace in the com.android.runtime APEX
+# ld.config.txt.
 namespace.default.links = runtime
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
 namespace.default.link.runtime.shared_libs  = libart.so:libartd.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
 
@@ -68,14 +73,50 @@
 ###############################################################################
 namespace.runtime.isolated = true
 
-# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+# Keep in sync with the default namespace in the com.android.runtime APEX
+# ld.config.txt.
 namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
 namespace.runtime.links = default
 # TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
 # when it exists.
 namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = default
+namespace.conscrypt.link.default.shared_libs  = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+
+###############################################################################
 # "sphal" namespace
 #
 # SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
@@ -221,6 +262,8 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
+additional.namespaces = runtime
+
 namespace.default.isolated = false
 
 namespace.default.search.paths  = /odm/${LIB}
@@ -260,6 +303,23 @@
 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 = libdexfile_external.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+###############################################################################
+namespace.runtime.isolated = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
 ###############################################################################
 # Namespace config for binaries under /postinstall.
 # Only default namespace is defined and default has no directories
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b34399d..537bf86 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -12,12 +12,6 @@
 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
 
@@ -46,8 +40,6 @@
     # cgroup for system_server and surfaceflinger
     mkdir /dev/memcg/system 0550 system system
 
-    prepare_bootstrap_bionic
-
     start ueventd
 
 on init
@@ -117,6 +109,8 @@
     mkdir /mnt/runtime/read/self 0755 root root
     mkdir /mnt/runtime/write 0755 root root
     mkdir /mnt/runtime/write/self 0755 root root
+    mkdir /mnt/runtime/full 0755 root root
+    mkdir /mnt/runtime/full/self 0755 root root
 
     # Symlink to keep legacy apps working in multi-user world
     symlink /storage/self/primary /sdcard
@@ -346,18 +340,14 @@
     trigger boot
 
 on post-fs
-    # Load properties from
-    #     /system/build.prop,
-    #     /odm/build.prop,
-    #     /vendor/build.prop and
-    #     /factory/factory.prop
-    load_system_props
     start vold
     exec - system system -- /system/bin/vdc checkpoint markBootAttempt
 
     # 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
@@ -593,14 +583,6 @@
     # 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.
@@ -822,3 +804,6 @@
 service flash_recovery /system/bin/install-recovery.sh
     class main
     oneshot
+
+on property:apexd.status=ready
+    parse_apex_configs
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index ac87979..e8c5d8e 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -4,6 +4,8 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    socket blastula_pool stream 660 root system
+    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index a535846..9c7e807 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -4,6 +4,8 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    socket blastula_pool stream 660 root system
+    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -19,5 +21,7 @@
     user root
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
+    socket blastula_pool_secondary stream 660 root system
+    updatable
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 6fc810b..9908c99 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -4,6 +4,8 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    socket blastula_pool stream 660 root system
+    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 7ddd52e..0b5edff 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -4,6 +4,8 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    socket blastula_pool stream 660 root system
+    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -19,5 +21,7 @@
     user root
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
+    socket blastula_pool_secondary stream 660 root system
+    updatable
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 79bed7b..450be66 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -88,6 +88,7 @@
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_SUFFIX := $(vndk_version_suffix)
 $(LOCAL_BUILT_MODULE): PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
 $(LOCAL_BUILT_MODULE): PRIVATE_COMP_CHECK_SCRIPT := $(compatibility_check_script)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_TAG := \#VNDK$(vndk_version)\#
 deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
   $(vndkprivate_libraries_file)
 ifeq ($(check_backward_compatibility),true)
@@ -114,10 +115,12 @@
 	paste -sd ":" $(PRIVATE_INTERMEDIATES_DIR)/private_llndk | \
 	sed -i.bak -e "s?%PRIVATE_LLNDK_LIBRARIES%?$$(cat -)?g" $@
 
-	$(hide) sed -i.bak -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $@
-	$(hide) sed -i.bak -e 's?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g' $@
-	$(hide) sed -i.bak -e 's?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g' $@
-	$(hide) sed -i.bak -e 's?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g' $@
+	$(hide) sed -i.bak -e "s?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g" $@
+	$(hide) sed -i.bak -e "s?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g" $@
+	$(hide) sed -i.bak -e "s?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g" $@
+	$(hide) sed -i.bak -e "s?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g" $@
+	$(hide) sed -i.bak -e "s?^$(PRIVATE_VNDK_VERSION_TAG)??g" $@
+	$(hide) sed -i.bak "/^\#VNDK[0-9]\{2\}\#.*$$/d" $@
 	$(hide) rm -f $@.bak
 
 ld_config_template :=
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index dc36596..e1de130 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -160,6 +160,7 @@
     std::string dest_path_default = "/mnt/runtime/default/" + label;
     std::string dest_path_read = "/mnt/runtime/read/" + label;
     std::string dest_path_write = "/mnt/runtime/write/" + label;
+    std::string dest_path_full = "/mnt/runtime/full/" + label;
 
     umask(0);
     if (multi_user) {
@@ -172,7 +173,10 @@
                                       default_normal, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
-                                      derive_gid, default_normal, use_esdfs)) {
+                                      derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
+                                      default_normal, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -186,7 +190,10 @@
                                       derive_gid, default_normal, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
-                                      derive_gid, default_normal, use_esdfs)) {
+                                      derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
+                                      default_normal, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 2d4a26f..5ce43d5 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -18,6 +18,8 @@
         "newfs_msdos",
         "reboot",
         "sh",
+        "simpleperf",
+        "simpleperf_app_runner",
         "tcpdump",
         "toolbox",
         "toybox",
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 77c6167..6897663 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -341,20 +341,14 @@
     if (mConfig.event_time_check_usec &&
         clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
         check_time = false;
-        static time_t state_a;
-        IF_ALOG_RATELIMIT_LOCAL(300, &state_a) {
-            PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
-        }
+        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
     }
 
     event();
 
     if (mConfig.event_time_check_usec && check_time) {
         if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
-            static time_t state_b;
-            IF_ALOG_RATELIMIT_LOCAL(300, &state_b) {
-                PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
-            }
+            PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
             return;
         }
         int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index 39033ad..e2c77c3 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -530,6 +530,9 @@
     const char *device = NULL;
     const char *device_path = "/dev/input";
 
+    /* disable buffering on stdout */
+    setbuf(stdout, NULL);
+
     opterr = 0;
     do {
         c = getopt(argc, argv, "tns:Sv::dpilqc:rh");
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 32499e3..9676b79 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -17,12 +17,10 @@
     vendor: true,
 
     srcs: ["tipc_test.c"],
-    static_libs: [
-        "libtrusty",
-    ],
     shared_libs: [
         "libc",
         "liblog",
+        "libtrusty",
     ],
     gtest: false,
     cflags: [