Merge "liblog: Fix memory leaks"
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..c47230f
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "adbd_test"
+    }
+  ]
+}
diff --git a/adb/Android.bp b/adb/Android.bp
index bccc71a..00e98fe 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -25,6 +25,7 @@
         "-Wvla",
     ],
     rtti: true,
+    cpp_std: "gnu++17",
 
     use_version_lib: true,
 
@@ -41,7 +42,10 @@
 
     target: {
         android: {
-            cflags: ["-DADB_HOST=0"],
+            cflags: [
+                "-DADB_HOST=0",
+                "-Wthread-safety",
+            ],
         },
 
         host: {
@@ -174,6 +178,12 @@
         "libdiagnose_usb",
         "libmdnssd",
         "libusb",
+        "libandroidfw",
+        "libziparchive",
+        "libz",
+        "libutils",
+        "liblog",
+        "libcutils",
     ],
 }
 
@@ -256,6 +266,12 @@
         "liblog",
         "libmdnssd",
         "libusb",
+        "libandroidfw",
+        "libziparchive",
+        "libz",
+        "libutils",
+        "liblog",
+        "libcutils",
     ],
 
     stl: "libc++_static",
@@ -299,6 +315,8 @@
         "daemon/auth.cpp",
         "daemon/jdwp_service.cpp",
         "daemon/usb.cpp",
+        "daemon/usb_ffs.cpp",
+        "daemon/usb_legacy.cpp",
     ],
 
     local_include_dirs: [
@@ -474,7 +492,7 @@
     },
 }
 
-python_binary_host {
+python_test_host {
     name: "adb_integration_test_device",
     main: "test_device.py",
     srcs: [
@@ -483,6 +501,8 @@
     libs: [
         "adb_py",
     ],
+    test_config: "adb_integration_test_device.xml",
+    test_suites: ["general-tests"],
     version: {
         py2: {
             enabled: true,
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 62e8908..9c0eeca 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -73,39 +73,6 @@
         android::base::GetExecutablePath().c_str());
 }
 
-void fatal(const char *fmt, ...) {
-    va_list ap;
-    va_start(ap, fmt);
-    char buf[1024];
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-
-#if ADB_HOST
-    fprintf(stderr, "error: %s\n", buf);
-#else
-    LOG(ERROR) << "error: " << buf;
-#endif
-
-    va_end(ap);
-    abort();
-}
-
-void fatal_errno(const char* fmt, ...) {
-    int err = errno;
-    va_list ap;
-    va_start(ap, fmt);
-    char buf[1024];
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-
-#if ADB_HOST
-    fprintf(stderr, "error: %s: %s\n", buf, strerror(err));
-#else
-    LOG(ERROR) << "error: " << buf << ": " << strerror(err);
-#endif
-
-    va_end(ap);
-    abort();
-}
-
 uint32_t calculate_apacket_checksum(const apacket* p) {
     uint32_t sum = 0;
     for (size_t i = 0; i < p->msg.data_length; ++i) {
@@ -118,7 +85,7 @@
 {
     apacket* p = new apacket();
     if (p == nullptr) {
-      fatal("failed to allocate an apacket");
+        LOG(FATAL) << "failed to allocate an apacket";
     }
 
     memset(&p->msg, 0, sizeof(p->msg));
diff --git a/adb/adb.h b/adb/adb.h
index f434e2d..8c37c4b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -59,7 +59,7 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 40
+#define ADB_SERVER_VERSION 41
 
 using TransportId = uint64_t;
 class atransport;
@@ -124,9 +124,6 @@
 
 void print_packet(const char* label, apacket* p);
 
-void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
-void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
-
 void handle_packet(apacket* p, atransport* t);
 
 int launch_server(const std::string& socket_spec);
diff --git a/adb/adb_integration_test_device.xml b/adb/adb_integration_test_device.xml
new file mode 100644
index 0000000..b892377
--- /dev/null
+++ b/adb/adb_integration_test_device.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config to run adb integration tests for device">
+    <option name="test-suite-tag" value="adb_tests" />
+    <option name="test-suite-tag" value="adb_integration_device" />
+    <target_preparer class="com.android.tradefed.targetprep.SemaphoreTokenTargetPreparer">
+        <option name="disable" value="false" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.adb.AdbStopServerPreparer" />
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="adb_integration_test_device" />
+        <option name="inject-android-serial" value="true" />
+        <option name="test-timeout" value="2m" />
+    </test>
+</configuration>
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index f4a92e3..051ab73 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -21,6 +21,7 @@
 
 #include <algorithm>
 #include <list>
+#include <memory>
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -29,7 +30,6 @@
 
 #include "socket_spec.h"
 #include "sysdeps.h"
-#include "sysdeps/memory.h"
 #include "transport.h"
 
 // A listener is an entity which binds to a local port and, upon receiving a connection on that
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 35017f0..6960345 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -57,11 +57,11 @@
 void close_stdin() {
     int fd = unix_open(kNullFileName, O_RDONLY);
     if (fd == -1) {
-        fatal_errno("failed to open %s", kNullFileName);
+        PLOG(FATAL) << "failed to open " << kNullFileName;
     }
 
     if (TEMP_FAILURE_RETRY(dup2(fd, STDIN_FILENO)) == -1) {
-        fatal_errno("failed to redirect stdin to %s", kNullFileName);
+        PLOG(FATAL) << "failed to redirect stdin to " << kNullFileName;
     }
     unix_close(fd);
 }
@@ -186,6 +186,48 @@
     return line;
 }
 
+std::string dump_header(const amessage* msg) {
+    unsigned command = msg->command;
+    int len = msg->data_length;
+    char cmd[9];
+    char arg0[12], arg1[12];
+    int n;
+
+    for (n = 0; n < 4; n++) {
+        int b = (command >> (n * 8)) & 255;
+        if (b < 32 || b >= 127) break;
+        cmd[n] = (char)b;
+    }
+    if (n == 4) {
+        cmd[4] = 0;
+    } else {
+        // There is some non-ASCII name in the command, so dump the hexadecimal value instead
+        snprintf(cmd, sizeof cmd, "%08x", command);
+    }
+
+    if (msg->arg0 < 256U)
+        snprintf(arg0, sizeof arg0, "%d", msg->arg0);
+    else
+        snprintf(arg0, sizeof arg0, "0x%x", msg->arg0);
+
+    if (msg->arg1 < 256U)
+        snprintf(arg1, sizeof arg1, "%d", msg->arg1);
+    else
+        snprintf(arg1, sizeof arg1, "0x%x", msg->arg1);
+
+    return android::base::StringPrintf("[%s] arg0=%s arg1=%s (len=%d) ", cmd, arg0, arg1, len);
+}
+
+std::string dump_packet(const char* name, const char* func, const apacket* p) {
+    std::string result = name;
+    result += ": ";
+    result += func;
+    result += ": ";
+    result += dump_header(&p->msg);
+    result += dump_hex(p->payload.data(), p->payload.size());
+    return result;
+}
+
 std::string perror_str(const char* msg) {
     return android::base::StringPrintf("%s: %s", msg, strerror(errno));
 }
@@ -251,6 +293,9 @@
     struct passwd pwent;
     struct passwd* result;
     int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+    if (pwent_max == -1) {
+        pwent_max = 16384;
+    }
     std::vector<char> buf(pwent_max);
     int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
     if (rc == 0 && result) {
@@ -274,18 +319,6 @@
     return android_dir;
 }
 
-int syntax_error(const char* fmt, ...) {
-    fprintf(stderr, "adb: usage: ");
-
-    va_list ap;
-    va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-
-    fprintf(stderr, "\n");
-    return 1;
-}
-
 std::string GetLogFilePath() {
 #if defined(_WIN32)
     const char log_name[] = "adb.log";
@@ -295,13 +328,13 @@
     DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
     if (nchars >= arraysize(temp_path) || nchars == 0) {
         // If string truncation or some other error.
-        fatal("cannot retrieve temporary file path: %s\n",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "cannot retrieve temporary file path: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     std::string temp_path_utf8;
     if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
-        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+        PLOG(FATAL) << "cannot convert temporary file path from UTF-16 to UTF-8";
     }
 
     return temp_path_utf8 + log_name;
@@ -311,3 +344,33 @@
     return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
 #endif
 }
+
+[[noreturn]] static void error_exit_va(int error, const char* fmt, va_list va) {
+    fflush(stdout);
+    fprintf(stderr, "%s: ", android::base::Basename(android::base::GetExecutablePath()).c_str());
+
+    vfprintf(stderr, fmt, va);
+
+    if (error != 0) {
+        fprintf(stderr, ": %s", strerror(error));
+    }
+
+    putc('\n', stderr);
+    fflush(stderr);
+
+    exit(EXIT_FAILURE);
+}
+
+void error_exit(const char* fmt, ...) {
+    va_list va;
+    va_start(va, fmt);
+    error_exit_va(0, fmt, va);
+    va_end(va);
+}
+
+void perror_exit(const char* fmt, ...) {
+    va_list va;
+    va_start(va, fmt);
+    error_exit_va(errno, fmt, va);
+    va_end(va);
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f6ce8e2..6d12225 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _ADB_UTILS_H_
-#define _ADB_UTILS_H_
+#pragma once
 
 #include <condition_variable>
 #include <mutex>
@@ -24,7 +23,7 @@
 
 #include <android-base/macros.h>
 
-int syntax_error(const char*, ...) __attribute__((__format__(__printf__, 1, 2)));
+#include "adb.h"
 
 void close_stdin();
 
@@ -42,12 +41,17 @@
 std::string escape_arg(const std::string& s);
 
 std::string dump_hex(const void* ptr, size_t byte_count);
+std::string dump_header(const amessage* msg);
+std::string dump_packet(const char* name, const char* func, const apacket* p);
 
 std::string perror_str(const char* msg);
 
+[[noreturn]] void error_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+[[noreturn]] void perror_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+
 bool set_file_block_mode(int fd, bool block);
 
-extern int adb_close(int fd);
+int adb_close(int fd);
 
 // Given forward/reverse targets, returns true if they look sane. If an error is found, fills
 // |error| and returns false.
@@ -90,5 +94,3 @@
 };
 
 std::string GetLogFilePath();
-
-#endif
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
index 00c2315..e56ef5a 100755
--- a/adb/benchmark_device.py
+++ b/adb/benchmark_device.py
@@ -60,8 +60,6 @@
     if device == None:
         device = adb.get_device()
 
-    lock_max(device)
-
     remote_path = "/dev/null"
     local_path = "/tmp/adb_benchmark_temp"
 
@@ -69,7 +67,7 @@
         f.truncate(file_size_mb * 1024 * 1024)
 
     speeds = list()
-    for _ in range(0, 5):
+    for _ in range(0, 10):
         begin = time.time()
         device.push(local=local_path, remote=remote_path)
         end = time.time()
@@ -81,15 +79,13 @@
     if device == None:
         device = adb.get_device()
 
-    lock_max(device)
-
     remote_path = "/data/local/tmp/adb_benchmark_temp"
     local_path = "/tmp/adb_benchmark_temp"
 
     device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
                   "count=" + str(file_size_mb)])
     speeds = list()
-    for _ in range(0, 5):
+    for _ in range(0, 10):
         begin = time.time()
         device.pull(remote=remote_path, local=local_path)
         end = time.time()
@@ -101,10 +97,8 @@
     if device == None:
         device = adb.get_device()
 
-    lock_max(device)
-
     speeds = list()
-    for _ in range(0, 5):
+    for _ in range(0, 10):
         begin = time.time()
         device.shell(["dd", "if=/dev/zero", "bs=1m",
                       "count=" + str(file_size_mb)])
@@ -114,7 +108,10 @@
     analyze("shell %dMiB" % file_size_mb, speeds)
 
 def main():
-    benchmark_pull()
+    device = adb.get_device()
+    unlock(device)
+    benchmark_push(device)
+    benchmark_pull(device)
 
 if __name__ == "__main__":
     main()
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index c00d955..0008f72 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -14,42 +14,46 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG ADB
+#include "adb_install.h"
 
 #include <stdio.h>
 #include <stdlib.h>
-
-#include "adb.h"
-#include "adb_client.h"
-#include "adb_install.h"
-#include "adb_utils.h"
-#include "client/file_sync_client.h"
-#include "commandline.h"
-#include "fastdeploy.h"
-#include "sysdeps.h"
-
+#include <unistd.h>
 #include <algorithm>
 #include <iostream>
 #include <string>
 #include <vector>
 
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/test_utils.h>
+#include "adb.h"
+#include "adb_client.h"
+#include "adb_utils.h"
+#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+#include "android-base/test_utils.h"
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "fastdeploy.h"
+#include "sysdeps.h"
 
 static constexpr int kFastDeployMinApi = 24;
 
-static bool _use_legacy_install() {
+static bool can_use_feature(const char* feature) {
     FeatureSet features;
     std::string error;
     if (!adb_get_feature_set(&features, &error)) {
         fprintf(stderr, "error: %s\n", error.c_str());
         return true;
     }
-    return !CanUseFeature(features, kFeatureCmd);
+    return CanUseFeature(features, feature);
+}
+
+static bool use_legacy_install() {
+    return !can_use_feature(kFeatureCmd);
+}
+
+static bool is_apex_supported() {
+    return can_use_feature(kFeatureApex);
 }
 
 static int pm_command(int argc, const char** argv) {
@@ -106,7 +110,7 @@
 }
 
 int uninstall_app(int argc, const char** argv) {
-    if (_use_legacy_install()) {
+    if (use_legacy_install()) {
         return uninstall_app_legacy(argc, argv);
     }
     return uninstall_app_streamed(argc, argv);
@@ -137,8 +141,21 @@
 
     // The last argument must be the APK file
     const char* file = argv[argc - 1];
-    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
-        return syntax_error("filename doesn't end .apk: %s", file);
+    if (!android::base::EndsWithIgnoreCase(file, ".apk") &&
+        !android::base::EndsWithIgnoreCase(file, ".apex")) {
+        error_exit("filename doesn't end .apk or .apex: %s", file);
+    }
+
+    bool is_apex = false;
+    if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+        is_apex = true;
+    }
+    if (is_apex && !is_apex_supported()) {
+        error_exit(".apex is not supported on the target device");
+    }
+
+    if (is_apex && use_fastdeploy) {
+        error_exit("--fastdeploy doesn't support .apex files");
     }
 
     if (use_fastdeploy == true) {
@@ -146,41 +163,16 @@
         TemporaryFile patchTmpFile;
 
         FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
-        int metadata_len = extract_metadata(file, metadataFile);
+        extract_metadata(file, metadataFile);
         fclose(metadataFile);
 
-        int result = -1;
-        if (metadata_len <= 0) {
-            printf("failed to extract metadata %d\n", metadata_len);
-            return 1;
-        } else {
-            int create_patch_result = create_patch(file, metadataTmpFile.path, patchTmpFile.path);
-            if (create_patch_result != 0) {
-                printf("Patch creation failure, error code: %d\n", create_patch_result);
-                result = create_patch_result;
-                goto cleanup_streamed_apk;
-            } else {
-                std::vector<const char*> pm_args;
-                // pass all but 1st (command) and last (apk path) parameters through to pm for
-                // session creation
-                for (int i = 1; i < argc - 1; i++) {
-                    pm_args.push_back(argv[i]);
-                }
-                int apply_patch_result =
-                        install_patch(file, patchTmpFile.path, pm_args.size(), pm_args.data());
-                if (apply_patch_result != 0) {
-                    printf("Patch application failure, error code: %d\n", apply_patch_result);
-                    result = apply_patch_result;
-                    goto cleanup_streamed_apk;
-                }
-            }
-        }
-
-    cleanup_streamed_apk:
-        if (use_fastdeploy == true) {
-            delete_device_patch_file(file);
-        }
-        return result;
+        create_patch(file, metadataTmpFile.path, patchTmpFile.path);
+        // 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());
+        delete_device_patch_file(file);
+        return 0;
     } else {
         struct stat sb;
         if (stat(file, &sb) == -1) {
@@ -206,6 +198,10 @@
         // do last to override any user specified value
         cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
 
+        if (is_apex) {
+            cmd += " --apex";
+        }
+
         int remoteFd = adb_connect(cmd, &error);
         if (remoteFd < 0) {
             fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
@@ -247,13 +243,16 @@
     // All other arguments passed through verbatim.
     int last_apk = -1;
     for (int i = argc - 1; i >= 0; i--) {
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
+            error_exit("APEX packages are only compatible with Streamed Install");
+        }
         if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
             last_apk = i;
             break;
         }
     }
 
-    if (last_apk == -1) return syntax_error("need APK file on command line");
+    if (last_apk == -1) error_exit("need APK file on command line");
 
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
@@ -265,29 +264,11 @@
         TemporaryFile patchTmpFile;
 
         FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
-        int metadata_len = extract_metadata(apk_file[0], metadataFile);
+        extract_metadata(apk_file[0], metadataFile);
         fclose(metadataFile);
 
-        if (metadata_len <= 0) {
-            printf("failed to extract metadata %d\n", metadata_len);
-            return 1;
-        } else {
-            int create_patch_result =
-                    create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
-            if (create_patch_result != 0) {
-                printf("Patch creation failure, error code: %d\n", create_patch_result);
-                result = create_patch_result;
-                goto cleanup_apk;
-            } else {
-                int apply_patch_result =
-                        apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
-                if (apply_patch_result != 0) {
-                    printf("Patch application failure, error code: %d\n", apply_patch_result);
-                    result = apply_patch_result;
-                    goto cleanup_apk;
-                }
-            }
-        }
+        create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
+        apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
     } else {
         if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
     }
@@ -347,19 +328,18 @@
             use_localagent = true;
 #endif
         }
-        // TODO: --installlog <filename>
     }
 
     if (installMode == INSTALL_DEFAULT) {
-        if (_use_legacy_install()) {
+        if (use_legacy_install()) {
             installMode = INSTALL_PUSH;
         } else {
             installMode = INSTALL_STREAM;
         }
     }
 
-    if (installMode == INSTALL_STREAM && _use_legacy_install() == true) {
-        return syntax_error("Attempting to use streaming install on unsupported deivce.");
+    if (installMode == INSTALL_STREAM && use_legacy_install() == true) {
+        error_exit("Attempting to use streaming install on unsupported device");
     }
 
     if (use_fastdeploy == true && is_reinstall == false) {
@@ -407,8 +387,13 @@
     uint64_t total_size = 0;
     for (int i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
+            error_exit("APEX packages are not compatible with install-multiple");
+        }
 
-        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+        if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+            android::base::EndsWithIgnoreCase(file, ".dm") ||
+            android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
             struct stat sb;
             if (stat(file, &sb) != -1) total_size += sb.st_size;
             first_apk = i;
@@ -417,10 +402,10 @@
         }
     }
 
-    if (first_apk == -1) return syntax_error("need APK file on command line");
+    if (first_apk == -1) error_exit("need APK file on command line");
 
     std::string install_cmd;
-    if (_use_legacy_install()) {
+    if (use_legacy_install()) {
         install_cmd = "exec:pm";
     } else {
         install_cmd = "exec:cmd package";
@@ -470,9 +455,9 @@
         }
 
         std::string cmd =
-                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
+                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
                                             install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
-                                            session_id, i, android::base::Basename(file).c_str());
+                                            session_id, android::base::Basename(file).c_str());
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
diff --git a/adb/client/adb_install.h b/adb/client/adb_install.h
index e9410a9..5b6c4cb 100644
--- a/adb/client/adb_install.h
+++ b/adb/client/adb_install.h
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef ADB_INSTALL_H
-#define ADB_INSTALL_H
+#pragma once
 
-#include "fastdeploy.h"
+#include <string>
 
 int install_app(int argc, const char** argv);
 int install_multiple_app(int argc, const char** argv);
@@ -26,4 +25,3 @@
 int delete_device_file(const std::string& filename);
 int delete_host_file(const std::string& filename);
 
-#endif
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
index fe98737..8ca44e8 100644
--- a/adb/client/bugreport.cpp
+++ b/adb/client/bugreport.cpp
@@ -197,7 +197,7 @@
 };
 
 int Bugreport::DoIt(int argc, const char** argv) {
-    if (argc > 2) return syntax_error("adb bugreport [PATH]");
+    if (argc > 2) error_exit("usage: adb bugreport [PATH]");
 
     // Gets bugreportz version.
     std::string bugz_stdout, bugz_stderr;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index e38e305..b5bed28 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -63,7 +63,6 @@
 #include "services.h"
 #include "shell_protocol.h"
 #include "sysdeps/chrono.h"
-#include "sysdeps/memory.h"
 
 extern int gListenAll;
 
@@ -72,8 +71,7 @@
 static std::string product_file(const std::string& file) {
     const char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
     if (ANDROID_PRODUCT_OUT == nullptr) {
-        fprintf(stderr, "adb: product directory not specified; set $ANDROID_PRODUCT_OUT\n");
-        exit(1);
+        error_exit("product directory not specified; set $ANDROID_PRODUCT_OUT");
     }
     return std::string{ANDROID_PRODUCT_OUT} + OS_PATH_SEPARATOR_STR + file;
 }
@@ -206,8 +204,8 @@
         " sideload OTAPACKAGE      sideload the given full OTA package\n"
         " root                     restart adbd with root permissions\n"
         " unroot                   restart adbd without root permissions\n"
-        " usb                      restart adb server listening on USB\n"
-        " tcpip PORT               restart adb server listening on TCP on PORT\n"
+        " usb                      restart adbd listening on USB\n"
+        " tcpip PORT               restart adbd listening on TCP on PORT\n"
         "\n"
         "internal debugging:\n"
         " start-server             ensure that there is a server running\n"
@@ -319,7 +317,7 @@
 #ifdef _WIN32
         old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
         if (old_stdin_mode == -1) {
-            fatal_errno("could not set stdin to binary");
+            PLOG(FATAL) << "could not set stdin to binary";
         }
 #endif
     }
@@ -328,7 +326,7 @@
     if (outFd == STDOUT_FILENO) {
         old_stdout_mode = _setmode(STDOUT_FILENO, _O_BINARY);
         if (old_stdout_mode == -1) {
-            fatal_errno("could not set stdout to binary");
+            PLOG(FATAL) << "could not set stdout to binary";
         }
     }
 #endif
@@ -339,7 +337,7 @@
         stdin_raw_restore();
 #ifdef _WIN32
         if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
-            fatal_errno("could not restore stdin mode");
+            PLOG(FATAL) << "could not restore stdin mode";
         }
 #endif
     }
@@ -347,7 +345,7 @@
 #ifdef _WIN32
     if (outFd == STDOUT_FILENO) {
         if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
-            fatal_errno("could not restore stdout mode");
+            PLOG(FATAL) << "could not restore stdout mode";
         }
     }
 #endif
@@ -661,9 +659,9 @@
 
 static int adb_shell(int argc, const char** argv) {
     FeatureSet features;
-    std::string error;
-    if (!adb_get_feature_set(&features, &error)) {
-        fprintf(stderr, "error: %s\n", error.c_str());
+    std::string error_message;
+    if (!adb_get_feature_set(&features, &error_message)) {
+        fprintf(stderr, "error: %s\n", error_message.c_str());
         return 1;
     }
 
@@ -686,7 +684,7 @@
         switch (opt) {
             case 'e':
                 if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
-                    return syntax_error("-e requires a single-character argument or 'none'");
+                    error_exit("-e requires a single-character argument or 'none'");
                 }
                 escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
                 break;
@@ -934,31 +932,29 @@
  */
 static int ppp(int argc, const char** argv) {
 #if defined(_WIN32)
-    fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
-    return -1;
+    error_exit("adb %s not implemented on Win32", argv[0]);
+    __builtin_unreachable();
 #else
-    if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
+    if (argc < 2) error_exit("usage: adb %s <adb service name> [ppp opts]", argv[0]);
 
     const char* adb_service_name = argv[1];
-    std::string error;
-    int fd = adb_connect(adb_service_name, &error);
+    std::string error_message;
+    int fd = adb_connect(adb_service_name, &error_message);
     if (fd < 0) {
-        fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
-        return 1;
+        error_exit("could not open adb service %s: %s", adb_service_name, error_message.c_str());
     }
 
     pid_t pid = fork();
+    if (pid == -1) {
+        perror_exit("fork failed");
+    }
 
-    if (pid < 0) {
-        perror("from fork()");
-        return 1;
-    } else if (pid == 0) {
-        int err;
+    if (pid == 0) {
+        // child side
         int i;
-        const char **ppp_args;
 
         // copy args
-        ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
+        const char** ppp_args = (const char**)alloca(sizeof(char*) * argc + 1);
         ppp_args[0] = "pppd";
         for (i = 2 ; i < argc ; i++) {
             //argv[2] and beyond become ppp_args[1] and beyond
@@ -966,25 +962,18 @@
         }
         ppp_args[i-1] = nullptr;
 
-        // child side
-
         dup2(fd, STDIN_FILENO);
         dup2(fd, STDOUT_FILENO);
         adb_close(STDERR_FILENO);
         adb_close(fd);
 
-        err = execvp("pppd", (char * const *)ppp_args);
-
-        if (err < 0) {
-            perror("execing pppd");
-        }
-        exit(-1);
-    } else {
-        // parent side
-
-        adb_close(fd);
-        return 0;
+        execvp("pppd", (char* const*)ppp_args);
+        perror_exit("exec pppd failed");
     }
+
+    // parent side
+    adb_close(fd);
+    return 0;
 #endif /* !defined(_WIN32) */
 }
 
@@ -1162,7 +1151,7 @@
     /* find, extract, and use any -f argument */
     for (int i = 1; i < argc; i++) {
         if (!strcmp("-f", argv[i])) {
-            if (i == argc - 1) return syntax_error("backup -f passed with no filename");
+            if (i == argc - 1) error_exit("backup -f passed with no filename");
             filename = argv[i+1];
             for (int j = i+2; j <= argc; ) {
                 argv[i++] = argv[j++];
@@ -1174,7 +1163,7 @@
 
     // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
     // a list of packages is required.
-    if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
+    if (argc < 2) error_exit("backup either needs a list of packages or -all/-shared");
 
     adb_unlink(filename);
     int outFd = adb_creat(filename, 0640);
@@ -1210,7 +1199,7 @@
 }
 
 static int restore(int argc, const char** argv) {
-    if (argc != 2) return syntax_error("restore requires an argument");
+    if (argc != 2) error_exit("restore requires an argument");
 
     const char* filename = argv[1];
     int tarFd = adb_open(filename, O_RDONLY);
@@ -1264,8 +1253,7 @@
             } else if (!strcmp(*arg, "--")) {
                 ignore_flags = true;
             } else {
-                syntax_error("unrecognized option '%s'", *arg);
-                exit(1);
+                error_exit("unrecognized option '%s'", *arg);
             }
         }
         ++arg;
@@ -1343,7 +1331,7 @@
             /* this is a special flag used only when the ADB client launches the ADB Server */
             is_daemon = true;
         } else if (!strcmp(argv[0], "--reply-fd")) {
-            if (argc < 2) return syntax_error("--reply-fd requires an argument");
+            if (argc < 2) error_exit("--reply-fd requires an argument");
             const char* reply_fd_str = argv[1];
             argc--;
             argv++;
@@ -1356,7 +1344,7 @@
             if (isdigit(argv[0][2])) {
                 serial = argv[0] + 2;
             } else {
-                if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
+                if (argc < 2 || argv[0][2] != '\0') error_exit("-s requires an argument");
                 serial = argv[1];
                 argc--;
                 argv++;
@@ -1372,7 +1360,7 @@
             }
             transport_id = strtoll(id, const_cast<char**>(&id), 10);
             if (*id != '\0') {
-                return syntax_error("invalid transport id");
+                error_exit("invalid transport id");
             }
         } else if (!strcmp(argv[0],"-d")) {
             transport_type = kTransportUsb;
@@ -1382,7 +1370,7 @@
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return syntax_error("-H requires an argument");
+                if (argc < 2) error_exit("-H requires an argument");
                 server_host_str = argv[1];
                 argc--;
                 argv++;
@@ -1391,7 +1379,7 @@
             }
         } else if (!strncmp(argv[0], "-P", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return syntax_error("-P requires an argument");
+                if (argc < 2) error_exit("-P requires an argument");
                 server_port_str = argv[1];
                 argc--;
                 argv++;
@@ -1399,7 +1387,7 @@
                 server_port_str = argv[0] + 2;
             }
         } else if (!strcmp(argv[0], "-L")) {
-            if (argc < 2) return syntax_error("-L requires an argument");
+            if (argc < 2) error_exit("-L requires an argument");
             server_socket_str = argv[1];
             argc--;
             argv++;
@@ -1412,7 +1400,7 @@
     }
 
     if ((server_host_str || server_port_str) && server_socket_str) {
-        return syntax_error("-L is incompatible with -H or -P");
+        error_exit("-L is incompatible with -H or -P");
     }
 
     // If -L, -H, or -P are specified, ignore environment variables.
@@ -1429,11 +1417,10 @@
         server_port_str = server_port_str ? server_port_str : getenv("ANDROID_ADB_SERVER_PORT");
         if (server_port_str && strlen(server_port_str) > 0) {
             if (!android::base::ParseInt(server_port_str, &server_port, 1, 65535)) {
-                fprintf(stderr,
-                        "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive"
-                        " number less than 65535. Got \"%s\"\n",
+                error_exit(
+                        "$ANDROID_ADB_SERVER_PORT must be a positive number less than 65535: "
+                        "got \"%s\"",
                         server_port_str);
-                exit(1);
             }
         }
 
@@ -1445,7 +1432,7 @@
             rc = asprintf(&temp, "tcp:%d", server_port);
         }
         if (rc < 0) {
-            fatal("failed to allocate server socket specification");
+            LOG(FATAL) << "failed to allocate server socket specification";
         }
         server_socket_str = temp;
     }
@@ -1507,7 +1494,7 @@
         } else if (argc == 2 && !strcmp(argv[1], "-l")) {
             listopt = argv[1];
         } else {
-            return syntax_error("adb devices [-l]");
+            error_exit("adb devices [-l]");
         }
 
         std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
@@ -1515,13 +1502,13 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
+        if (argc != 2) error_exit("usage: adb connect <host>[:<port>]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "disconnect")) {
-        if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
+        if (argc > 2) error_exit("usage: adb disconnect [<host>[:<port>]]");
 
         std::string query = android::base::StringPrintf("host:disconnect:%s",
                                                         (argc == 2) ? argv[1] : "");
@@ -1536,7 +1523,7 @@
     else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
         int exec_in = !strcmp(argv[0], "exec-in");
 
-        if (argc < 2) return syntax_error("adb %s command", argv[0]);
+        if (argc < 2) error_exit("usage: adb %s command", argv[0]);
 
         std::string cmd = "exec:";
         cmd += argv[1];
@@ -1566,29 +1553,34 @@
         return adb_kill_server() ? 0 : 1;
     }
     else if (!strcmp(argv[0], "sideload")) {
-        if (argc != 2) return syntax_error("sideload requires an argument");
+        if (argc != 2) error_exit("sideload requires an argument");
         if (adb_sideload_host(argv[1])) {
             return 1;
         } else {
             return 0;
         }
     } else if (!strcmp(argv[0], "tcpip")) {
-        if (argc != 2) return syntax_error("tcpip requires an argument");
+        if (argc != 2) error_exit("tcpip requires an argument");
         int port;
         if (!android::base::ParseInt(argv[1], &port, 1, 65535)) {
-            return syntax_error("tcpip: invalid port: %s", argv[1]);
+            error_exit("tcpip: invalid port: %s", argv[1]);
         }
         return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
     }
+    // clang-format off
     else if (!strcmp(argv[0], "remount") ||
              !strcmp(argv[0], "reboot") ||
              !strcmp(argv[0], "reboot-bootloader") ||
+             !strcmp(argv[0], "reboot-fastboot") ||
              !strcmp(argv[0], "usb") ||
              !strcmp(argv[0], "disable-verity") ||
              !strcmp(argv[0], "enable-verity")) {
+        // clang-format on
         std::string command;
         if (!strcmp(argv[0], "reboot-bootloader")) {
             command = "reboot:bootloader";
+        } else if (!strcmp(argv[0], "reboot-fastboot")) {
+            command = "reboot:fastboot";
         } else if (argc > 1) {
             command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
         } else {
@@ -1603,7 +1595,7 @@
     } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
         bool reverse = !strcmp(argv[0], "reverse");
         --argc;
-        if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
+        if (argc < 1) error_exit("%s requires an argument", argv[0]);
         ++argv;
 
         // Determine the <host-prefix> for this command.
@@ -1622,46 +1614,44 @@
             }
         }
 
-        std::string cmd, error;
+        std::string cmd, error_message;
         if (strcmp(argv[0], "--list") == 0) {
-            if (argc != 1) return syntax_error("--list doesn't take any arguments");
+            if (argc != 1) error_exit("--list doesn't take any arguments");
             return adb_query_command(host_prefix + ":list-forward");
         } else if (strcmp(argv[0], "--remove-all") == 0) {
-            if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
+            if (argc != 1) error_exit("--remove-all doesn't take any arguments");
             cmd = host_prefix + ":killforward-all";
         } else if (strcmp(argv[0], "--remove") == 0) {
             // forward --remove <local>
-            if (argc != 2) return syntax_error("--remove requires an argument");
+            if (argc != 2) error_exit("--remove requires an argument");
             cmd = host_prefix + ":killforward:" + argv[1];
         } else if (strcmp(argv[0], "--no-rebind") == 0) {
             // forward --no-rebind <local> <remote>
-            if (argc != 3) return syntax_error("--no-rebind takes two arguments");
-            if (forward_targets_are_valid(argv[1], argv[2], &error)) {
+            if (argc != 3) error_exit("--no-rebind takes two arguments");
+            if (forward_targets_are_valid(argv[1], argv[2], &error_message)) {
                 cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
             }
         } else {
             // forward <local> <remote>
-            if (argc != 2) return syntax_error("forward takes two arguments");
-            if (forward_targets_are_valid(argv[0], argv[1], &error)) {
+            if (argc != 2) error_exit("forward takes two arguments");
+            if (forward_targets_are_valid(argv[0], argv[1], &error_message)) {
                 cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
             }
         }
 
-        if (!error.empty()) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
+        if (!error_message.empty()) {
+            error_exit("error: %s", error_message.c_str());
         }
 
-        int fd = adb_connect(cmd, &error);
-        if (fd < 0 || !adb_status(fd, &error)) {
+        int fd = adb_connect(cmd, &error_message);
+        if (fd < 0 || !adb_status(fd, &error_message)) {
             adb_close(fd);
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
+            error_exit("error: %s", error_message.c_str());
         }
 
         // Server or device may optionally return a resolved TCP port number.
         std::string resolved_port;
-        if (ReadProtocolString(fd, &resolved_port, &error) && !resolved_port.empty()) {
+        if (ReadProtocolString(fd, &resolved_port, &error_message) && !resolved_port.empty()) {
             printf("%s\n", resolved_port.c_str());
         }
 
@@ -1670,7 +1660,7 @@
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
-        if (argc != 2) return syntax_error("ls requires an argument");
+        if (argc != 2) error_exit("ls requires an argument");
         return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
@@ -1680,7 +1670,7 @@
         const char* dst = nullptr;
 
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
-        if (srcs.empty() || !dst) return syntax_error("push requires an argument");
+        if (srcs.empty() || !dst) error_exit("push requires an argument");
         return do_sync_push(srcs, dst, sync) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
@@ -1689,19 +1679,19 @@
         const char* dst = ".";
 
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
-        if (srcs.empty()) return syntax_error("pull requires an argument");
+        if (srcs.empty()) error_exit("pull requires an argument");
         return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
-        if (argc < 2) return syntax_error("install requires an argument");
+        if (argc < 2) error_exit("install requires an argument");
         return install_app(argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
-        if (argc < 2) return syntax_error("install-multiple requires an argument");
+        if (argc < 2) error_exit("install-multiple requires an argument");
         return install_multiple_app(argc, argv);
     }
     else if (!strcmp(argv[0], "uninstall")) {
-        if (argc < 2) return syntax_error("uninstall requires an argument");
+        if (argc < 2) error_exit("uninstall requires an argument");
         return uninstall_app(argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
@@ -1715,7 +1705,7 @@
         } else if (argc == 2) {
             src = argv[1];
         } else {
-            return syntax_error("adb sync [-l] [PARTITION]");
+            error_exit("usage: adb sync [-l] [PARTITION]");
         }
 
         if (src.empty()) src = "all";
@@ -1730,7 +1720,8 @@
                 if (!do_sync_sync(src_dir, "/" + partition, list_only)) return 1;
             }
         }
-        return found ? 0 : syntax_error("don't know how to sync %s partition", src.c_str());
+        if (!found) error_exit("don't know how to sync %s partition", src.c_str());
+        return 0;
     }
     /* passthrough commands */
     else if (!strcmp(argv[0],"get-state") ||
@@ -1761,7 +1752,7 @@
         return restore(argc, argv);
     }
     else if (!strcmp(argv[0], "keygen")) {
-        if (argc != 2) return syntax_error("keygen requires an argument");
+        if (argc != 2) error_exit("keygen requires an argument");
         // Always print key generation information for keygen command.
         adb_trace_enable(AUTH);
         return adb_auth_keygen(argv[1]);
@@ -1776,7 +1767,7 @@
         return adb_connect_command("host:track-devices");
     } else if (!strcmp(argv[0], "raw")) {
         if (argc != 2) {
-            return syntax_error("adb raw SERVICE");
+            error_exit("usage: adb raw SERVICE");
         }
         return adb_connect_command(argv[1]);
     }
@@ -1819,11 +1810,11 @@
                 std::string err;
                 return adb_query_command("host:reconnect-offline");
             } else {
-                return syntax_error("adb reconnect [device|offline]");
+                error_exit("usage: adb reconnect [device|offline]");
             }
         }
     }
 
-    syntax_error("unknown command %s", argv[0]);
-    return 1;
+    error_exit("unknown command %s", argv[0]);
+    __builtin_unreachable();
 }
diff --git a/adb/client/console.cpp b/adb/client/console.cpp
index 9563eac..4e8a3f8 100644
--- a/adb/client/console.cpp
+++ b/adb/client/console.cpp
@@ -135,6 +135,7 @@
     // preventing the emulator from reading the command that adb has sent.
     // https://code.google.com/p/android/issues/detail?id=21021
     int result;
+    std::string emulator_output;
     do {
         char buf[BUFSIZ];
         result = adb_read(fd, buf, sizeof(buf));
@@ -146,8 +147,37 @@
         // appended above, and that causes the emulator to close the socket
         // which should cause zero bytes (orderly/graceful shutdown) to be
         // returned.
+        if (result > 0) emulator_output.append(buf, result);
     } while (result > 0);
 
+    // Note: the following messages are expected to be quite stable from emulator.
+    //
+    // Emulator console will send the following message upon connection:
+    //
+    // Android Console: Authentication required
+    // Android Console: type 'auth <auth_token>' to authenticate
+    // Android Console: you can find your <auth_token> in
+    // '/<path-to-home>/.emulator_console_auth_token'
+    // OK\r\n
+    //
+    // and the following after authentication:
+    // Android Console: type 'help' for a list of commands
+    // OK\r\n
+    //
+    // So try search and skip first two "OK\r\n", print the rest.
+    //
+    const std::string delims = "OK\r\n";
+    size_t found = 0;
+    for (int i = 0; i < 2; ++i) {
+        const size_t result = emulator_output.find(delims, found);
+        if (result == std::string::npos) {
+            break;
+        } else {
+            found = result + delims.size();
+        }
+    }
+
+    printf("%s", emulator_output.c_str() + found);
     adb_close(fd);
 
     return 0;
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index d3f35c8..45f3cca 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -14,17 +14,22 @@
  * limitations under the License.
  */
 
-#include <libgen.h>
+#include "fastdeploy.h"
+
 #include <algorithm>
 #include <array>
+#include <memory>
 
 #include "android-base/file.h"
 #include "android-base/strings.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/ZipFileRO.h"
 #include "client/file_sync_client.h"
 #include "commandline.h"
-#include "fastdeploy.h"
 #include "fastdeploycallbacks.h"
-#include "utils/String16.h"
+#include "sysdeps.h"
+
+#include "adb_utils.h"
 
 static constexpr long kRequiredAgentVersion = 0x00000001;
 
@@ -69,13 +74,14 @@
 static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
     std::string adb_dir = android::base::GetExecutableDirectory();
     if (adb_dir.empty()) {
-        fatal("Could not determine location of adb!");
+        error_exit("Could not determine location of adb!");
     }
 
     if (g_use_localagent) {
         const char* product_out = getenv("ANDROID_PRODUCT_OUT");
         if (product_out == nullptr) {
-            fatal("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined", local_path);
+            error_exit("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined",
+                       local_path);
         }
         return android::base::StringPrintf("%s%s", product_out, local_path);
     } else {
@@ -100,10 +106,10 @@
                 android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
         int ret = send_shell_command(chmodCommand);
         if (ret != 0) {
-            fatal("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
+            error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
         }
     } else {
-        fatal("Error pushing agent files to device");
+        error_exit("Error pushing agent files to device");
     }
 
     return true;
@@ -133,65 +139,89 @@
 
     agent_version = get_agent_version();
     if (agent_version != kRequiredAgentVersion) {
-        fatal("After update agent version remains incorrect! Expected %ld but version is %ld",
-              kRequiredAgentVersion, agent_version);
+        error_exit("After update agent version remains incorrect! Expected %ld but version is %ld",
+                   kRequiredAgentVersion, agent_version);
     }
 }
 
-static std::string get_aapt2_path() {
-    if (g_use_localagent) {
-        // This should never happen on a Windows machine
-        const char* host_out = getenv("ANDROID_HOST_OUT");
-        if (host_out == nullptr) {
-            fatal("Could not locate aapt2 because $ANDROID_HOST_OUT is not defined");
-        }
-        return android::base::StringPrintf("%s/bin/aapt2", host_out);
+static std::string get_string_from_utf16(const char16_t* input, int input_len) {
+    ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
+    if (utf8_length <= 0) {
+        return {};
     }
-
-    std::string adb_dir = android::base::GetExecutableDirectory();
-    if (adb_dir.empty()) {
-        fatal("Could not locate aapt2");
-    }
-    return adb_dir + "/aapt2";
+    std::string utf8;
+    utf8.resize(utf8_length);
+    utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
+    return utf8;
 }
 
-static int system_capture(const char* cmd, std::string& output) {
-    FILE* pipe = popen(cmd, "re");
-    int fd = -1;
-
-    if (pipe != nullptr) {
-        fd = fileno(pipe);
-    }
-
-    if (fd == -1) {
-        fatal_errno("Could not create pipe for process '%s'", cmd);
-    }
-
-    if (!android::base::ReadFdToString(fd, &output)) {
-        fatal_errno("Error reading from process '%s'", cmd);
-    }
-
-    return pclose(pipe);
-}
-
-// output is required to point to a valid output string (non-null)
 static std::string get_packagename_from_apk(const char* apkPath) {
-    const char* kAapt2DumpNameCommandPattern = R"(%s dump packagename "%s")";
-    std::string aapt2_path_string = get_aapt2_path();
-    std::string getPackagenameCommand = android::base::StringPrintf(
-            kAapt2DumpNameCommandPattern, aapt2_path_string.c_str(), apkPath);
-
-    std::string package_name;
-    int exit_code = system_capture(getPackagenameCommand.c_str(), package_name);
-    if (exit_code != 0) {
-        fatal("Error executing '%s' exitcode: %d", getPackagenameCommand.c_str(), exit_code);
+#undef open
+    std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
+#define open ___xxx_unix_open
+    if (zipFile == nullptr) {
+        perror_exit("Could not open %s", apkPath);
     }
-
-    // strip any line end characters from the output
-    return android::base::Trim(package_name);
+    android::ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
+    if (entry == nullptr) {
+        error_exit("Could not find AndroidManifest.xml inside %s", apkPath);
+    }
+    uint32_t manifest_len = 0;
+    if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
+        error_exit("Could not read AndroidManifest.xml inside %s", apkPath);
+    }
+    std::vector<char> manifest_data(manifest_len);
+    if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
+        error_exit("Could not uncompress AndroidManifest.xml inside %s", apkPath);
+    }
+    android::ResXMLTree tree;
+    android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
+    if (setto_status != android::OK) {
+        error_exit("Could not parse AndroidManifest.xml inside %s", apkPath);
+    }
+    android::ResXMLParser::event_code_t code;
+    while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
+           code != android::ResXMLParser::END_DOCUMENT) {
+        switch (code) {
+            case android::ResXMLParser::START_TAG: {
+                size_t element_name_length;
+                const char16_t* element_name = tree.getElementName(&element_name_length);
+                if (element_name == nullptr) {
+                    continue;
+                }
+                std::u16string element_name_string(element_name, element_name_length);
+                if (element_name_string == u"manifest") {
+                    for (size_t i = 0; i < tree.getAttributeCount(); i++) {
+                        size_t attribute_name_length;
+                        const char16_t* attribute_name_text =
+                                tree.getAttributeName(i, &attribute_name_length);
+                        if (attribute_name_text == nullptr) {
+                            continue;
+                        }
+                        std::u16string attribute_name_string(attribute_name_text,
+                                                             attribute_name_length);
+                        if (attribute_name_string == u"package") {
+                            size_t attribute_value_length;
+                            const char16_t* attribute_value_text =
+                                    tree.getAttributeStringValue(i, &attribute_value_length);
+                            if (attribute_value_text == nullptr) {
+                                continue;
+                            }
+                            return get_string_from_utf16(attribute_value_text,
+                                                         attribute_value_length);
+                        }
+                    }
+                }
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
 }
 
-int extract_metadata(const char* apkPath, FILE* outputFp) {
+void extract_metadata(const char* apkPath, FILE* outputFp) {
     std::string packageName = get_packagename_from_apk(apkPath);
     const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent extract %s";
     std::string extractCommand =
@@ -200,13 +230,10 @@
     std::vector<char> extractErrorBuffer;
     int statusCode;
     DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
-    int ret = send_shell_command(extractCommand, false, &cb);
-
-    if (ret == 0) {
-        return cb.getBytesWritten();
+    int returnCode = send_shell_command(extractCommand, false, &cb);
+    if (returnCode != 0) {
+        error_exit("Executing %s returned %d", extractCommand.c_str(), returnCode);
     }
-
-    return ret;
 }
 
 static std::string get_patch_generator_command() {
@@ -214,8 +241,9 @@
         // This should never happen on a Windows machine
         const char* host_out = getenv("ANDROID_HOST_OUT");
         if (host_out == nullptr) {
-            fatal("Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT is not "
-                  "defined");
+            error_exit(
+                    "Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT "
+                    "is not defined");
         }
         return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
                                            host_out);
@@ -223,17 +251,20 @@
 
     std::string adb_dir = android::base::GetExecutableDirectory();
     if (adb_dir.empty()) {
-        fatal("Could not locate deploypatchgenerator.jar");
+        error_exit("Could not locate deploypatchgenerator.jar");
     }
     return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
                                        adb_dir.c_str());
 }
 
-int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
+void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
     std::string generatePatchCommand = android::base::StringPrintf(
             R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
             patchPath);
-    return system(generatePatchCommand.c_str());
+    int returnCode = system(generatePatchCommand.c_str());
+    if (returnCode != 0) {
+        error_exit("Executing %s returned %d", generatePatchCommand.c_str(), returnCode);
+    }
 }
 
 std::string get_patch_path(const char* apkPath) {
@@ -243,36 +274,38 @@
     return patchDevicePath;
 }
 
-int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
+void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
     const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -o %s";
     std::string packageName = get_packagename_from_apk(apkPath);
     std::string patchDevicePath = get_patch_path(apkPath);
 
     std::vector<const char*> srcs = {patchPath};
     bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
-
     if (!push_ok) {
-        return -1;
+        error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
     }
 
     std::string applyPatchCommand =
             android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
                                         patchDevicePath.c_str(), outputPath);
 
-    return send_shell_command(applyPatchCommand);
+    int returnCode = send_shell_command(applyPatchCommand);
+    if (returnCode != 0) {
+        error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
+    }
 }
 
-int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
+void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
     const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
     std::string packageName = get_packagename_from_apk(apkPath);
-    std::vector<const char*> srcs;
+
     std::string patchDevicePath =
             android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
-    srcs.push_back(patchPath);
-    bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
 
+    std::vector<const char*> srcs{patchPath};
+    bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
     if (!push_ok) {
-        return -1;
+        error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
     }
 
     std::vector<unsigned char> applyOutputBuffer;
@@ -287,5 +320,8 @@
     std::string applyPatchCommand =
             android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
                                         patchDevicePath.c_str(), argsString.c_str());
-    return send_shell_command(applyPatchCommand);
+    int returnCode = send_shell_command(applyPatchCommand);
+    if (returnCode != 0) {
+        error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
+    }
 }
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
index e5e7663..a6b10d3 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include "adb.h"
+#include <string>
 
 enum FastDeploy_AgentUpdateStrategy {
     FastDeploy_AgentUpdateAlways,
@@ -27,8 +27,8 @@
 void fastdeploy_set_local_agent(bool use_localagent);
 int get_device_api_level();
 void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy);
-int extract_metadata(const char* apkPath, FILE* outputFp);
-int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
-int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
-int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
+void extract_metadata(const char* apkPath, FILE* outputFp);
+void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
+void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
+void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
 std::string get_patch_path(const char* apkPath);
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index cb9bcfa..f0f9a80 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -325,12 +325,12 @@
         memset(st, 0, sizeof(*st));
         if (have_stat_v2_) {
             if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) {
-                fatal_errno("protocol fault: failed to read stat response");
+                PLOG(FATAL) << "protocol fault: failed to read stat response";
             }
 
             if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) {
-                fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
-                            msg.stat_v2.id);
+                PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
+                            << msg.stat_v2.id;
             }
 
             if (msg.stat_v2.error != 0) {
@@ -351,12 +351,12 @@
             return true;
         } else {
             if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) {
-                fatal_errno("protocol fault: failed to read stat response");
+                PLOG(FATAL) << "protocol fault: failed to read stat response";
             }
 
             if (msg.stat_v1.id != ID_LSTAT_V1) {
-                fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
-                            msg.stat_v1.id);
+                PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
+                            << msg.stat_v1.id;
             }
 
             if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) {
@@ -894,7 +894,8 @@
     //
     // TODO(b/25457350): We don't preserve permissions on directories.
     // TODO: Find all of the leaves and `mkdir -p` them instead?
-    if (CanUseFeature(sc.Features(), kFeatureShell2)) {
+    if (!CanUseFeature(sc.Features(), kFeatureFixedPushMkdir) &&
+        CanUseFeature(sc.Features(), kFeatureShell2)) {
         SilentStandardStreamsCallbackInterface cb;
         std::string cmd = "mkdir";
         for (const auto& dir : directory_list) {
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index a7e454d..fb581a6 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -42,13 +42,13 @@
     const std::string log_file_path(GetLogFilePath());
     int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
     if (fd == -1) {
-        fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
+        PLOG(FATAL) << "cannot open " << log_file_path;
     }
     if (dup2(fd, STDOUT_FILENO) == -1) {
-        fatal("cannot redirect stdout: %s", strerror(errno));
+        PLOG(FATAL) << "cannot redirect stdout";
     }
     if (dup2(fd, STDERR_FILENO) == -1) {
-        fatal("cannot redirect stderr: %s", strerror(errno));
+        PLOG(FATAL) << "cannot redirect stderr";
     }
     unix_close(fd);
 
@@ -81,10 +81,10 @@
     // This also keeps stderr unbuffered when it is redirected to adb.log.
     if (is_daemon) {
         if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) {
-            fatal("cannot make stdout unbuffered: %s", strerror(errno));
+            PLOG(FATAL) << "cannot make stdout unbuffered";
         }
         if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) {
-            fatal("cannot make stderr unbuffered: %s", strerror(errno));
+            PLOG(FATAL) << "cannot make stderr unbuffered";
         }
     }
 
@@ -137,7 +137,7 @@
     while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
            INSTALL_STATUS_OK) {
         if (std::chrono::steady_clock::now() - start > 0.5s) {
-            fatal("could not install *smartsocket* listener: %s", error.c_str());
+            LOG(FATAL) << "could not install *smartsocket* listener: " << error;
         }
 
         std::this_thread::sleep_for(100ms);
@@ -153,7 +153,7 @@
         // setsid will fail with EPERM if it's already been a lead process of new session.
         // Ignore such error.
         if (setsid() == -1 && errno != EPERM) {
-            fatal("setsid() failed: %s", strerror(errno));
+            PLOG(FATAL) << "setsid() failed";
         }
 #endif
 
@@ -171,19 +171,19 @@
             const DWORD bytes_to_write = arraysize(ack) - 1;
             DWORD written = 0;
             if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
-                fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
-                      android::base::SystemErrorCodeToString(GetLastError()).c_str());
+                LOG(FATAL) << "cannot write ACK to handle " << ack_reply_handle
+                           << android::base::SystemErrorCodeToString(GetLastError());
             }
             if (written != bytes_to_write) {
-                fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
-                      written);
+                LOG(FATAL) << "cannot write " << bytes_to_write << " bytes of ACK: only wrote "
+                           << written << " bytes";
             }
             CloseHandle(ack_reply_handle);
 #else
             // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
             // "OKAY".
             if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
-                fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+                PLOG(FATAL) << "error writing ACK to fd " << ack_reply_fd;
             }
             unix_close(ack_reply_fd);
 #endif
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 00aeb9b..cfa5cf4 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -212,8 +212,8 @@
     const HINSTANCE instance = GetModuleHandleW(nullptr);
     if (!instance) {
         // This is such a common API call that this should never fail.
-        fatal("GetModuleHandleW failed: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "GetModuleHandleW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     WNDCLASSEXW wndclass;
@@ -223,15 +223,15 @@
     wndclass.hInstance = instance;
     wndclass.lpszClassName = kPowerNotificationWindowClassName;
     if (!RegisterClassExW(&wndclass)) {
-        fatal("RegisterClassExW failed: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "RegisterClassExW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
                          L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
                          instance, nullptr)) {
-        fatal("CreateWindowExW failed: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        LOG(FATAL) << "CreateWindowExW failed: "
+                   << android::base::SystemErrorCodeToString(GetLastError());
     }
 
     MSG msg;
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 8c39a20..d55096a 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -32,6 +32,10 @@
 #include <unistd.h>
 #include <utime.h>
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -47,6 +51,7 @@
 #include "security_log_tags.h"
 #include "sysdeps/errno.h"
 
+using android::base::Dirname;
 using android::base::StringPrintf;
 
 static bool should_use_fs_config(const std::string& path) {
@@ -219,7 +224,7 @@
     }
 
     if (fd < 0 && errno == ENOENT) {
-        if (!secure_mkdirs(android::base::Dirname(path))) {
+        if (!secure_mkdirs(Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
             goto fail;
         }
@@ -327,8 +332,6 @@
 #else
 static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
     syncmsg msg;
-    unsigned int len;
-    int ret;
 
     if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
 
@@ -337,24 +340,28 @@
         return false;
     }
 
-    len = msg.data.size;
+    unsigned int len = msg.data.size;
     if (len > buffer.size()) { // TODO: resize buffer?
         SendSyncFail(s, "oversize data message");
         return false;
     }
     if (!ReadFdExactly(s, &buffer[0], len)) return false;
 
-    ret = symlink(&buffer[0], path.c_str());
-    if (ret && errno == ENOENT) {
-        if (!secure_mkdirs(android::base::Dirname(path))) {
-            SendSyncFailErrno(s, "secure_mkdirs failed");
+    std::string buf_link;
+    if (!android::base::Readlink(path, &buf_link) || (buf_link != &buffer[0])) {
+        adb_unlink(path.c_str());
+        auto ret = symlink(&buffer[0], path.c_str());
+        if (ret && errno == ENOENT) {
+            if (!secure_mkdirs(Dirname(path))) {
+                SendSyncFailErrno(s, "secure_mkdirs failed");
+                return false;
+            }
+            ret = symlink(&buffer[0], path.c_str());
+        }
+        if (ret) {
+            SendSyncFailErrno(s, "symlink failed");
             return false;
         }
-        ret = symlink(&buffer[0], path.c_str());
-    }
-    if (ret) {
-        SendSyncFailErrno(s, "symlink failed");
-        return false;
     }
 
     if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
@@ -391,7 +398,8 @@
 
     // Don't delete files before copying if they are not "regular" or symlinks.
     struct stat st;
-    bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+    bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
+                     (S_ISLNK(st.st_mode) && !S_ISLNK(mode));
     if (do_unlink) {
         adb_unlink(path.c_str());
     }
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 8d28c49..2a6418a 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -32,6 +32,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "fdevent.h"
 
 /* TODO:
@@ -78,7 +79,7 @@
         const char* command = "screencap";
         const char *args[2] = {command, nullptr};
         execvp(command, (char**)args);
-        exit(1);
+        perror_exit("exec screencap failed");
     }
 
     adb_close(fds[1]);
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
index 78e4cd5..3213f69 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -62,3 +62,5 @@
 };
 
 usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size);
+bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out,
+                     android::base::unique_fd* bulk_in);
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index b40faee..1363976 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -136,7 +136,7 @@
         this->fde = fdevent_create(socket, jdwp_process_event, this);
 
         if (!this->fde) {
-            fatal("could not create fdevent for new JDWP process");
+            LOG(FATAL) << "could not create fdevent for new JDWP process";
         }
 
         /* start by waiting for the PID */
@@ -200,7 +200,7 @@
     // Message is length-prefixed with 4 hex digits in ASCII.
     static constexpr size_t header_len = 4;
     if (bufferlen < header_len) {
-        fatal("invalid JDWP process list buffer size: %zu", bufferlen);
+        LOG(FATAL) << "invalid JDWP process list buffer size: " << bufferlen;
     }
 
     char head[header_len + 1];
@@ -212,6 +212,7 @@
 
 static void jdwp_process_event(int socket, unsigned events, void* _proc) {
     JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
+    CHECK_EQ(socket, proc->socket);
 
     if (events & FDE_READ) {
         if (proc->pid < 0) {
@@ -225,82 +226,50 @@
             D("Adding pid %d to jdwp process list", proc->pid);
             jdwp_process_list_updated();
         } else {
-            /* the pid was read, if we get there it's probably because the connection
-             * was closed (e.g. the JDWP process exited or crashed) */
-            char buf[32];
-
-            while (true) {
-                int len = TEMP_FAILURE_RETRY(recv(socket, buf, sizeof(buf), 0));
-
-                if (len == 0) {
-                    D("terminating JDWP %d connection: EOF", proc->pid);
-                    break;
-                } else if (len < 0) {
-                    if (len < 0 && errno == EAGAIN) {
-                        return;
-                    }
-
-                    D("terminating JDWP %d connection: EOF", proc->pid);
-                    break;
-                } else {
-                    D("ignoring unexpected JDWP %d control socket activity (%d bytes)", proc->pid,
-                      len);
-                }
-            }
-
+            // We already have the PID, if we can read from the socket, we've probably hit EOF.
+            D("terminating JDWP connection %d", proc->pid);
             goto CloseProcess;
         }
     }
 
     if (events & FDE_WRITE) {
         D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
-        if (!proc->out_fds.empty()) {
-            int fd = proc->out_fds.back().get();
-            struct cmsghdr* cmsg;
-            struct msghdr msg;
-            struct iovec iov;
-            char dummy = '!';
-            char buffer[sizeof(struct cmsghdr) + sizeof(int)];
+        CHECK(!proc->out_fds.empty());
 
-            iov.iov_base = &dummy;
-            iov.iov_len = 1;
-            msg.msg_name = nullptr;
-            msg.msg_namelen = 0;
-            msg.msg_iov = &iov;
-            msg.msg_iovlen = 1;
-            msg.msg_flags = 0;
-            msg.msg_control = buffer;
-            msg.msg_controllen = sizeof(buffer);
+        int fd = proc->out_fds.back().get();
+        struct cmsghdr* cmsg;
+        struct msghdr msg;
+        struct iovec iov;
+        char dummy = '!';
+        char buffer[sizeof(struct cmsghdr) + sizeof(int)];
 
-            cmsg = CMSG_FIRSTHDR(&msg);
-            cmsg->cmsg_len = msg.msg_controllen;
-            cmsg->cmsg_level = SOL_SOCKET;
-            cmsg->cmsg_type = SCM_RIGHTS;
-            ((int*)CMSG_DATA(cmsg))[0] = fd;
+        iov.iov_base = &dummy;
+        iov.iov_len = 1;
+        msg.msg_name = nullptr;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+        msg.msg_flags = 0;
+        msg.msg_control = buffer;
+        msg.msg_controllen = sizeof(buffer);
 
-            if (!set_file_block_mode(proc->socket, true)) {
-                VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
-                goto CloseProcess;
-            }
+        cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_len = msg.msg_controllen;
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        ((int*)CMSG_DATA(cmsg))[0] = fd;
 
-            int ret = TEMP_FAILURE_RETRY(sendmsg(proc->socket, &msg, 0));
-            if (ret < 0) {
-                D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
-                goto CloseProcess;
-            }
+        int ret = TEMP_FAILURE_RETRY(sendmsg(socket, &msg, 0));
+        if (ret < 0) {
+            D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
+            goto CloseProcess;
+        }
 
-            D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
+        D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
 
-            proc->out_fds.pop_back();
-
-            if (!set_file_block_mode(proc->socket, false)) {
-                VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
-                goto CloseProcess;
-            }
-
-            if (proc->out_fds.empty()) {
-                fdevent_del(proc->fde, FDE_WRITE);
-            }
+        proc->out_fds.pop_back();
+        if (proc->out_fds.empty()) {
+            fdevent_del(proc->fde, FDE_WRITE);
         }
     }
 
@@ -406,9 +375,10 @@
     return 0;
 }
 
-static void jdwp_control_event(int s, unsigned events, void* _control) {
+static void jdwp_control_event(int fd, unsigned events, void* _control) {
     JdwpControl* control = (JdwpControl*)_control;
 
+    CHECK_EQ(fd, control->listen_socket);
     if (events & FDE_READ) {
         int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
         if (s < 0) {
@@ -425,7 +395,7 @@
 
         auto proc = std::make_unique<JdwpProcess>(s);
         if (!proc) {
-            fatal("failed to allocate JdwpProcess");
+            LOG(FATAL) << "failed to allocate JdwpProcess";
         }
 
         _jdwp_list.emplace_back(std::move(proc));
@@ -484,7 +454,7 @@
     JdwpSocket* s = new JdwpSocket();
 
     if (!s) {
-        fatal("failed to allocate JdwpSocket");
+        LOG(FATAL) << "failed to allocate JdwpSocket";
     }
 
     install_local_socket(s);
@@ -561,7 +531,7 @@
 asocket* create_jdwp_tracker_service_socket(void) {
     auto t = std::make_unique<JdwpTracker>();
     if (!t) {
-        fatal("failed to allocate JdwpTracker");
+        LOG(FATAL) << "failed to allocate JdwpTracker";
     }
 
     memset(t.get(), 0, sizeof(asocket));
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 2bac486..720ec6a 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -202,6 +202,27 @@
     return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
 }
 
+static void spin_service(unique_fd fd) {
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "refusing to spin on non-debuggable build\n");
+        return;
+    }
+
+    // A service that creates an fdevent that's always pending, and then ignores it.
+    unique_fd pipe_read, pipe_write;
+    if (!Pipe(&pipe_read, &pipe_write)) {
+        WriteFdExactly(fd.get(), "failed to create pipe\n");
+        return;
+    }
+
+    fdevent_run_on_main_thread([fd = pipe_read.release()]() {
+        fdevent* fde = fdevent_create(fd, [](int, unsigned, void*) {}, nullptr);
+        fdevent_add(fde, FDE_READ);
+    });
+
+    WriteFdExactly(fd.get(), "spinning\n");
+}
+
 unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
     if (!strncmp("dev:", name, 4)) {
         return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
@@ -254,6 +275,9 @@
     } else if (!strcmp(name, "reconnect")) {
         return create_service_thread(
                 "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
+    } else if (!strcmp(name, "spin")) {
+        return create_service_thread("spin", spin_service);
     }
+
     return unique_fd{};
 }
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 9d495b0..f603d13 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -18,528 +18,568 @@
 
 #include "sysdeps.h"
 
-#include <dirent.h>
 #include <errno.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/functionfs.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
-#include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <algorithm>
-#include <atomic>
-#include <chrono>
-#include <condition_variable>
+#include <linux/usb/functionfs.h>
+#include <sys/eventfd.h>
+
+#include <array>
+#include <future>
+#include <memory>
 #include <mutex>
-#include <thread>
+#include <optional>
+#include <vector>
+
+#include <asyncio/AsyncIO.h>
 
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/properties.h>
+#include <android-base/thread_annotations.h>
 
-#include "adb.h"
-#include "adbd/usb.h"
+#include <adbd/usb.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps/chrono.h"
 #include "transport.h"
+#include "types.h"
 
-using namespace std::chrono_literals;
+using android::base::StringPrintf;
 
-#define MAX_PACKET_SIZE_FS 64
-#define MAX_PACKET_SIZE_HS 512
-#define MAX_PACKET_SIZE_SS 1024
+static constexpr size_t kUsbReadQueueDepth = 16;
+static constexpr size_t kUsbReadSize = 16384;
 
-#define USB_FFS_BULK_SIZE 16384
+static constexpr size_t kUsbWriteQueueDepth = 16;
 
-// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
-#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
-
-#define cpu_to_le16(x) htole16(x)
-#define cpu_to_le32(x) htole32(x)
-
-static unique_fd& dummy_fd = *new unique_fd();
-
-struct func_desc {
-    struct usb_interface_descriptor intf;
-    struct usb_endpoint_descriptor_no_audio source;
-    struct usb_endpoint_descriptor_no_audio sink;
-} __attribute__((packed));
-
-struct ss_func_desc {
-    struct usb_interface_descriptor intf;
-    struct usb_endpoint_descriptor_no_audio source;
-    struct usb_ss_ep_comp_descriptor source_comp;
-    struct usb_endpoint_descriptor_no_audio sink;
-    struct usb_ss_ep_comp_descriptor sink_comp;
-} __attribute__((packed));
-
-struct desc_v1 {
-    struct usb_functionfs_descs_head_v1 {
-        __le32 magic;
-        __le32 length;
-        __le32 fs_count;
-        __le32 hs_count;
-    } __attribute__((packed)) header;
-    struct func_desc fs_descs, hs_descs;
-} __attribute__((packed));
-
-struct desc_v2 {
-    struct usb_functionfs_descs_head_v2 header;
-    // The rest of the structure depends on the flags in the header.
-    __le32 fs_count;
-    __le32 hs_count;
-    __le32 ss_count;
-    __le32 os_count;
-    struct func_desc fs_descs, hs_descs;
-    struct ss_func_desc ss_descs;
-    struct usb_os_desc_header os_header;
-    struct usb_ext_compat_desc os_desc;
-} __attribute__((packed));
-
-static struct func_desc fs_descriptors = {
-    .intf = {
-        .bLength = sizeof(fs_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(fs_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
-    },
-    .sink = {
-        .bLength = sizeof(fs_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
-    },
-};
-
-static struct func_desc hs_descriptors = {
-    .intf = {
-        .bLength = sizeof(hs_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(hs_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
-    },
-    .sink = {
-        .bLength = sizeof(hs_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
-    },
-};
-
-static struct ss_func_desc ss_descriptors = {
-    .intf = {
-        .bLength = sizeof(ss_descriptors.intf),
-        .bDescriptorType = USB_DT_INTERFACE,
-        .bInterfaceNumber = 0,
-        .bNumEndpoints = 2,
-        .bInterfaceClass = ADB_CLASS,
-        .bInterfaceSubClass = ADB_SUBCLASS,
-        .bInterfaceProtocol = ADB_PROTOCOL,
-        .iInterface = 1, /* first string from the provided table */
-    },
-    .source = {
-        .bLength = sizeof(ss_descriptors.source),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 1 | USB_DIR_OUT,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
-    },
-    .source_comp = {
-        .bLength = sizeof(ss_descriptors.source_comp),
-        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-        .bMaxBurst = 4,
-    },
-    .sink = {
-        .bLength = sizeof(ss_descriptors.sink),
-        .bDescriptorType = USB_DT_ENDPOINT,
-        .bEndpointAddress = 2 | USB_DIR_IN,
-        .bmAttributes = USB_ENDPOINT_XFER_BULK,
-        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
-    },
-    .sink_comp = {
-        .bLength = sizeof(ss_descriptors.sink_comp),
-        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-        .bMaxBurst = 4,
-    },
-};
-
-struct usb_ext_compat_desc os_desc_compat = {
-    .bFirstInterfaceNumber = 0,
-    .Reserved1 = cpu_to_le32(1),
-    .CompatibleID = {0},
-    .SubCompatibleID = {0},
-    .Reserved2 = {0},
-};
-
-static struct usb_os_desc_header os_desc_header = {
-    .interface = cpu_to_le32(1),
-    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
-    .bcdVersion = cpu_to_le32(1),
-    .wIndex = cpu_to_le32(4),
-    .bCount = cpu_to_le32(1),
-    .Reserved = cpu_to_le32(0),
-};
-
-#define STR_INTERFACE_ "ADB Interface"
-
-static const struct {
-    struct usb_functionfs_strings_head header;
-    struct {
-        __le16 code;
-        const char str1[sizeof(STR_INTERFACE_)];
-    } __attribute__((packed)) lang0;
-} __attribute__((packed)) strings = {
-    .header = {
-        .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
-        .length = cpu_to_le32(sizeof(strings)),
-        .str_count = cpu_to_le32(1),
-        .lang_count = cpu_to_le32(1),
-    },
-    .lang0 = {
-        cpu_to_le16(0x0409), /* en-us */
-        STR_INTERFACE_,
-    },
-};
-
-static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
-    aiob->iocb.resize(num_bufs);
-    aiob->iocbs.resize(num_bufs);
-    aiob->events.resize(num_bufs);
-    aiob->num_submitted = 0;
-    for (unsigned i = 0; i < num_bufs; i++) {
-        aiob->iocbs[i] = &aiob->iocb[i];
-    }
-    memset(&aiob->ctx, 0, sizeof(aiob->ctx));
-    if (io_setup(num_bufs, &aiob->ctx)) {
-        D("[ aio: got error on io_setup (%d) ]", errno);
+static const char* to_string(enum usb_functionfs_event_type type) {
+    switch (type) {
+        case FUNCTIONFS_BIND:
+            return "FUNCTIONFS_BIND";
+        case FUNCTIONFS_UNBIND:
+            return "FUNCTIONFS_UNBIND";
+        case FUNCTIONFS_ENABLE:
+            return "FUNCTIONFS_ENABLE";
+        case FUNCTIONFS_DISABLE:
+            return "FUNCTIONFS_DISABLE";
+        case FUNCTIONFS_SETUP:
+            return "FUNCTIONFS_SETUP";
+        case FUNCTIONFS_SUSPEND:
+            return "FUNCTIONFS_SUSPEND";
+        case FUNCTIONFS_RESUME:
+            return "FUNCTIONFS_RESUME";
     }
 }
 
-static int getMaxPacketSize(int ffs_fd) {
-    usb_endpoint_descriptor desc;
-    if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
-        D("[ could not get endpoint descriptor! (%d) ]", errno);
-        return MAX_PACKET_SIZE_HS;
-    } else {
-        return desc.wMaxPacketSize;
+enum class TransferDirection : uint64_t {
+    READ = 0,
+    WRITE = 1,
+};
+
+struct TransferId {
+    TransferDirection direction : 1;
+    uint64_t id : 63;
+
+    TransferId() : TransferId(TransferDirection::READ, 0) {}
+
+  private:
+    TransferId(TransferDirection direction, uint64_t id) : direction(direction), id(id) {}
+
+  public:
+    explicit operator uint64_t() const {
+        uint64_t result;
+        static_assert(sizeof(*this) == sizeof(result));
+        memcpy(&result, this, sizeof(*this));
+        return result;
     }
-}
 
-static bool init_functionfs(struct usb_handle* h) {
-    LOG(INFO) << "initializing functionfs";
+    static TransferId read(uint64_t id) { return TransferId(TransferDirection::READ, id); }
+    static TransferId write(uint64_t id) { return TransferId(TransferDirection::WRITE, id); }
 
-    ssize_t ret;
-    struct desc_v1 v1_descriptor;
-    struct desc_v2 v2_descriptor;
+    static TransferId from_value(uint64_t value) {
+        TransferId result;
+        memcpy(&result, &value, sizeof(value));
+        return result;
+    }
+};
 
-    v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
-    v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
-    v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
-                                 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
-    v2_descriptor.fs_count = 3;
-    v2_descriptor.hs_count = 3;
-    v2_descriptor.ss_count = 5;
-    v2_descriptor.os_count = 1;
-    v2_descriptor.fs_descs = fs_descriptors;
-    v2_descriptor.hs_descs = hs_descriptors;
-    v2_descriptor.ss_descs = ss_descriptors;
-    v2_descriptor.os_header = os_desc_header;
-    v2_descriptor.os_desc = os_desc_compat;
+struct IoBlock {
+    bool pending;
+    struct iocb control;
+    Block payload;
 
-    if (h->control < 0) { // might have already done this before
-        LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
-        h->control.reset(adb_open(USB_FFS_ADB_EP0, O_WRONLY));
-        if (h->control < 0) {
-            PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
-            goto err;
+    TransferId id() const { return TransferId::from_value(control.aio_data); }
+};
+
+struct ScopedAioContext {
+    ScopedAioContext() = default;
+    ~ScopedAioContext() { reset(); }
+
+    ScopedAioContext(ScopedAioContext&& move) { reset(move.release()); }
+    ScopedAioContext(const ScopedAioContext& copy) = delete;
+
+    ScopedAioContext& operator=(ScopedAioContext&& move) {
+        reset(move.release());
+        return *this;
+    }
+    ScopedAioContext& operator=(const ScopedAioContext& copy) = delete;
+
+    static ScopedAioContext Create(size_t max_events) {
+        aio_context_t ctx = 0;
+        if (io_setup(max_events, &ctx) != 0) {
+            PLOG(FATAL) << "failed to create aio_context_t";
+        }
+        ScopedAioContext result;
+        result.reset(ctx);
+        return result;
+    }
+
+    aio_context_t release() {
+        aio_context_t result = context_;
+        context_ = 0;
+        return result;
+    }
+
+    void reset(aio_context_t new_context = 0) {
+        if (context_ != 0) {
+            io_destroy(context_);
         }
 
-        ret = adb_write(h->control.get(), &v2_descriptor, sizeof(v2_descriptor));
-        if (ret < 0) {
-            v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
-            v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
-            v1_descriptor.header.fs_count = 3;
-            v1_descriptor.header.hs_count = 3;
-            v1_descriptor.fs_descs = fs_descriptors;
-            v1_descriptor.hs_descs = hs_descriptors;
-            D("[ %s: Switching to V1_descriptor format errno=%d ]", USB_FFS_ADB_EP0, errno);
-            ret = adb_write(h->control.get(), &v1_descriptor, sizeof(v1_descriptor));
-            if (ret < 0) {
-                D("[ %s: write descriptors failed: errno=%d ]", USB_FFS_ADB_EP0, errno);
-                goto err;
+        context_ = new_context;
+    }
+
+    aio_context_t get() { return context_; }
+
+  private:
+    aio_context_t context_ = 0;
+};
+
+struct UsbFfsConnection : public Connection {
+    UsbFfsConnection(unique_fd control, unique_fd read, unique_fd write,
+                     std::promise<void> destruction_notifier)
+        : stopped_(false),
+          destruction_notifier_(std::move(destruction_notifier)),
+          control_fd_(std::move(control)),
+          read_fd_(std::move(read)),
+          write_fd_(std::move(write)) {
+        LOG(INFO) << "UsbFfsConnection constructed";
+        event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+        if (event_fd_ == -1) {
+            PLOG(FATAL) << "failed to create eventfd";
+        }
+
+        aio_context_ = ScopedAioContext::Create(kUsbReadQueueDepth + kUsbWriteQueueDepth);
+    }
+
+    ~UsbFfsConnection() {
+        LOG(INFO) << "UsbFfsConnection being destroyed";
+        Stop();
+        monitor_thread_.join();
+        destruction_notifier_.set_value();
+    }
+
+    virtual bool Write(std::unique_ptr<apacket> packet) override final {
+        LOG(DEBUG) << "USB write: " << dump_header(&packet->msg);
+        Block header(sizeof(packet->msg));
+        memcpy(header.data(), &packet->msg, sizeof(packet->msg));
+
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
+        if (!packet->payload.empty()) {
+            write_requests_.push_back(
+                    CreateWriteBlock(std::move(packet->payload), next_write_id_++));
+        }
+        SubmitWrites();
+        return true;
+    }
+
+    virtual void Start() override final { StartMonitor(); }
+
+    virtual void Stop() override final {
+        if (stopped_.exchange(true)) {
+            return;
+        }
+        stopped_ = true;
+        uint64_t notify = 1;
+        ssize_t rc = adb_write(event_fd_.get(), &notify, sizeof(notify));
+        if (rc < 0) {
+            PLOG(FATAL) << "failed to notify eventfd to stop UsbFfsConnection";
+        }
+        CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+    }
+
+  private:
+    void StartMonitor() {
+        // This is a bit of a mess.
+        // It's possible for io_submit to end up blocking, if we call it as the endpoint
+        // becomes disabled. Work around this by having a monitor thread to listen for functionfs
+        // lifecycle events. If we notice an error condition (either we've become disabled, or we
+        // were never enabled in the first place), we send interruption signals to the worker thread
+        // until it dies, and then report failure to the transport via HandleError, which will
+        // eventually result in the transport being destroyed, which will result in UsbFfsConnection
+        // being destroyed, which unblocks the open thread and restarts this entire process.
+        static constexpr int kInterruptionSignal = SIGUSR1;
+        static std::once_flag handler_once;
+        std::call_once(handler_once, []() { signal(kInterruptionSignal, [](int) {}); });
+
+        monitor_thread_ = std::thread([this]() {
+            adb_thread_setname("UsbFfs-monitor");
+
+            bool bound = false;
+            bool started = false;
+            bool running = true;
+            while (running) {
+                if (!bound || !started) {
+                    adb_pollfd pfd = {.fd = control_fd_.get(), .events = POLLIN, .revents = 0};
+                    int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, 5000 /*ms*/));
+                    if (rc == -1) {
+                        PLOG(FATAL) << "poll on USB control fd failed";
+                    } else if (rc == 0) {
+                        // Something in the kernel presumably went wrong.
+                        // Close our endpoints, wait for a bit, and then try again.
+                        aio_context_.reset();
+                        read_fd_.reset();
+                        write_fd_.reset();
+                        control_fd_.reset();
+                        std::this_thread::sleep_for(5s);
+                        HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
+                        return;
+                    }
+                }
+
+                struct usb_functionfs_event event;
+                if (TEMP_FAILURE_RETRY(adb_read(control_fd_.get(), &event, sizeof(event))) !=
+                    sizeof(event)) {
+                    PLOG(FATAL) << "failed to read functionfs event";
+                }
+
+                LOG(INFO) << "USB event: "
+                          << to_string(static_cast<usb_functionfs_event_type>(event.type));
+
+                switch (event.type) {
+                    case FUNCTIONFS_BIND:
+                        CHECK(!started) << "received FUNCTIONFS_ENABLE while already bound?";
+                        bound = true;
+                        break;
+
+                    case FUNCTIONFS_ENABLE:
+                        CHECK(!started) << "received FUNCTIONFS_ENABLE while already running?";
+                        started = true;
+                        StartWorker();
+                        break;
+
+                    case FUNCTIONFS_DISABLE:
+                        running = false;
+                        break;
+                }
+            }
+
+            pthread_t worker_thread_handle = worker_thread_.native_handle();
+            while (true) {
+                int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
+                if (rc != 0) {
+                    LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+                    break;
+                }
+
+                std::this_thread::sleep_for(100ms);
+
+                rc = pthread_kill(worker_thread_handle, 0);
+                if (rc == 0) {
+                    continue;
+                } else if (rc == ESRCH) {
+                    break;
+                } else {
+                    LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+                }
+            }
+
+            worker_thread_.join();
+
+            aio_context_.reset();
+            read_fd_.reset();
+            write_fd_.reset();
+        });
+    }
+
+    void StartWorker() {
+        worker_thread_ = std::thread([this]() {
+            adb_thread_setname("UsbFfs-worker");
+            for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
+                read_requests_[i] = CreateReadBlock(next_read_id_++);
+                SubmitRead(&read_requests_[i]);
+            }
+
+            while (!stopped_) {
+                uint64_t dummy;
+                ssize_t rc = adb_read(event_fd_.get(), &dummy, sizeof(dummy));
+                if (rc == -1) {
+                    PLOG(FATAL) << "failed to read from eventfd";
+                } else if (rc == 0) {
+                    LOG(FATAL) << "hit EOF on eventfd";
+                }
+
+                WaitForEvents();
+            }
+        });
+    }
+
+    void PrepareReadBlock(IoBlock* block, uint64_t id) {
+        block->pending = false;
+        block->payload.resize(kUsbReadSize);
+        block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+        block->control.aio_nbytes = block->payload.size();
+    }
+
+    IoBlock CreateReadBlock(uint64_t id) {
+        IoBlock block;
+        PrepareReadBlock(&block, id);
+        block.control.aio_rw_flags = 0;
+        block.control.aio_lio_opcode = IOCB_CMD_PREAD;
+        block.control.aio_reqprio = 0;
+        block.control.aio_fildes = read_fd_.get();
+        block.control.aio_offset = 0;
+        block.control.aio_flags = IOCB_FLAG_RESFD;
+        block.control.aio_resfd = event_fd_.get();
+        return block;
+    }
+
+    void WaitForEvents() {
+        static constexpr size_t kMaxEvents = kUsbReadQueueDepth + kUsbWriteQueueDepth;
+        struct io_event events[kMaxEvents];
+        struct timespec timeout = {.tv_sec = 0, .tv_nsec = 0};
+        int rc = io_getevents(aio_context_.get(), 0, kMaxEvents, events, &timeout);
+        if (rc == -1) {
+            HandleError(StringPrintf("io_getevents failed while reading: %s", strerror(errno)));
+            return;
+        }
+
+        for (int event_idx = 0; event_idx < rc; ++event_idx) {
+            auto& event = events[event_idx];
+            TransferId id = TransferId::from_value(event.data);
+
+            if (event.res < 0) {
+                std::string error =
+                        StringPrintf("%s %" PRIu64 " failed with error %s",
+                                     id.direction == TransferDirection::READ ? "read" : "write",
+                                     id.id, strerror(-event.res));
+                HandleError(error);
+                return;
+            }
+
+            if (id.direction == TransferDirection::READ) {
+                HandleRead(id, event.res);
+            } else {
+                HandleWrite(id);
+            }
+        }
+    }
+
+    void HandleRead(TransferId id, int64_t size) {
+        uint64_t read_idx = id.id % kUsbReadQueueDepth;
+        IoBlock* block = &read_requests_[read_idx];
+        block->pending = false;
+        block->payload.resize(size);
+
+        // Notification for completed reads can be received out of order.
+        if (block->id().id != needed_read_id_) {
+            LOG(VERBOSE) << "read " << block->id().id << " completed while waiting for "
+                         << needed_read_id_;
+            return;
+        }
+
+        for (uint64_t id = needed_read_id_;; ++id) {
+            size_t read_idx = id % kUsbReadQueueDepth;
+            IoBlock* current_block = &read_requests_[read_idx];
+            if (current_block->pending) {
+                break;
+            }
+            ProcessRead(current_block);
+            ++needed_read_id_;
+        }
+    }
+
+    void ProcessRead(IoBlock* block) {
+        if (!block->payload.empty()) {
+            if (!incoming_header_.has_value()) {
+                CHECK_EQ(sizeof(amessage), block->payload.size());
+                amessage msg;
+                memcpy(&msg, block->payload.data(), sizeof(amessage));
+                LOG(DEBUG) << "USB read:" << dump_header(&msg);
+                incoming_header_ = msg;
+            } else {
+                size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
+                Block payload = std::move(block->payload);
+                CHECK_LE(payload.size(), bytes_left);
+                incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
+            }
+
+            if (incoming_header_->data_length == incoming_payload_.size()) {
+                auto packet = std::make_unique<apacket>();
+                packet->msg = *incoming_header_;
+
+                // TODO: Make apacket contain an IOVector so we don't have to coalesce.
+                packet->payload = incoming_payload_.coalesce();
+                read_callback_(this, std::move(packet));
+
+                incoming_header_.reset();
+                incoming_payload_.clear();
             }
         }
 
-        ret = adb_write(h->control.get(), &strings, sizeof(strings));
-        if (ret < 0) {
-            D("[ %s: writing strings failed: errno=%d]", USB_FFS_ADB_EP0, errno);
-            goto err;
+        PrepareReadBlock(block, block->id().id + kUsbReadQueueDepth);
+        SubmitRead(block);
+    }
+
+    void SubmitRead(IoBlock* block) {
+        block->pending = true;
+        struct iocb* iocb = &block->control;
+        if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+            HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
+            return;
         }
-        //Signal only when writing the descriptors to ffs
-        android::base::SetProperty("sys.usb.ffs.ready", "1");
     }
 
-    h->bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
-    if (h->bulk_out < 0) {
-        PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
-        goto err;
+    void HandleWrite(TransferId id) {
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        auto it =
+                std::find_if(write_requests_.begin(), write_requests_.end(), [id](const auto& req) {
+                    return static_cast<uint64_t>(req->id()) == static_cast<uint64_t>(id);
+                });
+        CHECK(it != write_requests_.end());
+
+        write_requests_.erase(it);
+        size_t outstanding_writes = --writes_submitted_;
+        LOG(DEBUG) << "USB write: reaped, down to " << outstanding_writes;
+
+        SubmitWrites();
     }
 
-    h->bulk_in.reset(adb_open(USB_FFS_ADB_IN, O_WRONLY));
-    if (h->bulk_in < 0) {
-        PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
-        goto err;
+    std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+        auto block = std::make_unique<IoBlock>();
+        block->payload = std::move(payload);
+        block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
+        block->control.aio_rw_flags = 0;
+        block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
+        block->control.aio_reqprio = 0;
+        block->control.aio_fildes = write_fd_.get();
+        block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+        block->control.aio_nbytes = block->payload.size();
+        block->control.aio_offset = 0;
+        block->control.aio_flags = IOCB_FLAG_RESFD;
+        block->control.aio_resfd = event_fd_.get();
+        return block;
     }
 
-    h->read_aiob.fd = h->bulk_out;
-    h->write_aiob.fd = h->bulk_in;
-    h->reads_zero_packets = true;
-    return true;
+    void SubmitWrites() REQUIRES(write_mutex_) {
+        if (writes_submitted_ == kUsbWriteQueueDepth) {
+            return;
+        }
 
-err:
-    h->bulk_in.reset();
-    h->bulk_out.reset();
-    h->control.reset();
-    return false;
-}
+        ssize_t writes_to_submit = std::min(kUsbWriteQueueDepth - writes_submitted_,
+                                            write_requests_.size() - writes_submitted_);
+        CHECK_GE(writes_to_submit, 0);
+        if (writes_to_submit == 0) {
+            return;
+        }
 
-static void usb_ffs_open_thread(usb_handle *usb) {
+        struct iocb* iocbs[kUsbWriteQueueDepth];
+        for (int i = 0; i < writes_to_submit; ++i) {
+            CHECK(!write_requests_[writes_submitted_ + i]->pending);
+            write_requests_[writes_submitted_ + i]->pending = true;
+            iocbs[i] = &write_requests_[writes_submitted_ + i]->control;
+            LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
+        }
+
+        int rc = io_submit(aio_context_.get(), writes_to_submit, iocbs);
+        if (rc == -1) {
+            HandleError(StringPrintf("failed to submit write requests: %s", strerror(errno)));
+            return;
+        } else if (rc != writes_to_submit) {
+            LOG(FATAL) << "failed to submit all writes: wanted to submit " << writes_to_submit
+                       << ", actually submitted " << rc;
+        }
+
+        writes_submitted_ += rc;
+    }
+
+    void HandleError(const std::string& error) {
+        std::call_once(error_flag_, [&]() {
+            error_callback_(this, error);
+            if (!stopped_) {
+                Stop();
+            }
+        });
+    }
+
+    std::thread monitor_thread_;
+    std::thread worker_thread_;
+
+    std::atomic<bool> stopped_;
+    std::promise<void> destruction_notifier_;
+    std::once_flag error_flag_;
+
+    unique_fd event_fd_;
+
+    ScopedAioContext aio_context_;
+    unique_fd control_fd_;
+    unique_fd read_fd_;
+    unique_fd write_fd_;
+
+    std::optional<amessage> incoming_header_;
+    IOVector incoming_payload_;
+
+    std::array<IoBlock, kUsbReadQueueDepth> read_requests_;
+    IOVector read_data_;
+
+    // ID of the next request that we're going to send out.
+    size_t next_read_id_ = 0;
+
+    // ID of the next packet we're waiting for.
+    size_t needed_read_id_ = 0;
+
+    std::mutex write_mutex_;
+    std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
+    size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
+    size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
+};
+
+static void usb_ffs_open_thread() {
     adb_thread_setname("usb ffs open");
 
     while (true) {
-        // wait until the USB device needs opening
-        std::unique_lock<std::mutex> lock(usb->lock);
-        while (!usb->open_new_connection) {
-            usb->notify.wait(lock);
-        }
-        usb->open_new_connection = false;
-        lock.unlock();
-
-        while (true) {
-            if (init_functionfs(usb)) {
-                LOG(INFO) << "functionfs successfully initialized";
-                break;
-            }
+        unique_fd control;
+        unique_fd bulk_out;
+        unique_fd bulk_in;
+        if (!open_functionfs(&control, &bulk_out, &bulk_in)) {
             std::this_thread::sleep_for(1s);
-        }
-
-        LOG(INFO) << "registering usb transport";
-        register_usb_transport(usb, nullptr, nullptr, 1);
-    }
-
-    // never gets here
-    abort();
-}
-
-static int usb_ffs_write(usb_handle* h, const void* data, int len) {
-    D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
-
-    const char* buf = static_cast<const char*>(data);
-    int orig_len = len;
-    while (len > 0) {
-        int write_len = std::min(USB_FFS_BULK_SIZE, len);
-        int n = adb_write(h->bulk_in, buf, write_len);
-        if (n < 0) {
-            D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
-            return -1;
-        }
-        buf += n;
-        len -= n;
-    }
-
-    D("[ done fd=%d ]", h->bulk_in.get());
-    return orig_len;
-}
-
-static int usb_ffs_read(usb_handle* h, void* data, int len) {
-    D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
-
-    char* buf = static_cast<char*>(data);
-    int orig_len = len;
-    while (len > 0) {
-        int read_len = std::min(USB_FFS_BULK_SIZE, len);
-        int n = adb_read(h->bulk_out, buf, read_len);
-        if (n < 0) {
-            D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
-            return -1;
-        }
-        buf += n;
-        len -= n;
-    }
-
-    D("[ done fd=%d ]", h->bulk_out.get());
-    return orig_len;
-}
-
-static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
-    aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
-    bool zero_packet = false;
-
-    int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
-    const char* cur_data = reinterpret_cast<const char*>(data);
-    int packet_size = getMaxPacketSize(aiob->fd);
-
-    if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
-        0) {
-        D("[ Failed to madvise: %d ]", errno);
-    }
-
-    for (int i = 0; i < num_bufs; i++) {
-        int buf_len = std::min(len, static_cast<int>(h->io_size));
-        io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
-
-        len -= buf_len;
-        cur_data += buf_len;
-
-        if (len == 0 && buf_len % packet_size == 0 && read) {
-            // adb does not expect the device to send a zero packet after data transfer,
-            // but the host *does* send a zero packet for the device to read.
-            zero_packet = h->reads_zero_packets;
-        }
-    }
-    if (zero_packet) {
-        io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
-                packet_size, 0, read);
-        num_bufs += 1;
-    }
-
-    while (true) {
-        if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
-            PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
-            return -1;
-        }
-        if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
-                                            nullptr)) < num_bufs) {
-            PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
-            return -1;
-        }
-        if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
             continue;
         }
-        int ret = 0;
-        for (int i = 0; i < num_bufs; i++) {
-            if (aiob->events[i].res < 0) {
-                errno = -aiob->events[i].res;
-                PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
-                            << " total bufs " << num_bufs;
-                return -1;
-            }
-            ret += aiob->events[i].res;
-        }
-        return ret;
+
+        atransport* transport = new atransport();
+        transport->serial = "UsbFfs";
+        std::promise<void> destruction_notifier;
+        std::future<void> future = destruction_notifier.get_future();
+        transport->SetConnection(std::make_unique<UsbFfsConnection>(
+                std::move(control), std::move(bulk_out), std::move(bulk_in),
+                std::move(destruction_notifier)));
+        register_transport(transport);
+        future.wait();
     }
 }
 
-static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
-    return usb_ffs_do_aio(h, data, len, true);
-}
-
-static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
-    return usb_ffs_do_aio(h, data, len, false);
-}
-
-static void usb_ffs_kick(usb_handle* h) {
-    int err;
-
-    err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
-    if (err < 0) {
-        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
-    }
-
-    err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
-    if (err < 0) {
-        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
-    }
-
-    // don't close ep0 here, since we may not need to reinitialize it with
-    // the same descriptors again. if however ep1/ep2 fail to re-open in
-    // init_functionfs, only then would we close and open ep0 again.
-    // Ditto the comment in usb_adb_kick.
-    h->kicked = true;
-    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
-    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
-}
-
-static void usb_ffs_close(usb_handle* h) {
-    LOG(INFO) << "closing functionfs transport";
-
-    h->kicked = false;
-    h->bulk_out.reset();
-    h->bulk_in.reset();
-
-    // Notify usb_adb_open_thread to open a new connection.
-    h->lock.lock();
-    h->open_new_connection = true;
-    h->lock.unlock();
-    h->notify.notify_one();
-}
-
-usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size) {
-    usb_handle* h = new usb_handle();
-
-    if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
-        // Devices on older kernels (< 3.18) will not have aio support for ffs
-        // unless backported. Fall back on the non-aio functions instead.
-        h->write = usb_ffs_write;
-        h->read = usb_ffs_read;
-    } else {
-        h->write = usb_ffs_aio_write;
-        h->read = usb_ffs_aio_read;
-        aio_block_init(&h->read_aiob, num_bufs);
-        aio_block_init(&h->write_aiob, num_bufs);
-    }
-    h->io_size = io_size;
-    h->kick = usb_ffs_kick;
-    h->close = usb_ffs_close;
-    return h;
-}
-
+void usb_init_legacy();
 void usb_init() {
-    D("[ usb_init - using FunctionFS ]");
-    dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
-    CHECK_NE(-1, dummy_fd.get());
-
-    std::thread(usb_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE)).detach();
-}
-
-int usb_write(usb_handle* h, const void* data, int len) {
-    return h->write(h, data, len);
-}
-
-int usb_read(usb_handle* h, void* data, int len) {
-    return h->read(h, data, len);
-}
-
-int usb_close(usb_handle* h) {
-    h->close(h);
-    return 0;
-}
-
-void usb_kick(usb_handle* h) {
-    h->kick(h);
+    if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
+        usb_init_legacy();
+    } else {
+        std::thread(usb_ffs_open_thread).detach();
+    }
 }
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
new file mode 100644
index 0000000..07b4ba8
--- /dev/null
+++ b/adb/daemon/usb_ffs.cpp
@@ -0,0 +1,278 @@
+/*
+ * 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 USB
+
+#include "sysdeps.h"
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include "adb.h"
+#include "adbd/usb.h"
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#define USB_FFS_BULK_SIZE 16384
+
+// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
+#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+
+#define cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
+
+struct func_desc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_endpoint_descriptor_no_audio sink;
+} __attribute__((packed));
+
+struct ss_func_desc {
+    struct usb_interface_descriptor intf;
+    struct usb_endpoint_descriptor_no_audio source;
+    struct usb_ss_ep_comp_descriptor source_comp;
+    struct usb_endpoint_descriptor_no_audio sink;
+    struct usb_ss_ep_comp_descriptor sink_comp;
+} __attribute__((packed));
+
+struct desc_v1 {
+    struct usb_functionfs_descs_head_v1 {
+        __le32 magic;
+        __le32 length;
+        __le32 fs_count;
+        __le32 hs_count;
+    } __attribute__((packed)) header;
+    struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+struct desc_v2 {
+    struct usb_functionfs_descs_head_v2 header;
+    // The rest of the structure depends on the flags in the header.
+    __le32 fs_count;
+    __le32 hs_count;
+    __le32 ss_count;
+    __le32 os_count;
+    struct func_desc fs_descs, hs_descs;
+    struct ss_func_desc ss_descs;
+    struct usb_os_desc_header os_header;
+    struct usb_ext_compat_desc os_desc;
+} __attribute__((packed));
+
+// clang-format off
+static struct func_desc fs_descriptors = {
+    .intf = {
+        .bLength = sizeof(fs_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(fs_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+    },
+    .sink = {
+        .bLength = sizeof(fs_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+    },
+};
+
+static struct func_desc hs_descriptors = {
+    .intf = {
+        .bLength = sizeof(hs_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(hs_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+    },
+    .sink = {
+        .bLength = sizeof(hs_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+    },
+};
+
+static struct ss_func_desc ss_descriptors = {
+    .intf = {
+        .bLength = sizeof(ss_descriptors.intf),
+        .bDescriptorType = USB_DT_INTERFACE,
+        .bInterfaceNumber = 0,
+        .bNumEndpoints = 2,
+        .bInterfaceClass = ADB_CLASS,
+        .bInterfaceSubClass = ADB_SUBCLASS,
+        .bInterfaceProtocol = ADB_PROTOCOL,
+        .iInterface = 1, /* first string from the provided table */
+    },
+    .source = {
+        .bLength = sizeof(ss_descriptors.source),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 1 | USB_DIR_OUT,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+    },
+    .source_comp = {
+        .bLength = sizeof(ss_descriptors.source_comp),
+        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
+    },
+    .sink = {
+        .bLength = sizeof(ss_descriptors.sink),
+        .bDescriptorType = USB_DT_ENDPOINT,
+        .bEndpointAddress = 2 | USB_DIR_IN,
+        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+        .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+    },
+    .sink_comp = {
+        .bLength = sizeof(ss_descriptors.sink_comp),
+        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+        .bMaxBurst = 4,
+    },
+};
+
+struct usb_ext_compat_desc os_desc_compat = {
+    .bFirstInterfaceNumber = 0,
+    .Reserved1 = cpu_to_le32(1),
+    .CompatibleID = {0},
+    .SubCompatibleID = {0},
+    .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+    .interface = cpu_to_le32(1),
+    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+    .bcdVersion = cpu_to_le32(1),
+    .wIndex = cpu_to_le32(4),
+    .bCount = cpu_to_le32(1),
+    .Reserved = cpu_to_le32(0),
+};
+
+#define STR_INTERFACE_ "ADB Interface"
+
+static const struct {
+    struct usb_functionfs_strings_head header;
+    struct {
+        __le16 code;
+        const char str1[sizeof(STR_INTERFACE_)];
+    } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+    .header = {
+        .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+        .length = cpu_to_le32(sizeof(strings)),
+        .str_count = cpu_to_le32(1),
+        .lang_count = cpu_to_le32(1),
+    },
+    .lang0 = {
+        cpu_to_le16(0x0409), /* en-us */
+        STR_INTERFACE_,
+    },
+};
+// clang-format on
+
+bool open_functionfs(android::base::unique_fd* out_control, android::base::unique_fd* out_bulk_out,
+                     android::base::unique_fd* out_bulk_in) {
+    unique_fd control, bulk_out, bulk_in;
+    struct desc_v1 v1_descriptor = {};
+    struct desc_v2 v2_descriptor = {};
+
+    v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
+    v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
+    v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+                                 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
+    v2_descriptor.fs_count = 3;
+    v2_descriptor.hs_count = 3;
+    v2_descriptor.ss_count = 5;
+    v2_descriptor.os_count = 1;
+    v2_descriptor.fs_descs = fs_descriptors;
+    v2_descriptor.hs_descs = hs_descriptors;
+    v2_descriptor.ss_descs = ss_descriptors;
+    v2_descriptor.os_header = os_desc_header;
+    v2_descriptor.os_desc = os_desc_compat;
+
+    if (out_control->get() < 0) {  // might have already done this before
+        LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
+        control.reset(adb_open(USB_FFS_ADB_EP0, O_RDWR));
+        if (control < 0) {
+            PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
+            return false;
+        }
+
+        if (adb_write(control.get(), &v2_descriptor, sizeof(v2_descriptor)) < 0) {
+            D("[ %s: Switching to V1_descriptor format errno=%s ]", USB_FFS_ADB_EP0,
+              strerror(errno));
+            v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+            v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
+            v1_descriptor.header.fs_count = 3;
+            v1_descriptor.header.hs_count = 3;
+            v1_descriptor.fs_descs = fs_descriptors;
+            v1_descriptor.hs_descs = hs_descriptors;
+            if (adb_write(control.get(), &v1_descriptor, sizeof(v1_descriptor)) < 0) {
+                PLOG(ERROR) << "failed to write USB descriptors";
+                return false;
+            }
+        }
+
+        if (adb_write(control.get(), &strings, sizeof(strings)) < 0) {
+            PLOG(ERROR) << "failed to write USB strings";
+            return false;
+        }
+        // Signal only when writing the descriptors to ffs
+        android::base::SetProperty("sys.usb.ffs.ready", "1");
+    }
+
+    bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
+    if (bulk_out < 0) {
+        PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
+        return false;
+    }
+
+    bulk_in.reset(adb_open(USB_FFS_ADB_IN, O_WRONLY));
+    if (bulk_in < 0) {
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
+        return false;
+    }
+
+    *out_control = std::move(control);
+    *out_bulk_in = std::move(bulk_in);
+    *out_bulk_out = std::move(bulk_out);
+    return true;
+}
diff --git a/adb/daemon/usb_legacy.cpp b/adb/daemon/usb_legacy.cpp
new file mode 100644
index 0000000..7ace59d
--- /dev/null
+++ b/adb/daemon/usb_legacy.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2007 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 USB
+
+#include "sysdeps.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "adb.h"
+#include "adbd/usb.h"
+#include "transport.h"
+
+using namespace std::chrono_literals;
+
+#define MAX_PACKET_SIZE_FS 64
+#define MAX_PACKET_SIZE_HS 512
+#define MAX_PACKET_SIZE_SS 1024
+
+#define USB_FFS_BULK_SIZE 16384
+
+// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
+#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+
+static unique_fd& dummy_fd = *new unique_fd();
+
+static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
+    aiob->iocb.resize(num_bufs);
+    aiob->iocbs.resize(num_bufs);
+    aiob->events.resize(num_bufs);
+    aiob->num_submitted = 0;
+    for (unsigned i = 0; i < num_bufs; i++) {
+        aiob->iocbs[i] = &aiob->iocb[i];
+    }
+    memset(&aiob->ctx, 0, sizeof(aiob->ctx));
+    if (io_setup(num_bufs, &aiob->ctx)) {
+        D("[ aio: got error on io_setup (%d) ]", errno);
+    }
+}
+
+static int getMaxPacketSize(int ffs_fd) {
+    usb_endpoint_descriptor desc;
+    if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
+        D("[ could not get endpoint descriptor! (%d) ]", errno);
+        return MAX_PACKET_SIZE_HS;
+    } else {
+        return desc.wMaxPacketSize;
+    }
+}
+
+static bool init_functionfs(struct usb_handle* h) {
+    LOG(INFO) << "initializing functionfs";
+    if (!open_functionfs(&h->control, &h->bulk_out, &h->bulk_in)) {
+        return false;
+    }
+
+    h->read_aiob.fd = h->bulk_out.get();
+    h->write_aiob.fd = h->bulk_in.get();
+    h->reads_zero_packets = true;
+    return true;
+}
+
+static void usb_legacy_ffs_open_thread(usb_handle* usb) {
+    adb_thread_setname("usb legacy ffs open");
+
+    while (true) {
+        // wait until the USB device needs opening
+        std::unique_lock<std::mutex> lock(usb->lock);
+        while (!usb->open_new_connection) {
+            usb->notify.wait(lock);
+        }
+        usb->open_new_connection = false;
+        lock.unlock();
+
+        while (true) {
+            if (init_functionfs(usb)) {
+                LOG(INFO) << "functionfs successfully initialized";
+                break;
+            }
+            std::this_thread::sleep_for(1s);
+        }
+
+        LOG(INFO) << "registering usb transport";
+        register_usb_transport(usb, nullptr, nullptr, 1);
+    }
+
+    // never gets here
+    abort();
+}
+
+static int usb_ffs_write(usb_handle* h, const void* data, int len) {
+    D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
+
+    const char* buf = static_cast<const char*>(data);
+    int orig_len = len;
+    while (len > 0) {
+        int write_len = std::min(USB_FFS_BULK_SIZE, len);
+        int n = adb_write(h->bulk_in, buf, write_len);
+        if (n < 0) {
+            D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
+            return -1;
+        }
+        buf += n;
+        len -= n;
+    }
+
+    D("[ done fd=%d ]", h->bulk_in.get());
+    return orig_len;
+}
+
+static int usb_ffs_read(usb_handle* h, void* data, int len) {
+    D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
+
+    char* buf = static_cast<char*>(data);
+    int orig_len = len;
+    while (len > 0) {
+        int read_len = std::min(USB_FFS_BULK_SIZE, len);
+        int n = adb_read(h->bulk_out, buf, read_len);
+        if (n < 0) {
+            D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
+            return -1;
+        }
+        buf += n;
+        len -= n;
+    }
+
+    D("[ done fd=%d ]", h->bulk_out.get());
+    return orig_len;
+}
+
+static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
+    aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
+    bool zero_packet = false;
+
+    int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
+    const char* cur_data = reinterpret_cast<const char*>(data);
+    int packet_size = getMaxPacketSize(aiob->fd);
+
+    if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
+        0) {
+        D("[ Failed to madvise: %d ]", errno);
+    }
+
+    for (int i = 0; i < num_bufs; i++) {
+        int buf_len = std::min(len, static_cast<int>(h->io_size));
+        io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
+
+        len -= buf_len;
+        cur_data += buf_len;
+
+        if (len == 0 && buf_len % packet_size == 0 && read) {
+            // adb does not expect the device to send a zero packet after data transfer,
+            // but the host *does* send a zero packet for the device to read.
+            zero_packet = h->reads_zero_packets;
+        }
+    }
+    if (zero_packet) {
+        io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
+                packet_size, 0, read);
+        num_bufs += 1;
+    }
+
+    while (true) {
+        if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
+            PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
+            return -1;
+        }
+        if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
+                                            nullptr)) < num_bufs) {
+            PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
+            return -1;
+        }
+        if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
+            continue;
+        }
+        int ret = 0;
+        for (int i = 0; i < num_bufs; i++) {
+            if (aiob->events[i].res < 0) {
+                errno = -aiob->events[i].res;
+                PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
+                            << " total bufs " << num_bufs;
+                return -1;
+            }
+            ret += aiob->events[i].res;
+        }
+        return ret;
+    }
+}
+
+static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
+    return usb_ffs_do_aio(h, data, len, true);
+}
+
+static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
+    return usb_ffs_do_aio(h, data, len, false);
+}
+
+static void usb_ffs_kick(usb_handle* h) {
+    int err;
+
+    err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
+    if (err < 0) {
+        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
+    }
+
+    err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
+    if (err < 0) {
+        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
+    }
+
+    // don't close ep0 here, since we may not need to reinitialize it with
+    // the same descriptors again. if however ep1/ep2 fail to re-open in
+    // init_functionfs, only then would we close and open ep0 again.
+    // Ditto the comment in usb_adb_kick.
+    h->kicked = true;
+    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
+    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
+}
+
+static void usb_ffs_close(usb_handle* h) {
+    LOG(INFO) << "closing functionfs transport";
+
+    h->kicked = false;
+    h->bulk_out.reset();
+    h->bulk_in.reset();
+
+    // Notify usb_adb_open_thread to open a new connection.
+    h->lock.lock();
+    h->open_new_connection = true;
+    h->lock.unlock();
+    h->notify.notify_one();
+}
+
+usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size) {
+    usb_handle* h = new usb_handle();
+
+    if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
+        // Devices on older kernels (< 3.18) will not have aio support for ffs
+        // unless backported. Fall back on the non-aio functions instead.
+        h->write = usb_ffs_write;
+        h->read = usb_ffs_read;
+    } else {
+        h->write = usb_ffs_aio_write;
+        h->read = usb_ffs_aio_read;
+        aio_block_init(&h->read_aiob, num_bufs);
+        aio_block_init(&h->write_aiob, num_bufs);
+    }
+    h->io_size = io_size;
+    h->kick = usb_ffs_kick;
+    h->close = usb_ffs_close;
+    return h;
+}
+
+void usb_init_legacy() {
+    D("[ usb_init - using legacy FunctionFS ]");
+    dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
+    CHECK_NE(-1, dummy_fd.get());
+
+    std::thread(usb_legacy_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE))
+            .detach();
+}
+
+int usb_write(usb_handle* h, const void* data, int len) {
+    return h->write(h, data, len);
+}
+
+int usb_read(usb_handle* h, void* data, int len) {
+    return h->read(h, data, len);
+}
+
+int usb_close(usb_handle* h) {
+    h->close(h);
+    return 0;
+}
+
+void usb_kick(usb_handle* h) {
+    h->kick(h);
+}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 98a73eb..e096560 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -35,6 +35,8 @@
 #include <unordered_map>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
@@ -44,6 +46,7 @@
 #include "adb_trace.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "sysdeps/chrono.h"
 
 #define FDE_EVENTMASK  0x00ff
 #define FDE_STATEMASK  0xff00
@@ -247,6 +250,7 @@
     }
     CHECK_GT(pollfds.size(), 0u);
     D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+
     int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
     if (ret == -1) {
         PLOG(ERROR) << "poll(), ret = " << ret;
@@ -367,10 +371,66 @@
     }
 }
 
+static void fdevent_check_spin(uint64_t cycle) {
+    // Check to see if we're spinning because we forgot about an fdevent
+    // by keeping track of how long fdevents have been continuously pending.
+    struct SpinCheck {
+        fdevent* fde;
+        android::base::boot_clock::time_point timestamp;
+        uint64_t cycle;
+    };
+    static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
+    static auto last_cycle = android::base::boot_clock::now();
+
+    auto now = android::base::boot_clock::now();
+    if (now - last_cycle > 10ms) {
+        // We're not spinning.
+        g_continuously_pending.clear();
+        last_cycle = now;
+        return;
+    }
+    last_cycle = now;
+
+    for (auto* fde : g_pending_list) {
+        auto it = g_continuously_pending.find(fde->id);
+        if (it == g_continuously_pending.end()) {
+            g_continuously_pending[fde->id] =
+                    SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
+        } else {
+            it->second.cycle = cycle;
+        }
+    }
+
+    for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
+        if (it->second.cycle != cycle) {
+            it = g_continuously_pending.erase(it);
+        } else {
+            // Use an absurdly long window, since all we really care about is
+            // getting a bugreport eventually.
+            if (now - it->second.timestamp > 300s) {
+                LOG(FATAL_WITHOUT_ABORT)
+                        << "detected spin in fdevent: " << dump_fde(it->second.fde);
+#if defined(__linux__)
+                int fd = it->second.fde->fd.get();
+                std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+                std::string path;
+                if (!android::base::Readlink(fd_path, &path)) {
+                    PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
+                }
+                LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
+#endif
+                abort();
+            }
+            ++it;
+        }
+    }
+}
+
 void fdevent_loop() {
     set_main_thread();
     fdevent_run_setup();
 
+    uint64_t cycle = 0;
     while (true) {
         if (terminate_loop) {
             return;
@@ -380,6 +440,8 @@
 
         fdevent_process();
 
+        fdevent_check_spin(cycle++);
+
         while (!g_pending_list.empty()) {
             fdevent* fde = g_pending_list.front();
             g_pending_list.pop_front();
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 0cb2439..816134f 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -19,6 +19,7 @@
 #include <gtest/gtest.h>
 
 #include <limits>
+#include <memory>
 #include <queue>
 #include <string>
 #include <thread>
@@ -26,7 +27,6 @@
 
 #include "adb_io.h"
 #include "fdevent_test.h"
-#include "sysdeps/memory.h"
 
 class FdHandler {
   public:
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index 5a417e0..8d853c3 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -20,6 +20,7 @@
 #include <mutex>
 #include <thread>
 
+#include "adb_io.h"
 #include "socket.h"
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 1534792..8b07f74 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -78,7 +78,7 @@
 
     // Socket ids should never be 0.
     if (local_socket_next_id == 0) {
-        fatal("local socket id overflow");
+        LOG(FATAL) << "local socket id overflow";
     }
 
     local_socket_list.push_back(s);
@@ -451,7 +451,7 @@
 // Returns a new non-NULL asocket handle.
 asocket* create_remote_socket(unsigned id, atransport* t) {
     if (id == 0) {
-        fatal("invalid remote socket id (0)");
+        LOG(FATAL) << "invalid remote socket id (0)";
     }
     asocket* s = new asocket();
     s->id = id;
@@ -477,9 +477,7 @@
     p->payload.assign(destination, destination + strlen(destination) + 1);
     p->msg.data_length = p->payload.size();
 
-    if (p->msg.data_length > s->get_max_payload()) {
-        fatal("destination oversized");
-    }
+    CHECK_LE(p->msg.data_length, s->get_max_payload());
 
     send_packet(p, s->transport);
 }
diff --git a/adb/sysdeps/errno.cpp b/adb/sysdeps/errno.cpp
index 6869947..9a37ea2 100644
--- a/adb/sysdeps/errno.cpp
+++ b/adb/sysdeps/errno.cpp
@@ -24,10 +24,6 @@
 
 #include "adb.h"
 
-#if defined(_WIN32)
-#define ETXTBSY EBUSY
-#endif
-
 // Use the linux asm-generic values for errno (which are used on all android archs but mips).
 #define ERRNO_VALUES()             \
     ERRNO_VALUE(EACCES, 13);       \
diff --git a/adb/sysdeps/memory.h b/adb/sysdeps/memory.h
deleted file mode 100644
index 4108aff..0000000
--- a/adb/sysdeps/memory.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#pragma once
-
-/*
- * 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 <memory>
-#include <type_traits>
-
-#if defined(_WIN32)
-// We don't have C++14 on Windows yet.
-// Reimplement std::make_unique ourselves until we do.
-
-namespace internal {
-
-template <typename T>
-struct array_known_bounds;
-
-template <typename T>
-struct array_known_bounds<T[]> {
-    constexpr static bool value = false;
-};
-
-template <typename T, size_t N>
-struct array_known_bounds<T[N]> {
-    constexpr static bool value = true;
-};
-
-}  // namespace internal
-
-namespace std {
-
-template <typename T, typename... Args>
-typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(
-    Args&&... args) {
-    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-
-template <typename T>
-typename std::enable_if<std::is_array<T>::value && !internal::array_known_bounds<T>::value,
-                        std::unique_ptr<T>>::type
-make_unique(std::size_t size) {
-    return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
-}
-
-template <typename T, typename... Args>
-typename std::enable_if<std::is_array<T>::value && internal::array_known_bounds<T>::value,
-                        std::unique_ptr<T>>::type
-make_unique(Args&&... args) = delete;
-
-}  // namespace std
-
-#endif
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 8784757..8a6541d 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -35,6 +35,7 @@
 #include <cutils/sockets.h>
 
 #include <android-base/errors.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
@@ -46,8 +47,6 @@
 
 #include "sysdeps/uio.h"
 
-extern void fatal(const char *fmt, ...);
-
 /* forward declarations */
 
 typedef const struct FHClassRec_* FHClass;
@@ -94,10 +93,9 @@
     _fh_socket_writev,
 };
 
-#define assert(cond)                                                                       \
-    do {                                                                                   \
-        if (!(cond)) fatal("assertion failed '%s' on %s:%d\n", #cond, __FILE__, __LINE__); \
-    } while (0)
+#if defined(assert)
+#undef assert
+#endif
 
 void handle_deleter::operator()(HANDLE h) {
     // CreateFile() is documented to return INVALID_HANDLE_FILE on error,
@@ -726,8 +724,8 @@
         WSADATA wsaData;
         int rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
         if (rc != 0) {
-            fatal("adb: could not initialize Winsock: %s",
-                  android::base::SystemErrorCodeToString(rc).c_str());
+            LOG(FATAL) << "could not initialize Winsock: "
+                       << android::base::SystemErrorCodeToString(rc);
         }
 
         // Note that we do not call atexit() to register WSACleanup to be called
@@ -1283,11 +1281,11 @@
         }
 
         if (read_count == 0) {   // should be impossible
-            fatal("ReadConsoleInputA returned 0");
+            LOG(FATAL) << "ReadConsoleInputA returned 0";
         }
 
         if (read_count != 1) {   // should be impossible
-            fatal("ReadConsoleInputA did not return one input record");
+            LOG(FATAL) << "ReadConsoleInputA did not return one input record";
         }
 
         // If the console window is resized, emulate SIGWINCH by breaking out
@@ -1305,8 +1303,7 @@
         if ((input_record->EventType == KEY_EVENT) &&
             (input_record->Event.KeyEvent.bKeyDown)) {
             if (input_record->Event.KeyEvent.wRepeatCount == 0) {
-                fatal("ReadConsoleInputA returned a key event with zero repeat"
-                      " count");
+                LOG(FATAL) << "ReadConsoleInputA returned a key event with zero repeat count";
             }
 
             // Got an interesting INPUT_RECORD, so return
@@ -2189,7 +2186,7 @@
     for (int i = 0; i < argc; ++i) {
         std::string arg_narrow;
         if (!android::base::WideToUTF8(argv[i], &arg_narrow)) {
-            fatal_errno("cannot convert argument from UTF-16 to UTF-8");
+            PLOG(FATAL) << "cannot convert argument from UTF-16 to UTF-8";
         }
         narrow_args[i] = strdup(arg_narrow.c_str());
     }
@@ -2640,7 +2637,7 @@
         // If _wenviron is null, then -municode probably wasn't used. That
         // linker flag will cause the entry point to setup _wenviron. It will
         // also require an implementation of wmain() (which we provide above).
-        fatal("_wenviron is not set, did you link with -municode?");
+        LOG(FATAL) << "_wenviron is not set, did you link with -municode?";
     }
 
     // Read name/value pairs from UTF-16 _wenviron and write new name/value
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 7e73818..430fc3d 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -31,6 +31,7 @@
 import threading
 import time
 import unittest
+import warnings
 
 
 @contextlib.contextmanager
@@ -229,6 +230,10 @@
                                 stderr=subprocess.STDOUT)
 
         try:
+            # We get warnings for unclosed files for the subprocess's pipes,
+            # and it's somewhat cumbersome to close them, so just ignore this.
+            warnings.simplefilter("ignore", ResourceWarning)
+
             # Run the adb client and have it start the adb server.
             proc = subprocess.Popen(["adb", "-P", str(port), "start-server"],
                                     stdin=subprocess.PIPE,
diff --git a/adb/test_device.py b/adb/test_device.py
index c3166ff..34f8fd9 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -750,7 +750,7 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
-    def test_push_empty(self):
+    def disabled_test_push_empty(self):
         """Push an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
         self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 95df490..03a9f30 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -17,7 +17,6 @@
 #define TRACE_TAG TRANSPORT
 
 #include "sysdeps.h"
-#include "sysdeps/memory.h"
 
 #include "transport.h"
 
@@ -32,6 +31,7 @@
 #include <algorithm>
 #include <deque>
 #include <list>
+#include <memory>
 #include <mutex>
 #include <set>
 #include <thread>
@@ -52,7 +52,6 @@
 #include "fdevent.h"
 #include "sysdeps/chrono.h"
 
-static void register_transport(atransport* transport);
 static void remove_transport(atransport* transport);
 static void transport_unref(atransport* transport);
 
@@ -67,6 +66,8 @@
 const char* const kFeatureStat2 = "stat_v2";
 const char* const kFeatureLibusb = "libusb";
 const char* const kFeaturePushSync = "push_sync";
+const char* const kFeatureApex = "apex";
+const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
 
 namespace {
 
@@ -408,42 +409,6 @@
     fd_.reset();
 }
 
-static std::string dump_packet(const char* name, const char* func, apacket* p) {
-    unsigned command = p->msg.command;
-    int len = p->msg.data_length;
-    char cmd[9];
-    char arg0[12], arg1[12];
-    int n;
-
-    for (n = 0; n < 4; n++) {
-        int b = (command >> (n * 8)) & 255;
-        if (b < 32 || b >= 127) break;
-        cmd[n] = (char)b;
-    }
-    if (n == 4) {
-        cmd[4] = 0;
-    } else {
-        /* There is some non-ASCII name in the command, so dump
-            * the hexadecimal value instead */
-        snprintf(cmd, sizeof cmd, "%08x", command);
-    }
-
-    if (p->msg.arg0 < 256U)
-        snprintf(arg0, sizeof arg0, "%d", p->msg.arg0);
-    else
-        snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0);
-
-    if (p->msg.arg1 < 256U)
-        snprintf(arg1, sizeof arg1, "%d", p->msg.arg1);
-    else
-        snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
-
-    std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
-                                                     func, cmd, arg0, arg1, len);
-    result += dump_hex(p->payload.data(), p->payload.size());
-    return result;
-}
-
 void send_packet(apacket* p, atransport* t) {
     p->msg.magic = p->msg.command ^ 0xffffffff;
     // compute a checksum for connection/auth packets for compatibility reasons
@@ -456,7 +421,7 @@
     VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "to remote", p);
 
     if (t == nullptr) {
-        fatal("Transport is null");
+        LOG(FATAL) << "Transport is null";
     }
 
     if (t->Write(p) != 0) {
@@ -562,7 +527,7 @@
 
 asocket* create_device_tracker(bool long_output) {
     device_tracker* tracker = new device_tracker();
-    if (tracker == nullptr) fatal("cannot allocate device tracker");
+    if (tracker == nullptr) LOG(FATAL) << "cannot allocate device tracker";
 
     D("device tracker %p created", tracker);
 
@@ -668,7 +633,7 @@
     }
 
     if (transport_read_action(_fd, &m)) {
-        fatal_errno("cannot read transport registration socket");
+        PLOG(FATAL) << "cannot read transport registration socket";
     }
 
     t = m.transport;
@@ -707,7 +672,7 @@
             return true;
         });
         t->connection()->SetErrorCallback([t](Connection*, const std::string& error) {
-            D("%s: connection terminated: %s", t->serial.c_str(), error.c_str());
+            LOG(INFO) << t->serial_name() << ": connection terminated: " << error;
             fdevent_run_on_main_thread([t]() {
                 handle_offline(t);
                 transport_unref(t);
@@ -742,7 +707,7 @@
     int s[2];
 
     if (adb_socketpair(s)) {
-        fatal_errno("cannot open transport registration socketpair");
+        PLOG(FATAL) << "cannot open transport registration socketpair";
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
@@ -766,13 +731,13 @@
 }
 
 /* the fdevent select pump is single threaded */
-static void register_transport(atransport* transport) {
+void register_transport(atransport* transport) {
     tmsg m;
     m.transport = transport;
     m.action = 1;
     D("transport: %s registered", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
-        fatal_errno("cannot write transport registration socket\n");
+        PLOG(FATAL) << "cannot write transport registration socket";
     }
 }
 
@@ -782,7 +747,7 @@
     m.action = 0;
     D("transport: %s removed", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
-        fatal_errno("cannot write transport registration socket\n");
+        PLOG(FATAL) << "cannot write transport registration socket";
     }
 }
 
@@ -794,6 +759,7 @@
     CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
+        LOG(INFO) << "destroying transport " << t->serial_name();
         t->connection()->Stop();
 #if ADB_HOST
         if (t->IsTcpDevice() && !t->kicked()) {
@@ -1043,7 +1009,10 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-        kFeatureShell2, kFeatureCmd, kFeatureStat2,
+        kFeatureShell2, kFeatureCmd, kFeatureStat2, kFeatureFixedPushMkdir,
+#if ADB_HOST
+                kFeatureApex
+#endif
         // Increment ADB_SERVER_VERSION whenever the feature list changes to
         // make sure that the adb client and server features stay in sync
         // (http://b/24370690).
@@ -1329,6 +1298,7 @@
     register_transport(t);
 }
 
+#if ADB_HOST
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
@@ -1340,6 +1310,7 @@
         return false;
     });
 }
+#endif
 
 bool check_header(apacket* p, atransport* t) {
     if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
diff --git a/adb/transport.h b/adb/transport.h
index f854ce5..9894bdf 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -57,8 +57,12 @@
 extern const char* const kFeatureStat2;
 // The server is running with libusb enabled.
 extern const char* const kFeatureLibusb;
-// The server supports `push --sync`.
+// adbd supports `push --sync`.
 extern const char* const kFeaturePushSync;
+// adbd supports installing .apex packages.
+extern const char* const kFeatureApex;
+// adbd has b/110953234 fixed.
+extern const char* const kFeatureFixedPushMkdir;
 
 TransportId NextTransportId();
 
@@ -362,6 +366,7 @@
 void kick_all_tcp_devices();
 void kick_all_transports();
 
+void register_transport(atransport* transport);
 void register_usb_transport(usb_handle* h, const char* serial,
                             const char* devpath, unsigned writeable);
 
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
index 85f3c52..ec61279 100644
--- a/adb/transport_fd.cpp
+++ b/adb/transport_fd.cpp
@@ -17,6 +17,7 @@
 #include <stdint.h>
 
 #include <deque>
+#include <memory>
 #include <mutex>
 #include <string>
 #include <thread>
@@ -28,7 +29,6 @@
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "sysdeps.h"
-#include "sysdeps/memory.h"
 #include "transport.h"
 #include "types.h"
 
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 8353d89..dc87ac7 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 
 #include <condition_variable>
+#include <memory>
 #include <mutex>
 #include <thread>
 #include <unordered_map>
@@ -45,7 +46,6 @@
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "sysdeps/chrono.h"
-#include "sysdeps/memory.h"
 
 #if ADB_HOST
 
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 8c628d8..b66f8fa 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -19,13 +19,16 @@
 #include <gtest/gtest.h>
 
 #include "adb.h"
+#include "fdevent_test.h"
+
+struct TransportTest : public FdeventTest {};
 
 static void DisconnectFunc(void* arg, atransport*) {
     int* count = reinterpret_cast<int*>(arg);
     ++*count;
 }
 
-TEST(transport, RunDisconnects) {
+TEST_F(TransportTest, RunDisconnects) {
     atransport t;
     // RunDisconnects() can be called with an empty atransport.
     t.RunDisconnects();
@@ -49,7 +52,7 @@
     ASSERT_EQ(0, count);
 }
 
-TEST(transport, SetFeatures) {
+TEST_F(TransportTest, SetFeatures) {
     atransport t;
     ASSERT_EQ(0U, t.features().size());
 
@@ -77,8 +80,7 @@
     ASSERT_EQ(0U, t.features().size());
 }
 
-TEST(transport, parse_banner_no_features) {
-    set_main_thread();
+TEST_F(TransportTest, parse_banner_no_features) {
     atransport t;
 
     parse_banner("host::", &t);
@@ -91,7 +93,7 @@
     ASSERT_EQ(std::string(), t.device);
 }
 
-TEST(transport, parse_banner_product_features) {
+TEST_F(TransportTest, parse_banner_product_features) {
     atransport t;
 
     const char banner[] =
@@ -107,9 +109,8 @@
     ASSERT_EQ(std::string("baz"), t.device);
 }
 
-TEST(transport, parse_banner_features) {
+TEST_F(TransportTest, parse_banner_features) {
     atransport t;
-
     const char banner[] =
         "host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;"
         "features=woodly,doodly";
@@ -126,7 +127,7 @@
     ASSERT_EQ(std::string("baz"), t.device);
 }
 
-TEST(transport, test_matches_target) {
+TEST_F(TransportTest, test_matches_target) {
     std::string serial = "foo";
     std::string devpath = "/path/to/bar";
     std::string product = "test_product";
@@ -157,7 +158,7 @@
     }
 }
 
-TEST(transport, test_matches_target_local) {
+TEST_F(TransportTest, test_matches_target_local) {
     std::string serial = "100.100.100.100:5555";
 
     atransport t;
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 602970c..c471bf9 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -16,8 +16,9 @@
 
 #define TRACE_TAG TRANSPORT
 
+#include <memory>
+
 #include "sysdeps.h"
-#include "sysdeps/memory.h"
 #include "transport.h"
 
 #include <stdio.h>
diff --git a/adb/types.h b/adb/types.h
index a3e5d48..0c71c3a 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -25,7 +25,6 @@
 
 #include <android-base/logging.h>
 
-#include "sysdeps/memory.h"
 #include "sysdeps/uio.h"
 
 // Essentially std::vector<char>, except without zero initialization or reallocation.
@@ -42,14 +41,14 @@
     }
 
     Block(const Block& copy) = delete;
-    Block(Block&& move) {
+    Block(Block&& move) noexcept {
         std::swap(data_, move.data_);
         std::swap(capacity_, move.capacity_);
         std::swap(size_, move.size_);
     }
 
     Block& operator=(const Block& copy) = delete;
-    Block& operator=(Block&& move) {
+    Block& operator=(Block&& move) noexcept {
         clear();
 
         std::swap(data_, move.data_);
@@ -147,12 +146,10 @@
     }
 
     IOVector(const IOVector& copy) = delete;
-    IOVector(IOVector&& move) : IOVector() {
-        *this = std::move(move);
-    }
+    IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); }
 
     IOVector& operator=(const IOVector& copy) = delete;
-    IOVector& operator=(IOVector&& move) {
+    IOVector& operator=(IOVector&& move) noexcept {
         chain_ = std::move(move.chain_);
         chain_length_ = move.chain_length_;
         begin_offset_ = move.begin_offset_;
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
index 31ab90a..1fbd2ca 100644
--- a/adb/types_test.cpp
+++ b/adb/types_test.cpp
@@ -16,7 +16,7 @@
 
 #include <gtest/gtest.h>
 
-#include "sysdeps/memory.h"
+#include <memory>
 #include "types.h"
 
 static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
diff --git a/base/Android.bp b/base/Android.bp
index daa820a..741664b 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -47,6 +47,7 @@
         "chrono_utils.cpp",
         "file.cpp",
         "logging.cpp",
+        "mapped_file.cpp",
         "parsenetaddress.cpp",
         "properties.cpp",
         "quick_exit.cpp",
@@ -124,6 +125,7 @@
         "file_test.cpp",
         "logging_test.cpp",
         "macros_test.cpp",
+        "mapped_file_test.cpp",
         "parsedouble_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index d6fe753..3834ed4 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -38,7 +38,6 @@
 #endif
 #if defined(_WIN32)
 #include <windows.h>
-#define O_CLOEXEC O_NOINHERIT
 #define O_NOFOLLOW 0
 #endif
 
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 908690b..86d537d 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -20,13 +20,16 @@
 #include <sys/types.h>
 #include <string>
 
+#include "android-base/off64_t.h"
+
 #if !defined(_WIN32) && !defined(O_BINARY)
+/** Windows needs O_BINARY, but Unix never mangles line endings. */
 #define O_BINARY 0
 #endif
 
-#if defined(__APPLE__)
-/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
-typedef off_t off64_t;
+#if defined(_WIN32) && !defined(O_CLOEXEC)
+/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
+#define O_CLOEXEC O_NOINHERIT
 #endif
 
 namespace android {
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 1748665..5abf514 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -75,46 +75,6 @@
 
 #define arraysize(array) (sizeof(ArraySizeHelper(array)))
 
-// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
-// but can be used on anonymous types or types defined inside
-// functions.  It's less safe than arraysize as it accepts some
-// (although not all) pointers.  Therefore, you should use arraysize
-// whenever possible.
-//
-// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
-// size_t.
-//
-// ARRAYSIZE_UNSAFE catches a few type errors.  If you see a compiler error
-//
-//   "warning: division by zero in ..."
-//
-// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
-// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
-//
-// The following comments are on the implementation details, and can
-// be ignored by the users.
-//
-// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
-// the array) and sizeof(*(arr)) (the # of bytes in one array
-// element).  If the former is divisible by the latter, perhaps arr is
-// indeed an array, in which case the division result is the # of
-// elements in the array.  Otherwise, arr cannot possibly be an array,
-// and we generate a compiler error to prevent the code from
-// compiling.
-//
-// Since the size of bool is implementation-defined, we need to cast
-// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
-// result has type size_t.
-//
-// This macro is not perfect as it wrongfully accepts certain
-// pointers, namely where the pointer size is divisible by the pointee
-// size.  Since all our code has to go through a 32-bit compiler,
-// where a pointer is 4 bytes, this means all pointers to a type whose
-// size is 3 or greater than 4 will be (righteously) rejected.
-#define ARRAYSIZE_UNSAFE(a)     \
-  ((sizeof(a) / sizeof(*(a))) / \
-    static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
-
 #define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
 
 // Changing this definition will cause you a lot of pain.  A majority of
@@ -153,23 +113,23 @@
 //    case 42:
 //      ...
 //
-//  As shown in the example above, the FALLTHROUGH_INTENDED macro should be
-//  followed by a semicolon. It is designed to mimic control-flow statements
-//  like 'break;', so it can be placed in most places where 'break;' can, but
-//  only if there are no statements on the execution path between it and the
-//  next switch label.
+// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+// followed by a semicolon. It is designed to mimic control-flow statements
+// like 'break;', so it can be placed in most places where 'break;' can, but
+// only if there are no statements on the execution path between it and the
+// next switch label.
 //
-//  When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
-//  [[clang::fallthrough]] attribute, which is analysed when performing switch
-//  labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
-//  documentation on language extensions for details:
-//  http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
+// [[clang::fallthrough]] attribute, which is analysed when performing switch
+// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
+// documentation on language extensions for details:
+// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
 //
-//  When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
-//  effect on diagnostics.
+// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+// effect on diagnostics.
 //
-//  In either case this macro has no effect on runtime behavior and performance
-//  of code.
+// In either case this macro has no effect on runtime behavior and performance
+// of code.
 #ifndef FALLTHROUGH_INTENDED
 #define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
 #endif
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
new file mode 100644
index 0000000..80513b1
--- /dev/null
+++ b/base/include/android-base/mapped_file.h
@@ -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.
+ */
+
+#pragma once
+
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+
+#include <sys/types.h>
+
+#include <memory>
+
+#if defined(_WIN32)
+#include <windows.h>
+#define PROT_READ 1
+#define PROT_WRITE 2
+#else
+#include <sys/mman.h>
+#endif
+
+namespace android {
+namespace base {
+
+/**
+ * A region of a file mapped into memory.
+ */
+class MappedFile {
+ public:
+  /**
+   * Creates a new mapping of the file pointed to by `fd`. Unlike the underlying OS primitives,
+   * `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping
+   * will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`.
+   */
+  static std::unique_ptr<MappedFile> FromFd(int fd, off64_t offset, size_t length, int prot);
+
+  /**
+   * Removes the mapping.
+   */
+  ~MappedFile();
+
+  char* data() { return base_ + offset_; }
+  size_t size() { return size_; }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
+
+  char* base_;
+  size_t size_;
+
+  size_t offset_;
+
+#if defined(_WIN32)
+  MappedFile(char* base, size_t size, size_t offset, HANDLE handle)
+      : base_(base), size_(size), offset_(offset), handle_(handle) {}
+  HANDLE handle_;
+#else
+  MappedFile(char* base, size_t size, size_t offset) : base_(base), size_(size), offset_(offset) {}
+#endif
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/libbacktrace/GetPss.h b/base/include/android-base/off64_t.h
similarity index 74%
rename from libbacktrace/GetPss.h
rename to base/include/android-base/off64_t.h
index 787c33d..e6b71b8 100644
--- a/libbacktrace/GetPss.h
+++ b/base/include/android-base/off64_t.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef _LIBBACKTRACE_GET_PSS_H
-#define _LIBBACKTRACE_GET_PSS_H
+#pragma once
 
-size_t GetPssBytes();
-
-#endif // _LIBBACKTRACE_GET_PSS_H
+#if defined(__APPLE__)
+/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 9444fdd..be8b97b 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -22,6 +22,7 @@
 
 #include <limits>
 #include <string>
+#include <type_traits>
 
 namespace android {
 namespace base {
@@ -33,6 +34,7 @@
 template <typename T>
 bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
                bool allow_suffixes = false) {
+  static_assert(std::is_unsigned<T>::value, "ParseUint can only be used with unsigned types");
   while (isspace(*s)) {
     s++;
   }
@@ -96,6 +98,7 @@
 bool ParseInt(const char* s, T* out,
               T min = std::numeric_limits<T>::min(),
               T max = std::numeric_limits<T>::max()) {
+  static_assert(std::is_signed<T>::value, "ParseInt can only be used with signed types");
   while (isspace(*s)) {
     s++;
   }
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index e6a9d10..5a224d6 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -28,7 +28,7 @@
  public:
   ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
 
-  ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+  ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
     that.active_ = false;
   }
 
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 9c35560..fc5c1ce 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -61,6 +61,7 @@
 bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
 bool StartsWith(const std::string& s, const std::string& prefix);
 bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
+bool StartsWith(const std::string& s, char prefix);
 
 // Tests whether 's' ends with 'suffix'.
 // TODO: string_view
@@ -68,6 +69,7 @@
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
 bool EndsWith(const std::string& s, const std::string& suffix);
 bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
+bool EndsWith(const std::string& s, char suffix);
 
 // Tests whether 'lhs' equals 'rhs', ignoring case.
 bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 71025ad..cd2dc04 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -90,8 +90,8 @@
   explicit unique_fd_impl(int fd) { reset(fd); }
   ~unique_fd_impl() { reset(); }
 
-  unique_fd_impl(unique_fd_impl&& other) { reset(other.release()); }
-  unique_fd_impl& operator=(unique_fd_impl&& s) {
+  unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
+  unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
     int fd = s.fd_;
     s.fd_ = -1;
     reset(fd, &s);
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 4b91623..1a414ec 100644
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #ifdef _WIN32
+#include <sys/types.h>
 #include <string>
 #else
 // Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
diff --git a/base/logging.cpp b/base/logging.cpp
index d60d91d..bd09069 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -53,6 +53,7 @@
 #include <unistd.h>
 #endif
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
@@ -71,14 +72,8 @@
   static char progname[MAX_PATH] = {};
 
   if (first) {
-    CHAR longname[MAX_PATH];
-    DWORD nchars = GetModuleFileNameA(nullptr, longname, arraysize(longname));
-    if ((nchars >= arraysize(longname)) || (nchars == 0)) {
-      // String truncation or some other error.
-      strcpy(progname, "<unknown>");
-    } else {
-      strcpy(progname, basename(longname));
-    }
+    snprintf(progname, sizeof(progname), "%s",
+             android::base::Basename(android::base::GetExecutablePath()).c_str());
     first = false;
   }
 
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
new file mode 100644
index 0000000..f7901af
--- /dev/null
+++ b/base/mapped_file.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 "android-base/mapped_file.h"
+
+namespace android {
+namespace base {
+
+static off64_t InitPageSize() {
+#if defined(_WIN32)
+  SYSTEM_INFO si;
+  GetSystemInfo(&si);
+  return si.dwAllocationGranularity;
+#else
+  return sysconf(_SC_PAGE_SIZE);
+#endif
+}
+
+std::unique_ptr<MappedFile> MappedFile::FromFd(int fd, off64_t offset, size_t length, int prot) {
+  static off64_t page_size = InitPageSize();
+  size_t slop = offset % page_size;
+  off64_t file_offset = offset - slop;
+  off64_t file_length = length + slop;
+
+#if defined(_WIN32)
+  HANDLE handle =
+      CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
+                        (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+  if (handle == nullptr) return nullptr;
+  void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
+                             file_offset, file_length);
+  if (base == nullptr) {
+    CloseHandle(handle);
+    return nullptr;
+  }
+  return std::unique_ptr<MappedFile>(
+      new MappedFile{static_cast<char*>(base), length, slop, handle});
+#else
+  void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd, file_offset);
+  if (base == MAP_FAILED) return nullptr;
+  return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
+#endif
+}
+
+MappedFile::~MappedFile() {
+#if defined(_WIN32)
+  if (base_ != nullptr) UnmapViewOfFile(base_);
+  if (handle_ != nullptr) CloseHandle(handle_);
+#else
+  if (base_ != nullptr) munmap(base_, size_);
+#endif
+
+  base_ = nullptr;
+  offset_ = size_ = 0;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
new file mode 100644
index 0000000..57fde6f
--- /dev/null
+++ b/base/mapped_file_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "android-base/mapped_file.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include "android-base/unique_fd.h"
+
+TEST(mapped_file, smoke) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_TRUE(android::base::WriteStringToFd("hello world", tf.fd));
+
+  auto m = android::base::MappedFile::FromFd(tf.fd, 3, 2, PROT_READ);
+  ASSERT_EQ(2u, m->size());
+  ASSERT_EQ('l', m->data()[0]);
+  ASSERT_EQ('o', m->data()[1]);
+}
diff --git a/base/strings.cpp b/base/strings.cpp
index a8bb2a9..2d6eef0 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -95,6 +95,10 @@
   return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
 }
 
+bool StartsWith(const std::string& s, char prefix) {
+  return *s.c_str() == prefix;  // Use c_str() to guarantee there is at least a '\0'.
+}
+
 bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
   return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
 }
@@ -121,6 +125,10 @@
   return EndsWith(s, suffix.c_str(), suffix.size(), true);
 }
 
+bool EndsWith(const std::string& s, char suffix) {
+  return EndsWith(s, &suffix, 1, true);
+}
+
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
   return EndsWith(s, suffix, strlen(suffix), false);
 }
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index b8639ea..9d74094 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -198,6 +198,12 @@
   ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
 }
 
+TEST(strings, StartsWith_char) {
+  ASSERT_FALSE(android::base::StartsWith("", 'f'));
+  ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
+  ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
+}
+
 TEST(strings, EndsWith_empty) {
   ASSERT_FALSE(android::base::EndsWith("", "foo"));
   ASSERT_TRUE(android::base::EndsWith("", ""));
@@ -273,6 +279,12 @@
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
 }
 
+TEST(strings, EndsWith_char) {
+  ASSERT_FALSE(android::base::EndsWith("", 'o'));
+  ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
+  ASSERT_FALSE(android::base::EndsWith("foo", "f"));
+}
+
 TEST(strings, EqualsIgnoreCase) {
   ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
   ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index ed7423b..15c0265 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -250,11 +250,12 @@
         }
 
         uint64_t expected = pack_thread_fd(-1, -1);
-        if (!trace_output.compare_exchange_strong(expected,
-                                                  pack_thread_fd(tid, pipe_write.release()))) {
+        int sent_fd = pipe_write.release();
+        if (!trace_output.compare_exchange_strong(expected, pack_thread_fd(tid, sent_fd))) {
           auto [tid, fd] = unpack_thread_fd(expected);
           async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                                 "thread %d is already outputting to fd %d?", tid, fd);
+          close(sent_fd);
           return false;
         }
 
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 8bdc02f..d0c5234 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -382,6 +382,8 @@
         case TRAP_TRACE: return "TRAP_TRACE";
         case TRAP_BRANCH: return "TRAP_BRANCH";
         case TRAP_HWBKPT: return "TRAP_HWBKPT";
+        case TRAP_UNK:
+          return "TRAP_UNDIAGNOSED";
       }
       if ((si->si_code & 0xff) == SIGTRAP) {
         switch ((si->si_code >> 8) & 0xff) {
@@ -403,7 +405,7 @@
             return "PTRACE_EVENT_STOP";
         }
       }
-      static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+      static_assert(NSIGTRAP == TRAP_UNK, "missing TRAP_* si_code");
       break;
   }
   // Then the other codes...
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 2eaf006..705da33 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -63,3 +63,4 @@
 #define FB_VAR_VARIANT "variant"
 #define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
 #define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
+#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b02d968..6e45133 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -27,6 +27,7 @@
 #include <android-base/unique_fd.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/wipe.h>
+#include <fs_mgr.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <uuid/uuid.h>
@@ -97,6 +98,7 @@
             {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
             {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
             {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+            {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
             {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
 
     if (args.size() < 2) {
@@ -129,6 +131,11 @@
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
     }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
+    }
+
     PartitionHandle handle;
     if (!OpenPartition(device, args[1], &handle)) {
         return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
@@ -161,9 +168,15 @@
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
     }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Download is not allowed on locked devices");
+    }
+
     // arg[0] is the command name, arg[1] contains size of data to be downloaded
     unsigned int size;
-    if (!android::base::ParseUint("0x" + args[1], &size, UINT_MAX)) {
+    if (!android::base::ParseUint("0x" + args[1], &size, kMaxDownloadSizeDefault)) {
         return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
     }
     device->download_data().resize(size);
@@ -201,6 +214,11 @@
         return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
     }
 
+    if (GetDeviceLockStatus()) {
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "set_active command is not allowed on locked devices");
+    }
+
     // Slot suffix needs to be between 'a' and 'z'.
     Slot slot;
     if (!GetSlotNumber(args[1], &slot)) {
@@ -219,7 +237,13 @@
     CommandResult ret;
     auto cb = [&ret](CommandResult result) { ret = result; };
     auto result = boot_control_hal->setActiveBootSlot(slot, cb);
-    if (result.isOk() && ret.success) return device->WriteStatus(FastbootResult::OKAY, "");
+    if (result.isOk() && ret.success) {
+        // Save as slot suffix to match the suffix format as returned from
+        // the boot control HAL.
+        auto current_slot = "_" + args[1];
+        device->set_active_slot(current_slot);
+        return device->WriteStatus(FastbootResult::OKAY, "");
+    }
     return device->WriteStatus(FastbootResult::FAIL, "Unable to set slot");
 }
 
@@ -310,7 +334,7 @@
 };
 
 PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
-    auto super_device = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name());
     if (!super_device) {
         return;
     }
@@ -353,13 +377,7 @@
         return device->WriteFail("Partition already exists");
     }
 
-    // Make a random UUID, since they're not currently used.
-    uuid_t uuid;
-    char uuid_str[37];
-    uuid_generate_random(uuid);
-    uuid_unparse(uuid, uuid_str);
-
-    Partition* partition = builder->AddPartition(partition_name, uuid_str, 0);
+    Partition* partition = builder->AddPartition(partition_name, 0);
     if (!partition) {
         return device->WriteFail("Failed to add partition");
     }
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index 9df43a9..bb1f988 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -19,6 +19,8 @@
 #include <string>
 #include <vector>
 
+constexpr unsigned int kMaxDownloadSizeDefault = 0x20000000;
+
 class FastbootDevice;
 
 enum class FastbootResult {
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index b843c05..7be721a 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -57,7 +57,8 @@
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
       health_hal_(get_health_service()),
-      fastboot_hal_(IFastboot::getService()) {}
+      fastboot_hal_(IFastboot::getService()),
+      active_slot_("") {}
 
 FastbootDevice::~FastbootDevice() {
     CloseDevice();
@@ -68,6 +69,11 @@
 }
 
 std::string FastbootDevice::GetCurrentSlot() {
+    // Check if a set_active ccommand was issued earlier since the boot control HAL
+    // returns the slot that is currently booted into.
+    if (!active_slot_.empty()) {
+        return active_slot_;
+    }
     // Non-A/B devices must not have boot control HALs.
     if (!boot_control_hal_) {
         return "";
@@ -131,7 +137,7 @@
         std::string cmd_name;
         if (android::base::StartsWith(command, "oem ")) {
             args = {command};
-            cmd_name = "oem";
+            cmd_name = FB_CMD_OEM;
         } else {
             args = android::base::Split(command, ":");
             cmd_name = args[0];
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 2eb7177..091aadf 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -56,6 +56,8 @@
     }
     android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
 
+    void set_active_slot(const std::string& active_slot) { active_slot_ = active_slot; }
+
   private:
     const std::unordered_map<std::string, CommandHandler> kCommandMap;
 
@@ -64,4 +66,5 @@
     android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
     android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
     std::vector<char> download_data_;
+    std::string active_slot_;
 };
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index a383c54..66b90bf 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -21,10 +21,15 @@
 
 #include <algorithm>
 #include <memory>
+#include <set>
+#include <string>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <sparse/sparse.h>
@@ -32,13 +37,35 @@
 #include "fastboot_device.h"
 #include "utility.h"
 
+using namespace android::fs_mgr;
+using namespace std::literals;
+
 namespace {
 
 constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a;
 
-}  // namespace
+void WipeOverlayfsForPartition(FastbootDevice* device, const std::string& partition_name) {
+    // May be called, in the case of sparse data, multiple times so cache/skip.
+    static std::set<std::string> wiped;
+    if (wiped.find(partition_name) != wiped.end()) return;
+    wiped.insert(partition_name);
+    // Following appears to have a first time 2% impact on flashing speeds.
 
-using namespace android::fs_mgr;
+    // Convert partition_name to a validated mount point and wipe.
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    for (auto i = 0; i < fstab->num_entries; i++) {
+        const auto mount_point = fstab->recs[i].mount_point;
+        if (!mount_point) continue;
+        auto partition = android::base::Basename(mount_point);
+        if ("/"s == mount_point) partition = "system";
+        if ((partition + device->GetCurrentSlot()) == partition_name) {
+            fs_mgr_overlayfs_teardown(mount_point);
+        }
+    }
+}
+
+}  // namespace
 
 int FlashRawDataChunk(int fd, const char* data, size_t len) {
     size_t ret = 0;
@@ -72,7 +99,7 @@
 }
 
 int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
-    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, true);
+    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false);
     if (!file) {
         return -ENOENT;
     }
@@ -101,15 +128,11 @@
     } else if (data.size() > get_block_device_size(handle.fd())) {
         return -EOVERFLOW;
     }
+    WipeOverlayfsForPartition(device, partition_name);
     return FlashBlockDevice(handle.fd(), data);
 }
 
-bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe) {
-    std::optional<std::string> super = FindPhysicalPartition(partition_name);
-    if (!super) {
-        return device->WriteFail("Could not find partition: " + partition_name);
-    }
-
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
     std::vector<char> data = std::move(device->download_data());
     if (data.empty()) {
         return device->WriteFail("No data available");
@@ -125,48 +148,17 @@
     // image.
     std::string slot_suffix = device->GetCurrentSlot();
     uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
-    std::unique_ptr<LpMetadata> metadata = ReadMetadata(super->c_str(), slot_number);
-    if (!metadata || wipe) {
-        if (!FlashPartitionTable(super.value(), *new_metadata.get())) {
+    if (wipe || !ReadMetadata(super_name, slot_number)) {
+        if (!FlashPartitionTable(super_name, *new_metadata.get())) {
             return device->WriteFail("Unable to flash new partition table");
         }
         return device->WriteOkay("Successfully flashed partition table");
     }
 
-    // There's a working super partition, and we don't want to wipe it - it may
-    // may contain partitions created for the user. Instead, we create a zero-
-    // sized partition for each entry in the new partition table. It is then
-    // the host's responsibility to size it correctly via resize-logical-partition.
-    std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*metadata.get());
-    if (!builder) {
-        return device->WriteFail("Unable to create a metadata builder");
-    }
-    for (const auto& partition : new_metadata->partitions) {
-        std::string name = GetPartitionName(partition);
-        if (builder->FindPartition(name)) {
-            continue;
-        }
-        std::string guid = GetPartitionGuid(partition);
-        if (!builder->AddPartition(name, guid, partition.attributes)) {
-            return device->WriteFail("Unable to add partition: " + name);
-        }
-    }
-
-    // The scratch partition may exist as temporary storage, created for
-    // use by adb remount for overlayfs. If we're performing a flashall
-    // operation then we want to start over with a clean slate, so we
-    // remove the scratch partition until it is requested again.
-    builder->RemovePartition("scratch");
-
-    new_metadata = builder->Export();
-    if (!new_metadata) {
-        return device->WriteFail("Unable to export new partition table");
-    }
-
     // Write the new table to every metadata slot.
     bool ok = true;
     for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
-        ok &= UpdatePartitionTable(super.value(), *new_metadata.get(), i);
+        ok &= UpdatePartitionTable(super_name, *new_metadata.get(), i);
     }
 
     if (!ok) {
diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h
index 89e20fc..b15f28b 100644
--- a/fastboot/device/flashing.h
+++ b/fastboot/device/flashing.h
@@ -22,4 +22,4 @@
 class FastbootDevice;
 
 int Flash(FastbootDevice* device, const std::string& partition_name);
-bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe);
+bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe);
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 02f6f2c..b844b9f 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -23,6 +23,8 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <liblp/liblp.h>
 
@@ -44,7 +46,7 @@
 
 static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
                                  PartitionHandle* handle) {
-    std::optional<std::string> path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    std::optional<std::string> path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
     if (!path) {
         return false;
     }
@@ -81,6 +83,10 @@
 }
 
 std::optional<std::string> FindPhysicalPartition(const std::string& name) {
+    // Check for an invalid file name
+    if (android::base::StartsWith(name, "../") || name.find("/../") != std::string::npos) {
+        return {};
+    }
     std::string path = "/dev/block/by-name/" + name;
     if (access(path.c_str(), W_OK) < 0) {
         return {};
@@ -100,7 +106,7 @@
 
 bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
                             bool* is_zero_length) {
-    auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
     if (!path) {
         return false;
     }
@@ -149,7 +155,7 @@
     }
 
     // Next get logical partitions.
-    if (auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME)) {
+    if (auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name())) {
         uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
         if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
             for (const auto& partition : metadata->partitions) {
@@ -163,6 +169,9 @@
 
 bool GetDeviceLockStatus() {
     std::string cmdline;
-    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    // Return lock status true if unable to read kernel command line.
+    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+        return true;
+    }
     return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
 }
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 01415d7..cbd2856 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -36,7 +36,6 @@
 using ::android::hardware::fastboot::V1_0::Result;
 using ::android::hardware::fastboot::V1_0::Status;
 
-constexpr int kMaxDownloadSizeDefault = 0x20000000;
 constexpr char kFastbootProtocolVersion[] = "0.4";
 
 bool GetVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
@@ -96,6 +95,56 @@
     return true;
 }
 
+bool GetBatteryVoltageHelper(FastbootDevice* device, int32_t* battery_voltage) {
+    using android::hardware::health::V2_0::HealthInfo;
+    using android::hardware::health::V2_0::Result;
+
+    auto health_hal = device->health_hal();
+    if (!health_hal) {
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
+        *battery_voltage = info.legacy.batteryVoltage;
+        ret = result;
+    });
+    if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                     std::string* message) {
+    int32_t battery_voltage = 0;
+    if (!GetBatteryVoltageHelper(device, &battery_voltage)) {
+        *message = "Unable to read battery voltage";
+        return false;
+    }
+
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->getBatteryVoltageFlashingThreshold(
+            [&](int32_t voltage_threshold, Result result) {
+                *message = battery_voltage >= voltage_threshold ? "yes" : "no";
+                ret = result;
+            });
+
+    if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+        *message = "Unable to get battery voltage flashing threshold";
+        return false;
+    }
+
+    return true;
+}
+
 bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& /* args */,
                            std::string* message) {
     auto fastboot_hal = device->fastboot_hal();
@@ -120,26 +169,13 @@
 
 bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& /* args */,
                        std::string* message) {
-    using android::hardware::health::V2_0::HealthInfo;
-    using android::hardware::health::V2_0::Result;
-
-    auto health_hal = device->health_hal();
-    if (!health_hal) {
-        *message = "Health HAL not found";
-        return false;
+    int32_t battery_voltage = 0;
+    if (GetBatteryVoltageHelper(device, &battery_voltage)) {
+        *message = std::to_string(battery_voltage);
+        return true;
     }
-
-    Result ret;
-    auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
-        *message = std::to_string(info.legacy.batteryVoltage);
-        ret = result;
-    });
-    if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
-        *message = "Unable to get battery voltage";
-        return false;
-    }
-
-    return true;
+    *message = "Unable to get battery voltage";
+    return false;
 }
 
 bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
@@ -252,7 +288,7 @@
     bool is_zero_length;
     if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) &&
         is_zero_length) {
-        *message = "0";
+        *message = "0x0";
         return true;
     }
     // Otherwise, open the partition as normal.
@@ -272,7 +308,14 @@
         *message = "Missing argument";
         return false;
     }
+
     std::string partition_name = args[0];
+    if (!FindPhysicalPartition(partition_name) &&
+        !LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
+        *message = "Invalid partition";
+        return false;
+    }
+
     auto fastboot_hal = device->fastboot_hal();
     if (!fastboot_hal) {
         *message = "Fastboot HAL not found";
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index e7c3c7c..59b71e8 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -57,6 +57,9 @@
                            std::string* message);
 bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,
                        std::string* message);
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
+                     std::string* message);
+
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
 std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5962650..3c6b1b7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -64,6 +64,7 @@
 #include <ziparchive/zip_archive.h>
 
 #include "bootimg_utils.h"
+#include "constants.h"
 #include "diagnose_usb.h"
 #include "fastboot_driver.h"
 #include "fs.h"
@@ -78,10 +79,6 @@
 using android::base::Trim;
 using android::base::unique_fd;
 
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
 static const char* serial = nullptr;
 
 static bool g_long_listing = false;
@@ -155,10 +152,10 @@
     { nullptr,    "system_other.img", "system.sig",   "system",   true,  ImageType::Normal },
     { "userdata", "userdata.img",     "userdata.sig", "userdata", true,  ImageType::Extra },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  ImageType::BootCritical },
-    { "vbmeta_mainline",
-                  "vbmeta_mainline.img",
-                                      "vbmeta_mainline.sig",
-                                                      "vbmeta_mainline",
+    { "vbmeta_system",
+                  "vbmeta_system.img",
+                                      "vbmeta_system.sig",
+                                                      "vbmeta_system",
                                                                   true,  ImageType::BootCritical },
     { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
@@ -855,7 +852,7 @@
         buf->image_size = sz;
     }
 
-    lseek64(fd, 0, SEEK_SET);
+    lseek(fd, 0, SEEK_SET);
     int64_t limit = get_sparse_limit(sz);
     if (limit) {
         sparse_file** s = load_sparse_files(fd, limit);
@@ -1095,12 +1092,20 @@
     }
 }
 
+static bool is_logical(const std::string& partition) {
+    std::string value;
+    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
+}
+
 static void do_flash(const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
     if (!load_buf(fname, &buf)) {
         die("cannot load '%s': %s", fname, strerror(errno));
     }
+    if (is_logical(pname)) {
+        fb->ResizePartition(pname, std::to_string(buf.image_size));
+    }
     flash_buf(pname, &buf);
 }
 
@@ -1143,11 +1148,6 @@
     return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
 }
 
-static bool is_logical(const std::string& partition) {
-    std::string value;
-    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
-}
-
 static void reboot_to_userspace_fastboot() {
     fb->RebootTo("fastboot");
 
@@ -1315,6 +1315,9 @@
     if (!is_userspace_fastboot()) {
         reboot_to_userspace_fastboot();
     }
+    if (!is_userspace_fastboot()) {
+        die("Failed to boot into userspace; one or more components might be unbootable.");
+    }
     fb->Download("super", fd, get_file_size(fd));
 
     std::string command = "update-super:super";
@@ -1697,10 +1700,10 @@
     while (!args.empty()) {
         std::string command = next_arg(&args);
 
-        if (command == "getvar") {
+        if (command == FB_CMD_GETVAR) {
             std::string variable = next_arg(&args);
             DisplayVarOrError(variable, variable);
-        } else if (command == "erase") {
+        } else if (command == FB_CMD_ERASE) {
             std::string partition = next_arg(&args);
             auto erase = [&](const std::string& partition) {
                 std::string partition_type;
@@ -1742,7 +1745,7 @@
             if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
             fb->Download("signature", data);
             fb->RawCommand("signature", "installing signature");
-        } else if (command == "reboot") {
+        } else if (command == FB_CMD_REBOOT) {
             wants_reboot = true;
 
             if (args.size() == 1) {
@@ -1762,15 +1765,15 @@
 
             }
             if (!args.empty()) syntax_error("junk after reboot command");
-        } else if (command == "reboot-bootloader") {
+        } else if (command == FB_CMD_REBOOT_BOOTLOADER) {
             wants_reboot_bootloader = true;
-        } else if (command == "reboot-recovery") {
+        } else if (command == FB_CMD_REBOOT_RECOVERY) {
             wants_reboot_recovery = true;
-        } else if (command == "reboot-fastboot") {
+        } else if (command == FB_CMD_REBOOT_FASTBOOT) {
             wants_reboot_fastboot = true;
-        } else if (command == "continue") {
+        } else if (command == FB_CMD_CONTINUE) {
             fb->Continue();
-        } else if (command == "boot") {
+        } else if (command == FB_CMD_BOOT) {
             std::string kernel = next_arg(&args);
             std::string ramdisk;
             if (!args.empty()) ramdisk = next_arg(&args);
@@ -1780,7 +1783,7 @@
             auto data = LoadBootableImage(kernel, ramdisk, second_stage);
             fb->Download("boot.img", data);
             fb->Boot();
-        } else if (command == "flash") {
+        } else if (command == FB_CMD_FLASH) {
             std::string pname = next_arg(&args);
 
             std::string fname;
@@ -1827,7 +1830,7 @@
             }
             do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
             wants_reboot = true;
-        } else if (command == "set_active") {
+        } else if (command == FB_CMD_SET_ACTIVE) {
             std::string slot = verify_slot(next_arg(&args), false);
             fb->SetActive(slot);
         } else if (command == "stage") {
@@ -1841,8 +1844,8 @@
         } else if (command == "get_staged") {
             std::string filename = next_arg(&args);
             fb->Upload(filename);
-        } else if (command == "oem") {
-            do_oem_command("oem", &args);
+        } else if (command == FB_CMD_OEM) {
+            do_oem_command(FB_CMD_OEM, &args);
         } else if (command == "flashing") {
             if (args.empty()) {
                 syntax_error("missing 'flashing' command");
@@ -1854,14 +1857,14 @@
             } else {
                 syntax_error("unknown 'flashing' command %s", args[0].c_str());
             }
-        } else if (command == "create-logical-partition") {
+        } else if (command == FB_CMD_CREATE_PARTITION) {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
             fb->CreatePartition(partition, size);
-        } else if (command == "delete-logical-partition") {
+        } else if (command == FB_CMD_DELETE_PARTITION) {
             std::string partition = next_arg(&args);
             fb->DeletePartition(partition);
-        } else if (command == "resize-logical-partition") {
+        } else if (command == FB_CMD_RESIZE_PARTITION) {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
             fb->ResizePartition(partition, size);
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index b1f3bc9..65a5247 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -41,10 +41,10 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/mapped_file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <utils/FileMap.h>
 
 #include "constants.h"
 #include "transport.h"
@@ -467,15 +467,14 @@
 
     while (remaining) {
         // Memory map the file
-        android::FileMap filemap;
         size_t len = std::min(remaining, MAX_MAP_SIZE);
-
-        if (!filemap.create(NULL, fd, offset, len, true)) {
+        auto mapping{android::base::MappedFile::FromFd(fd, offset, len, PROT_READ)};
+        if (!mapping) {
             error_ = "Creating filemap failed";
             return IO_ERROR;
         }
 
-        if ((ret = SendBuffer(filemap.getDataPtr(), len))) {
+        if ((ret = SendBuffer(mapping->data(), mapping->size()))) {
             return ret;
         }
 
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 62bbe52..af02637 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -37,15 +37,14 @@
 #include <bootimg.h>
 #include <inttypes.h>
 #include <sparse/sparse.h>
+
+#include "constants.h"
 #include "transport.h"
 
 class Transport;
 
 namespace fastboot {
 
-static constexpr int FB_COMMAND_SZ = 64;
-static constexpr int FB_RESPONSE_SZ = 64;
-
 enum RetCode : int {
     SUCCESS = 0,
     BAD_ARG,
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index c18af1d..280cfb6 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -86,6 +86,12 @@
     return true;
 }
 
+bool FastBootTest::UserSpaceFastboot() {
+    std::string value;
+    fb->GetVar("is-userspace", &value);
+    return value == "yes";
+}
+
 RetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,
                                       std::vector<std::string>* info) {
     return fb->DownloadCommand(size, response, info);
@@ -158,6 +164,12 @@
         return;
     }
 
+    // User space fastboot implementations are not allowed to communicate to
+    // secure hardware and hence cannot lock/unlock the device.
+    if (UserSpaceFastboot()) {
+        return;
+    }
+
     std::string resp;
     std::vector<std::string> info;
     // To avoid risk of bricking device, make sure unlock ability is set to 1
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index e47d0fd..e0f829e 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -47,6 +47,7 @@
 
     static int MatchFastboot(usb_ifc_info* info, const char* local_serial = nullptr);
     bool UsbStillAvailible();
+    bool UserSpaceFastboot();
 
   protected:
     RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 90a2e74..479a06a 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -43,6 +43,7 @@
 #include <thread>
 #include <vector>
 
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 #include <sparse/sparse.h>
@@ -289,6 +290,12 @@
 TEST_F(Conformance, UnlockAbility) {
     std::string resp;
     std::vector<std::string> info;
+    // Userspace fastboot implementations do not have a way to get this
+    // information.
+    if (UserSpaceFastboot()) {
+        GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
+        return;
+    }
     EXPECT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
             << "'flashing get_unlock_ability' failed";
     // There are two ways this can be reported, through info or the actual response
@@ -325,8 +332,9 @@
                 << cmd + " responded with a string with leading whitespace";
         EXPECT_FALSE(resp.compare(0, 2, "0x"))
                 << cmd + "responded with a string that does not start with 0x...";
-        int64_t size = strtoll(resp.c_str(), nullptr, 16);
-        EXPECT_GT(size, 0) << "'" + resp + "' is not a valid response from " + cmd;
+        uint64_t size;
+        ASSERT_TRUE(android::base::ParseUint(resp, &size))
+                << "'" + resp + "' is not a valid response from " + cmd;
     }
 }
 
@@ -412,6 +420,10 @@
     ASSERT_TRUE(resp == "yes" || resp == "no")
             << "Device did not respond with 'yes' or 'no' for getvar:unlocked";
     bool curr = resp == "yes";
+    if (UserSpaceFastboot()) {
+        GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
+        return;
+    }
 
     for (int i = 0; i < 2; i++) {
         std::string action = !curr ? "unlock" : "lock";
@@ -735,7 +747,7 @@
 }
 
 TEST_F(Fuzz, BadCommandTooLarge) {
-    std::string s = RandomString(fastboot::FB_COMMAND_SZ + 1, rand_legal);
+    std::string s = RandomString(FB_COMMAND_SZ + 1, rand_legal);
     EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
             << "Device did not respond with failure after sending length " << s.size()
             << " string of random ASCII chars";
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index d29ccf4..ae2e2fe 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -37,6 +37,7 @@
 #include <memory>
 #include <string>
 #include <thread>
+#include <utility>
 #include <vector>
 
 #include <android-base/file.h>
@@ -49,12 +50,12 @@
 #include <cutils/partition_utils.h>
 #include <cutils/properties.h>
 #include <ext4_utils/ext4.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <ext4_utils/ext4_sb.h>
 #include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr_overlayfs.h>
 #include <libdm/dm.h>
+#include <liblp/metadata_format.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <linux/magic.h>
@@ -529,8 +530,18 @@
     errno = 0;
     ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
     save_errno = errno;
-    PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
-          << ",type=" << rec->fs_type << ")=" << ret;
+    const char* target_missing = "";
+    const char* source_missing = "";
+    if (save_errno == ENOENT) {
+        if (access(target, F_OK)) {
+            target_missing = "(missing)";
+        } else if (access(source, F_OK)) {
+            source_missing = "(missing)";
+        }
+        errno = save_errno;
+    }
+    PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
+          << target_missing << ",type=" << rec->fs_type << ")=" << ret;
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
@@ -839,23 +850,115 @@
     return true;
 }
 
-bool fs_mgr_update_checkpoint_partition(struct fstab_rec* rec) {
-    if (fs_mgr_is_checkpoint(rec)) {
-        if (!strcmp(rec->fs_type, "f2fs")) {
-            std::string opts(rec->fs_options);
+class CheckpointManager {
+  public:
+    CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
 
-            opts += ",checkpoint=disable";
-            free(rec->fs_options);
-            rec->fs_options = strdup(opts.c_str());
-        } else {
-            LERROR << rec->fs_type << " does not implement checkpoints.";
+    bool Update(struct fstab_rec* rec) {
+        if (!fs_mgr_is_checkpoint(rec)) {
+            return true;
         }
-    } else if (rec->fs_mgr_flags & MF_CHECKPOINT_BLK) {
-        LERROR << "Block based checkpoint not implemented.";
-        return false;
+
+        if (fs_mgr_is_checkpoint_blk(rec)) {
+            call_vdc({"checkpoint", "restoreCheckpoint", rec->blk_device});
+        }
+
+        if (needs_checkpoint_ == UNKNOWN &&
+            !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
+            LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+            needs_checkpoint_ = NO;
+        }
+
+        if (needs_checkpoint_ != YES) {
+            return true;
+        }
+
+        if (!UpdateCheckpointPartition(rec)) {
+            LERROR << "Could not set up checkpoint partition, skipping!";
+            return false;
+        }
+
+        return true;
     }
-    return true;
-}
+
+    bool Revert(struct fstab_rec* rec) {
+        if (!fs_mgr_is_checkpoint(rec)) {
+            return true;
+        }
+
+        if (device_map_.find(rec->blk_device) == device_map_.end()) {
+            return true;
+        }
+
+        std::string bow_device = rec->blk_device;
+        free(rec->blk_device);
+        rec->blk_device = strdup(device_map_[bow_device].c_str());
+        device_map_.erase(bow_device);
+
+        DeviceMapper& dm = DeviceMapper::Instance();
+        if (!dm.DeleteDevice("bow")) {
+            PERROR << "Failed to remove bow device";
+        }
+
+        return true;
+    }
+
+  private:
+    bool UpdateCheckpointPartition(struct fstab_rec* rec) {
+        if (fs_mgr_is_checkpoint_fs(rec)) {
+            if (!strcmp(rec->fs_type, "f2fs")) {
+                std::string opts(rec->fs_options);
+
+                opts += ",checkpoint=disable";
+                free(rec->fs_options);
+                rec->fs_options = strdup(opts.c_str());
+            } else {
+                LERROR << rec->fs_type << " does not implement checkpoints.";
+            }
+        } else if (fs_mgr_is_checkpoint_blk(rec)) {
+            android::base::unique_fd fd(
+                    TEMP_FAILURE_RETRY(open(rec->blk_device, O_RDONLY | O_CLOEXEC)));
+            if (!fd) {
+                PERROR << "Cannot open device " << rec->blk_device;
+                return false;
+            }
+
+            uint64_t size = get_block_device_size(fd) / 512;
+            if (!size) {
+                PERROR << "Cannot get device size";
+                return false;
+            }
+
+            android::dm::DmTable table;
+            if (!table.AddTarget(
+                        std::make_unique<android::dm::DmTargetBow>(0, size, rec->blk_device))) {
+                LERROR << "Failed to add bow target";
+                return false;
+            }
+
+            DeviceMapper& dm = DeviceMapper::Instance();
+            if (!dm.CreateDevice("bow", table)) {
+                PERROR << "Failed to create bow device";
+                return false;
+            }
+
+            std::string name;
+            if (!dm.GetDmDevicePathByName("bow", &name)) {
+                PERROR << "Failed to get bow device name";
+                return false;
+            }
+
+            device_map_[name] = rec->blk_device;
+            free(rec->blk_device);
+            rec->blk_device = strdup(name.c_str());
+        }
+        return true;
+    }
+
+    enum { UNKNOWN = -1, NO = 0, YES = 1 };
+    int needs_checkpoint_;
+    std::map<std::string, std::string> device_map_;
+};
 
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
@@ -869,7 +972,7 @@
     int mret = -1;
     int mount_errno = 0;
     int attempted_idx = -1;
-    int need_checkpoint = -1;
+    CheckpointManager checkpoint_manager;
     FsManagerAvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
@@ -916,16 +1019,8 @@
             }
         }
 
-        if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
-            if (need_checkpoint == -1 &&
-                !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
-                LERROR << "Failed to find if checkpointing is needed. Assuming no.";
-                need_checkpoint = 0;
-            }
-            if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
-                LERROR << "Could not set up checkpoint partition, skipping!";
-                continue;
-            }
+        if (!checkpoint_manager.Update(&fstab->recs[i])) {
+            continue;
         }
 
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
@@ -1008,6 +1103,9 @@
                    << " is wiped and " << fstab->recs[top_idx].mount_point
                    << " " << fstab->recs[top_idx].fs_type
                    << " is formattable. Format it.";
+
+            checkpoint_manager.Revert(&fstab->recs[top_idx]);
+
             if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
                 strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
                 int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
@@ -1128,11 +1226,12 @@
  * in turn, and stop on 1st success, or no more match.
  */
 static int fs_mgr_do_mount_helper(fstab* fstab, const char* n_name, char* n_blk_device,
-                                  char* tmp_mount_point, int need_checkpoint) {
+                                  char* tmp_mount_point, int needs_checkpoint) {
     int i = 0;
     int mount_errors = 0;
     int first_mount_errno = 0;
     char* mount_point;
+    CheckpointManager checkpoint_manager(needs_checkpoint);
     FsManagerAvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
@@ -1161,16 +1260,9 @@
             }
         }
 
-        if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
-            if (need_checkpoint == -1 &&
-                !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
-                LERROR << "Failed to find if checkpointing is needed. Assuming no.";
-                need_checkpoint = 0;
-            }
-            if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
-                LERROR << "Could not set up checkpoint partition, skipping!";
-                continue;
-            }
+        if (!checkpoint_manager.Update(&fstab->recs[i])) {
+            LERROR << "Could not set up checkpoint partition, skipping!";
+            continue;
         }
 
         /* First check the filesystem if requested */
@@ -1247,8 +1339,8 @@
 }
 
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
-                    bool needs_cp) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_cp);
+                    bool needs_checkpoint) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
 }
 
 /*
@@ -1300,29 +1392,25 @@
              * on a system (all the memory comes from the same pool) so
              * we can assume the device number is 0.
              */
-            FILE *zram_fp;
-            FILE *zram_mcs_fp;
-
             if (fstab->recs[i].max_comp_streams >= 0) {
-               zram_mcs_fp = fopen(ZRAM_CONF_MCS, "r+");
-              if (zram_mcs_fp == NULL) {
-                LERROR << "Unable to open zram conf comp device "
-                       << ZRAM_CONF_MCS;
-                ret = -1;
-                continue;
-              }
-              fprintf(zram_mcs_fp, "%d\n", fstab->recs[i].max_comp_streams);
-              fclose(zram_mcs_fp);
+                auto zram_mcs_fp = std::unique_ptr<FILE, decltype(&fclose)>{
+                        fopen(ZRAM_CONF_MCS, "re"), fclose};
+                if (zram_mcs_fp == NULL) {
+                    LERROR << "Unable to open zram conf comp device " << ZRAM_CONF_MCS;
+                    ret = -1;
+                    continue;
+                }
+                fprintf(zram_mcs_fp.get(), "%d\n", fstab->recs[i].max_comp_streams);
             }
 
-            zram_fp = fopen(ZRAM_CONF_DEV, "r+");
+            auto zram_fp =
+                    std::unique_ptr<FILE, decltype(&fclose)>{fopen(ZRAM_CONF_DEV, "re+"), fclose};
             if (zram_fp == NULL) {
                 LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
                 ret = -1;
                 continue;
             }
-            fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
-            fclose(zram_fp);
+            fprintf(zram_fp.get(), "%u\n", fstab->recs[i].zram_size);
         }
 
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
@@ -1457,19 +1545,18 @@
 
     DeviceMapper& dm = DeviceMapper::Instance();
 
-    bool system_root = android::base::GetProperty("ro.build.system_root_image", "") == "true";
-
     for (int i = 0; i < fstab->num_entries; i++) {
-        if (!fs_mgr_is_verified(&fstab->recs[i]) && !fs_mgr_is_avb(&fstab->recs[i])) {
+        auto fsrec = &fstab->recs[i];
+        if (!fs_mgr_is_verified(fsrec) && !fs_mgr_is_avb(fsrec)) {
             continue;
         }
 
         std::string mount_point;
-        if (system_root && !strcmp(fstab->recs[i].mount_point, "/")) {
+        if (!strcmp(fsrec->mount_point, "/")) {
             // In AVB, the dm device name is vroot instead of system.
-            mount_point = fs_mgr_is_avb(&fstab->recs[i]) ? "vroot" : "system";
+            mount_point = fs_mgr_is_avb(fsrec) ? "vroot" : "system";
         } else {
-            mount_point = basename(fstab->recs[i].mount_point);
+            mount_point = basename(fsrec->mount_point);
         }
 
         if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
@@ -1477,15 +1564,14 @@
             continue;
         }
 
-        const char* status = nullptr;
+        const char* status;
         std::vector<DeviceMapper::TargetInfo> table;
         if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
-            if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
-                status = "V";
-            } else {
-                PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point.c_str();
+            if (!fs_mgr_is_verifyatboot(fsrec)) {
+                PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point;
                 continue;
             }
+            status = "V";
         } else {
             status = table[0].data.c_str();
         }
@@ -1495,9 +1581,13 @@
         // instead of [partition.vroot.verified].
         if (mount_point == "vroot") mount_point = "system";
         if (*status == 'C' || *status == 'V') {
-            callback(&fstab->recs[i], mount_point.c_str(), mode, *status);
+            callback(fsrec, mount_point.c_str(), mode, *status);
         }
     }
 
     return true;
 }
+
+std::string fs_mgr_get_super_partition_name(int /* slot */) {
+    return LP_METADATA_DEFAULT_PARTITION_NAME;
+}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 804069a..4dacebf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -33,6 +33,7 @@
 
 #include <sstream>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -50,8 +51,21 @@
 using DmTargetZero = android::dm::DmTargetZero;
 using DmTargetLinear = android::dm::DmTargetLinear;
 
-static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
-                          const LpMetadataPartition& partition, DmTable* table) {
+bool GetPhysicalPartitionDevicePath(const LpMetadataBlockDevice& block_device,
+                                    std::string* result) {
+    // Note: device-mapper will not accept symlinks, so we must use realpath
+    // here.
+    std::string name = GetBlockDevicePartitionName(block_device);
+    std::string path = "/dev/block/by-name/" + name;
+    if (!android::base::Realpath(path, result)) {
+        PERROR << "realpath: " << path;
+        return false;
+    }
+    return true;
+}
+
+static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
+                          DmTable* table) {
     uint64_t sector = 0;
     for (size_t i = 0; i < partition.num_extents; i++) {
         const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -60,10 +74,17 @@
             case LP_TARGET_TYPE_ZERO:
                 target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
                 break;
-            case LP_TARGET_TYPE_LINEAR:
-                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+            case LP_TARGET_TYPE_LINEAR: {
+                const auto& block_device = metadata.block_devices[extent.target_source];
+                std::string path;
+                if (!GetPhysicalPartitionDevicePath(block_device, &path)) {
+                    LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
+                    return false;
+                }
+                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
                                                           extent.target_data);
                 break;
+            }
             default:
                 LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
                 return false;
@@ -79,13 +100,13 @@
     return true;
 }
 
-static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
-                                   const LpMetadataPartition& partition, bool force_writable,
-                                   const std::chrono::milliseconds& timeout_ms, std::string* path) {
+static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
+                                   bool force_writable, const std::chrono::milliseconds& timeout_ms,
+                                   std::string* path) {
     DeviceMapper& dm = DeviceMapper::Instance();
 
     DmTable table;
-    if (!CreateDmTable(block_device, metadata, partition, &table)) {
+    if (!CreateDmTable(metadata, partition, &table)) {
         return false;
     }
     if (force_writable) {
@@ -122,7 +143,7 @@
             continue;
         }
         std::string path;
-        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, {}, &path)) {
+        if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
@@ -140,8 +161,8 @@
     }
     for (const auto& partition : metadata->partitions) {
         if (GetPartitionName(partition) == partition_name) {
-            return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
-                                          timeout_ms, path);
+            return CreateLogicalPartition(*metadata.get(), partition, force_writable, timeout_ms,
+                                          path);
         }
     }
     LERROR << "Could not find any partition with name: " << partition_name;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 250793a..fc3a05c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -732,21 +732,19 @@
 
 struct fstab *fs_mgr_read_fstab(const char *fstab_path)
 {
-    FILE *fstab_file;
     struct fstab *fstab;
 
-    fstab_file = fopen(fstab_path, "r");
+    auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fstab_path, "re"), fclose};
     if (!fstab_file) {
         PERROR << __FUNCTION__<< "(): cannot open file: '" << fstab_path << "'";
         return nullptr;
     }
 
-    fstab = fs_mgr_read_fstab_file(fstab_file, !strcmp("/proc/mounts", fstab_path));
+    fstab = fs_mgr_read_fstab_file(fstab_file.get(), !strcmp("/proc/mounts", fstab_path));
     if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
     }
 
-    fclose(fstab_file);
     return fstab;
 }
 
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 07b2a7a..2ac638a 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -29,6 +29,7 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <map>
 #include <memory>
 #include <string>
@@ -40,19 +41,38 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
 
 #include "fs_mgr_priv.h"
 
 using namespace std::literals;
+using namespace android::dm;
+using namespace android::fs_mgr;
 
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
-bool fs_mgr_overlayfs_mount_all(const fstab*) {
+bool fs_mgr_overlayfs_mount_all(fstab*) {
     return false;
 }
 
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>&) {
+    return false;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab*) {
+    return {};
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const std::vector<const fstab_rec*>&) {
+    return {};
+}
+
 bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change) {
     if (change) *change = false;
     return false;
@@ -67,8 +87,10 @@
 
 namespace {
 
-// acceptable overlayfs backing storage
-const auto kOverlayMountPoint = "/cache"s;
+// list of acceptable overlayfs backing storage
+const auto kScratchMountPoint = "/mnt/scratch"s;
+const auto kCacheMountPoint = "/cache"s;
+const std::vector<const std::string> kOverlayMountPoints = {kScratchMountPoint, kCacheMountPoint};
 
 // Return true if everything is mounted, but before adb is started.  Right
 // after 'trigger load_persist_props_action' is done.
@@ -114,13 +136,63 @@
     return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
 }
 
-bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
+bool fs_mgr_overlayfs_enabled(struct fstab_rec* fsrec) {
     // readonly filesystem, can not be mount -o remount,rw
     // if squashfs or if free space is (near) zero making such a remount
     // virtually useless, or if there are shared blocks that prevent remount,rw
-    return ("squashfs"s == fsrec->fs_type) ||
-           fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device) ||
-           !fs_mgr_filesystem_has_space(fsrec->mount_point);
+    if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+        return true;
+    }
+    if (fs_mgr_is_logical(fsrec)) {
+        fs_mgr_update_logical_partition(fsrec);
+    }
+    return fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device);
+}
+
+bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
+    auto save_errno = errno;
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+    if (!dir) {
+        if (errno == ENOENT) {
+            errno = save_errno;
+            return true;
+        }
+        PERROR << "opendir " << path << " depth=" << level;
+        if ((errno == EPERM) && (level != 0)) {
+            errno = save_errno;
+            return true;
+        }
+        return false;
+    }
+    dirent* entry;
+    auto ret = true;
+    while ((entry = readdir(dir.get()))) {
+        if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
+        auto file = path + "/" + entry->d_name;
+        if (entry->d_type == DT_UNKNOWN) {
+            struct stat st;
+            save_errno = errno;
+            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
+            errno = save_errno;
+        }
+        if (entry->d_type == DT_DIR) {
+            ret &= fs_mgr_rm_all(file, change, level + 1);
+            if (!rmdir(file.c_str())) {
+                if (change) *change = true;
+            } else {
+                if (errno != ENOENT) ret = false;
+                PERROR << "rmdir " << file << " depth=" << level;
+            }
+            continue;
+        }
+        if (!unlink(file.c_str())) {
+            if (change) *change = true;
+        } else {
+            if (errno != ENOENT) ret = false;
+            PERROR << "rm " << file << " depth=" << level;
+        }
+    }
+    return ret;
 }
 
 const auto kUpperName = "upper"s;
@@ -129,14 +201,17 @@
 
 std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
     if (!fs_mgr_is_dir(mount_point)) return "";
-    auto dir =
-            kOverlayMountPoint + kOverlayTopDir + "/" + android::base::Basename(mount_point) + "/";
-    auto upper = dir + kUpperName;
-    if (!fs_mgr_is_dir(upper)) return "";
-    auto work = dir + kWorkName;
-    if (!fs_mgr_is_dir(work)) return "";
-    if (!fs_mgr_dir_is_writable(work)) return "";
-    return dir;
+    const auto base = android::base::Basename(mount_point) + "/";
+    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+        auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
+        auto upper = dir + kUpperName;
+        if (!fs_mgr_is_dir(upper)) continue;
+        auto work = dir + kWorkName;
+        if (!fs_mgr_is_dir(work)) continue;
+        if (!fs_mgr_dir_is_writable(work)) continue;
+        return dir;
+    }
+    return "";
 }
 
 const auto kLowerdirOption = "lowerdir="s;
@@ -147,35 +222,34 @@
     auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
     if (candidate.empty()) return "";
 
-    return "override_creds=off,"s + kLowerdirOption + mount_point + "," + kUpperdirOption +
+    return "override_creds=off," + kLowerdirOption + mount_point + "," + kUpperdirOption +
            candidate + kUpperName + ",workdir=" + candidate + kWorkName;
 }
 
-bool fs_mgr_system_root_image(const fstab* fstab) {
-    if (!fstab) {  // can not happen?
-        // This will return empty on init first_stage_mount,
-        // hence why we prefer checking the fstab instead.
-        return android::base::GetBoolProperty("ro.build.system_root_image", false);
-    }
-    for (auto i = 0; i < fstab->num_entries; i++) {
-        const auto fsrec = &fstab->recs[i];
-        auto fsrec_mount_point = fsrec->mount_point;
-        if (!fsrec_mount_point) continue;
-        if ("/system"s == fsrec_mount_point) return false;
-    }
-    return true;
-}
-
-const char* fs_mgr_mount_point(const fstab* fstab, const char* mount_point) {
+const char* fs_mgr_mount_point(const char* mount_point) {
     if (!mount_point) return mount_point;
     if ("/"s != mount_point) return mount_point;
-    if (!fs_mgr_system_root_image(fstab)) return mount_point;
     return "/system";
 }
 
+bool fs_mgr_access(const std::string& path) {
+    auto save_errno = errno;
+    auto ret = access(path.c_str(), F_OK) == 0;
+    errno = save_errno;
+    return ret;
+}
+
+bool fs_mgr_rw_access(const std::string& path) {
+    if (path.empty()) return false;
+    auto save_errno = errno;
+    auto ret = access(path.c_str(), R_OK | W_OK) == 0;
+    errno = save_errno;
+    return ret;
+}
+
 // return true if system supports overlayfs
 bool fs_mgr_wants_overlayfs() {
-    // This will return empty on init first_stage_mount, so speculative
+    // Properties will return empty on init first_stage_mount, so speculative
     // determination, empty (unset) _or_ "1" is true which differs from the
     // official ro.debuggable policy.  ALLOW_ADBD_DISABLE_VERITY == 0 should
     // protect us from false in any case, so this is insurance.
@@ -183,16 +257,10 @@
     if (debuggable != "1") return false;
 
     // Overlayfs available in the kernel, and patched for override_creds?
-    static signed char overlayfs_in_kernel = -1;  // cache for constant condition
-    if (overlayfs_in_kernel == -1) {
-        auto save_errno = errno;
-        overlayfs_in_kernel = !access("/sys/module/overlay/parameters/override_creds", F_OK);
-        errno = save_errno;
-    }
-    return overlayfs_in_kernel;
+    return fs_mgr_access("/sys/module/overlay/parameters/override_creds");
 }
 
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
                                                                fs_mgr_free_fstab);
     if (!fstab) return false;
@@ -201,10 +269,11 @@
         const auto fsrec = &fstab->recs[i];
         const auto fs_type = fsrec->fs_type;
         if (!fs_type) continue;
-        if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+        if (overlay_only && ("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
         auto fsrec_mount_point = fsrec->mount_point;
         if (!fsrec_mount_point) continue;
         if (mount_point != fsrec_mount_point) continue;
+        if (!overlay_only) return true;
         const auto fs_options = fsrec->fs_options;
         if (!fs_options) continue;
         const auto options = android::base::Split(fs_options, ",");
@@ -217,16 +286,15 @@
     return false;
 }
 
-bool fs_mgr_overlayfs_verity_enabled(const std::string& basename_mount_point) {
-    auto found = false;
-    fs_mgr_update_verity_state(
-            [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
-                if (mount_point && (basename_mount_point == mount_point)) found = true;
-            });
-    return found;
+std::vector<std::string> fs_mgr_overlayfs_verity_enabled_list() {
+    std::vector<std::string> ret;
+    fs_mgr_update_verity_state([&ret](fstab_rec*, const char* mount_point, int, int) {
+        ret.emplace_back(mount_point);
+    });
+    return ret;
 }
 
-bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
+bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
     if (!fsrec) return false;
 
     auto fsrec_mount_point = fsrec->mount_point;
@@ -249,51 +317,32 @@
 
     if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
 
-    return !fs_mgr_overlayfs_verity_enabled(android::base::Basename(fsrec_mount_point));
+    return true;
 }
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
 
-bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr) {
-    auto save_errno = errno;
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
-    if (!dir) {
-        if (errno == ENOENT) {
-            errno = save_errno;
-            return true;
-        }
-        PERROR << "opendir " << path;
-        return false;
-    }
-    dirent* entry;
+bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
     auto ret = true;
-    while ((entry = readdir(dir.get()))) {
-        if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
-        auto file = path + "/" + entry->d_name;
-        if (entry->d_type == DT_UNKNOWN) {
-            struct stat st;
-            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
-        }
-        if (entry->d_type == DT_DIR) {
-            ret &= fs_mgr_rm_all(file, change);
-            if (!rmdir(file.c_str())) {
-                if (change) *change = true;
-            } else {
-                ret = false;
-                PERROR << "rmdir " << file;
-            }
-            continue;
-        }
-        if (!unlink(file.c_str())) {
-            if (change) *change = true;
-        } else {
-            ret = false;
-            PERROR << "rm " << file;
-        }
+    auto top = dir + kOverlayTopDir;
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        ret = false;
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
     }
+    auto save_errno = errno;
+    if (!mkdir(top.c_str(), 0755)) {
+        if (change) *change = true;
+    } else if (errno != EEXIST) {
+        ret = false;
+        PERROR << "mkdir " << top;
+    } else {
+        errno = save_errno;
+    }
+    setfscreatecon(nullptr);
+
+    if (overlay) *overlay = std::move(top);
     return ret;
 }
 
-constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
-
 bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
                                 bool* change) {
     auto ret = true;
@@ -344,6 +393,131 @@
     return ret;
 }
 
+uint32_t fs_mgr_overlayfs_slot_number() {
+    return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+}
+
+std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
+    return "/dev/block/by-name/" + fs_mgr_get_super_partition_name(slot_number);
+}
+
+bool fs_mgr_overlayfs_has_logical(const fstab* fstab) {
+    if (!fstab) return false;
+    for (auto i = 0; i < fstab->num_entries; i++) {
+        const auto fsrec = &fstab->recs[i];
+        if (fs_mgr_is_logical(fsrec)) return true;
+    }
+    return false;
+}
+
+// reduce 'DM_DEV_STATUS failed for scratch: No such device or address' noise
+std::string scratch_device_cache;
+
+bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change) {
+    // umount and delete kScratchMountPoint storage if we have logical partitions
+    if (overlay != kScratchMountPoint) return true;
+    scratch_device_cache.erase();
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return true;
+
+    auto save_errno = errno;
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+        // Lazy umount will allow us to move on and possibly later
+        // establish a new fresh mount without requiring a reboot should
+        // the developer wish to restart.  Old references should melt
+        // away or have no data.  Main goal is to shut the door on the
+        // current overrides with an expectation of a subsequent reboot,
+        // thus any errors here are ignored.
+        umount2(kScratchMountPoint.c_str(), MNT_DETACH);
+    }
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        errno = save_errno;
+        return true;
+    }
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+    if (builder->FindPartition(partition_name) == nullptr) {
+        errno = save_errno;
+        return true;
+    }
+    builder->RemovePartition(partition_name);
+    auto metadata = builder->Export();
+    if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+        if (change) *change = true;
+        if (!DestroyLogicalPartition(partition_name, 0s)) return false;
+    } else {
+        PERROR << "delete partition " << overlay;
+        return false;
+    }
+    errno = save_errno;
+    return true;
+}
+
+bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
+                                   bool* change) {
+    const auto top = overlay + kOverlayTopDir;
+
+    if (!fs_mgr_access(top)) return fs_mgr_overlayfs_teardown_scratch(overlay, change);
+
+    auto cleanup_all = mount_point.empty();
+    const auto partition_name = android::base::Basename(mount_point);
+    const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
+    const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir.substr(1) + ".teardown"
+                                     : top + "/." + partition_name + ".teardown";
+    auto ret = fs_mgr_rm_all(newpath);
+    auto save_errno = errno;
+    if (!rename(oldpath.c_str(), newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "mv " << oldpath << " " << newpath;
+    } else {
+        errno = save_errno;
+    }
+    ret &= fs_mgr_rm_all(newpath, change);
+    save_errno = errno;
+    if (!rmdir(newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "rmdir " << newpath;
+    } else {
+        errno = save_errno;
+    }
+    if (!cleanup_all) {
+        save_errno = errno;
+        if (!rmdir(top.c_str())) {
+            if (change) *change = true;
+            cleanup_all = true;
+        } else if (errno == ENOTEMPTY) {
+            cleanup_all = true;
+            // cleanup all if the content is all hidden (leading .)
+            std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);
+            if (!dir) {
+                PERROR << "opendir " << top;
+            } else {
+                dirent* entry;
+                while ((entry = readdir(dir.get()))) {
+                    if (entry->d_name[0] != '.') {
+                        cleanup_all = false;
+                        break;
+                    }
+                }
+            }
+            errno = save_errno;
+        } else if (errno == ENOENT) {
+            cleanup_all = true;
+            errno = save_errno;
+        } else {
+            ret = false;
+            PERROR << "rmdir " << top;
+        }
+    }
+    if (cleanup_all) ret &= fs_mgr_overlayfs_teardown_scratch(overlay, change);
+    return ret;
+}
+
 bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
     auto options = fs_mgr_get_overlayfs_options(mount_point);
     if (options.empty()) return false;
@@ -370,16 +544,20 @@
     }
 }
 
-std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
-                                               const char* mount_point = nullptr) {
+std::vector<std::string> fs_mgr_candidate_list(fstab* fstab, const char* mount_point = nullptr) {
     std::vector<std::string> mounts;
     if (!fstab) return mounts;
 
+    auto verity = fs_mgr_overlayfs_verity_enabled_list();
     for (auto i = 0; i < fstab->num_entries; i++) {
         const auto fsrec = &fstab->recs[i];
         if (!fs_mgr_wants_overlayfs(fsrec)) continue;
-        std::string new_mount_point(fs_mgr_mount_point(fstab, fsrec->mount_point));
+        std::string new_mount_point(fs_mgr_mount_point(fsrec->mount_point));
         if (mount_point && (new_mount_point != mount_point)) continue;
+        if (std::find(verity.begin(), verity.end(), android::base::Basename(new_mount_point)) !=
+            verity.end()) {
+            continue;
+        }
         auto duplicate_or_more_specific = false;
         for (auto it = mounts.begin(); it != mounts.end();) {
             if ((*it == new_mount_point) ||
@@ -395,43 +573,239 @@
         }
         if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
     }
-    // if not itemized /system or /, system as root, fake up
-    // fs_mgr_wants_overlayfs evaluation of /system as candidate.
 
-    if ((std::find(mounts.begin(), mounts.end(), "/system") == mounts.end()) &&
-        !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/") &&
-        !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system") &&
-        !fs_mgr_overlayfs_verity_enabled("system")) {
-        mounts.emplace_back("/system");
+    // if not itemized /system or /, system as root, fake one up?
+
+    // do we want or need to?
+    if (mount_point && ("/system"s != mount_point)) return mounts;
+    if (std::find(mounts.begin(), mounts.end(), "/system") != mounts.end()) return mounts;
+
+    // fs_mgr_overlayfs_verity_enabled_list says not to?
+    if (std::find(verity.begin(), verity.end(), "system") != verity.end()) return mounts;
+
+    // confirm that fstab is missing system
+    if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/")) {
+        return mounts;
     }
+    if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system")) {
+        return mounts;
+    }
+
+    // We have a stunted fstab (w/o system or / ) passed in by the caller,
+    // verity claims are assumed accurate because they are collected internally
+    // from fs_mgr_fstab_default() from within fs_mgr_update_verity_state(),
+    // Can (re)evaluate /system with impunity since we know it is ever-present.
+    mounts.emplace_back("/system");
     return mounts;
 }
 
+// Mount kScratchMountPoint
+bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type) {
+    if (!fs_mgr_rw_access(device_path)) return false;
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    }
+    if (mkdir(kScratchMountPoint.c_str(), 0755) && (errno != EEXIST)) {
+        PERROR << "create " << kScratchMountPoint;
+    }
+
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> local_fstab(
+            static_cast<fstab*>(calloc(1, sizeof(fstab))), fs_mgr_free_fstab);
+    auto fsrec = static_cast<fstab_rec*>(calloc(1, sizeof(fstab_rec)));
+    local_fstab->num_entries = 1;
+    local_fstab->recs = fsrec;
+    fsrec->blk_device = strdup(device_path.c_str());
+    fsrec->mount_point = strdup(kScratchMountPoint.c_str());
+    fsrec->fs_type = strdup(mnt_type.c_str());
+    fsrec->flags = MS_RELATIME;
+    fsrec->fs_options = strdup("");
+    auto save_errno = errno;
+    auto mounted = fs_mgr_do_mount_one(fsrec) == 0;
+    if (!mounted) {
+        free(fsrec->fs_type);
+        if (mnt_type == "f2fs") {
+            fsrec->fs_type = strdup("ext4");
+        } else {
+            fsrec->fs_type = strdup("f2fs");
+        }
+        mounted = fs_mgr_do_mount_one(fsrec) == 0;
+        if (!mounted) save_errno = errno;
+    }
+    setfscreatecon(nullptr);
+    if (!mounted) rmdir(kScratchMountPoint.c_str());
+    errno = save_errno;
+    return mounted;
+}
+
+const std::string kMkF2fs("/system/bin/make_f2fs");
+const std::string kMkExt4("/system/bin/mke2fs");
+
+// 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";
+    return "auto";
+}
+
+std::string fs_mgr_overlayfs_scratch_device() {
+    if (!scratch_device_cache.empty()) return scratch_device_cache;
+
+    auto& dm = DeviceMapper::Instance();
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+    std::string path;
+    if (!dm.GetDmDevicePathByName(partition_name, &path)) return "";
+    return scratch_device_cache = path;
+}
+
+// Create and mount kScratchMountPoint storage if we have logical partitions
+bool fs_mgr_overlayfs_setup_scratch(const fstab* fstab, bool* change) {
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+    auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
+    auto scratch_device = fs_mgr_overlayfs_scratch_device();
+    auto partition_exists = fs_mgr_rw_access(scratch_device);
+    if (!partition_exists) {
+        auto slot_number = fs_mgr_overlayfs_slot_number();
+        auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+        if (!fs_mgr_rw_access(super_device)) return false;
+        if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
+        auto builder = MetadataBuilder::New(super_device, slot_number);
+        if (!builder) {
+            PERROR << "open " << super_device << " metadata";
+            return false;
+        }
+        const auto partition_name = android::base::Basename(kScratchMountPoint);
+        partition_exists = builder->FindPartition(partition_name) != nullptr;
+        if (!partition_exists) {
+            auto partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+            if (!partition) {
+                PERROR << "create " << partition_name;
+                return false;
+            }
+            auto partition_size = builder->AllocatableSpace() - builder->UsedSpace();
+            // 512MB or half the remaining available space, whichever is greater.
+            partition_size = std::max(uint64_t(512 * 1024 * 1024), partition_size / 2);
+            if (!builder->ResizePartition(partition, partition_size)) {
+                PERROR << "resize " << partition_name;
+                return false;
+            }
+
+            auto metadata = builder->Export();
+            if (!metadata) {
+                LERROR << "generate new metadata " << partition_name;
+                return false;
+            }
+            if (!UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+                LERROR << "update " << partition_name;
+                return false;
+            }
+
+            if (change) *change = true;
+        }
+
+        if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+                                    &scratch_device))
+            return false;
+    }
+
+    if (partition_exists) {
+        if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
+            if (change) *change = true;
+            return true;
+        }
+        // partition existed, but was not initialized;
+        errno = 0;
+    }
+
+    auto ret = system((mnt_type == "f2fs")
+                              ? ((kMkF2fs + " -d1 " + scratch_device).c_str())
+                              : ((kMkExt4 + " -b 4096 -t ext4 -m 0 -M " + kScratchMountPoint +
+                                  " -O has_journal " + scratch_device)
+                                         .c_str()));
+    if (ret) {
+        LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " error=" << ret;
+        return false;
+    }
+
+    if (change) *change = true;
+
+    return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
+}
+
+bool fs_mgr_overlayfs_scratch_can_be_mounted(const std::string& scratch_device) {
+    if (scratch_device.empty()) return false;
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return false;
+    if (fs_mgr_rw_access(scratch_device)) return true;
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return false;
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) return false;
+    return builder->FindPartition(android::base::Basename(kScratchMountPoint)) != nullptr;
+}
+
 }  // namespace
 
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
     auto ret = false;
 
     if (!fs_mgr_wants_overlayfs()) return ret;
 
     if (!fstab) return ret;
 
+    auto scratch_can_be_mounted = true;
     for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
         if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+        if (scratch_can_be_mounted) {
+            scratch_can_be_mounted = false;
+            auto scratch_device = fs_mgr_overlayfs_scratch_device();
+            if (fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device) &&
+                fs_mgr_wait_for_file(scratch_device, 10s) &&
+                fs_mgr_overlayfs_mount_scratch(scratch_device,
+                                               fs_mgr_overlayfs_scratch_mount_type()) &&
+                !fs_mgr_access(kScratchMountPoint + kOverlayTopDir)) {
+                umount2(kScratchMountPoint.c_str(), MNT_DETACH);
+                rmdir(kScratchMountPoint.c_str());
+            }
+        }
         if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
     }
     return ret;
 }
 
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>& fsrecs) {
+    std::vector<fstab_rec> recs;
+    for (const auto& rec : fsrecs) recs.push_back(*rec);
+    fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
+    return fs_mgr_overlayfs_mount_all(&fstab);
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
+    if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
+        return {};
+    }
+
+    for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
+        if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+        auto device = fs_mgr_overlayfs_scratch_device();
+        if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
+        return {device};
+    }
+    return {};
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(
+        const std::vector<const fstab_rec*>& fsrecs) {
+    std::vector<fstab_rec> recs;
+    for (const auto& rec : fsrecs) recs.push_back(*rec);
+    fstab fstab = {static_cast<int>(fsrecs.size()), &recs[0]};
+    return fs_mgr_overlayfs_required_devices(&fstab);
+}
+
 // Returns false if setup not permitted, errno set to last error.
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
     if (change) *change = false;
     auto ret = false;
-    if (backing && (kOverlayMountPoint != backing)) {
-        errno = EINVAL;
-        return ret;
-    }
     if (!fs_mgr_wants_overlayfs()) return ret;
     if (!fs_mgr_boot_completed()) {
         errno = EBUSY;
@@ -441,26 +815,32 @@
 
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
                                                                fs_mgr_free_fstab);
-    if (fstab && !fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
-    auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(fstab.get(), mount_point));
-    if (fstab && mounts.empty()) return ret;
+    if (!fstab) return ret;
+    auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(mount_point));
+    if (mounts.empty()) return ret;
 
-    if (setfscreatecon(kOverlayfsFileContext)) {
-        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    std::string dir;
+    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.get())) {
+                continue;
+            }
+            if (!fs_mgr_overlayfs_setup_scratch(fstab.get(), change)) continue;
+        } else {
+            if (!fs_mgr_get_entry_for_mount_point(fstab.get(), overlay_mount_point)) continue;
+        }
+        dir = overlay_mount_point;
+        break;
     }
-    auto overlay = kOverlayMountPoint + kOverlayTopDir;
-    auto save_errno = errno;
-    if (!mkdir(overlay.c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        PERROR << "mkdir " << overlay;
-    } else {
-        errno = save_errno;
+    if (dir.empty()) {
+        errno = ESRCH;
+        return ret;
     }
-    setfscreatecon(nullptr);
-    if (!fstab && mount_point && fs_mgr_overlayfs_setup_one(overlay, mount_point, change)) {
-        ret = true;
-    }
+
+    std::string overlay;
+    ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
     for (const auto& fsrec_mount_point : mounts) {
         ret |= fs_mgr_overlayfs_setup_one(overlay, fsrec_mount_point, change);
     }
@@ -471,44 +851,23 @@
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
     if (change) *change = false;
-    mount_point = fs_mgr_mount_point(std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>(
-                                             fs_mgr_read_fstab_default(), fs_mgr_free_fstab)
-                                             .get(),
-                                     mount_point);
+    mount_point = fs_mgr_mount_point(mount_point);
     auto ret = true;
-    const auto overlay = kOverlayMountPoint + kOverlayTopDir;
-    const auto oldpath = overlay + (mount_point ? "/"s + mount_point : ""s);
-    const auto newpath = oldpath + ".teardown";
-    ret &= fs_mgr_rm_all(newpath);
-    auto save_errno = errno;
-    if (!rename(oldpath.c_str(), newpath.c_str())) {
-        if (change) *change = true;
-    } else if (errno != ENOENT) {
-        ret = false;
-        PERROR << "mv " << oldpath << " " << newpath;
-    } else {
-        errno = save_errno;
-    }
-    ret &= fs_mgr_rm_all(newpath, change);
-    save_errno = errno;
-    if (!rmdir(newpath.c_str())) {
-        if (change) *change = true;
-    } else if (errno != ENOENT) {
-        ret = false;
-        PERROR << "rmdir " << newpath;
-    } else {
-        errno = save_errno;
-    }
-    if (mount_point) {
-        save_errno = errno;
-        if (!rmdir(overlay.c_str())) {
-            if (change) *change = true;
-        } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
-            ret = false;
-            PERROR << "rmdir " << overlay;
-        } else {
-            errno = save_errno;
+    // If scratch exists, but is not mounted, lets gain access to clean
+    // specific override entries.
+    if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+        auto scratch_device = fs_mgr_overlayfs_scratch_device();
+        if (scratch_device.empty()) {
+            auto slot_number = fs_mgr_overlayfs_slot_number();
+            auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+            const auto partition_name = android::base::Basename(kScratchMountPoint);
+            CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+                                   &scratch_device);
         }
+        fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type());
+    }
+    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+        ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
     }
     if (!fs_mgr_wants_overlayfs()) {
         // After obligatory teardown to make sure everything is clean, but if
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 506e81d..5e83cfb 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -128,10 +128,10 @@
                           FileWaitMode wait_mode = FileWaitMode::Exists);
 
 int fs_mgr_set_blk_ro(const char* blockdev);
-bool fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_update_for_slotselect(fstab* fstab);
 bool fs_mgr_is_device_unlocked();
 const std::string& get_android_dt_dir();
 bool is_dt_compatible();
-int load_verity_state(struct fstab_rec* fstab, int* mode);
+int load_verity_state(fstab_rec* fstab, int* mode);
 
 #endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fb4ebb..2727a6d 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -89,24 +89,21 @@
 {
     uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
 
-    FILE* f = fopen(path, "r");
+    auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
     if (!f) {
         LERROR << "Can't open " << path;
-        return NULL;
+        return nullptr;
     }
 
-    if (!fread(key_data, sizeof(key_data), 1, f)) {
+    if (!fread(key_data, sizeof(key_data), 1, f.get())) {
         LERROR << "Could not read key!";
-        fclose(f);
-        return NULL;
+        return nullptr;
     }
 
-    fclose(f);
-
-    RSA* key = NULL;
+    RSA* key = nullptr;
     if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
         LERROR << "Could not parse key!";
-        return NULL;
+        return nullptr;
     }
 
     return key;
@@ -368,7 +365,6 @@
 static int metadata_find(const char *fname, const char *stag,
         unsigned int slength, off64_t *offset)
 {
-    FILE *fp = NULL;
     char tag[METADATA_TAG_MAX_LENGTH + 1];
     int rc = -1;
     int n;
@@ -380,75 +376,64 @@
         return -1;
     }
 
-    fp = fopen(fname, "r+");
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
 
     if (!fp) {
         PERROR << "Failed to open " << fname;
-        goto out;
+        return -1;
     }
 
     /* check magic */
-    if (fseek(fp, start, SEEK_SET) < 0 ||
-        fread(&magic, sizeof(magic), 1, fp) != 1) {
+    if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
         PERROR << "Failed to read magic from " << fname;
-        goto out;
+        return -1;
     }
 
     if (magic != METADATA_MAGIC) {
         magic = METADATA_MAGIC;
 
-        if (fseek(fp, start, SEEK_SET) < 0 ||
-            fwrite(&magic, sizeof(magic), 1, fp) != 1) {
+        if (fseek(fp.get(), start, SEEK_SET) < 0 ||
+            fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
             PERROR << "Failed to write magic to " << fname;
-            goto out;
+            return -1;
         }
 
-        rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
+        rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
         if (rc < 0) {
             PERROR << "Failed to add metadata to " << fname;
         }
 
-        goto out;
+        return rc;
     }
 
     start += sizeof(magic);
 
     while (1) {
-        n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
-                tag, &length);
+        n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
 
         if (n == 2 && strcmp(tag, METADATA_EOD)) {
             /* found a tag */
-            start = ftell(fp);
+            start = ftell(fp.get());
 
             if (!strcmp(tag, stag) && length == slength) {
                 *offset = start;
-                rc = 0;
-                goto out;
+                return 0;
             }
 
             start += length;
 
-            if (fseek(fp, length, SEEK_CUR) < 0) {
+            if (fseek(fp.get(), length, SEEK_CUR) < 0) {
                 PERROR << "Failed to seek " << fname;
-                goto out;
+                return -1;
             }
         } else {
-            rc = metadata_add(fp, start, stag, slength, offset);
+            rc = metadata_add(fp.get(), start, stag, slength, offset);
             if (rc < 0) {
                 PERROR << "Failed to write metadata to " << fname;
             }
-            goto out;
+            return rc;
         }
-   }
-
-out:
-    if (fp) {
-        fflush(fp);
-        fclose(fp);
     }
-
-    return rc;
 }
 
 static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index cee069b..5dabe76 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -23,6 +23,7 @@
 #include <linux/dm-ioctl.h>
 
 #include <functional>
+#include <string>
 
 #include <fstab/fstab.h>
 
@@ -50,8 +51,8 @@
 };
 
 // Callback function for verity status
-typedef void fs_mgr_verity_state_callback(struct fstab_rec* fstab, const char* mount_point,
-                                          int mode, int status);
+typedef void fs_mgr_verity_state_callback(fstab_rec* fstab, const char* mount_point, int mode,
+                                          int status);
 
 #define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
 #define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
@@ -62,31 +63,35 @@
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
 #define FS_MGR_MNTALL_FAIL (-1)
-int fs_mgr_mount_all(struct fstab *fstab, int mount_mode);
+int fs_mgr_mount_all(fstab* fstab, int mount_mode);
 
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
 #define FS_MGR_DOMNT_SUCCESS 0
 
-int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
-                    char *tmp_mount_point);
-int fs_mgr_do_mount(struct fstab* fstab, const char* n_name, char* n_blk_device,
-                    char* tmp_mount_point, bool need_cp);
-int fs_mgr_do_mount_one(struct fstab_rec *rec);
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+                    bool need_cp);
+int fs_mgr_do_mount_one(fstab_rec* rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
-struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
-void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
+fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab);
+void fs_mgr_get_crypt_info(fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
 bool fs_mgr_load_verity_state(int* mode);
 bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback);
 int fs_mgr_swapon_all(struct fstab *fstab);
 bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
 
-int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
+int fs_mgr_do_format(fstab_rec* fstab, bool reserve_footer);
 
 #define FS_MGR_SETUP_VERITY_SKIPPED  (-3)
 #define FS_MGR_SETUP_VERITY_DISABLED (-2)
 #define FS_MGR_SETUP_VERITY_FAIL (-1)
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
+int fs_mgr_setup_verity(fstab_rec* fstab, bool wait_for_verity_dev);
+
+// Return the name of the super partition if it exists. If a slot number is
+// specified, the super partition for the corresponding metadata slot will be
+// returned. Otherwise, it will use the current slot.
+std::string fs_mgr_get_super_partition_name(int slot = -1);
 
 #endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 251dd9b..9182532 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -19,8 +19,13 @@
 #include <fstab/fstab.h>
 
 #include <string>
+#include <vector>
 
-bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(fstab* fstab);
+bool fs_mgr_overlayfs_mount_all(const std::vector<const fstab_rec*>& fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(
+        const std::vector<const fstab_rec*>& fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index cc61917..70823c6 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -59,7 +59,8 @@
         : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
         valid_ = dm_.CreateDevice(name, table);
     }
-    TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+    TempDevice(TempDevice&& other) noexcept
+        : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
         other.valid_ = false;
     }
     ~TempDevice() {
@@ -103,7 +104,7 @@
     TempDevice(const TempDevice&) = delete;
     TempDevice& operator=(const TempDevice&) = delete;
 
-    TempDevice& operator=(TempDevice&& other) {
+    TempDevice& operator=(TempDevice&& other) noexcept {
         name_ = other.name_;
         valid_ = other.valid_;
         other.valid_ = false;
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index aab89e5..175b0f0 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -156,6 +156,20 @@
     std::string target_string_;
 };
 
+// dm-bow is the backup on write target that can provide checkpoint capability
+// for file systems that do not support checkpoints natively
+class DmTargetBow final : public DmTarget {
+  public:
+    DmTargetBow(uint64_t start, uint64_t length, const std::string& target_string)
+        : DmTarget(start, length), target_string_(target_string) {}
+
+    std::string name() const override { return "bow"; }
+    std::string GetParameterString() const override { return target_string_; }
+
+  private:
+    std::string target_string_;
+};
+
 }  // namespace dm
 }  // namespace android
 
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 89282db..5689bdf 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -25,6 +25,7 @@
     srcs: [
         "builder.cpp",
         "images.cpp",
+        "partition_opener.cpp",
         "reader.cpp",
         "utility.cpp",
         "writer.cpp",
@@ -35,7 +36,6 @@
         "libcrypto",
         "libcrypto_utils",
         "libsparse",
-        "libext2_uuid",
         "libext4_utils",
         "libz",
     ],
@@ -48,14 +48,19 @@
     cppflags: [
         "-Wno-unused-parameter",
     ],
+    static_libs: [
+        "libgmock",
+    ],
     shared_libs: [
         "liblp",
         "libbase",
         "libfs_mgr",
+        "libsparse",
     ],
     srcs: [
         "builder_test.cpp",
         "io_test.cpp",
+        "test_partition_opener.cpp",
         "utility_test.cpp",
     ],
 }
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 352647b..3cd33b1 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -16,16 +16,11 @@
 
 #include "liblp/builder.h"
 
-#if defined(__linux__)
-#include <linux/fs.h>
-#endif
 #include <string.h>
-#include <sys/ioctl.h>
 
 #include <algorithm>
 
 #include <android-base/unique_fd.h>
-#include <uuid/uuid.h>
 
 #include "liblp/liblp.h"
 #include "reader.h"
@@ -34,67 +29,39 @@
 namespace android {
 namespace fs_mgr {
 
-bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
-#if defined(__linux__)
-    android::base::unique_fd fd(open(block_device.c_str(), O_RDONLY));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+bool LinearExtent::AddTo(LpMetadata* out) const {
+    if (device_index_ >= out->block_devices.size()) {
+        LERROR << "Extent references unknown block device.";
         return false;
     }
-    if (!GetDescriptorSize(fd, &device_info->size)) {
-        return false;
-    }
-    if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
-        return false;
-    }
-
-    int alignment_offset;
-    if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
-        return false;
-    }
-    int logical_block_size;
-    if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
-        return false;
-    }
-
-    device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
-    device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+    out->extents.emplace_back(
+            LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});
     return true;
-#else
-    (void)block_device;
-    (void)device_info;
-    LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
-    return false;
-#endif
 }
 
-void LinearExtent::AddTo(LpMetadata* out) const {
-    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+bool ZeroExtent::AddTo(LpMetadata* out) const {
+    out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
+    return true;
 }
 
-void ZeroExtent::AddTo(LpMetadata* out) const {
-    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
-}
-
-Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
-    : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
+    : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
 
 void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
     size_ += extent->num_sectors() * LP_SECTOR_SIZE;
 
     if (LinearExtent* new_extent = extent->AsLinearExtent()) {
-        if (!extents_.empty() && extents_.back()->AsLinearExtent() &&
-            extents_.back()->AsLinearExtent()->end_sector() == new_extent->physical_sector()) {
-            // If the previous extent can be merged into this new one, do so
-            // to avoid creating unnecessary extents.
+        if (!extents_.empty() && extents_.back()->AsLinearExtent()) {
             LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
-            extent = std::make_unique<LinearExtent>(
-                    prev_extent->num_sectors() + new_extent->num_sectors(),
-                    prev_extent->physical_sector());
-            extents_.pop_back();
+            if (prev_extent->end_sector() == new_extent->physical_sector() &&
+                prev_extent->device_index() == new_extent->device_index()) {
+                // If the previous extent can be merged into this new one, do so
+                // to avoid creating unnecessary extents.
+                extent = std::make_unique<LinearExtent>(
+                        prev_extent->num_sectors() + new_extent->num_sectors(),
+                        prev_extent->device_index(), prev_extent->physical_sector());
+                extents_.pop_back();
+            }
         }
     }
     extents_.push_back(std::move(extent));
@@ -128,9 +95,21 @@
     DCHECK(size_ == aligned_size);
 }
 
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+uint64_t Partition::BytesOnDisk() const {
+    uint64_t sectors = 0;
+    for (const auto& extent : extents_) {
+        if (!extent->AsLinearExtent()) {
+            continue;
+        }
+        sectors += extent->num_sectors();
+    }
+    return sectors * LP_SECTOR_SIZE;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const IPartitionOpener& opener,
+                                                      const std::string& super_partition,
                                                       uint32_t slot_number) {
-    std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(opener, super_partition, slot_number);
     if (!metadata) {
         return nullptr;
     }
@@ -138,18 +117,26 @@
     if (!builder) {
         return nullptr;
     }
-    BlockDeviceInfo device_info;
-    if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
-        builder->set_block_device_info(device_info);
+    for (size_t i = 0; i < builder->block_devices_.size(); i++) {
+        std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+        BlockDeviceInfo device_info;
+        if (opener.GetInfo(partition_name, &device_info)) {
+            builder->UpdateBlockDeviceInfo(i, device_info);
+        }
     }
     return builder;
 }
 
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
-                                                      uint32_t metadata_max_size,
-                                                      uint32_t metadata_slot_count) {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
+                                                      uint32_t slot_number) {
+    return New(PartitionOpener(), super_partition, slot_number);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(
+        const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+        uint32_t metadata_max_size, uint32_t metadata_slot_count) {
     std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
-    if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
+    if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) {
         return nullptr;
     }
     return builder;
@@ -175,14 +162,25 @@
     header_.header_size = sizeof(header_);
     header_.partitions.entry_size = sizeof(LpMetadataPartition);
     header_.extents.entry_size = sizeof(LpMetadataExtent);
+    header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
+    header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice);
 }
 
 bool MetadataBuilder::Init(const LpMetadata& metadata) {
     geometry_ = metadata.geometry;
+    block_devices_ = metadata.block_devices;
+
+    for (const auto& group : metadata.groups) {
+        std::string group_name = GetPartitionGroupName(group);
+        if (!AddGroup(group_name, group.maximum_size)) {
+            return false;
+        }
+    }
 
     for (const auto& partition : metadata.partitions) {
-        Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
-                                          partition.attributes);
+        std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
+        Partition* builder =
+                AddPartition(GetPartitionName(partition), group_name, partition.attributes);
         if (!builder) {
             return false;
         }
@@ -190,7 +188,8 @@
         for (size_t i = 0; i < partition.num_extents; i++) {
             const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
             if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
-                auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+                auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+                                                           extent.target_data);
                 builder->AddExtent(std::move(copy));
             } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
                 auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
@@ -198,14 +197,40 @@
             }
         }
     }
-
-    device_info_.alignment = geometry_.alignment;
-    device_info_.alignment_offset = geometry_.alignment_offset;
-    device_info_.logical_block_size = geometry_.logical_block_size;
     return true;
 }
 
-bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
+static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+    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.";
+        return false;
+    }
+    if (device_info.size % device_info.logical_block_size != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " size must be a multiple of its block size.";
+        return false;
+    }
+    if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " alignment offset is not sector-aligned.";
+        return false;
+    }
+    if (device_info.alignment % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " partition alignment is not sector-aligned.";
+        return false;
+    }
+    if (device_info.alignment_offset > device_info.alignment) {
+        LERROR << "Block device " << device_info.partition_name
+               << " partition alignment offset is greater than its alignment.";
+        return false;
+    }
+    return true;
+}
+
+bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
+                           const std::string& super_partition, uint32_t metadata_max_size,
                            uint32_t metadata_slot_count) {
     if (metadata_max_size < sizeof(LpMetadataHeader)) {
         LERROR << "Invalid metadata maximum size.";
@@ -215,87 +240,123 @@
         LERROR << "Invalid metadata slot count.";
         return false;
     }
+    if (block_devices.empty()) {
+        LERROR << "No block devices were specified.";
+        return false;
+    }
 
     // Align the metadata size up to the nearest sector.
     metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
 
-    // Check that device properties are sane.
-    device_info_ = device_info;
-    if (device_info_.size % LP_SECTOR_SIZE != 0) {
-        LERROR << "Block device size must be a multiple of 512.";
+    // Validate and build the block device list.
+    uint32_t logical_block_size = 0;
+    for (const auto& device_info : block_devices) {
+        if (!VerifyDeviceProperties(device_info)) {
+            return false;
+        }
+
+        if (!logical_block_size) {
+            logical_block_size = device_info.logical_block_size;
+        }
+        if (logical_block_size != device_info.logical_block_size) {
+            LERROR << "All partitions must have the same logical block size.";
+            return false;
+        }
+
+        LpMetadataBlockDevice out = {};
+        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)) {
+            LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
+            return false;
+        }
+        strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name));
+
+        // In the case of the super partition, this field will be adjusted
+        // later. For all partitions, the first 512 bytes are considered
+        // untouched to be compatible code that looks for an MBR. Thus we
+        // start counting free sectors at sector 1, not 0.
+        uint64_t free_area_start = LP_SECTOR_SIZE;
+        if (out.alignment || out.alignment_offset) {
+            free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+        } else {
+            free_area_start = AlignTo(free_area_start, logical_block_size);
+        }
+        out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+        // There must be one logical block of space available.
+        uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size;
+        if (device_info.size < minimum_size) {
+            LERROR << "Block device " << device_info.partition_name
+                   << " is too small to hold any logical partitions.";
+            return false;
+        }
+
+        // The "root" of the super partition is always listed first.
+        if (device_info.partition_name == super_partition) {
+            block_devices_.emplace(block_devices_.begin(), out);
+        } else {
+            block_devices_.emplace_back(out);
+        }
+    }
+    if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+        LERROR << "No super partition was specified.";
         return false;
     }
-    if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) {
-        LERROR << "Logical block size must be a multiple of 512.";
-        return false;
-    }
-    if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
-        LERROR << "Alignment offset is not sector-aligned.";
-        return false;
-    }
-    if (device_info_.alignment % LP_SECTOR_SIZE != 0) {
-        LERROR << "Partition alignment is not sector-aligned.";
-        return false;
-    }
-    if (device_info_.alignment_offset > device_info_.alignment) {
-        LERROR << "Partition alignment offset is greater than its alignment.";
-        return false;
-    }
+
+    LpMetadataBlockDevice& super = block_devices_[0];
 
     // We reserve a geometry block (4KB) plus space for each copy of the
     // maximum size of a metadata blob. Then, we double that space since
     // we store a backup copy of everything.
-    uint64_t reserved =
-            LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
-    uint64_t total_reserved = reserved * 2;
-    if (device_info_.size < total_reserved) {
+    uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
+    if (super.size < total_reserved) {
         LERROR << "Attempting to create metadata on a block device that is too small.";
         return false;
     }
 
     // Compute the first free sector, factoring in alignment.
-    uint64_t free_area = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset);
-    uint64_t first_sector = free_area / LP_SECTOR_SIZE;
+    uint64_t free_area_start = total_reserved;
+    if (super.alignment || super.alignment_offset) {
+        free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
+    } else {
+        free_area_start = AlignTo(free_area_start, logical_block_size);
+    }
+    super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
 
-    // Compute the last free sector, which is inclusive. We subtract 1 to make
-    // sure that logical partitions won't overlap with the same sector as the
-    // backup metadata, which could happen if the block device was not aligned
-    // to LP_SECTOR_SIZE.
-    uint64_t last_sector = ((device_info_.size - reserved) / LP_SECTOR_SIZE) - 1;
-
-    // If this check fails, it means either (1) we did not have free space to
-    // allocate a single sector, or (2) we did, but the alignment was high
-    // enough to bump the first sector out of range. Either way, we cannot
-    // continue.
-    if (first_sector > last_sector) {
-        LERROR << "Not enough space to allocate any partition tables.";
+    // There must be one logical block of free space remaining (enough for one partition).
+    uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size;
+    if (super.size < minimum_disk_size) {
+        LERROR << "Device must be at least " << minimum_disk_size << " bytes, only has "
+               << super.size;
         return false;
     }
 
-    // Finally, the size of the allocatable space must be a multiple of the
-    // logical block size. If we have no more free space after this
-    // computation, then we abort. Note that the last sector is inclusive,
-    // so we have to account for that.
-    uint64_t num_free_sectors = last_sector - first_sector + 1;
-    uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
-    if (num_free_sectors < sectors_per_block) {
-        LERROR << "Not enough space to allocate any partition tables.";
-        return false;
-    }
-    last_sector = first_sector + (num_free_sectors / sectors_per_block) * sectors_per_block - 1;
-
-    geometry_.first_logical_sector = first_sector;
-    geometry_.last_logical_sector = last_sector;
     geometry_.metadata_max_size = metadata_max_size;
     geometry_.metadata_slot_count = metadata_slot_count;
-    geometry_.alignment = device_info_.alignment;
-    geometry_.alignment_offset = device_info_.alignment_offset;
-    geometry_.block_device_size = device_info_.size;
-    geometry_.logical_block_size = device_info.logical_block_size;
+    geometry_.logical_block_size = logical_block_size;
+
+    if (!AddGroup("default", 0)) {
+        return false;
+    }
     return true;
 }
 
-Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
+    if (FindGroup(group_name)) {
+        LERROR << "Group already exists: " << group_name;
+        return false;
+    }
+    groups_.push_back(std::make_unique<PartitionGroup>(group_name, maximum_size));
+    return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
+    return AddPartition(name, "default", attributes);
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
                                          uint32_t attributes) {
     if (name.empty()) {
         LERROR << "Partition must have a non-empty name.";
@@ -305,7 +366,11 @@
         LERROR << "Attempting to create duplication partition with name: " << name;
         return nullptr;
     }
-    partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+    if (!FindGroup(group_name)) {
+        LERROR << "Could not find partition group: " << group_name;
+        return nullptr;
+    }
+    partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
     return partitions_.back().get();
 }
 
@@ -318,6 +383,26 @@
     return nullptr;
 }
 
+PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) {
+    for (const auto& group : groups_) {
+        if (group->name() == group_name) {
+            return group.get();
+        }
+    }
+    return nullptr;
+}
+
+uint64_t MetadataBuilder::TotalSizeOfGroup(PartitionGroup* group) const {
+    uint64_t total = 0;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() != group->name()) {
+            continue;
+        }
+        total += partition->BytesOnDisk();
+    }
+    return total;
+}
+
 void MetadataBuilder::RemovePartition(const std::string& name) {
     for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
         if ((*iter)->name() == name) {
@@ -327,44 +412,16 @@
     }
 }
 
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
-    // Figure out how much we need to allocate.
-    uint64_t space_needed = aligned_size - partition->size();
-    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
-    DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
-
-    struct Interval {
-        uint64_t start;
-        uint64_t end;
-
-        Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
-        uint64_t length() const { return end - start; }
-        bool operator<(const Interval& other) const { return start < other.start; }
-    };
-
-    // Collect all extents in the partition table, then sort them by starting
-    // sector.
-    std::vector<Interval> extents;
-    for (const auto& partition : partitions_) {
-        for (const auto& extent : partition->extents()) {
-            LinearExtent* linear = extent->AsLinearExtent();
-            if (!linear) {
-                continue;
-            }
-            extents.emplace_back(linear->physical_sector(),
-                                 linear->physical_sector() + extent->num_sectors());
-        }
-    }
-    std::sort(extents.begin(), extents.end());
-
+void MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents,
+                                        std::vector<Interval>* free_regions) const {
     // Convert the extent list into a list of gaps between the extents; i.e.,
     // the list of ranges that are free on the disk.
-    std::vector<Interval> free_regions;
     for (size_t i = 1; i < extents.size(); i++) {
         const Interval& previous = extents[i - 1];
         const Interval& current = extents[i];
+        DCHECK(previous.device_index == current.device_index);
 
-        uint64_t aligned = AlignSector(previous.end);
+        uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
         if (aligned >= current.start) {
             // There is no gap between these two extents, try the next one.
             // Note that we check with >= instead of >, since alignment may
@@ -374,18 +431,70 @@
 
         // The new interval represents the free space starting at the end of
         // the previous interval, and ending at the start of the next interval.
-        free_regions.emplace_back(aligned, current.start);
+        free_regions->emplace_back(current.device_index, aligned, current.start);
+    }
+}
+
+auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
+    std::vector<Interval> free_regions;
+
+    // Collect all extents in the partition table, per-device, then sort them
+    // by starting sector.
+    std::vector<std::vector<Interval>> device_extents(block_devices_.size());
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear) {
+                continue;
+            }
+            CHECK(linear->device_index() < device_extents.size());
+            auto& extents = device_extents[linear->device_index()];
+            extents.emplace_back(linear->device_index(), linear->physical_sector(),
+                                 linear->physical_sector() + extent->num_sectors());
+        }
     }
 
-    // Add a final interval representing the remainder of the free space.
-    uint64_t last_free_extent_start =
-            extents.empty() ? geometry_.first_logical_sector : extents.back().end;
-    last_free_extent_start = AlignSector(last_free_extent_start);
-    if (last_free_extent_start <= geometry_.last_logical_sector) {
-        free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1);
+    // Add 0-length intervals for the first and last sectors. This will cause
+    // ExtentToFreeList() to treat the space in between as available.
+    for (size_t i = 0; i < device_extents.size(); i++) {
+        auto& extents = device_extents[i];
+        const auto& block_device = block_devices_[i];
+
+        uint64_t first_sector = block_device.first_logical_sector;
+        uint64_t last_sector = block_device.size / LP_SECTOR_SIZE;
+        extents.emplace_back(i, first_sector, first_sector);
+        extents.emplace_back(i, last_sector, last_sector);
+
+        std::sort(extents.begin(), extents.end());
+        ExtentsToFreeList(extents, &free_regions);
+    }
+    return free_regions;
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+    PartitionGroup* group = FindGroup(partition->group_name());
+    CHECK(group);
+
+    // Figure out how much we need to allocate, and whether our group has
+    // enough space remaining.
+    uint64_t space_needed = aligned_size - partition->size();
+    if (group->maximum_size() > 0) {
+        uint64_t group_size = TotalSizeOfGroup(group);
+        if (group_size >= group->maximum_size() ||
+            group->maximum_size() - group_size < space_needed) {
+            LERROR << "Partition " << partition->name() << " is part of group " << group->name()
+                   << " which does not have enough space free (" << space_needed << "requested, "
+                   << group_size << " used out of " << group->maximum_size();
+            return false;
+        }
     }
 
-    const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+    DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+    std::vector<Interval> free_regions = GetFreeRegions();
+
+    const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;
     CHECK_NE(sectors_per_block, 0);
     CHECK(sectors_needed % sectors_per_block == 0);
 
@@ -413,7 +522,7 @@
         uint64_t sectors = std::min(sectors_needed, region.length());
         CHECK(sectors % sectors_per_block == 0);
 
-        auto extent = std::make_unique<LinearExtent>(sectors, region.start);
+        auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
         new_extents.push_back(std::move(extent));
         sectors_needed -= sectors;
         if (!sectors_needed) {
@@ -441,6 +550,24 @@
     metadata->header = header_;
     metadata->geometry = geometry_;
 
+    // Assign this early so the extent table can read it.
+    metadata->block_devices = block_devices_;
+
+    std::map<std::string, size_t> group_indices;
+    for (const auto& group : groups_) {
+        LpMetadataPartitionGroup out = {};
+
+        if (group->name().size() > sizeof(out.name)) {
+            LERROR << "Partition group name is too long: " << group->name();
+            return nullptr;
+        }
+        strncpy(out.name, group->name().c_str(), sizeof(out.name));
+        out.maximum_size = group->maximum_size();
+
+        group_indices[group->name()] = metadata->groups.size();
+        metadata->groups.push_back(out);
+    }
+
     // Flatten the partition and extent structures into an LpMetadata, which
     // makes it very easy to validate, serialize, or pass on to device-mapper.
     for (const auto& partition : partitions_) {
@@ -457,29 +584,40 @@
         }
 
         strncpy(part.name, partition->name().c_str(), sizeof(part.name));
-        if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
-            LERROR << "Could not parse guid " << partition->guid() << " for partition "
-                   << partition->name();
-            return nullptr;
-        }
-
         part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
         part.num_extents = static_cast<uint32_t>(partition->extents().size());
         part.attributes = partition->attributes();
 
+        auto iter = group_indices.find(partition->group_name());
+        if (iter == group_indices.end()) {
+            LERROR << "Partition " << partition->name() << " is a member of unknown group "
+                   << partition->group_name();
+            return nullptr;
+        }
+        part.group_index = iter->second;
+
         for (const auto& extent : partition->extents()) {
-            extent->AddTo(metadata.get());
+            if (!extent->AddTo(metadata.get())) {
+                return nullptr;
+            }
         }
         metadata->partitions.push_back(part);
     }
 
     metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
     metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+    metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
+    metadata->header.block_devices.num_entries =
+            static_cast<uint32_t>(metadata->block_devices.size());
     return metadata;
 }
 
 uint64_t MetadataBuilder::AllocatableSpace() const {
-    return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
+    uint64_t total_size = 0;
+    for (const auto& block_device : block_devices_) {
+        total_size += block_device.size - (block_device.first_logical_sector * LP_SECTOR_SIZE);
+    }
+    return total_size;
 }
 
 uint64_t MetadataBuilder::UsedSpace() const {
@@ -490,36 +628,80 @@
     return size;
 }
 
-uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
+uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
+                                      uint64_t sector) const {
     // Note: when reading alignment info from the Kernel, we don't assume it
     // is aligned to the sector size, so we round up to the nearest sector.
     uint64_t lba = sector * LP_SECTOR_SIZE;
-    uint64_t aligned = AlignTo(lba, device_info_.alignment, device_info_.alignment_offset);
+    uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
     return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
 }
 
-void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
-    device_info_.size = device_info.size;
+bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
+                                            uint32_t* index) const {
+    for (size_t i = 0; i < block_devices_.size(); i++) {
+        if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+            *index = i;
+            return true;
+        }
+    }
+    return false;
+}
 
-    // Note that if the logical block size changes, we're probably in trouble:
-    // we could have already built extents that will only work on the previous
-    // size.
-    DCHECK(partitions_.empty() ||
-           device_info_.logical_block_size == device_info.logical_block_size);
+bool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name,
+                                         BlockDeviceInfo* info) const {
+    uint32_t index;
+    if (!FindBlockDeviceByName(partition_name, &index)) {
+        LERROR << "No device named " << partition_name;
+        return false;
+    }
+    info->size = block_devices_[index].size;
+    info->alignment = block_devices_[index].alignment;
+    info->alignment_offset = block_devices_[index].alignment_offset;
+    info->logical_block_size = geometry_.logical_block_size;
+    info->partition_name = partition_name;
+    return true;
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(const std::string& partition_name,
+                                            const BlockDeviceInfo& device_info) {
+    uint32_t index;
+    if (!FindBlockDeviceByName(partition_name, &index)) {
+        LERROR << "No device named " << partition_name;
+        return false;
+    }
+    return UpdateBlockDeviceInfo(index, device_info);
+}
+
+bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) {
+    CHECK(index < block_devices_.size());
+
+    LpMetadataBlockDevice& block_device = block_devices_[index];
+    if (device_info.size != block_device.size) {
+        LERROR << "Device size does not match (got " << device_info.size << ", expected "
+               << 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 << ")";
+        return false;
+    }
 
     // The kernel does not guarantee these values are present, so we only
     // replace existing values if the new values are non-zero.
     if (device_info.alignment) {
-        device_info_.alignment = device_info.alignment;
+        block_device.alignment = device_info.alignment;
     }
     if (device_info.alignment_offset) {
-        device_info_.alignment_offset = device_info.alignment_offset;
+        block_device.alignment_offset = device_info.alignment_offset;
     }
+    return true;
 }
 
 bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) {
     // Align the space needed up to the nearest sector.
-    uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size);
+    uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
     uint64_t old_size = partition->size();
 
     if (aligned_size > old_size) {
@@ -530,10 +712,43 @@
         ShrinkPartition(partition, aligned_size);
     }
 
-    LINFO << "Partition " << partition->name() << " will resize from " << old_size << " bytes to "
-          << aligned_size << " bytes";
+    if (partition->size() != old_size) {
+        LINFO << "Partition " << partition->name() << " will resize from " << old_size
+              << " bytes to " << aligned_size << " bytes";
+    }
     return true;
 }
 
+std::vector<std::string> MetadataBuilder::ListGroups() const {
+    std::vector<std::string> names;
+    for (const auto& group : groups_) {
+        names.emplace_back(group->name());
+    }
+    return names;
+}
+
+void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
+    if (group_name == "default") {
+        // Cannot remove the default group.
+        return;
+    }
+    std::vector<std::string> partition_names;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() == group_name) {
+            partition_names.emplace_back(partition->name());
+        }
+    }
+
+    for (const auto& partition_name : partition_names) {
+        RemovePartition(partition_name);
+    }
+    for (auto iter = groups_.begin(); iter != groups_.end(); iter++) {
+        if ((*iter)->name() == group_name) {
+            groups_.erase(iter);
+            break;
+        }
+    }
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 0c7e43d..c27e300 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,24 +14,24 @@
  * limitations under the License.
  */
 
+#include <fs_mgr.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
-#include "fs_mgr.h"
+
 #include "utility.h"
 
 using namespace std;
 using namespace android::fs_mgr;
-
-static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
-static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
+using ::testing::ElementsAre;
 
 TEST(liblp, BuildBasic) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
 
-    Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(partition, nullptr);
     EXPECT_EQ(partition->name(), "system");
-    EXPECT_EQ(partition->guid(), TEST_GUID);
     EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
     EXPECT_EQ(partition->size(), 0);
     EXPECT_EQ(builder->FindPartition("system"), partition);
@@ -42,8 +42,9 @@
 
 TEST(liblp, ResizePartition) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
     EXPECT_EQ(system->size(), 65536);
@@ -52,8 +53,11 @@
     LinearExtent* extent = system->extents()[0]->AsLinearExtent();
     ASSERT_NE(extent, nullptr);
     EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    // The first logical sector will be (4096+1024*2)/512 = 12.
-    EXPECT_EQ(extent->physical_sector(), 12);
+    // The first logical sector will be:
+    //      (LP_PARTITION_RESERVED_BYTES + 4096*2 + 1024*4) / 512
+    // Or, in terms of sectors (reserved + geometry + metadata):
+    //      (8 + 16 + 8) = 32
+    EXPECT_EQ(extent->physical_sector(), 32);
 
     // Test resizing to the same size.
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -82,7 +86,7 @@
     extent = system->extents()[0]->AsLinearExtent();
     ASSERT_NE(extent, nullptr);
     EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(extent->physical_sector(), 12);
+    EXPECT_EQ(extent->physical_sector(), 32);
 
     // Test shrinking to 0.
     builder->ResizePartition(system, 0);
@@ -92,9 +96,10 @@
 
 TEST(liblp, PartitionAlignment) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
 
     // Test that we align up to one sector.
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 10000), true);
     EXPECT_EQ(system->size(), 12288);
@@ -118,6 +123,7 @@
 TEST(liblp, MetadataAlignment) {
     // Make sure metadata sizes get aligned up.
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+    ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
@@ -125,13 +131,14 @@
 
 TEST(liblp, InternalAlignment) {
     // Test the metadata fitting within alignment.
-    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 1536);
 
     // Test a large alignment offset thrown in.
     device_info.alignment_offset = 753664;
@@ -139,8 +146,9 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 2031);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 1472);
 
     // Alignment offset without alignment doesn't mean anything.
     device_info.alignment = 0;
@@ -154,8 +162,9 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 78);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 1973);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 174);
 
     // Test a small alignment with no alignment offset.
     device_info.alignment = 11 * 1024;
@@ -163,17 +172,18 @@
     ASSERT_NE(builder, nullptr);
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    EXPECT_EQ(exported->geometry.first_logical_sector, 72);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+    super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+    EXPECT_EQ(super_device->first_logical_sector, 160);
 }
 
 TEST(liblp, InternalPartitionAlignment) {
-    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
+    BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
 
-    Partition* a = builder->AddPartition("a", TEST_GUID, 0);
+    Partition* a = builder->AddPartition("a", 0);
     ASSERT_NE(a, nullptr);
-    Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
+    Partition* b = builder->AddPartition("b", 0);
     ASSERT_NE(b, nullptr);
 
     // Add a bunch of small extents to each, interleaving.
@@ -210,11 +220,12 @@
     // maximum size of a metadata blob. Then, we double that space since
     // we store a backup copy of everything.
     static constexpr uint64_t geometry = 4 * 1024;
-    static constexpr uint64_t allocatable = total - (metadata * slots + geometry) * 2;
+    static constexpr uint64_t allocatable =
+            total - (metadata * slots + geometry) * 2 - LP_PARTITION_RESERVED_BYTES;
     EXPECT_EQ(builder->AllocatableSpace(), allocatable);
     EXPECT_EQ(builder->UsedSpace(), 0);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, allocatable), true);
     EXPECT_EQ(system->size(), allocatable);
@@ -229,8 +240,8 @@
 TEST(liblp, BuildComplex) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
-    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -251,11 +262,11 @@
     ASSERT_NE(system2, nullptr);
     ASSERT_NE(vendor1, nullptr);
     EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system1->physical_sector(), 12);
+    EXPECT_EQ(system1->physical_sector(), 32);
     EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system2->physical_sector(), 204);
+    EXPECT_EQ(system2->physical_sector(), 224);
     EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(vendor1->physical_sector(), 140);
+    EXPECT_EQ(vendor1->physical_sector(), 160);
     EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
     EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
 }
@@ -263,15 +274,15 @@
 TEST(liblp, AddInvalidPartition) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
-    Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(partition, nullptr);
 
     // Duplicate name.
-    partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
     EXPECT_EQ(partition, nullptr);
 
     // Empty name.
-    partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    partition = builder->AddPartition("", LP_PARTITION_ATTR_READONLY);
     EXPECT_EQ(partition, nullptr);
 }
 
@@ -282,8 +293,8 @@
     unique_ptr<MetadataBuilder> builder =
             MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
-    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -293,6 +304,9 @@
     unique_ptr<LpMetadata> exported = builder->Export();
     EXPECT_NE(exported, nullptr);
 
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
+
     // Verify geometry. Some details of this may change if we change the
     // metadata structures. So in addition to checking the exact values, we
     // also check that they are internally consistent after.
@@ -301,14 +315,11 @@
     EXPECT_EQ(geometry.struct_size, sizeof(geometry));
     EXPECT_EQ(geometry.metadata_max_size, 1024);
     EXPECT_EQ(geometry.metadata_slot_count, 2);
-    EXPECT_EQ(geometry.first_logical_sector, 12);
-    EXPECT_EQ(geometry.last_logical_sector, 2035);
+    EXPECT_EQ(super_device->first_logical_sector, 32);
 
     static const size_t kMetadataSpace =
-            (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
-    uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
-    EXPECT_GE(space_at_end, kMetadataSpace);
-    EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+            ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;
+    EXPECT_GE(super_device->first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
 
     // Verify header.
     const LpMetadataHeader& header = exported->header;
@@ -322,7 +333,6 @@
     for (const auto& partition : exported->partitions) {
         Partition* original = builder->FindPartition(GetPartitionName(partition));
         ASSERT_NE(original, nullptr);
-        EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
         for (size_t i = 0; i < partition.num_extents; i++) {
             const auto& extent = exported->extents[partition.first_extent_index + i];
             LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
@@ -337,8 +347,8 @@
 TEST(liblp, BuilderImport) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
-    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
     ASSERT_NE(vendor, nullptr);
     EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -357,20 +367,18 @@
 
     EXPECT_EQ(system->size(), 98304);
     ASSERT_EQ(system->extents().size(), 2);
-    EXPECT_EQ(system->guid(), TEST_GUID);
     EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
     EXPECT_EQ(vendor->size(), 32768);
     ASSERT_EQ(vendor->extents().size(), 1);
-    EXPECT_EQ(vendor->guid(), TEST_GUID2);
     EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
 
     LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
     LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
     LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
     EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system1->physical_sector(), 12);
+    EXPECT_EQ(system1->physical_sector(), 32);
     EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
-    EXPECT_EQ(system2->physical_sector(), 204);
+    EXPECT_EQ(system2->physical_sector(), 224);
     EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
 }
 
@@ -378,17 +386,7 @@
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
 
     std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
-    Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
-    EXPECT_NE(system, nullptr);
-
-    unique_ptr<LpMetadata> exported = builder->Export();
-    EXPECT_EQ(exported, nullptr);
-}
-
-TEST(liblp, ExportInvalidGuid) {
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
-
-    Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+    Partition* system = builder->AddPartition(name + name, LP_PARTITION_ATTR_READONLY);
     EXPECT_NE(system, nullptr);
 
     unique_ptr<LpMetadata> exported = builder->Export();
@@ -400,12 +398,12 @@
     static const size_t kMetadataSize = 64 * 1024;
 
     // No space to store metadata + geometry.
-    BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
     // No space to store metadata + geometry + one free sector.
-    device_info.size += LP_METADATA_GEOMETRY_SIZE * 2;
+    device_info.size += LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2);
     builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
 
@@ -430,13 +428,10 @@
                                                                fs_mgr_free_fstab);
     ASSERT_NE(fstab, nullptr);
 
-    // This should read from the "super" partition once we have a well-defined
-    // way to access it.
-    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), "/data");
-    ASSERT_NE(rec, nullptr);
+    PartitionOpener opener;
 
     BlockDeviceInfo device_info;
-    ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
+    ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
 
     // Sanity check that the device doesn't give us some weird inefficient
     // alignment.
@@ -450,40 +445,55 @@
 }
 
 TEST(liblp, UpdateBlockDeviceInfo) {
-    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
-    EXPECT_EQ(builder->block_device_info().size, device_info.size);
-    EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
-    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
-    EXPECT_EQ(builder->block_device_info().logical_block_size, device_info.logical_block_size);
+    BlockDeviceInfo new_info;
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+
+    EXPECT_EQ(new_info.size, device_info.size);
+    EXPECT_EQ(new_info.alignment, device_info.alignment);
+    EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
+    EXPECT_EQ(new_info.logical_block_size, device_info.logical_block_size);
 
     device_info.alignment = 0;
     device_info.alignment_offset = 2048;
-    builder->set_block_device_info(device_info);
-    EXPECT_EQ(builder->block_device_info().alignment, 4096);
-    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.alignment, 4096);
+    EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);
 
     device_info.alignment = 8192;
     device_info.alignment_offset = 0;
-    builder->set_block_device_info(device_info);
-    EXPECT_EQ(builder->block_device_info().alignment, 8192);
-    EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
+    ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.alignment, 8192);
+    EXPECT_EQ(new_info.alignment_offset, 2048);
+
+    new_info.size += 4096;
+    ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.size, 1024 * 1024);
+
+    new_info.logical_block_size = 512;
+    ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
+    ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+    EXPECT_EQ(new_info.logical_block_size, 4096);
 }
 
 TEST(liblp, InvalidBlockSize) {
-    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     EXPECT_EQ(builder, nullptr);
 }
 
 TEST(liblp, AlignedExtentSize) {
-    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
-    Partition* partition = builder->AddPartition("system", TEST_GUID, 0);
+    Partition* partition = builder->AddPartition("system", 0);
     ASSERT_NE(partition, nullptr);
     ASSERT_TRUE(builder->ResizePartition(partition, 512));
     EXPECT_EQ(partition->size(), 4096);
@@ -491,7 +501,134 @@
 
 TEST(liblp, AlignedFreeSpace) {
     // Only one sector free - at least one block is required.
-    BlockDeviceInfo device_info(10240, 0, 0, 4096);
+    BlockDeviceInfo device_info("super", 10240, 0, 0, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
     ASSERT_EQ(builder, nullptr);
 }
+
+TEST(liblp, HasDefaultGroup) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    EXPECT_FALSE(builder->AddGroup("default", 0));
+}
+
+TEST(liblp, GroupSizeLimits) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("google", 16384));
+
+    Partition* partition = builder->AddPartition("system", "google", 0);
+    ASSERT_NE(partition, nullptr);
+    EXPECT_TRUE(builder->ResizePartition(partition, 8192));
+    EXPECT_EQ(partition->size(), 8192);
+    EXPECT_TRUE(builder->ResizePartition(partition, 16384));
+    EXPECT_EQ(partition->size(), 16384);
+    EXPECT_FALSE(builder->ResizePartition(partition, 32768));
+    EXPECT_EQ(partition->size(), 16384);
+}
+
+constexpr unsigned long long operator"" _GiB(unsigned long long x) {  // NOLINT
+    return x << 30;
+}
+constexpr unsigned long long operator"" _MiB(unsigned long long x) {  // NOLINT
+    return x << 20;
+}
+
+TEST(liblp, RemoveAndAddFirstPartition) {
+    auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
+    ASSERT_NE(nullptr, builder);
+    ASSERT_TRUE(builder->AddGroup("foo_a", 5_GiB));
+    ASSERT_TRUE(builder->AddGroup("foo_b", 5_GiB));
+    android::fs_mgr::Partition* p;
+    p = builder->AddPartition("system_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));
+    p = builder->AddPartition("vendor_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+    p = builder->AddPartition("system_b", "foo_b", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));
+    p = builder->AddPartition("vendor_b", "foo_b", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+
+    builder->RemovePartition("system_a");
+    builder->RemovePartition("vendor_a");
+    p = builder->AddPartition("system_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 3_GiB));
+    p = builder->AddPartition("vendor_a", "foo_a", 0);
+    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
+}
+
+TEST(liblp, ListGroups) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(builder->AddGroup("example", 0));
+
+    std::vector<std::string> groups = builder->ListGroups();
+    ASSERT_THAT(groups, ElementsAre("default", "example"));
+}
+
+TEST(liblp, RemoveGroupAndPartitions) {
+    BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(builder->AddGroup("example", 0));
+    ASSERT_NE(builder->AddPartition("system", "default", 0), nullptr);
+    ASSERT_NE(builder->AddPartition("vendor", "example", 0), nullptr);
+
+    builder->RemoveGroupAndPartitions("example");
+    ASSERT_NE(builder->FindPartition("system"), nullptr);
+    ASSERT_EQ(builder->FindPartition("vendor"), nullptr);
+    ASSERT_THAT(builder->ListGroups(), ElementsAre("default"));
+
+    builder->RemoveGroupAndPartitions("default");
+    ASSERT_NE(builder->FindPartition("system"), nullptr);
+}
+
+TEST(liblp, MultipleBlockDevices) {
+    std::vector<BlockDeviceInfo> partitions = {
+            BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096),
+            BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096),
+            BlockDeviceInfo("product_a", 64_MiB, 786432, 753664, 4096),
+    };
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
+    ASSERT_NE(builder, nullptr);
+    EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+
+    // Create a partition that spans 3 devices.
+    Partition* p = builder->AddPartition("system_a", 0);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(p, 466976768));
+
+    unique_ptr<LpMetadata> metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+    ASSERT_EQ(metadata->block_devices.size(), 3);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "system_a");
+    EXPECT_EQ(metadata->block_devices[0].size, 256_MiB);
+    EXPECT_EQ(metadata->block_devices[0].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[0].alignment_offset, 229376);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[1]), "vendor_a");
+    EXPECT_EQ(metadata->block_devices[1].size, 128_MiB);
+    EXPECT_EQ(metadata->block_devices[1].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[1].alignment_offset, 753664);
+    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[2]), "product_a");
+    EXPECT_EQ(metadata->block_devices[2].size, 64_MiB);
+    EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
+    EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
+    ASSERT_EQ(metadata->extents.size(), 3);
+    EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+    EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[0].target_data, 1984);
+    EXPECT_EQ(metadata->extents[0].target_source, 0);
+    EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+    EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[1].target_data, 1472);
+    EXPECT_EQ(metadata->extents[1].target_source, 1);
+    EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+    EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
+    EXPECT_EQ(metadata->extents[2].target_data, 1472);
+    EXPECT_EQ(metadata->extents[2].target_source, 2);
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 742b1d0..46bdfa4 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -19,8 +19,6 @@
 #include <limits.h>
 
 #include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <sparse/sparse.h>
 
 #include "reader.h"
 #include "utility.h"
@@ -30,12 +28,17 @@
 namespace fs_mgr {
 
 std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
-    LpMetadataGeometry geometry;
-    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
         return nullptr;
     }
-    if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << " read failed";
+        return nullptr;
+    }
+    LpMetadataGeometry geometry;
+    if (!ParseGeometry(buffer.get(), &geometry)) {
         return nullptr;
     }
     return ParseMetadata(geometry, fd);
@@ -61,7 +64,7 @@
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
     android::base::unique_fd fd(open(file, O_RDONLY));
     if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
         return nullptr;
     }
     return ReadFromImageFile(fd);
@@ -74,7 +77,7 @@
     std::string everything = geometry + metadata;
 
     if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
-        PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " write " << everything.size() << " bytes failed";
         return false;
     }
     return true;
@@ -83,47 +86,54 @@
 bool WriteToImageFile(const char* file, const LpMetadata& input) {
     android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
     if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
         return false;
     }
     return WriteToImageFile(fd, input);
 }
 
-// We use an object to build the sparse file since it requires that data
-// pointers be held alive until the sparse file is destroyed. It's easier
-// to do this when the data pointers are all in one place.
-class SparseBuilder {
-  public:
-    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
-                  const std::map<std::string, std::string>& images);
-
-    bool Build();
-    bool Export(const char* file);
-    bool IsValid() const { return file_ != nullptr; }
-
-  private:
-    bool AddData(const std::string& blob, uint64_t sector);
-    bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
-    int OpenImageFile(const std::string& file);
-    bool SectorToBlock(uint64_t sector, uint32_t* block);
-
-    const LpMetadata& metadata_;
-    const LpMetadataGeometry& geometry_;
-    uint32_t block_size_;
-    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
-    std::string primary_blob_;
-    std::string backup_blob_;
-    std::map<std::string, std::string> images_;
-    std::vector<android::base::unique_fd> temp_fds_;
-};
-
 SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
                              const std::map<std::string, std::string>& images)
     : metadata_(metadata),
       geometry_(metadata.geometry),
       block_size_(block_size),
-      file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy),
-      images_(images) {}
+      file_(nullptr, sparse_file_destroy),
+      images_(images) {
+    uint64_t total_size = GetTotalSuperPartitionSize(metadata);
+    if (block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
+        return;
+    }
+    if (total_size % block_size != 0) {
+        LERROR << "Device size must be a multiple of the block size, " << block_size;
+        return;
+    }
+    if (metadata.geometry.metadata_max_size % block_size != 0) {
+        LERROR << "Metadata max size must be a multiple of the block size, " << block_size;
+        return;
+    }
+    if (LP_METADATA_GEOMETRY_SIZE % block_size != 0) {
+        LERROR << "Geometry size is not a multiple of the block size, " << block_size;
+        return;
+    }
+    if (LP_PARTITION_RESERVED_BYTES % block_size != 0) {
+        LERROR << "Reserved size is not a multiple of the block size, " << block_size;
+        return;
+    }
+
+    uint64_t num_blocks = total_size % block_size;
+    if (num_blocks >= UINT_MAX) {
+        // libsparse counts blocks in unsigned 32-bit integers, so we check to
+        // make sure we're not going to overflow.
+        LERROR << "Block device is too large to encode with libsparse.";
+        return;
+    }
+
+    file_.reset(sparse_file_new(block_size_, total_size));
+    if (!file_) {
+        LERROR << "Could not allocate sparse file of size " << total_size;
+    }
+}
 
 bool SparseBuilder::Export(const char* file) {
     android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
@@ -170,20 +180,21 @@
 }
 
 bool SparseBuilder::Build() {
+    if (sparse_file_add_fill(file_.get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
+        LERROR << "Could not add initial sparse block for reserved zeroes";
+        return false;
+    }
+
     std::string geometry_blob = SerializeGeometry(geometry_);
     std::string metadata_blob = SerializeMetadata(metadata_);
     metadata_blob.resize(geometry_.metadata_max_size);
 
-    std::string all_metadata;
-    for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
-        all_metadata += metadata_blob;
+    // Two copies of geometry, then two copies of each metadata slot.
+    all_metadata_ += geometry_blob + geometry_blob;
+    for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
+        all_metadata_ += metadata_blob;
     }
-
-    // Metadata immediately follows geometry, and we write the same metadata
-    // to all slots. Note that we don't bother trying to write skip chunks
-    // here since it's a small amount of data.
-    primary_blob_ = geometry_blob + all_metadata;
-    if (!AddData(primary_blob_, 0)) {
+    if (!AddData(all_metadata_, 0)) {
         return false;
     }
 
@@ -202,17 +213,6 @@
         LERROR << "Partition image was specified but no partition was found.";
         return false;
     }
-
-    // The backup area contains all metadata slots, and then geometry. Similar
-    // to before we write the metadata to every slot.
-    int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0);
-    uint64_t backups_start = geometry_.block_device_size + backup_offset;
-    uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
-
-    backup_blob_ = all_metadata + geometry_blob;
-    if (!AddData(backup_blob_, backup_sector)) {
-        return false;
-    }
     return true;
 }
 
@@ -336,31 +336,8 @@
 
 bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
                        const std::map<std::string, std::string>& images) {
-    if (block_size % LP_SECTOR_SIZE != 0) {
-        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
-        return false;
-    }
-    if (metadata.geometry.block_device_size % block_size != 0) {
-        LERROR << "Device size must be a multiple of the block size, " << block_size;
-        return false;
-    }
-    uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
-    if (num_blocks >= UINT_MAX) {
-        // libsparse counts blocks in unsigned 32-bit integers, so we check to
-        // make sure we're not going to overflow.
-        LERROR << "Block device is too large to encode with libsparse.";
-        return false;
-    }
-
     SparseBuilder builder(metadata, block_size, images);
-    if (!builder.IsValid()) {
-        LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
-        return false;
-    }
-    if (!builder.Build()) {
-        return false;
-    }
-    return builder.Export(file);
+    return builder.IsValid() && builder.Build() && builder.Export(file);
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 3a999b8..a9ef8ce 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -14,7 +14,14 @@
  * limitations under the License.
  */
 
+#include <stdint.h>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
 #include <liblp/liblp.h>
+#include <sparse/sparse.h>
 
 namespace android {
 namespace fs_mgr {
@@ -25,5 +32,34 @@
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 bool WriteToImageFile(int fd, const LpMetadata& metadata);
 
+// We use an object to build the sparse file since it requires that data
+// pointers be held alive until the sparse file is destroyed. It's easier
+// to do this when the data pointers are all in one place.
+class SparseBuilder {
+  public:
+    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                  const std::map<std::string, std::string>& images);
+
+    bool Build();
+    bool Export(const char* file);
+    bool IsValid() const { return file_ != nullptr; }
+
+    sparse_file* file() const { return file_.get(); }
+
+  private:
+    bool AddData(const std::string& blob, uint64_t sector);
+    bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
+    int OpenImageFile(const std::string& file);
+    bool SectorToBlock(uint64_t sector, uint32_t* block);
+
+    const LpMetadata& metadata_;
+    const LpMetadataGeometry& geometry_;
+    uint32_t block_size_;
+    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+    std::string all_metadata_;
+    std::map<std::string, std::string> images_;
+    std::vector<android::base::unique_fd> temp_fds_;
+};
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 2780825..f9de106 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -24,6 +24,7 @@
 #include <memory>
 
 #include "liblp.h"
+#include "partition_opener.h"
 
 namespace android {
 namespace fs_mgr {
@@ -34,34 +35,13 @@
 static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
 static const uint32_t kDefaultBlockSize = 4096;
 
-struct BlockDeviceInfo {
-    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
-    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset,
-                    uint32_t logical_block_size)
-        : size(size),
-          alignment(alignment),
-          alignment_offset(alignment_offset),
-          logical_block_size(logical_block_size) {}
-    // Size of the block device, in bytes.
-    uint64_t size;
-    // Optimal target alignment, in bytes. Partition extents will be aligned to
-    // this value by default. This value must be 0 or a multiple of 512.
-    uint32_t alignment;
-    // Alignment offset to parent device (if any), in bytes. The sector at
-    // |alignment_offset| on the target device is correctly aligned on its
-    // parent device. This value must be 0 or a multiple of 512.
-    uint32_t alignment_offset;
-    // Block size, for aligning extent sizes and partition sizes.
-    uint32_t logical_block_size;
-};
-
 // Abstraction around dm-targets that can be encoded into logical partition tables.
 class Extent {
   public:
     explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
     virtual ~Extent() {}
 
-    virtual void AddTo(LpMetadata* out) const = 0;
+    virtual bool AddTo(LpMetadata* out) const = 0;
     virtual LinearExtent* AsLinearExtent() { return nullptr; }
 
     uint64_t num_sectors() const { return num_sectors_; }
@@ -74,16 +54,18 @@
 // This corresponds to a dm-linear target.
 class LinearExtent final : public Extent {
   public:
-    LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
-        : Extent(num_sectors), physical_sector_(physical_sector) {}
+    LinearExtent(uint64_t num_sectors, uint32_t device_index, uint64_t physical_sector)
+        : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {}
 
-    void AddTo(LpMetadata* metadata) const override;
+    bool AddTo(LpMetadata* metadata) const override;
     LinearExtent* AsLinearExtent() override { return this; }
 
     uint64_t physical_sector() const { return physical_sector_; }
     uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
+    uint32_t device_index() const { return device_index_; }
 
   private:
+    uint32_t device_index_;
     uint64_t physical_sector_;
 };
 
@@ -92,14 +74,27 @@
   public:
     explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
 
-    void AddTo(LpMetadata* out) const override;
+    bool AddTo(LpMetadata* out) const override;
+};
+
+class PartitionGroup final {
+  public:
+    explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
+        : name_(name), maximum_size_(maximum_size) {}
+
+    const std::string& name() const { return name_; }
+    uint64_t maximum_size() const { return maximum_size_; }
+
+  private:
+    std::string name_;
+    uint64_t maximum_size_;
 };
 
 class Partition final {
     friend class MetadataBuilder;
 
   public:
-    Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+    Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
 
     // Add a raw extent.
     void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -107,9 +102,13 @@
     // Remove all extents from this partition.
     void RemoveExtents();
 
+    // Compute the size used by linear extents. This is the same as size(),
+    // but does not factor in extents which do not take up space.
+    uint64_t BytesOnDisk() const;
+
     const std::string& name() const { return name_; }
+    const std::string& group_name() const { return group_name_; }
     uint32_t attributes() const { return attributes_; }
-    const std::string& guid() const { return guid_; }
     const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
     uint64_t size() const { return size_; }
 
@@ -117,7 +116,7 @@
     void ShrinkTo(uint64_t aligned_size);
 
     std::string name_;
-    std::string guid_;
+    std::string group_name_;
     std::vector<std::unique_ptr<Extent>> extents_;
     uint32_t attributes_;
     uint64_t size_;
@@ -125,22 +124,29 @@
 
 class MetadataBuilder {
   public:
-    // Construct an empty logical partition table builder. The block device size
-    // and maximum metadata size must be specified, as this will determine which
-    // areas of the physical partition can be flashed for metadata vs for logical
-    // partitions.
+    // Construct an empty logical partition table builder given the specified
+    // map of partitions that are available for storing logical partitions.
+    //
+    // At least one partition in the list must be the "super" device, where
+    // metadata will be stored.
     //
     // If the parameters would yield invalid metadata, nullptr is returned. This
-    // could happen if the block device size is too small to store the metadata
-    // and backup copies.
-    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+    // could happen if the super device is too small to store all required
+    // metadata.
+    static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices,
+                                                const std::string& super_partition,
                                                 uint32_t metadata_max_size,
                                                 uint32_t metadata_slot_count);
 
     // Import an existing table for modification. This reads metadata off the
     // given block device and imports it. It also adjusts alignment information
     // based on run-time values in the operating system.
-    static std::unique_ptr<MetadataBuilder> New(const std::string& block_device,
+    static std::unique_ptr<MetadataBuilder> New(const IPartitionOpener& opener,
+                                                const std::string& super_partition,
+                                                uint32_t slot_number);
+
+    // Same as above, but use the default PartitionOpener.
+    static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
                                                 uint32_t slot_number);
 
     // Import an existing table for modification. If the table is not valid, for
@@ -148,21 +154,42 @@
     // This method is for testing or changing off-line tables.
     static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
 
+    // Helper function for a single super partition, for tests.
+    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+                                                uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count) {
+        return New({device_info}, device_info.partition_name, metadata_max_size,
+                   metadata_slot_count);
+    }
+
     // Wrapper around New() with a BlockDeviceInfo that only specifies a device
     // size. This is a convenience method for tests.
     static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
                                                 uint32_t metadata_slot_count) {
-        BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize);
+        BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0,
+                                    kDefaultBlockSize);
         return New(device_info, metadata_max_size, metadata_slot_count);
     }
 
+    // Define a new partition group. By default there is one group called
+    // "default", with an unrestricted size. A non-zero size will restrict the
+    // total space used by all partitions in the group.
+    //
+    // This can fail and return false if the group already exists.
+    bool AddGroup(const std::string& group_name, uint64_t maximum_size);
+
     // Export metadata so it can be serialized to an image, to disk, or mounted
     // via device-mapper.
     std::unique_ptr<LpMetadata> Export();
 
     // Add a partition, returning a handle so it can be sized as needed. If a
     // partition with the given name already exists, nullptr is returned.
-    Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+    Partition* AddPartition(const std::string& name, const std::string& group_name,
+                            uint32_t attributes);
+
+    // Same as AddPartition above, but uses the default partition group which
+    // has no size restrictions.
+    Partition* AddPartition(const std::string& name, uint32_t attributes);
 
     // Delete a partition by name if it exists.
     void RemovePartition(const std::string& name);
@@ -170,6 +197,9 @@
     // Find a partition by name. If no partition is found, nullptr is returned.
     Partition* FindPartition(const std::string& name);
 
+    // Find a group by name. If no group is found, nullptr is returned.
+    PartitionGroup* FindGroup(const std::string& name);
+
     // Grow or shrink a partition to the requested size. This size will be
     // rounded UP to the nearest block (512 bytes).
     //
@@ -186,10 +216,14 @@
     uint64_t AllocatableSpace() const;
     uint64_t UsedSpace() const;
 
-    // Merge new block device information into previous values. Alignment values
-    // are only overwritten if the new values are non-zero.
-    void set_block_device_info(const BlockDeviceInfo& device_info);
-    const BlockDeviceInfo& block_device_info() const { return device_info_; }
+    // Return a list of all group names.
+    std::vector<std::string> ListGroups() const;
+
+    // Remove all partitions belonging to a group, then remove the group.
+    void RemoveGroupAndPartitions(const std::string& group_name);
+
+    bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
+    bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
 
   private:
     MetadataBuilder();
@@ -197,16 +231,40 @@
     MetadataBuilder(MetadataBuilder&&) = delete;
     MetadataBuilder& operator=(const MetadataBuilder&) = delete;
     MetadataBuilder& operator=(MetadataBuilder&&) = delete;
-    bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+    bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+              uint32_t metadata_max_size, uint32_t metadata_slot_count);
     bool Init(const LpMetadata& metadata);
     bool GrowPartition(Partition* partition, uint64_t aligned_size);
     void ShrinkPartition(Partition* partition, uint64_t aligned_size);
-    uint64_t AlignSector(uint64_t sector);
+    uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
+    uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
+    bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
+    bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
+
+    struct Interval {
+        uint32_t device_index;
+        uint64_t start;
+        uint64_t end;
+
+        Interval(uint32_t device_index, uint64_t start, uint64_t end)
+            : device_index(device_index), start(start), end(end) {}
+        uint64_t length() const { return end - start; }
+
+        // Note: the device index is not included in sorting (intervals are
+        // sorted in per-device lists).
+        bool operator<(const Interval& other) const {
+            return (start == other.start) ? end < other.end : start < other.start;
+        }
+    };
+    std::vector<Interval> GetFreeRegions() const;
+    void ExtentsToFreeList(const std::vector<Interval>& extents,
+                           std::vector<Interval>* free_regions) const;
 
     LpMetadataGeometry geometry_;
     LpMetadataHeader header_;
     std::vector<std::unique_ptr<Partition>> partitions_;
-    BlockDeviceInfo device_info_;
+    std::vector<std::unique_ptr<PartitionGroup>> groups_;
+    std::vector<LpMetadataBlockDevice> block_devices_;
 };
 
 // Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 6da24f6..4669cea 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -24,7 +24,10 @@
 #include <memory>
 #include <string>
 
+#include <android-base/unique_fd.h>
+
 #include "metadata_format.h"
+#include "partition_opener.h"
 
 namespace android {
 namespace fs_mgr {
@@ -36,13 +39,16 @@
     LpMetadataHeader header;
     std::vector<LpMetadataPartition> partitions;
     std::vector<LpMetadataExtent> extents;
+    std::vector<LpMetadataPartitionGroup> groups;
+    std::vector<LpMetadataBlockDevice> block_devices;
 };
 
 // Place an initial partition table on the device. This will overwrite the
 // existing geometry, and should not be used for normal partition table
 // updates. False can be returned if the geometry is incompatible with the
 // block device or an I/O error occurs.
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata);
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                         const LpMetadata& metadata);
 
 // Update the partition table for a given metadata slot number. False is
 // returned if an error occurs, which can include:
@@ -50,12 +56,19 @@
 //  - I/O error.
 //  - Corrupt or missing metadata geometry on disk.
 //  - Incompatible geometry.
-bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
-                          uint32_t slot_number);
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number);
 
 // Read logical partition metadata from its predetermined location on a block
 // device. If readback fails, we also attempt to load from a backup copy.
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+                                         const std::string& super_partition, uint32_t slot_number);
+
+// Helper functions that use the default PartitionOpener.
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata);
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+                          uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
 
 // Read/Write logical partition metadata to an image file, for diagnostics or
 // flashing.
@@ -67,7 +80,15 @@
 
 // Helper to extract safe C++ strings from partition info.
 std::string GetPartitionName(const LpMetadataPartition& partition);
-std::string GetPartitionGuid(const LpMetadataPartition& partition);
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);
+
+// Return the block device that houses the super partition metadata; returns
+// null on failure.
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);
+
+// Return the total size of all partitions comprising the super partition.
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
 
 // Helper to return a slot number for a slot suffix.
 uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 52c80f7..1e40df3 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
 #define LP_METADATA_HEADER_MAGIC 0x414C5030
 
 /* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MAJOR_VERSION 8
 #define LP_METADATA_MINOR_VERSION 0
 
 /* Attributes for the LpMetadataPartition::attributes field.
@@ -58,22 +58,27 @@
  *     +--------------------+
  *     | Disk Geometry      |
  *     +--------------------+
- *     | Metadata           |
+ *     | Geometry Backup    |
  *     +--------------------+
- *     | Logical Partitions |
+ *     | Metadata           |
  *     +--------------------+
  *     | Backup Metadata    |
  *     +--------------------+
- *     | Geometry Backup    |
+ *     | Logical Partitions |
  *     +--------------------+
  */
-#define LP_METADATA_PARTITION_NAME "super"
+#define LP_METADATA_DEFAULT_PARTITION_NAME "super"
 
 /* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
 #define LP_SECTOR_SIZE 512
 
-/* This structure is stored at sector 0 in the first 4096 bytes of the
- * partition, and again in the very last 4096 bytes. It is never modified and
+/* Amount of space reserved at the start of every super partition to avoid
+ * creating an accidental boot sector.
+ */
+#define LP_PARTITION_RESERVED_BYTES 4096
+
+/* This structure is stored at block 0 in the first 4096 bytes of the
+ * partition, and again in the following block. It is never modified and
  * describes how logical partition information can be located.
  */
 typedef struct LpMetadataGeometry {
@@ -98,48 +103,10 @@
      */
     uint32_t metadata_slot_count;
 
-    /* 48: First usable sector for allocating logical partitions. this will be
-     * the first sector after the initial 4096 geometry block, followed by the
-     * space consumed by metadata_max_size*metadata_slot_count.
-     */
-    uint64_t first_logical_sector;
-
-    /* 56: Last usable sector, inclusive, for allocating logical partitions.
-     * At the end of this sector will follow backup metadata slots and the
-     * backup geometry block at the very end.
-     */
-    uint64_t last_logical_sector;
-
-    /* 64: Alignment for defining partitions or partition extents. For example,
-     * an alignment of 1MiB will require that all partitions have a size evenly
-     * divisible by 1MiB, and that the smallest unit the partition can grow by
-     * is 1MiB.
-     *
-     * Alignment is normally determined at runtime when growing or adding
-     * partitions. If for some reason the alignment cannot be determined, then
-     * this predefined alignment in the geometry is used instead. By default
-     * it is set to 1MiB.
-     */
-    uint32_t alignment;
-
-    /* 68: Alignment offset for "stacked" devices. For example, if the "super"
-     * partition itself is not aligned within the parent block device's
-     * partition table, then we adjust for this in deciding where to place
-     * |first_logical_sector|.
-     *
-     * Similar to |alignment|, this will be derived from the operating system.
-     * If it cannot be determined, it is assumed to be 0.
-     */
-    uint32_t alignment_offset;
-
-    /* 72: Block device size, as specified when the metadata was created. This
-     * can be used to verify the geometry against a target device.
-     */
-    uint64_t block_device_size;
-
-    /* 76: Logical block size of the super partition block device. This is the
-     * minimal alignment for partition and extent sizes, and it must be a
-     * multiple of LP_SECTOR_SIZE.
+    /* 48: Logical block size. This is the minimal alignment for partition and
+     * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
+     * this must be equal across all LUNs that comprise the super partition,
+     * and thus this field is stored in the geometry, not per-device.
      */
     uint32_t logical_block_size;
 } __attribute__((packed)) LpMetadataGeometry;
@@ -216,6 +183,10 @@
     LpMetadataTableDescriptor partitions;
     /* 92: Extent table descriptor. */
     LpMetadataTableDescriptor extents;
+    /* 104: Updateable group descriptor. */
+    LpMetadataTableDescriptor groups;
+    /* 116: Block device table. */
+    LpMetadataTableDescriptor block_devices;
 } __attribute__((packed)) LpMetadataHeader;
 
 /* This struct defines a logical partition entry, similar to what would be
@@ -230,21 +201,21 @@
      */
     char name[36];
 
-    /* 36: Globally unique identifier (GUID) of this partition. */
-    uint8_t guid[16];
-
-    /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+    /* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
     uint32_t attributes;
 
-    /* 56: Index of the first extent owned by this partition. The extent will
+    /* 40: Index of the first extent owned by this partition. The extent will
      * start at logical sector 0. Gaps between extents are not allowed.
      */
     uint32_t first_extent_index;
 
-    /* 60: Number of extents in the partition. Every partition must have at
+    /* 44: Number of extents in the partition. Every partition must have at
      * least one extent.
      */
     uint32_t num_extents;
+
+    /* 48: Group this partition belongs to. */
+    uint32_t group_index;
 } __attribute__((packed)) LpMetadataPartition;
 
 /* This extent is a dm-linear target, and the index is an index into the
@@ -269,8 +240,70 @@
      * ZERO: This field must be 0.
      */
     uint64_t target_data;
+
+    /* 20: Contents depends on target_type.
+     *
+     * LINEAR: Must be an index into the block devices table.
+     * ZERO: This field must be 0.
+     */
+    uint32_t target_source;
 } __attribute__((packed)) LpMetadataExtent;
 
+/* This struct defines an entry in the groups table. Each group has a maximum
+ * size, and partitions in a group must not exceed that size. There is always
+ * a "default" group of unlimited size, which is used when not using update
+ * groups or when using overlayfs or fastbootd.
+ */
+typedef struct LpMetadataPartitionGroup {
+    /*  0: Name of this group. Any unused characters must be 0. */
+    char name[36];
+
+    /* 36: Maximum size in bytes. If 0, the group has no maximum size. */
+    uint64_t maximum_size;
+} LpMetadataPartitionGroup;
+
+/* This struct defines an entry in the block_devices table. There must be at
+ * least one device, and the first device must represent the partition holding
+ * the super metadata.
+ */
+typedef struct LpMetadataBlockDevice {
+    /* 0: First usable sector for allocating logical partitions. this will be
+     * the first sector after the initial geometry blocks, followed by the
+     * space consumed by metadata_max_size*metadata_slot_count*2.
+     */
+    uint64_t first_logical_sector;
+
+    /* 8: Alignment for defining partitions or partition extents. For example,
+     * an alignment of 1MiB will require that all partitions have a size evenly
+     * divisible by 1MiB, and that the smallest unit the partition can grow by
+     * is 1MiB.
+     *
+     * Alignment is normally determined at runtime when growing or adding
+     * partitions. If for some reason the alignment cannot be determined, then
+     * this predefined alignment in the geometry is used instead. By default
+     * it is set to 1MiB.
+     */
+    uint32_t alignment;
+
+    /* 12: Alignment offset for "stacked" devices. For example, if the "super"
+     * partition itself is not aligned within the parent block device's
+     * partition table, then we adjust for this in deciding where to place
+     * |first_logical_sector|.
+     *
+     * Similar to |alignment|, this will be derived from the operating system.
+     * If it cannot be determined, it is assumed to be 0.
+     */
+    uint32_t alignment_offset;
+
+    /* 16: Block device size, as specified when the metadata was created. This
+     * can be used to verify the geometry against a target device.
+     */
+    uint64_t size;
+
+    /* 24: Partition name in the GPT. Any unused characters must be 0. */
+    char partition_name[36];
+} LpMetadataBlockDevice;
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
new file mode 100644
index 0000000..e506bd5
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -0,0 +1,77 @@
+//
+// 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 <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct BlockDeviceInfo {
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+    BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment,
+                    uint32_t alignment_offset, uint32_t logical_block_size)
+        : size(size),
+          alignment(alignment),
+          alignment_offset(alignment_offset),
+          logical_block_size(logical_block_size),
+          partition_name(partition_name) {}
+    // Size of the block device, in bytes.
+    uint64_t size;
+    // Optimal target alignment, in bytes. Partition extents will be aligned to
+    // this value by default. This value must be 0 or a multiple of 512.
+    uint32_t alignment;
+    // Alignment offset to parent device (if any), in bytes. The sector at
+    // |alignment_offset| on the target device is correctly aligned on its
+    // parent device. This value must be 0 or a multiple of 512.
+    uint32_t alignment_offset;
+    // Block size, for aligning extent sizes and partition sizes.
+    uint32_t logical_block_size;
+    // The physical partition name for this block device, as it would appear in
+    // the GPT or under /dev/block/by-name.
+    std::string partition_name;
+};
+
+// Test-friendly interface for interacting with partitions.
+class IPartitionOpener {
+  public:
+    virtual ~IPartitionOpener() = default;
+
+    // Open the given named physical partition with the provided open() flags.
+    // The name can be an absolute path if the full path is already known.
+    virtual android::base::unique_fd Open(const std::string& partition_name, int flags) const = 0;
+
+    // Return block device information about the given named physical partition.
+    // The name can be an absolute path if the full path is already known.
+    virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;
+};
+
+// Helper class to implement IPartitionOpener. If |partition_name| is not an
+// absolute path, /dev/block/by-name/ will be prepended.
+class PartitionOpener : public IPartitionOpener {
+  public:
+    virtual android::base::unique_fd Open(const std::string& partition_name,
+                                          int flags) const override;
+    virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index eda68fd..603e5c0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -26,6 +26,7 @@
 
 #include "images.h"
 #include "reader.h"
+#include "test_partition_opener.h"
 #include "utility.h"
 #include "writer.h"
 
@@ -37,8 +38,6 @@
 static const size_t kDiskSize = 131072;
 static const size_t kMetadataSize = 512;
 static const size_t kMetadataSlots = 2;
-static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
-static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
 
 // Helper function for creating an in-memory file descriptor. This lets us
 // simulate read/writing logical partition metadata as if we had a block device
@@ -81,7 +80,7 @@
 }
 
 static bool AddDefaultPartitions(MetadataBuilder* builder) {
-    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_NONE);
     if (!system) {
         return false;
     }
@@ -103,7 +102,9 @@
     if (!exported) {
         return {};
     }
-    if (!FlashPartitionTable(fd, *exported.get())) {
+
+    TestPartitionOpener opener({{"super", fd}});
+    if (!FlashPartitionTable(opener, "super", *exported.get())) {
         return {};
     }
     return fd;
@@ -118,14 +119,16 @@
     ASSERT_TRUE(GetDescriptorSize(fd, &size));
     ASSERT_EQ(size, kDiskSize);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     // Verify that we can't read unwritten metadata.
-    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+    ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr);
 }
 
 // Flashing metadata should not work if the metadata was created for a larger
 // disk than the destination disk.
 TEST(liblp, ExportDiskTooSmall) {
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
@@ -135,7 +138,9 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get()));
+    TestPartitionOpener opener({{"super", fd}});
+
+    EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get()));
 }
 
 // Test the basics of flashing a partition and reading it back.
@@ -147,23 +152,23 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     // Export and flash.
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
+    ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
 
     // Read back. Note that some fields are only filled in during
     // serialization, so exported and imported will not be identical. For
     // example, table sizes and checksums are computed in WritePartitionTable.
     // Therefore we check on a field-by-field basis.
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 
     // Check geometry and header.
     EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
     EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
-    EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
-    EXPECT_EQ(exported->geometry.last_logical_sector, imported->geometry.last_logical_sector);
     EXPECT_EQ(exported->header.major_version, imported->header.major_version);
     EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
     EXPECT_EQ(exported->header.header_size, imported->header.header_size);
@@ -171,7 +176,6 @@
     // Check partition tables.
     ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
     EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
-    EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
     EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
     EXPECT_EQ(exported->partitions[0].first_extent_index,
               imported->partitions[0].first_extent_index);
@@ -182,6 +186,11 @@
     EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
     EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
     EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+
+    // Check block devices table.
+    ASSERT_EQ(exported->block_devices.size(), imported->block_devices.size());
+    EXPECT_EQ(exported->block_devices[0].first_logical_sector,
+              imported->block_devices[0].first_logical_sector);
 }
 
 // Test that we can update metadata slots without disturbing others.
@@ -189,34 +198,40 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    TestPartitionOpener opener({{"super", fd}});
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
 
     // Change the name before writing to the next slot.
     strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
-    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
     // Read back the original slot, make sure it hasn't changed.
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
 
     // Now read back the new slot, and verify that it has a different name.
-    imported = ReadMetadata(fd, 1);
+    imported = ReadMetadata(opener, "super", 1);
     ASSERT_NE(imported, nullptr);
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
 
+    auto super_device = GetMetadataSuperBlockDevice(*imported.get());
+    ASSERT_NE(super_device, nullptr);
+
+    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
+
     // Verify that we didn't overwrite anything in the logical paritition area.
     // We expect the disk to be filled with 0xcc on creation so we can read
     // this back and compare it.
     char expected[LP_SECTOR_SIZE];
     memset(expected, 0xcc, sizeof(expected));
-    for (uint64_t i = imported->geometry.first_logical_sector;
-         i <= imported->geometry.last_logical_sector; i++) {
+    for (uint64_t i = super_device->first_logical_sector; i < last_sector; i++) {
         char buffer[LP_SECTOR_SIZE];
         ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
         ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
@@ -228,15 +243,17 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     // Make sure all slots are filled.
-    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
     ASSERT_NE(metadata, nullptr);
     for (uint32_t i = 1; i < kMetadataSlots; i++) {
-        ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
+        ASSERT_TRUE(UpdatePartitionTable(opener, "super", *metadata.get(), i));
     }
 
     // Verify that we can't read unavailable slots.
-    EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+    EXPECT_EQ(ReadMetadata(opener, "super", kMetadataSlots), nullptr);
 }
 
 // Test that updating a metadata slot does not allow it to be computed based
@@ -245,27 +262,28 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    TestPartitionOpener opener({{"super", fd}});
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
     imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.metadata_slot_count++;
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    imported->geometry.first_logical_sector++;
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+    ASSERT_EQ(imported->block_devices.size(), 1);
+    imported->block_devices[0].first_logical_sector++;
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1));
 
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    imported->geometry.last_logical_sector--;
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 }
 
 // Test that changing one bit of metadata is enough to break the checksum.
@@ -273,6 +291,8 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     LpMetadataGeometry geometry;
     ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
     ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
@@ -281,7 +301,7 @@
     bad_geometry.metadata_slot_count++;
     ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
 
-    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
     ASSERT_NE(metadata, nullptr);
     EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
 }
@@ -290,39 +310,45 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     char corruption[LP_METADATA_GEOMETRY_SIZE];
     memset(corruption, 0xff, sizeof(corruption));
 
-    // Corrupt the first 4096 bytes of the disk.
-    ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+    // Corrupt the primary geometry.
+    ASSERT_GE(lseek(fd, GetPrimaryGeometryOffset(), SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
-    EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+    EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
 
-    // Corrupt the last 4096 bytes too.
-    ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
+    // Corrupt the backup geometry.
+    ASSERT_GE(lseek(fd, GetBackupGeometryOffset(), SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
-    EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+    EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
 }
 
 TEST(liblp, ReadBackupMetadata) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    TestPartitionOpener opener({{"super", fd}});
+
+    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
 
     char corruption[kMetadataSize];
     memset(corruption, 0xff, sizeof(corruption));
 
-    ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
-    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
-    EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+    off_t offset = GetPrimaryMetadataOffset(metadata->geometry, 0);
 
-    off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
+    ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr);
+
+    offset = GetBackupMetadataOffset(metadata->geometry, 0);
 
     // Corrupt the backup metadata.
-    ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
+    ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);
     ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
-    EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+    EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
 }
 
 // Test that we don't attempt to write metadata if it would overflow its
@@ -331,21 +357,20 @@
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
 
-    // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
-    size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
-    EXPECT_LT(max_partitions, 10);
+    // Compute the maximum number of partitions we can fit in 512 bytes of
+    // metadata. By default there is the header, one partition group, and a
+    // block device entry.
+    static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) -
+                                                 sizeof(LpMetadataPartitionGroup) -
+                                                 sizeof(LpMetadataBlockDevice);
+    size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
 
     // Add this number of partitions.
     Partition* partition = nullptr;
     for (size_t i = 0; i < max_partitions; i++) {
-        std::string guid = std::string(TEST_GUID) + to_string(i);
-        partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+        partition = builder->AddPartition(to_string(i), LP_PARTITION_ATTR_NONE);
         ASSERT_NE(partition, nullptr);
     }
-    ASSERT_NE(partition, nullptr);
-    // Add one extent to any partition to fill up more space - we're at 508
-    // bytes after this, out of 512.
-    ASSERT_TRUE(builder->ResizePartition(partition, 1024));
 
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
@@ -353,28 +378,30 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     // Check that we are able to write our table.
-    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
+    ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
 
     // Check that adding one more partition overflows the metadata allotment.
-    partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+    partition = builder->AddPartition("final", LP_PARTITION_ATTR_NONE);
     EXPECT_NE(partition, nullptr);
 
     exported = builder->Export();
     ASSERT_NE(exported, nullptr);
 
     // The new table should be too large to be written.
-    ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *exported.get(), 1));
+
+    auto super_device = GetMetadataSuperBlockDevice(*exported.get());
+    ASSERT_NE(super_device, nullptr);
 
     // Check that the first and last logical sectors weren't touched when we
     // wrote this almost-full metadata.
     char expected[LP_SECTOR_SIZE];
     memset(expected, 0xcc, sizeof(expected));
     char buffer[LP_SECTOR_SIZE];
-    ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
-    ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
-    EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
-    ASSERT_GE(lseek(fd, exported->geometry.last_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+    ASSERT_GE(lseek(fd, super_device->first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
     ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
     EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
 }
@@ -385,6 +412,7 @@
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
     unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
 
     unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
     ASSERT_GE(fd, 0);
@@ -459,23 +487,25 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     BadWriter writer;
 
     // Read and write it back.
     writer.FailOnWrite(1);
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
 
     // We should still be able to read the backup copy.
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 
     // Flash again, this time fail the backup copy. We should still be able
     // to read the primary.
     writer.FailOnWrite(3);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
-    imported = ReadMetadata(fd, 0);
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 }
 
@@ -485,23 +515,25 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     BadWriter writer;
 
     // Read and write it back.
     writer.FailOnWrite(2);
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
 
     // We should still be able to read the primary copy.
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 
     // Flash again, this time fail the primary copy. We should still be able
     // to read the primary.
     writer.FailOnWrite(2);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
-    imported = ReadMetadata(fd, 0);
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer));
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
 }
 
@@ -512,20 +544,22 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
+    TestPartitionOpener opener({{"super", fd}});
+
     BadWriter writer;
 
     // Change the name of the existing partition.
-    unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> new_table = ReadMetadata(opener, "super", 0);
     ASSERT_NE(new_table, nullptr);
     ASSERT_GE(new_table->partitions.size(), 1);
     new_table->partitions[0].name[0]++;
 
     // Flash it, but fail to write the backup copy.
     writer.FailAfterWrite(2);
-    ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+    ASSERT_FALSE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer));
 
     // When we read back, we should get the updated primary copy.
-    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     ASSERT_GE(new_table->partitions.size(), 1);
     ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
@@ -534,10 +568,43 @@
     // Note that the sync step should have used the primary to sync, not
     // the backup.
     writer.Reset();
-    ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer));
 
-    imported = ReadMetadata(fd, 0);
+    imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
     ASSERT_GE(new_table->partitions.size(), 1);
     ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
 }
+
+// Test that writing a sparse image can be read back.
+TEST(liblp, FlashSparseImage) {
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 512);
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Build the sparse file.
+    SparseBuilder sparse(*exported.get(), 512, {});
+    ASSERT_TRUE(sparse.IsValid());
+    sparse_file_verbose(sparse.file());
+    ASSERT_TRUE(sparse.Build());
+
+    // Write it to the fake disk.
+    ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1);
+    int ret = sparse_file_write(sparse.file(), fd.get(), false, false, false);
+    ASSERT_EQ(ret, 0);
+
+    // Verify that we can read both sets of metadata.
+    LpMetadataGeometry geometry;
+    ASSERT_TRUE(ReadPrimaryGeometry(fd.get(), &geometry));
+    ASSERT_TRUE(ReadBackupGeometry(fd.get(), &geometry));
+    ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr);
+    ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
+}
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
new file mode 100644
index 0000000..77b0e62
--- /dev/null
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "liblp/partition_opener.h"
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+namespace {
+
+std::string GetPartitionAbsolutePath(const std::string& path) {
+    if (path[0] == '/') {
+        return path;
+    }
+    return "/dev/block/by-name/" + path;
+}
+
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
+#if defined(__linux__)
+    unique_fd fd(open(block_device.c_str(), O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+        return false;
+    }
+    if (!GetDescriptorSize(fd, &device_info->size)) {
+        return false;
+    }
+    if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+
+    int alignment_offset;
+    if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+    int logical_block_size;
+    if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+        return false;
+    }
+
+    device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);
+    device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);
+    device_info->partition_name = android::base::Basename(block_device);
+    return true;
+#else
+    (void)block_device;
+    (void)device_info;
+    LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
+    return false;
+#endif
+}
+
+}  // namespace
+
+unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
+    std::string path = GetPartitionAbsolutePath(partition_name);
+    return unique_fd{open(path.c_str(), flags)};
+}
+
+bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+    std::string path = GetPartitionAbsolutePath(partition_name);
+    return GetBlockDeviceInfo(path, info);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 190c650..a02e746 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -108,47 +108,44 @@
         LERROR << "Metadata max size is not sector-aligned.";
         return false;
     }
+    return true;
+}
 
-    // Check that the metadata area and logical partition areas don't overlap.
-    int64_t end_of_metadata =
-            GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
-            geometry->metadata_max_size;
-    if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
-        LERROR << "Logical partition metadata overlaps with logical partition contents.";
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
         return false;
     }
-    return true;
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << " read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+        return false;
+    }
+    return ParseGeometry(buffer.get(), geometry);
+}
+
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
+        return false;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << " backup read " << LP_METADATA_GEOMETRY_SIZE
+               << " bytes failed";
+        return false;
+    }
+    return ParseGeometry(buffer.get(), geometry);
 }
 
 // Read and validate geometry information from a block device that holds
 // logical partitions. If the information is corrupted, this will attempt
 // to read it from a secondary backup location.
 bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
-    // Read the first 4096 bytes.
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
-    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed";
-        return false;
-    }
-    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
-        PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
-        return false;
-    }
-    if (ParseGeometry(buffer.get(), geometry)) {
+    if (ReadPrimaryGeometry(fd, geometry)) {
         return true;
     }
-
-    // Try the backup copy in the last 4096 bytes.
-    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
-        return false;
-    }
-    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
-        PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
-               << " bytes failed";
-        return false;
-    }
-    return ParseGeometry(buffer.get(), geometry);
+    return ReadBackupGeometry(fd, geometry);
 }
 
 static bool ValidateTableBounds(const LpMetadataHeader& header,
@@ -188,7 +185,9 @@
         return false;
     }
     if (!ValidateTableBounds(header, header.partitions) ||
-        !ValidateTableBounds(header, header.extents)) {
+        !ValidateTableBounds(header, header.extents) ||
+        !ValidateTableBounds(header, header.groups) ||
+        !ValidateTableBounds(header, header.block_devices)) {
         LERROR << "Logical partition metadata has invalid table bounds.";
         return false;
     }
@@ -202,6 +201,10 @@
         LERROR << "Logical partition metadata has invalid extent table entry size.";
         return false;
     }
+    if (header.groups.entry_size != sizeof(LpMetadataPartitionGroup)) {
+        LERROR << "Logical partition metadata has invalid group table entry size.";
+        return false;
+    }
     return true;
 }
 
@@ -212,7 +215,7 @@
     // First read and validate the header.
     std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
     if (!reader->ReadFully(&metadata->header, sizeof(metadata->header))) {
-        PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " read " << sizeof(metadata->header) << "bytes failed";
         return nullptr;
     }
     if (!ValidateMetadataHeader(metadata->header)) {
@@ -230,7 +233,7 @@
         return nullptr;
     }
     if (!reader->ReadFully(buffer.get(), header.tables_size)) {
-        PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " read " << header.tables_size << "bytes failed";
         return nullptr;
     }
 
@@ -257,6 +260,10 @@
             LERROR << "Logical partition has invalid extent list.";
             return nullptr;
         }
+        if (partition.group_index >= header.groups.num_entries) {
+            LERROR << "Logical partition has invalid group index.";
+            return nullptr;
+        }
 
         metadata->partitions.push_back(partition);
     }
@@ -267,8 +274,46 @@
         memcpy(&extent, cursor, sizeof(extent));
         cursor += header.extents.entry_size;
 
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR &&
+            extent.target_source >= header.block_devices.num_entries) {
+            LERROR << "Logical partition extent has invalid block device.";
+            return nullptr;
+        }
+
         metadata->extents.push_back(extent);
     }
+
+    cursor = buffer.get() + header.groups.offset;
+    for (size_t i = 0; i < header.groups.num_entries; i++) {
+        LpMetadataPartitionGroup group = {};
+        memcpy(&group, cursor, sizeof(group));
+        cursor += header.groups.entry_size;
+
+        metadata->groups.push_back(group);
+    }
+
+    cursor = buffer.get() + header.block_devices.offset;
+    for (size_t i = 0; i < header.block_devices.num_entries; i++) {
+        LpMetadataBlockDevice device = {};
+        memcpy(&device, cursor, sizeof(device));
+        cursor += header.block_devices.entry_size;
+
+        metadata->block_devices.push_back(device);
+    }
+
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());
+    if (!super_device) {
+        LERROR << "Metadata does not specify a super device.";
+        return nullptr;
+    }
+
+    // Check that the metadata area and logical partition areas don't overlap.
+    uint64_t metadata_region =
+            GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);
+    if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Logical partition metadata overlaps with logical partition contents.";
+        return nullptr;
+    }
     return metadata;
 }
 
@@ -287,7 +332,7 @@
                                                 uint32_t slot_number) {
     int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
     if (SeekFile64(fd, offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
         return nullptr;
     }
     return ParseMetadata(geometry, fd);
@@ -296,21 +341,28 @@
 std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
                                                uint32_t slot_number) {
     int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
-    if (SeekFile64(fd, offset, SEEK_END) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
         return nullptr;
     }
     return ParseMetadata(geometry, fd);
 }
 
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
+                                         const std::string& super_partition, uint32_t slot_number) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return nullptr;
+    }
+
     LpMetadataGeometry geometry;
     if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
         return nullptr;
     }
 
     if (slot_number >= geometry.metadata_slot_count) {
-        LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
+        LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
         return nullptr;
     }
 
@@ -322,13 +374,8 @@
     return ReadBackupMetadata(fd, geometry, slot_number);
 }
 
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
-    android::base::unique_fd fd(open(block_device, O_RDONLY));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
-        return nullptr;
-    }
-    return ReadMetadata(fd, slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
+    return ReadMetadata(PartitionOpener(), super_partition, slot_number);
 }
 
 static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
@@ -345,5 +392,13 @@
     return NameFromFixedArray(partition.name, sizeof(partition.name));
 }
 
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {
+    return NameFromFixedArray(group.name, sizeof(group.name));
+}
+
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {
+    return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name));
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index 9f6ca6e..d5d5188 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -31,11 +31,12 @@
 bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
 
 // Helper functions for manually reading geometry and metadata.
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
 std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
 std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
                                           size_t size);
 bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
 
 // These functions assume a valid geometry and slot number.
 std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
diff --git a/fs_mgr/liblp/test_partition_opener.cpp b/fs_mgr/liblp/test_partition_opener.cpp
new file mode 100644
index 0000000..c796f6c
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "test_partition_opener.h"
+
+#include <errno.h>
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+TestPartitionOpener::TestPartitionOpener(
+        const std::map<std::string, int>& partition_map,
+        const std::map<std::string, BlockDeviceInfo>& partition_info)
+    : partition_map_(partition_map), partition_info_(partition_info) {}
+
+unique_fd TestPartitionOpener::Open(const std::string& partition_name, int flags) const {
+    auto iter = partition_map_.find(partition_name);
+    if (iter == partition_map_.end()) {
+        errno = ENOENT;
+        return {};
+    }
+    return unique_fd{dup(iter->second)};
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
+    auto iter = partition_info_.find(partition_name);
+    if (iter == partition_info_.end()) {
+        errno = ENOENT;
+        return false;
+    }
+    *info = iter->second;
+    return true;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/test_partition_opener.h b/fs_mgr/liblp/test_partition_opener.h
new file mode 100644
index 0000000..b90fee7
--- /dev/null
+++ b/fs_mgr/liblp/test_partition_opener.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <map>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <liblp/partition_opener.h>
+
+namespace android {
+namespace fs_mgr {
+
+class TestPartitionOpener : public PartitionOpener {
+  public:
+    explicit TestPartitionOpener(const std::map<std::string, int>& partition_map,
+                                 const std::map<std::string, BlockDeviceInfo>& partition_info = {});
+
+    android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+    bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+
+  private:
+    std::map<std::string, int> partition_map_;
+    std::map<std::string, BlockDeviceInfo> partition_info_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index a590037..742ad82 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -16,14 +16,12 @@
 
 #include <fcntl.h>
 #include <stdint.h>
-#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include <android-base/file.h>
 #include <ext4_utils/ext4_utils.h>
 #include <openssl/sha.h>
-#include <uuid/uuid.h>
 
 #include "utility.h"
 
@@ -57,22 +55,40 @@
     return lseek(fd, offset, whence);
 }
 
+int64_t GetPrimaryGeometryOffset() {
+    return LP_PARTITION_RESERVED_BYTES;
+}
+
+int64_t GetBackupGeometryOffset() {
+    return GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;
+}
+
 int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
     CHECK(slot_number < geometry.metadata_slot_count);
-
-    int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
-    CHECK(offset + geometry.metadata_max_size <=
-          int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
+    int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
+                     geometry.metadata_max_size * slot_number;
     return offset;
 }
 
 int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
     CHECK(slot_number < geometry.metadata_slot_count);
-    int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+    int64_t start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
                     int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
     return start + int64_t(geometry.metadata_max_size * slot_number);
 }
 
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
+    return LP_PARTITION_RESERVED_BYTES +
+           (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
+}
+
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
+    if (metadata.block_devices.empty()) {
+        return nullptr;
+    }
+    return &metadata.block_devices[0];
+}
+
 void SHA256(const void* data, size_t length, uint8_t out[32]) {
     SHA256_CTX c;
     SHA256_Init(&c);
@@ -80,15 +96,6 @@
     SHA256_Final(out, &c);
 }
 
-std::string GetPartitionGuid(const LpMetadataPartition& partition) {
-    // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
-    // macro to assist with buffer sizing.
-    static const size_t kGuidLen = 36;
-    char buffer[kGuidLen + 1];
-    uuid_unparse_upper(partition.guid, buffer);
-    return buffer;
-}
-
 uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
     if (suffix.empty()) {
         return 0;
@@ -101,5 +108,13 @@
     return suffix[1] - 'a';
 }
 
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
+    uint64_t size = 0;
+    for (const auto& block_device : metadata.block_devices) {
+        size += block_device.size;
+    }
+    return size;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 6ef5124..65e643b 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -23,7 +23,7 @@
 
 #include <android-base/logging.h>
 
-#include "liblp/metadata_format.h"
+#include "liblp/liblp.h"
 
 #define LP_TAG "[liblp]"
 #define LWARN LOG(WARNING) << LP_TAG
@@ -38,6 +38,10 @@
 // error. After calling this, the position of |fd| may have changed.
 bool GetDescriptorSize(int fd, uint64_t* size);
 
+// Return the offset of the primary or backup geometry.
+int64_t GetPrimaryGeometryOffset();
+int64_t GetBackupGeometryOffset();
+
 // Return the offset of a primary metadata slot, relative to the start of the
 // device.
 int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
@@ -46,6 +50,10 @@
 // device.
 int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
 
+// Return the total space at the start of the super partition that must be set
+// aside from headers/metadata and backups.
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);
+
 // Cross-platform helper for lseek64().
 int64_t SeekFile64(int fd, int64_t offset, int whence);
 
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 7bf42ae..bdf6dfd 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -36,21 +36,18 @@
                                    {0},
                                    16384,
                                    4,
-                                   10000,
-                                   80000,
-                                   0,
-                                   0,
-                                   1024 * 1024,
                                    4096};
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
-    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3);
+    static const uint64_t start = LP_PARTITION_RESERVED_BYTES;
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), start + 8192 + 16384);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), start + 8192 + 16384 * 2);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), start + 8192 + 16384 * 3);
 
-    EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1);
-    EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2);
-    EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
-    EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
+    static const uint64_t backup_start = start + 8192 + 16384 * 4;
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), backup_start + 16384 * 3);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), backup_start + 16384 * 2);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), backup_start + 16384 * 1);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
 }
 
 TEST(liblp, AlignTo) {
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 9dd2745..c740bd4 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -43,8 +43,7 @@
 static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
     return g1.metadata_max_size == g2.metadata_max_size &&
            g1.metadata_slot_count == g2.metadata_slot_count &&
-           g1.first_logical_sector == g2.first_logical_sector &&
-           g1.last_logical_sector == g2.last_logical_sector;
+           g1.logical_block_size == g2.logical_block_size;
 }
 
 std::string SerializeMetadata(const LpMetadata& input) {
@@ -56,14 +55,20 @@
                            metadata.partitions.size() * sizeof(LpMetadataPartition));
     std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
                         metadata.extents.size() * sizeof(LpMetadataExtent));
+    std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
+                       metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
+    std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
+                              metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
 
     // Compute positions of tables.
     header.partitions.offset = 0;
     header.extents.offset = header.partitions.offset + partitions.size();
-    header.tables_size = header.extents.offset + extents.size();
+    header.groups.offset = header.extents.offset + extents.size();
+    header.block_devices.offset = header.groups.offset + groups.size();
+    header.tables_size = header.block_devices.offset + block_devices.size();
 
     // Compute payload checksum.
-    std::string tables = partitions + extents;
+    std::string tables = partitions + extents + groups + block_devices;
     SHA256(tables.data(), tables.size(), header.tables_checksum);
 
     // Compute header checksum.
@@ -83,15 +88,11 @@
         return false;
     }
 
-    *blob = SerializeMetadata(metadata);
-
     const LpMetadataHeader& header = metadata.header;
     const LpMetadataGeometry& geometry = metadata.geometry;
-    // Validate the usable sector range.
-    if (geometry.first_logical_sector > geometry.last_logical_sector) {
-        LERROR << "Logical partition metadata has invalid sector range.";
-        return false;
-    }
+
+    *blob = SerializeMetadata(metadata);
+
     // Make sure we're writing within the space reserved.
     if (blob->size() > geometry.metadata_max_size) {
         LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
@@ -103,18 +104,22 @@
     // metadata.
     uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
                              uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
-    if (reserved_size > blockdevice_size ||
-        reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+    uint64_t total_reserved = reserved_size * 2;
+
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+    if (!super_device) {
+        LERROR << "Logical partition metadata does not have a super block device.";
+        return false;
+    }
+
+    if (total_reserved > blockdevice_size ||
+        total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
         LERROR << "Not enough space to store all logical partition metadata slots.";
         return false;
     }
-    if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
-        LERROR << "Not enough space to backup all logical partition metadata slots.";
-        return false;
-    }
-    if (blockdevice_size != metadata.geometry.block_device_size) {
+    if (blockdevice_size != super_device->size) {
         LERROR << "Block device size " << blockdevice_size
-               << " does not match metadata requested size " << metadata.geometry.block_device_size;
+               << " does not match metadata requested size " << super_device->size;
         return false;
     }
 
@@ -127,11 +132,12 @@
     }
 
     // Make sure all linear extents have a valid range.
+    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
     for (const auto& extent : metadata.extents) {
         if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
             uint64_t physical_sector = extent.target_data;
-            if (physical_sector < geometry.first_logical_sector ||
-                physical_sector + extent.num_sectors > geometry.last_logical_sector) {
+            if (physical_sector < super_device->first_logical_sector ||
+                physical_sector + extent.num_sectors > last_sector) {
                 LERROR << "Extent table entry is out of bounds.";
                 return false;
             }
@@ -140,54 +146,69 @@
     return true;
 }
 
-static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+// Check that the given region is within metadata bounds.
+static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+    if (!super_device) {
+        LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
+        return false;
+    }
+    if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
+               << " overlaps with logical partition contents";
+        return false;
+    }
+    return true;
+}
+
+static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
                                  const std::string& blob,
                                  const std::function<bool(int, const std::string&)>& writer) {
-    int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
+    if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
+        return false;
+    }
     if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
         return false;
     }
     if (!writer(fd, blob)) {
-        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
         return false;
     }
     return true;
 }
 
-static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
                                 const std::string& blob,
                                 const std::function<bool(int, const std::string&)>& writer) {
-    int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
-    int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
-    if (abs_offset == (int64_t)-1) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+    int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
+    if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
         return false;
     }
-    if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
-        PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
-               << " is within logical partition bounds, sector " << geometry.last_logical_sector;
+    if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
         return false;
     }
     if (!writer(fd, blob)) {
-        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
         return false;
     }
     return true;
 }
 
-static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
                           const std::string& blob,
                           const std::function<bool(int, const std::string&)>& writer) {
     // Make sure we're writing to a valid metadata slot.
-    if (slot_number >= geometry.metadata_slot_count) {
+    if (slot_number >= metadata.geometry.metadata_slot_count) {
         LERROR << "Invalid logical partition metadata slot number.";
         return false;
     }
-    if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
+    if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
         return false;
     }
-    if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
+    if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {
         return false;
     }
     return true;
@@ -197,7 +218,14 @@
     return android::base::WriteFully(fd, blob.data(), blob.size());
 }
 
-bool FlashPartitionTable(int fd, const LpMetadata& metadata) {
+bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                         const LpMetadata& metadata) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return false;
+    }
+
     // Before writing geometry and/or logical partition tables, perform some
     // basic checks that the geometry and tables are coherent, and will fit
     // on the given block device.
@@ -206,39 +234,63 @@
         return false;
     }
 
-    // Write geometry to the first and last 4096 bytes of the device.
-    std::string blob = SerializeGeometry(metadata.geometry);
+    // Write zeroes to the first block.
+    std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
     if (SeekFile64(fd, 0, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset 0";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, zeroes.data(), zeroes.size())) {
+        PERROR << __PRETTY_FUNCTION__ << " write " << zeroes.size() << " bytes failed";
+        return false;
+    }
+
+    LWARN << "Flashing new logical partition geometry to " << super_partition;
+
+    // Write geometry to the primary and backup locations.
+    std::string blob = SerializeGeometry(metadata.geometry);
+    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: primary geometry";
         return false;
     }
     if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
         return false;
     }
-    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: backup geometry";
         return false;
     }
     if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
         return false;
     }
 
     bool ok = true;
     for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
-        ok &= WriteMetadata(fd, metadata.geometry, i, metadata_blob, DefaultWriter);
+        ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
     }
     return ok;
 }
 
+bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) {
+    return FlashPartitionTable(PartitionOpener(), super_partition, metadata);
+}
+
 static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
     return !memcmp(a.header.header_checksum, b.header.header_checksum,
                    sizeof(a.header.header_checksum));
 }
 
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number,
                           const std::function<bool(int, const std::string&)>& writer) {
+    android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return false;
+    }
+
     // Before writing geometry and/or logical partition tables, perform some
     // basic checks that the geometry and tables are coherent, and will fit
     // on the given block device.
@@ -279,7 +331,7 @@
             LERROR << "Error serializing primary metadata to repair corrupted backup";
             return false;
         }
-        if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
+        if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) {
             LERROR << "Error writing primary metadata to repair corrupted backup";
             return false;
         }
@@ -291,46 +343,31 @@
             LERROR << "Error serializing primary metadata to repair corrupted backup";
             return false;
         }
-        if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
+        if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
             LERROR << "Error writing primary metadata to repair corrupted backup";
             return false;
         }
     }
 
     // Both copies should now be in sync, so we can continue the update.
-    return WriteMetadata(fd, geometry, slot_number, blob, writer);
-}
+    if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) {
+        return false;
+    }
 
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {
-    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
-        return false;
-    }
-    if (!FlashPartitionTable(fd, metadata)) {
-        return false;
-    }
-    LWARN << "Flashed new logical partition geometry to " << block_device;
-    return true;
-}
-
-bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
-                          uint32_t slot_number) {
-    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
-        return false;
-    }
-    if (!UpdatePartitionTable(fd, metadata, slot_number)) {
-        return false;
-    }
     LINFO << "Updated logical partition table at slot " << slot_number << " on device "
-          << block_device;
+          << super_partition;
     return true;
 }
 
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
-    return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter);
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number) {
+    return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
+}
+
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+                          uint32_t slot_number) {
+    PartitionOpener opener;
+    return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
index ab18d45..6f1da0f 100644
--- a/fs_mgr/liblp/writer.h
+++ b/fs_mgr/liblp/writer.h
@@ -30,10 +30,8 @@
 
 // These variants are for testing only. The path-based functions should be used
 // for actual operation, so that open() is called with the correct flags.
-bool FlashPartitionTable(int fd, const LpMetadata& metadata);
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
-
-bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number,
                           const std::function<bool(int, const std::string&)>& writer);
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
new file mode 100755
index 0000000..b6a8eef
--- /dev/null
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -0,0 +1,305 @@
+#! /bin/bash
+
+USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+
+adb remount tests (overlayfs focus)
+
+Conditions:
+ - Must be a userdebug build.
+ - Must be in adb mode.
+ - Kernel must have overlayfs enabled and patched to support override_creds.
+ - Must have either squashfs, ext4-dedupe or right-sized partitions.
+ - Minimum expectation system and vender are overlayfs covered partitions.
+"
+
+if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
+  echo "${USAGE}" >&2
+  exit 0
+fi
+
+# Helper Variables
+
+SPACE=" "
+# A _real_ embedded tab character
+TAB="`echo | tr '\n' '\t'`"
+# A _real_ embedded escape character
+ESCAPE="`echo | tr '\n' '\033'`"
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+ORANGE="${ESCAPE}[38;5;255:165:0m"
+NORMAL="${ESCAPE}[0m"
+
+# Helper functions
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+  fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+  adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: adb_sh <commands>
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+  adb shell "${@}"
+}
+
+[ "USAGE: get_property <prop>
+
+Returns the property value" ]
+get_property() {
+  adb_sh getprop ${1} 2>&1 </dev/null
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
+    false
+  fi
+}
+
+[ "USAGE: adb_su <commands>
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+  adb_sh su root "${@}"
+}
+
+[ "USAGE: adb_cat <file> >stdout
+
+Returns: content of file to stdout with carriage returns skipped,
+         true of the file exists" ]
+adb_cat() {
+    OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
+    retval=${?}
+    echo "${OUTPUT}" | tr -d '\r'
+    return ${retval}
+}
+
+[ "USAGE: adb_reboot
+
+Returns: true if the reboot command succeeded" ]
+adb_reboot() {
+  adb reboot remount-test
+}
+
+[ "USAGE: adb_wait [timeout]
+
+Returns: waits until the device has returned or the optional timeout" ]
+adb_wait() {
+  if [ -n "${1}" ]; then
+    timeout --preserve-status --signal=KILL ${1} adb wait-for-device
+  else
+    adb wait-for-device
+  fi
+}
+
+[ "USAGE: adb_root
+
+Returns: true if device in root state" ]
+adb_root() {
+  adb root >/dev/null </dev/null 2>&1 &&
+  sleep 1 &&
+  adb_wait &&
+  sleep 1
+}
+
+die() {
+  echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
+  exit 1
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+  lval="${1}"
+  rval="${2}"
+  shift 2
+  if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
+    if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
+*}" ]; then
+      echo "ERROR: expected \"${lval}\"" >&2
+      echo "       got \"${rval}\"" |
+        sed ': again
+             N
+             s/\(\n\)\([^ ]\)/\1             \2/
+             t again' >&2
+      if [ -n "${*}" ] ; then
+        echo "       ${*}" >&2
+      fi
+    else
+      echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
+    fi
+    return 1
+  fi
+  if [ -n "${*}" ] ; then
+    if [ X"${lval}" != X"${rval}" ]; then
+      if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
+        echo "INFO: ok \"${lval}\"" >&2
+        echo "       = \"${rval}\"" |
+          sed ': again
+               N
+               s/\(\n\)\([^ ]\)/\1          \2/
+               t again' >&2
+        if [ -n "${*}" ] ; then
+          echo "      ${*}" >&2
+        fi
+      else
+        echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+      fi
+    else
+      echo "INFO: ok \"${lval}\" ${*}" >&2
+    fi
+  fi
+  return 0
+}
+
+[ "USAGE: check_eq <lval> <rval> [message]
+
+Exits if (regex) lval mismatches rval" ]
+check_eq() {
+  left="${1}"
+  right="${2}"
+  shift 2
+  EXPECT_EQ "${left}" "${right}" ||
+    die "${@}"
+}
+
+[ "USAGE: skip_administrative_mounts
+
+Filters out all administrative (eg: sysfs) mounts" ]
+skip_administrative_mounts() {
+  grep -v -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\|/data/media\) " -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\|data\) "
+}
+
+if [ X"-s" = X"${1}" -a -n "${2}" ]; then
+  export ANDROID_SERIAL="${2}"
+  shift 2
+fi
+
+inFastboot && die "device in fastboot mode"
+if ! inAdb; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} device not in adb mode ... waiting 2 minutes"
+  adb_wait 2m
+fi
+inAdb || die "device not in adb mode"
+isDebuggable || die "device not a debug build"
+
+# Do something
+adb_wait || die "wait for device failed"
+adb_sh ls -d /sys/module/overlay </dev/null || die "overlay module not present"
+adb_su ls /sys/module/overlay/parameters/override_creds </dev/null ||
+  die "overlay module can not be used on ANDROID"
+adb_root &&
+  adb_wait &&
+  D=`adb disable-verity 2>&1` ||
+    die "setup for overlay"
+echo "${D}"
+if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+  echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
+fi
+if adb_sh ls -d /data/overlay </dev/null >/dev/null 2>&1; then
+  echo "/data/overlay setup, clearing out" >&2
+  adb_sh rm -rf /data/overlay </dev/null ||
+    die "/data/overlay removal"
+fi
+adb_sh ls -d /cache/overlay </dev/null >/dev/null 2>&1 ||
+  adb_sh ls -d /mnt/scratch/overlay </dev/null >/dev/null 2>&1 ||
+  die "overlay directory setup"
+adb_reboot &&
+  adb_wait &&
+  adb_sh df -k </dev/null | head -1 &&
+  adb_sh df -k </dev/null | grep "^overlay " ||
+  die "overlay takeover failed"
+adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+  echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
+
+adb_root &&
+  adb_wait &&
+  adb remount &&
+  adb_sh df -k </dev/null | head -1 &&
+  adb_sh df -k </dev/null | grep "^overlay " &&
+  adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+  die  "overlay takeover after remount"
+!(adb_sh grep "^overlay " /proc/mounts </dev/null | grep " overlay ro,") &&
+  !(adb_sh grep " rw," /proc/mounts </dev/null |
+  skip_administrative_mounts) ||
+    die "remount overlayfs missed a spot"
+
+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 ||
+  echo "${GREEN}[       OK ]${NORMAL} overlay takeover in first stage init" >&2
+
+# Check something
+A="Hello World! $(date)"
+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
+B="`adb_cat /vendor/hello`" ||
+  die "vendor hello"
+check_eq "${A}" "${B}" vendor before reboot
+adb_reboot &&
+  adb_wait &&
+  B="`adb_cat /system/hello`" ||
+  die "re-read system hello after reboot"
+check_eq "${A}" "${B}" system after reboot
+# Only root can read vendor if sepolicy permissions are as expected
+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
+adb_root &&
+  adb_wait &&
+  B="`adb_cat /vendor/hello`" ||
+  die "re-read vendor hello after reboot"
+check_eq "${A}" "${B}" vendor after reboot
+
+adb reboot-fastboot &&
+  fastboot flash vendor &&
+  fastboot reboot ||
+  die "fastbootd flash vendor"
+adb_wait &&
+  adb_root &&
+  adb_wait &&
+  adb_sh df -k </dev/null | head -1 &&
+  adb_sh df -k </dev/null | grep "^overlay " &&
+  adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+  die  "overlay system takeover after flash vendor"
+adb_sh df -k </dev/null | grep "^overlay .* /vendor\$" >/dev/null &&
+  die  "overlay minus vendor takeover after flash vendor"
+B="`adb_cat /system/hello`" ||
+  die "re-read system hello after flash vendor"
+check_eq "${A}" "${B}" system after flash vendor
+adb_root &&
+  adb_wait ||
+  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
+
+adb remount &&
+  ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
+  adb_sh rm /system/hello </dev/null ||
+  die "cleanup hello"
+B="`adb_cat /system/hello`" &&
+  die "re-read system hello after rm"
+check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
+B="`adb_cat /vendor/hello`" &&
+  die "re-read vendor hello after rm"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
+
+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 8b1c55a..db01c1e 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -139,7 +139,7 @@
     auto fstab = fs_mgr_read_fstab("/proc/mounts");
     ASSERT_NE(fstab, nullptr);
 
-    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "r"),
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "re"),
                                                            endmntent);
     ASSERT_NE(mounts, nullptr);
 
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 879ba21..f78093b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -42,6 +42,7 @@
 using DmTargetLinear = ::android::dm::DmTargetLinear;
 using DmTargetZero = ::android::dm::DmTargetZero;
 using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
+using DmTargetBow = ::android::dm::DmTargetBow;
 using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
 
@@ -108,6 +109,13 @@
             std::string block_device = NextArg();
             return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
                                                            block_device);
+        } else if (target_type == "bow") {
+            if (!HasArgs(1)) {
+                std::cerr << "Expected \"bow\" <block_device>" << std::endl;
+                return nullptr;
+            }
+            std::string block_device = NextArg();
+            return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
         } else {
             std::cerr << "Unrecognized target type: " << target_type << std::endl;
             return nullptr;
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp
index 1c339f4..43d5708 100644
--- a/gatekeeperd/IGateKeeperService.cpp
+++ b/gatekeeperd/IGateKeeperService.cpp
@@ -70,7 +70,7 @@
             } else {
                 reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
             }
-            return NO_ERROR;
+            return OK;
         }
         case VERIFY: {
             CHECK_INTERFACE(IGateKeeperService, data, reply);
@@ -102,7 +102,7 @@
             } else {
                 reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
             }
-            return NO_ERROR;
+            return OK;
         }
         case VERIFY_CHALLENGE: {
             CHECK_INTERFACE(IGateKeeperService, data, reply);
@@ -141,7 +141,7 @@
             } else {
                 reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
             }
-            return NO_ERROR;
+            return OK;
         }
         case GET_SECURE_USER_ID: {
             CHECK_INTERFACE(IGateKeeperService, data, reply);
@@ -149,20 +149,20 @@
             uint64_t sid = getSecureUserId(uid);
             reply->writeNoException();
             reply->writeInt64(sid);
-            return NO_ERROR;
+            return OK;
         }
         case CLEAR_SECURE_USER_ID: {
             CHECK_INTERFACE(IGateKeeperService, data, reply);
             uint32_t uid = data.readInt32();
             clearSecureUserId(uid);
             reply->writeNoException();
-            return NO_ERROR;
+            return OK;
         }
         case REPORT_DEVICE_SETUP_COMPLETE: {
             CHECK_INTERFACE(IGateKeeperService, data, reply);
             reportDeviceSetupComplete();
             reply->writeNoException();
-            return NO_ERROR;
+            return OK;
         }
         default:
             return BBinder::onTransact(code, data, reply, flags);
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 5f3ce36..f2818f3 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -386,7 +386,7 @@
             write(fd, result, strlen(result) + 1);
         }
 
-        return NO_ERROR;
+        return OK;
     }
 
 private:
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 80c5afe..2a5667c 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -357,7 +357,7 @@
         if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryChargeCounterPath);
-            ret = NO_ERROR;
+            ret = OK;
         } else {
             ret = NAME_NOT_FOUND;
         }
@@ -367,7 +367,7 @@
         if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCurrentNowPath);
-            ret = NO_ERROR;
+            ret = OK;
         } else {
             ret = NAME_NOT_FOUND;
         }
@@ -377,7 +377,7 @@
         if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
-            ret = NO_ERROR;
+            ret = OK;
         } else {
             ret = NAME_NOT_FOUND;
         }
@@ -387,7 +387,7 @@
         if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCapacityPath);
-            ret = NO_ERROR;
+            ret = OK;
         } else {
             ret = NAME_NOT_FOUND;
         }
@@ -403,7 +403,7 @@
 
     case BATTERY_PROP_BATTERY_STATUS:
         val->valueInt64 = getChargeStatus();
-        ret = NO_ERROR;
+        ret = OK;
         break;
 
     default:
diff --git a/healthd/animation.h b/healthd/animation.h
index 562b689..f59fb38 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -20,7 +20,7 @@
 #include <inttypes.h>
 #include <string>
 
-struct GRSurface;
+class GRSurface;
 struct GRFont;
 
 namespace android {
diff --git a/init/Android.bp b/init/Android.bp
index c793971..ff3b61f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -74,6 +74,7 @@
         "libdl",
         "libext4_utils",
         "libfs_mgr",
+        "libfscrypt",
         "libhidl-gen-utils",
         "libkeyutils",
         "liblog",
diff --git a/init/Android.mk b/init/Android.mk
index 5554995..c85727c 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -51,7 +51,8 @@
     uevent_listener.cpp \
     util.cpp \
 
-LOCAL_MODULE := init
+LOCAL_MODULE := init_first_stage
+LOCAL_MODULE_STEM := init
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
@@ -72,6 +73,7 @@
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
+    libfscrypt \
     libseccomp_policy \
     libcrypto_utils \
     libsparse \
@@ -87,8 +89,32 @@
     libselinux \
     libcap \
 
-LOCAL_REQUIRED_MODULES := \
-    init_second_stage \
-
 LOCAL_SANITIZE := signed-integer-overflow
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init_system
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+LOCAL_REQUIRED_MODULES := \
+   init_first_stage \
+   init_second_stage \
+
+else
+LOCAL_REQUIRED_MODULES := \
+   init_second_stage \
+
+endif
+include $(BUILD_PHONY_PACKAGE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init_vendor
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+LOCAL_REQUIRED_MODULES := \
+   init_first_stage \
+
+endif
+include $(BUILD_PHONY_PACKAGE)
+
+
diff --git a/init/README.md b/init/README.md
index b45da21..6c51b37 100644
--- a/init/README.md
+++ b/init/README.md
@@ -189,7 +189,7 @@
 
 `critical`
 > This is a device-critical service. If it exits more than four times in
-  four minutes, the device will reboot into recovery mode.
+  four minutes, the device will reboot into bootloader.
 
 `disabled`
 > This service will not automatically start with its class.
@@ -213,7 +213,8 @@
 `interface <interface name> <instance name>`
 > Associates this service with a list of the HIDL services that it provides. The interface name
   must be a fully-qualified name and not a value name. This is used to allow hwservicemanager to
-  lazily start services.
+  lazily start services. When multiple interfaces are served, this tag should be used multiple
+  times.
   For example: interface vendor.foo.bar@1.0::IBaz default
 
 `ioprio <class> <priority>`
@@ -225,6 +226,15 @@
   keycodes are pressed at once, the service will start. This is typically used to start the
   bugreport service.
 
+> This option may take a property instead of a list of keycodes. In this case, only one option is
+  provided: the property name in the typical property expansion format. The property must contain
+  a comma separated list of keycode values or the text 'none' to indicate that
+  this service does not respond to keycodes.
+
+> For example, `keycodes ${some.property.name:-none}` where some.property.name expands
+  to "123,124,125". Since keycodes are handled very early in init,
+  only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
+
 `memcg.limit_in_bytes <value>`
 > Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
   which must be equal or greater than 0.
@@ -262,6 +272,13 @@
 > Scheduling priority of the service process. This value has to be in range
   -20 to 19. Default priority is 0. Priority is set via setpriority().
 
+`restart_period <seconds>`
+> If a non-oneshot service exits, it will be restarted at its start time plus
+  this period. It defaults to 5s to rate limit crashing services.
+  This can be increased for services that are meant to run periodically. For
+  example, it may be set to 3600 to indicate that the service should run every hour
+  or 86400 to indicate that the service should run every day.
+
 `rlimit <resource> <cur> <max>`
 > This applies the given rlimit to the service. rlimits are inherited by child
   processes, so this effectively applies the given rlimit to the process tree
@@ -298,6 +315,12 @@
   seclabel or computed based on the service executable file security context.
   For native executables see libcutils android\_get\_control\_socket().
 
+`timeout_period <seconds>`
+> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
+  here, so oneshot services do not automatically restart, however all other services will.
+  This is particularly useful for creating a periodic service combined with the restart_period
+  option described above.
+
 `user <username>`
 > Change to 'username' before exec'ing this service.
   Currently defaults to root.  (??? probably should default to nobody)
diff --git a/init/action.cpp b/init/action.cpp
index 11335ca..94ccef2 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -44,9 +44,12 @@
     return function(builtin_arguments);
 }
 
-Command::Command(BuiltinFunction f, bool execute_in_subcontext,
-                 const std::vector<std::string>& args, int line)
-    : func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
+Command::Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
+                 int line)
+    : func_(std::move(f)),
+      execute_in_subcontext_(execute_in_subcontext),
+      args_(std::move(args)),
+      line_(line) {}
 
 Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
     if (subcontext) {
@@ -80,7 +83,7 @@
 
 const KeywordFunctionMap* Action::function_map_ = nullptr;
 
-Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
+Result<Success> Action::AddCommand(std::vector<std::string>&& args, int line) {
     if (!function_map_) {
         return Error() << "no function map available";
     }
@@ -88,12 +91,12 @@
     auto function = function_map_->FindFunction(args);
     if (!function) return Error() << function.error();
 
-    commands_.emplace_back(function->second, function->first, args, line);
+    commands_.emplace_back(function->second, function->first, std::move(args), line);
     return Success();
 }
 
-void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
-    commands_.emplace_back(f, false, args, line);
+void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
+    commands_.emplace_back(f, false, std::move(args), line);
 }
 
 std::size_t Action::NumCommands() const {
diff --git a/init/action.h b/init/action.h
index 4f063cc..967c682 100644
--- a/init/action.h
+++ b/init/action.h
@@ -36,7 +36,7 @@
 
 class Command {
   public:
-    Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
+    Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
             int line);
 
     Result<Success> InvokeFunc(Subcontext* subcontext) const;
@@ -61,8 +61,8 @@
            const std::string& event_trigger,
            const std::map<std::string, std::string>& property_triggers);
 
-    Result<Success> AddCommand(const std::vector<std::string>& args, int line);
-    void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
+    Result<Success> AddCommand(std::vector<std::string>&& args, int line);
+    void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 22977bb..9de4085 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -47,9 +47,7 @@
 void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
     auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
                                            std::map<std::string, std::string>{});
-    std::vector<std::string> name_vector{name};
-
-    action->AddCommand(func, name_vector, 0);
+    action->AddCommand(func, {name}, 0);
 
     event_queue_.emplace(action.get());
     actions_.emplace_back(std::move(action));
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 1481162..4f8bd16 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -19,7 +19,11 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 
-#include "stable_properties.h"
+#if defined(__ANDROID__)
+#include "property_service.h"
+#else
+#include "host_init_stubs.h"
+#endif
 
 using android::base::GetBoolProperty;
 using android::base::StartsWith;
@@ -36,15 +40,19 @@
         return true;
     }
 
-    if (kExportedActionableProperties.count(prop_name) == 1) {
-        return true;
-    }
+    static constexpr const char* kPartnerPrefixes[] = {
+            "init.svc.vendor.", "ro.vendor.",    "persist.vendor.",
+            "vendor.",          "init.svc.odm.", "ro.odm.",
+            "persist.odm.",     "odm.",          "ro.boot.",
+    };
+
     for (const auto& prefix : kPartnerPrefixes) {
         if (android::base::StartsWith(prop_name, prefix)) {
             return true;
         }
     }
-    return false;
+
+    return CanReadProperty(subcontext->context(), prop_name);
 }
 
 Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 17d34e1..5d62c0b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -50,9 +50,9 @@
 #include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <ext4_utils/ext4_crypt.h>
-#include <ext4_utils/ext4_crypt_init_extensions.h>
 #include <fs_mgr.h>
+#include <fscrypt/fscrypt.h>
+#include <fscrypt/fscrypt_init_extensions.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -307,8 +307,8 @@
         }
     }
 
-    if (e4crypt_is_native()) {
-        if (e4crypt_set_directory_policy(args[1].c_str())) {
+    if (fscrypt_is_native()) {
+        if (fscrypt_set_directory_policy(args[1].c_str())) {
             return reboot_into_recovery(
                 {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
         }
@@ -517,8 +517,8 @@
         return reboot_into_recovery(options);
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
-        if (e4crypt_install_keyring()) {
-            return Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -528,8 +528,8 @@
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return Success();
     } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
-        if (e4crypt_install_keyring()) {
-            return Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -539,8 +539,8 @@
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-        if (e4crypt_install_keyring()) {
-            return Error() << "e4crypt_install_keyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -1016,9 +1016,13 @@
     }
     service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
-            if (e4crypt_is_native()) {
+            if (fscrypt_is_native()) {
                 LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
-                reboot_into_recovery({"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+                if (auto result = reboot_into_recovery(
+                            {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+                    !result) {
+                    LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+                }
             } else {
                 LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
             }
@@ -1034,7 +1038,7 @@
 static Result<Success> do_installkey(const BuiltinArguments& args) {
     if (!is_file_crypto()) return Success();
 
-    auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+    auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
     if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
         return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 71a8e0d..eb86eb0 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -68,6 +68,7 @@
     bool CreateLogicalPartitions();
     bool MountPartition(fstab_rec* fstab_rec);
     bool MountPartitions();
+    bool IsDmLinearEnabled();
     bool GetBackingDmLinearDevices();
 
     virtual ListenerAction UeventCallback(const Uevent& uevent);
@@ -82,6 +83,7 @@
     std::string lp_metadata_partition_;
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
+    std::string super_partition_name_;
     std::unique_ptr<DeviceHandler> device_handler_;
     UeventListener uevent_listener_;
 };
@@ -134,22 +136,6 @@
     return !ForceNormalBoot() && access("/system/bin/recovery", F_OK) == 0;
 }
 
-static inline bool IsDmLinearEnabled() {
-    static bool checked = false;
-    static bool enabled = false;
-    if (checked) {
-        return enabled;
-    }
-    import_kernel_cmdline(false,
-                          [](const std::string& key, const std::string& value, bool in_qemu) {
-                              if (key == "androidboot.logical_partitions" && value == "1") {
-                                  enabled = true;
-                              }
-                          });
-    checked = true;
-    return enabled;
-}
-
 // Class Definitions
 // -----------------
 FirstStageMount::FirstStageMount()
@@ -168,6 +154,8 @@
     device_handler_ = std::make_unique<DeviceHandler>(
             std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
             std::move(boot_devices), false);
+
+    super_partition_name_ = fs_mgr_get_super_partition_name();
 }
 
 std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -198,13 +186,20 @@
     return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
 }
 
+bool FirstStageMount::IsDmLinearEnabled() {
+    for (auto fstab_rec : mount_fstab_recs_) {
+        if (fs_mgr_is_logical(fstab_rec)) return true;
+    }
+    return false;
+}
+
 bool FirstStageMount::GetBackingDmLinearDevices() {
     // Add any additional devices required for dm-linear mappings.
     if (!IsDmLinearEnabled()) {
         return true;
     }
 
-    required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+    required_devices_partition_names_.emplace(super_partition_name_);
     return true;
 }
 
@@ -270,7 +265,7 @@
 
     if (lp_metadata_partition_.empty()) {
         LOG(ERROR) << "Could not locate logical partition tables in partition "
-                   << LP_METADATA_PARTITION_NAME;
+                   << super_partition_name_;
         return false;
     }
     return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
@@ -283,7 +278,7 @@
     auto iter = required_devices_partition_names_.find(name);
     if (iter != required_devices_partition_names_.end()) {
         LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
-        if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+        if (IsDmLinearEnabled() && name == super_partition_name_) {
             std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
             lp_metadata_partition_ = links[0];
         }
@@ -396,6 +391,12 @@
         }
     }
 
+    // heads up for instantiating required device(s) for overlayfs logic
+    const auto devices = fs_mgr_overlayfs_required_devices(device_tree_fstab_.get());
+    for (auto const& device : devices) {
+        InitMappedDevice(device);
+    }
+
     fs_mgr_overlayfs_mount_all(device_tree_fstab_.get());
 
     return true;
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index 8866bdc..b85e54a 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -30,6 +30,9 @@
 std::string default_console = "/dev/console";
 
 // property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+    return true;
+}
 uint32_t SetProperty(const std::string& key, const std::string& value) {
     android::base::SetProperty(key, value);
     return 0;
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 0af11f6..63ceead 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -39,6 +39,7 @@
 extern std::string default_console;
 
 // property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name);
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                            const std::string& source_context, const ucred& cr, std::string* error);
diff --git a/init/init.cpp b/init/init.cpp
index 47cfe32..b12ba8c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -61,6 +61,10 @@
 #include "ueventd.h"
 #include "util.h"
 
+#if __has_feature(address_sanitizer)
+#include <sanitizer/asan_interface.h>
+#endif
+
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
@@ -74,6 +78,25 @@
 namespace android {
 namespace init {
 
+#if __has_feature(address_sanitizer)
+// Load asan.options if it exists since these are not yet in the environment.
+// Always ensure detect_container_overflow=0 as there are false positives with this check.
+// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
+extern "C" const char* __asan_default_options() {
+    return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
+__sanitizer_report_error_summary(const char* summary) {
+    LOG(ERROR) << "Main stage (error summary): " << summary;
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
+AsanReportCallback(const char* str) {
+    LOG(ERROR) << "Main stage: " << str;
+}
+#endif
+
 static int property_triggers_enabled = 0;
 
 static char qemu[32];
@@ -187,23 +210,34 @@
     }
 }
 
-static std::optional<boot_clock::time_point> RestartProcesses() {
-    std::optional<boot_clock::time_point> next_process_restart_time;
+static std::optional<boot_clock::time_point> HandleProcessActions() {
+    std::optional<boot_clock::time_point> next_process_action_time;
     for (const auto& s : ServiceList::GetInstance()) {
+        if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
+            auto timeout_time = s->time_started() + *s->timeout_period();
+            if (boot_clock::now() > timeout_time) {
+                s->Timeout();
+            } else {
+                if (!next_process_action_time || timeout_time < *next_process_action_time) {
+                    next_process_action_time = timeout_time;
+                }
+            }
+        }
+
         if (!(s->flags() & SVC_RESTARTING)) continue;
 
-        auto restart_time = s->time_started() + 5s;
+        auto restart_time = s->time_started() + s->restart_period();
         if (boot_clock::now() > restart_time) {
             if (auto result = s->Start(); !result) {
                 LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
             }
         } else {
-            if (!next_process_restart_time || restart_time < *next_process_restart_time) {
-                next_process_restart_time = restart_time;
+            if (!next_process_action_time || restart_time < *next_process_action_time) {
+                next_process_action_time = restart_time;
             }
         }
     }
-    return next_process_restart_time;
+    return next_process_action_time;
 }
 
 static Result<Success> DoControlStart(Service* service) {
@@ -570,23 +604,6 @@
     RebootSystem(ANDROID_RB_RESTART2, "bootloader");
 }
 
-static void InitKernelLogging(char* argv[]) {
-    // Make stdin/stdout/stderr all point to /dev/null.
-    int fd = open("/sys/fs/selinux/null", O_RDWR);
-    if (fd == -1) {
-        int saved_errno = errno;
-        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
-        errno = saved_errno;
-        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
-    }
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    if (fd > 2) close(fd);
-
-    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
-}
-
 static void GlobalSeccomp() {
     import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
                                     bool in_qemu) {
@@ -625,6 +642,10 @@
 }
 
 int main(int argc, char** argv) {
+#if __has_feature(address_sanitizer)
+    __asan_set_error_report_callback(AsanReportCallback);
+#endif
+
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
     }
@@ -643,7 +664,8 @@
         SetupSelinux(argv);
     }
 
-    InitKernelLogging(argv);
+    // We need to set up stdin/stdout/stderr again now that we're running in init's context.
+    InitKernelLogging(argv, InitAborter);
     LOG(INFO) << "init second stage started!";
 
     // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
@@ -770,12 +792,12 @@
         }
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
             if (!shutting_down) {
-                auto next_process_restart_time = RestartProcesses();
+                auto next_process_action_time = HandleProcessActions();
 
                 // If there's a process that needs restarting, wake up in time for that.
-                if (next_process_restart_time) {
+                if (next_process_action_time) {
                     epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
-                        *next_process_restart_time - boot_clock::now());
+                            *next_process_action_time - boot_clock::now());
                     if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
                 }
             }
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 40706a1..d81ca5c 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -102,9 +102,11 @@
 
     // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
     // talk to the outside world...
-    android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
-        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
-    });
+    // We need to set up stdin/stdout/stderr for child processes forked from first
+    // stage init as part of the mount process.  This closes /dev/console if the
+    // kernel had previously opened it.
+    auto reboot_bootloader = [](const char*) { RebootSystem(ANDROID_RB_RESTART2, "bootloader"); };
+    InitKernelLogging(argv, reboot_bootloader);
 
     if (!errors.empty()) {
         for (const auto& [error_string, error_errno] : errors) {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index b8c1cfd..f5ac44f 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -41,7 +41,7 @@
 
 Keychords::~Keychords() noexcept {
     if (inotify_fd_ >= 0) {
-        epoll_->UnregisterHandler(inotify_fd_);
+        epoll_->UnregisterHandler(inotify_fd_).IgnoreError();
         ::close(inotify_fd_);
     }
     while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
@@ -186,7 +186,11 @@
         current_ |= mask & available & set;
         LambdaCheck();
     }
-    epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
+    if (auto result = epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
+        !result) {
+        LOG(WARNING) << "Could not register keychord epoll handler: " << result.error();
+        return false;
+    }
     return true;
 }
 
@@ -208,7 +212,7 @@
     auto it = registration_.find(device);
     if (it == registration_.end()) return;
     auto fd = (*it).second;
-    epoll_->UnregisterHandler(fd);
+    epoll_->UnregisterHandler(fd).IgnoreError();
     registration_.erase(it);
     ::close(fd);
 }
@@ -266,7 +270,11 @@
     }
 
     if (inotify_fd_ >= 0) {
-        epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+        if (auto result =
+                    epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+            !result) {
+            LOG(WARNING) << "Could not register keychord epoll handler: " << result.error();
+        }
     }
 }
 
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index c8c47a8..e5a6fd3 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -47,9 +47,9 @@
   public:
     EventHandler();
     EventHandler(const EventHandler&) = delete;
-    EventHandler(EventHandler&&);
+    EventHandler(EventHandler&&) noexcept;
     EventHandler& operator=(const EventHandler&) = delete;
-    EventHandler& operator=(EventHandler&&);
+    EventHandler& operator=(EventHandler&&) noexcept;
     ~EventHandler() noexcept;
 
     bool init();
@@ -64,11 +64,11 @@
 
 EventHandler::EventHandler() : fd_(-1) {}
 
-EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+EventHandler::EventHandler(EventHandler&& rval) noexcept : fd_(rval.fd_) {
     rval.fd_ = -1;
 }
 
-EventHandler& EventHandler::operator=(EventHandler&& rval) {
+EventHandler& EventHandler::operator=(EventHandler&& rval) noexcept {
     fd_ = rval.fd_;
     rval.fd_ = -1;
     return *this;
@@ -213,7 +213,7 @@
 }
 
 void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
-    epoll_.Wait(wait);
+    epoll_.Wait(wait).IgnoreError();
 }
 
 void TestFrame::SetChord(int key, bool value) {
diff --git a/init/parser.cpp b/init/parser.cpp
index fa0fd11..bbfbdc6 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -53,7 +53,12 @@
     int section_start_line = -1;
     std::vector<std::string> args;
 
+    // If we encounter a bad section start, there is no valid parser object to parse the subsequent
+    // sections, so we must suppress errors until the next valid section is found.
+    bool bad_section_found = false;
+
     auto end_section = [&] {
+        bad_section_found = false;
         if (section_parser == nullptr) return;
 
         if (auto result = section_parser->EndSection(); !result) {
@@ -101,6 +106,7 @@
                         parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
+                        bad_section_found = true;
                     }
                 } else if (section_parser) {
                     if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
@@ -108,7 +114,7 @@
                         parse_error_count_++;
                         LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
-                } else {
+                } else if (!bad_section_found) {
                     parse_error_count_++;
                     LOG(ERROR) << filename << ": " << state.line
                                << ": Invalid section keyword found";
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 5c8b92a..5328869 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -111,6 +111,22 @@
         LOG(FATAL) << "Failed to load serialized property info file";
     }
 }
+
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+    const char* target_context = nullptr;
+    property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
+
+    PropertyAuditData audit_data;
+
+    audit_data.name = name.c_str();
+
+    ucred cr = {.pid = 0, .uid = 0, .gid = 0};
+    audit_data.cr = &cr;
+
+    return selinux_check_access(source_context.c_str(), target_context, "file", "read",
+                                &audit_data) == 0;
+}
+
 static bool CheckMacPerms(const std::string& name, const char* target_context,
                           const char* source_context, const ucred& cr) {
     if (!target_context || !source_context) {
@@ -746,7 +762,7 @@
         return;
     }
 
-    int fd = open(rec->blk_device, O_RDONLY);
+    int fd = open(rec->blk_device, O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
         PLOG(ERROR) << "error opening block device " << rec->blk_device;
         return;
diff --git a/init/property_service.h b/init/property_service.h
index cacd987..9022f5a 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,6 +26,8 @@
 namespace android {
 namespace init {
 
+bool CanReadProperty(const std::string& source_context, const std::string& name);
+
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/init/reboot.cpp b/init/reboot.cpp
index b84bfd3..a145797 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -142,7 +142,9 @@
         LOG(WARNING) << "cannot find blank_screen in TurnOffBacklight";
         return;
     }
-    service->Start();
+    if (auto result = service->Start(); !result) {
+        LOG(WARNING) << "Could not start blank_screen service: " << result.error();
+    }
 }
 
 static void ShutdownVold() {
@@ -162,7 +164,7 @@
  */
 static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
                                    std::vector<MountEntry>* emulatedPartitions, bool dump) {
-    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
     if (fp == nullptr) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
         return false;
@@ -307,15 +309,14 @@
 
     auto shutdown_timeout = 0ms;
     if (!SHUTDOWN_ZERO_TIMEOUT) {
-        if (is_thermal_shutdown) {
-            constexpr unsigned int thermal_shutdown_timeout = 1;
-            shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
-        } else {
-            constexpr unsigned int shutdown_timeout_default = 6;
-            auto shutdown_timeout_property = android::base::GetUintProperty(
-                "ro.build.shutdown_timeout", shutdown_timeout_default);
-            shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+        constexpr unsigned int shutdown_timeout_default = 6;
+        constexpr unsigned int max_thermal_shutdown_timeout = 3;
+        auto shutdown_timeout_final = android::base::GetUintProperty("ro.build.shutdown_timeout",
+                                                                     shutdown_timeout_default);
+        if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout) {
+            shutdown_timeout_final = max_thermal_shutdown_timeout;
         }
+        shutdown_timeout = std::chrono::seconds(shutdown_timeout_final);
     }
     LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
 
diff --git a/init/result.h b/init/result.h
index fc03962..0e3fd3d 100644
--- a/init/result.h
+++ b/init/result.h
@@ -151,7 +151,7 @@
 }
 
 template <typename T>
-class Result {
+class [[nodiscard]] Result {
   public:
     Result() {}
 
@@ -170,6 +170,8 @@
         : contents_(std::in_place_index_t<1>(), std::move(result_error.error_string),
                     result_error.error_errno) {}
 
+    void IgnoreError() const {}
+
     bool has_value() const { return contents_.index() == 0; }
 
     T& value() & { return std::get<0>(contents_); }
diff --git a/init/service.cpp b/init/service.cpp
index d20e90a..7f49423 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -60,6 +60,7 @@
 using android::base::GetProperty;
 using android::base::Join;
 using android::base::ParseInt;
+using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::unique_fd;
@@ -369,7 +370,7 @@
         return;
     }
 
-    // If we crash > 4 times in 4 minutes, reboot into recovery.
+    // If we crash > 4 times in 4 minutes, reboot into bootloader.
     boot_clock::time_point now = boot_clock::now();
     if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
         if (now < time_crashed_ + 4min) {
@@ -400,7 +401,7 @@
                   [] (const auto& info) { LOG(INFO) << *info; });
 }
 
-Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
+Result<Success> Service::ParseCapabilities(std::vector<std::string>&& args) {
     capabilities_ = 0;
 
     if (!CapAmbientSupported()) {
@@ -429,29 +430,29 @@
     return Success();
 }
 
-Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
+Result<Success> Service::ParseClass(std::vector<std::string>&& args) {
     classnames_ = std::set<std::string>(args.begin() + 1, args.end());
     return Success();
 }
 
-Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
+Result<Success> Service::ParseConsole(std::vector<std::string>&& args) {
     flags_ |= SVC_CONSOLE;
     console_ = args.size() > 1 ? "/dev/" + args[1] : "";
     return Success();
 }
 
-Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
+Result<Success> Service::ParseCritical(std::vector<std::string>&& args) {
     flags_ |= SVC_CRITICAL;
     return Success();
 }
 
-Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
+Result<Success> Service::ParseDisabled(std::vector<std::string>&& args) {
     flags_ |= SVC_DISABLED;
     flags_ |= SVC_RC_DISABLED;
     return Success();
 }
 
-Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
+Result<Success> Service::ParseEnterNamespace(std::vector<std::string>&& args) {
     if (args[1] != "net") {
         return Error() << "Init only supports entering network namespaces";
     }
@@ -461,11 +462,11 @@
     // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
     // present. Therefore, they also require mount namespaces.
     namespace_flags_ |= CLONE_NEWNS;
-    namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
+    namespaces_to_enter_.emplace_back(CLONE_NEWNET, std::move(args[2]));
     return Success();
 }
 
-Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+Result<Success> Service::ParseGroup(std::vector<std::string>&& args) {
     auto gid = DecodeUid(args[1]);
     if (!gid) {
         return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
@@ -482,7 +483,7 @@
     return Success();
 }
 
-Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
+Result<Success> Service::ParsePriority(std::vector<std::string>&& args) {
     priority_ = 0;
     if (!ParseInt(args[1], &priority_,
                   static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
@@ -493,7 +494,7 @@
     return Success();
 }
 
-Result<Success> Service::ParseInterface(const std::vector<std::string>& args) {
+Result<Success> Service::ParseInterface(std::vector<std::string>&& args) {
     const std::string& interface_name = args[1];
     const std::string& instance_name = args[2];
 
@@ -524,7 +525,7 @@
     return Success();
 }
 
-Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
+Result<Success> Service::ParseIoprio(std::vector<std::string>&& args) {
     if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
         return Error() << "priority value must be range 0 - 7";
     }
@@ -542,36 +543,53 @@
     return Success();
 }
 
-Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
-    for (std::size_t i = 1; i < args.size(); i++) {
+Result<Success> Service::ParseKeycodes(std::vector<std::string>&& args) {
+    auto it = args.begin() + 1;
+    if (args.size() == 2 && StartsWith(args[1], "$")) {
+        std::string expanded;
+        if (!expand_props(args[1], &expanded)) {
+            return Error() << "Could not expand property '" << args[1] << "'";
+        }
+
+        // If the property is not set, it defaults to none, in which case there are no keycodes
+        // for this service.
+        if (expanded == "none") {
+            return Success();
+        }
+
+        args = Split(expanded, ",");
+        it = args.begin();
+    }
+
+    for (; it != args.end(); ++it) {
         int code;
-        if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+        if (ParseInt(*it, &code, 0, KEY_MAX)) {
             for (auto& key : keycodes_) {
-                if (key == code) return Error() << "duplicate keycode: " << args[i];
+                if (key == code) return Error() << "duplicate keycode: " << *it;
             }
             keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
         } else {
-            return Error() << "invalid keycode: " << args[i];
+            return Error() << "invalid keycode: " << *it;
         }
     }
     return Success();
 }
 
-Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
+Result<Success> Service::ParseOneshot(std::vector<std::string>&& args) {
     flags_ |= SVC_ONESHOT;
     return Success();
 }
 
-Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
-    std::vector<std::string> str_args(args.begin() + 1, args.end());
+Result<Success> Service::ParseOnrestart(std::vector<std::string>&& args) {
+    args.erase(args.begin());
     int line = onrestart_.NumCommands() + 1;
-    if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+    if (auto result = onrestart_.AddCommand(std::move(args), line); !result) {
         return Error() << "cannot add Onrestart command: " << result.error();
     }
     return Success();
 }
 
-Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
+Result<Success> Service::ParseNamespace(std::vector<std::string>&& args) {
     for (size_t i = 1; i < args.size(); i++) {
         if (args[i] == "pid") {
             namespace_flags_ |= CLONE_NEWPID;
@@ -586,40 +604,40 @@
     return Success();
 }
 
-Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
+Result<Success> Service::ParseOomScoreAdjust(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
         return Error() << "oom_score_adjust value must be in range -1000 - +1000";
     }
     return Success();
 }
 
-Result<Success> Service::ParseOverride(const std::vector<std::string>& args) {
+Result<Success> Service::ParseOverride(std::vector<std::string>&& args) {
     override_ = true;
     return Success();
 }
 
-Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
+Result<Success> Service::ParseMemcgSwappiness(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &swappiness_, 0)) {
         return Error() << "swappiness value must be equal or greater than 0";
     }
     return Success();
 }
 
-Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
+Result<Success> Service::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
         return Error() << "limit_in_bytes value must be equal or greater than 0";
     }
     return Success();
 }
 
-Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
+Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
         return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
     }
     return Success();
 }
 
-Result<Success> Service::ParseProcessRlimit(const std::vector<std::string>& args) {
+Result<Success> Service::ParseProcessRlimit(std::vector<std::string>&& args) {
     auto rlimit = ParseRlimit(args);
     if (!rlimit) return rlimit.error();
 
@@ -627,22 +645,31 @@
     return Success();
 }
 
-Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
-    seclabel_ = args[1];
+Result<Success> Service::ParseRestartPeriod(std::vector<std::string>&& args) {
+    int period;
+    if (!ParseInt(args[1], &period, 5)) {
+        return Error() << "restart_period value must be an integer >= 5";
+    }
+    restart_period_ = std::chrono::seconds(period);
     return Success();
 }
 
-Result<Success> Service::ParseSigstop(const std::vector<std::string>& args) {
+Result<Success> Service::ParseSeclabel(std::vector<std::string>&& args) {
+    seclabel_ = std::move(args[1]);
+    return Success();
+}
+
+Result<Success> Service::ParseSigstop(std::vector<std::string>&& args) {
     sigstop_ = true;
     return Success();
 }
 
-Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
-    environment_vars_.emplace_back(args[1], args[2]);
+Result<Success> Service::ParseSetenv(std::vector<std::string>&& args) {
+    environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
     return Success();
 }
 
-Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
+Result<Success> Service::ParseShutdown(std::vector<std::string>&& args) {
     if (args[1] == "critical") {
         flags_ |= SVC_SHUTDOWN_CRITICAL;
         return Success();
@@ -650,8 +677,17 @@
     return Error() << "Invalid shutdown option";
 }
 
+Result<Success> Service::ParseTimeoutPeriod(std::vector<std::string>&& args) {
+    int period;
+    if (!ParseInt(args[1], &period, 1)) {
+        return Error() << "timeout_period value must be an integer >= 1";
+    }
+    timeout_period_ = std::chrono::seconds(period);
+    return Success();
+}
+
 template <typename T>
-Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
+Result<Success> Service::AddDescriptor(std::vector<std::string>&& args) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
     Result<uid_t> uid = 0;
     Result<gid_t> gid = 0;
@@ -686,26 +722,26 @@
 }
 
 // name type perm [ uid gid context ]
-Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
+Result<Success> Service::ParseSocket(std::vector<std::string>&& args) {
     if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
         !StartsWith(args[2], "seqpacket")) {
         return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
     }
-    return AddDescriptor<SocketInfo>(args);
+    return AddDescriptor<SocketInfo>(std::move(args));
 }
 
 // name type perm [ uid gid context ]
-Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
+Result<Success> Service::ParseFile(std::vector<std::string>&& args) {
     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
         return Error() << "file type must be 'r', 'w' or 'rw'";
     }
     if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
         return Error() << "file name must not be relative";
     }
-    return AddDescriptor<FileInfo>(args);
+    return AddDescriptor<FileInfo>(std::move(args));
 }
 
-Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+Result<Success> Service::ParseUser(std::vector<std::string>&& args) {
     auto uid = DecodeUid(args[1]);
     if (!uid) {
         return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
@@ -714,8 +750,9 @@
     return Success();
 }
 
-Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
-    writepid_files_.assign(args.begin() + 1, args.end());
+Result<Success> Service::ParseWritepid(std::vector<std::string>&& args) {
+    args.erase(args.begin());
+    writepid_files_ = std::move(args);
     return Success();
 }
 
@@ -757,12 +794,16 @@
                         {1,     1,    &Service::ParseOomScoreAdjust}},
         {"override",    {0,     0,    &Service::ParseOverride}},
         {"priority",    {1,     1,    &Service::ParsePriority}},
+        {"restart_period",
+                        {1,     1,    &Service::ParseRestartPeriod}},
         {"rlimit",      {3,     3,    &Service::ParseProcessRlimit}},
         {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
         {"setenv",      {2,     2,    &Service::ParseSetenv}},
         {"shutdown",    {1,     1,    &Service::ParseShutdown}},
         {"sigstop",     {0,     0,    &Service::ParseSigstop}},
         {"socket",      {3,     6,    &Service::ParseSocket}},
+        {"timeout_period",
+                        {1,     1,    &Service::ParseTimeoutPeriod}},
         {"user",        {1,     1,    &Service::ParseUser}},
         {"writepid",    {1,     kMax, &Service::ParseWritepid}},
     };
@@ -770,13 +811,13 @@
     return option_parsers;
 }
 
-Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
+Result<Success> Service::ParseLine(std::vector<std::string>&& args) {
     static const OptionParserMap parser_map;
     auto parser = parser_map.FindFunction(args);
 
     if (!parser) return parser.error();
 
-    return std::invoke(*parser, this, args);
+    return std::invoke(*parser, this, std::move(args));
 }
 
 Result<Success> Service::ExecStart() {
@@ -1022,6 +1063,18 @@
     }
 }
 
+void Service::Timeout() {
+    // All process state flags will be taken care of in Reap(), we really just want to kill the
+    // process here when it times out.  Oneshot processes will transition to be disabled, and
+    // all other processes will transition to be restarting.
+    LOG(INFO) << "Service '" << name_ << "' expired its timeout of " << timeout_period_->count()
+              << " seconds and will now be killed";
+    if (pid_) {
+        KillProcessGroup(SIGKILL);
+        NotifyStateChange("stopping");
+    }
+}
+
 void Service::Restart() {
     if (flags_ & SVC_RUNNING) {
         /* Stop, wait, then start the service. */
diff --git a/init/service.h b/init/service.h
index ea79a07..c7beee9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -21,7 +21,9 @@
 #include <sys/resource.h>
 #include <sys/types.h>
 
+#include <chrono>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -41,7 +43,7 @@
 #define SVC_RUNNING 0x004         // currently active
 #define SVC_RESTARTING 0x008      // waiting to restart
 #define SVC_CONSOLE 0x010         // requires console
-#define SVC_CRITICAL 0x020        // will reboot into recovery if keeps crashing
+#define SVC_CRITICAL 0x020        // will reboot into bootloader if keeps crashing
 #define SVC_RESET 0x040           // Use when stopping a process,
                                   // but not disabling so it can be restarted with its class.
 #define SVC_RC_DISABLED 0x080     // Remember if the disabled flag was set in the rc script.
@@ -73,7 +75,7 @@
     static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
-    Result<Success> ParseLine(const std::vector<std::string>& args);
+    Result<Success> ParseLine(std::vector<std::string>&& args);
     Result<Success> ExecStart();
     Result<Success> Start();
     Result<Success> StartIfNotDisabled();
@@ -81,6 +83,7 @@
     void Reset();
     void Stop();
     void Terminate();
+    void Timeout();
     void Restart();
     void Reap(const siginfo_t& siginfo);
     void DumpState() const;
@@ -117,10 +120,12 @@
     bool process_cgroup_empty() const { return process_cgroup_empty_; }
     unsigned long start_order() const { return start_order_; }
     void set_sigstop(bool value) { sigstop_ = value; }
+    std::chrono::seconds restart_period() const { return restart_period_; }
+    std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
     const std::vector<std::string>& args() const { return args_; }
 
   private:
-    using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
+    using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
     class OptionParserMap;
 
     Result<Success> SetUpMountNamespace() const;
@@ -133,37 +138,39 @@
     void KillProcessGroup(int signal);
     void SetProcessAttributes();
 
-    Result<Success> ParseCapabilities(const std::vector<std::string>& args);
-    Result<Success> ParseClass(const std::vector<std::string>& args);
-    Result<Success> ParseConsole(const std::vector<std::string>& args);
-    Result<Success> ParseCritical(const std::vector<std::string>& args);
-    Result<Success> ParseDisabled(const std::vector<std::string>& args);
-    Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
-    Result<Success> ParseGroup(const std::vector<std::string>& args);
-    Result<Success> ParsePriority(const std::vector<std::string>& args);
-    Result<Success> ParseInterface(const std::vector<std::string>& args);
-    Result<Success> ParseIoprio(const std::vector<std::string>& args);
-    Result<Success> ParseKeycodes(const std::vector<std::string>& args);
-    Result<Success> ParseOneshot(const std::vector<std::string>& args);
-    Result<Success> ParseOnrestart(const std::vector<std::string>& args);
-    Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
-    Result<Success> ParseOverride(const std::vector<std::string>& args);
-    Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
-    Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
-    Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
-    Result<Success> ParseNamespace(const std::vector<std::string>& args);
-    Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
-    Result<Success> ParseSeclabel(const std::vector<std::string>& args);
-    Result<Success> ParseSetenv(const std::vector<std::string>& args);
-    Result<Success> ParseShutdown(const std::vector<std::string>& args);
-    Result<Success> ParseSigstop(const std::vector<std::string>& args);
-    Result<Success> ParseSocket(const std::vector<std::string>& args);
-    Result<Success> ParseFile(const std::vector<std::string>& args);
-    Result<Success> ParseUser(const std::vector<std::string>& args);
-    Result<Success> ParseWritepid(const std::vector<std::string>& args);
+    Result<Success> ParseCapabilities(std::vector<std::string>&& args);
+    Result<Success> ParseClass(std::vector<std::string>&& args);
+    Result<Success> ParseConsole(std::vector<std::string>&& args);
+    Result<Success> ParseCritical(std::vector<std::string>&& args);
+    Result<Success> ParseDisabled(std::vector<std::string>&& args);
+    Result<Success> ParseEnterNamespace(std::vector<std::string>&& args);
+    Result<Success> ParseGroup(std::vector<std::string>&& args);
+    Result<Success> ParsePriority(std::vector<std::string>&& args);
+    Result<Success> ParseInterface(std::vector<std::string>&& args);
+    Result<Success> ParseIoprio(std::vector<std::string>&& args);
+    Result<Success> ParseKeycodes(std::vector<std::string>&& args);
+    Result<Success> ParseOneshot(std::vector<std::string>&& args);
+    Result<Success> ParseOnrestart(std::vector<std::string>&& args);
+    Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args);
+    Result<Success> ParseOverride(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args);
+    Result<Success> ParseNamespace(std::vector<std::string>&& args);
+    Result<Success> ParseProcessRlimit(std::vector<std::string>&& args);
+    Result<Success> ParseRestartPeriod(std::vector<std::string>&& args);
+    Result<Success> ParseSeclabel(std::vector<std::string>&& args);
+    Result<Success> ParseSetenv(std::vector<std::string>&& args);
+    Result<Success> ParseShutdown(std::vector<std::string>&& args);
+    Result<Success> ParseSigstop(std::vector<std::string>&& args);
+    Result<Success> ParseSocket(std::vector<std::string>&& args);
+    Result<Success> ParseTimeoutPeriod(std::vector<std::string>&& args);
+    Result<Success> ParseFile(std::vector<std::string>&& args);
+    Result<Success> ParseUser(std::vector<std::string>&& args);
+    Result<Success> ParseWritepid(std::vector<std::string>&& args);
 
     template <typename T>
-    Result<Success> AddDescriptor(const std::vector<std::string>& args);
+    Result<Success> AddDescriptor(std::vector<std::string>&& args);
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
@@ -220,6 +227,9 @@
 
     bool sigstop_ = false;
 
+    std::chrono::seconds restart_period_ = 5s;
+    std::optional<std::chrono::seconds> timeout_period_;
+
     std::vector<std::string> args_;
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
diff --git a/init/stable_properties.h b/init/stable_properties.h
deleted file mode 100644
index 4972d10..0000000
--- a/init/stable_properties.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _INIT_STABLE_PROPERTIES_H
-#define _INIT_STABLE_PROPERTIES_H
-
-#include <set>
-#include <string>
-
-namespace android {
-namespace init {
-
-static constexpr const char* kPartnerPrefixes[] = {
-    "init.svc.vendor.", "ro.vendor.", "persist.vendor.", "vendor.", "init.svc.odm.", "ro.odm.",
-    "persist.odm.",     "odm.",       "ro.boot.",
-};
-
-static const std::set<std::string> kExportedActionableProperties = {
-    "dev.bootcomplete",
-    "init.svc.console",
-    "init.svc.mediadrm",
-    "init.svc.surfaceflinger",
-    "init.svc.zygote",
-    "persist.bluetooth.btsnoopenable",
-    "persist.sys.crash_rcu",
-    "persist.sys.usb.usbradio.config",
-    "persist.sys.zram_enabled",
-    "ro.board.platform",
-    "ro.bootmode",
-    "ro.build.type",
-    "ro.crypto.state",
-    "ro.crypto.type",
-    "ro.debuggable",
-    "sys.boot_completed",
-    "sys.boot_from_charger_mode",
-    "sys.retaildemo.enabled",
-    "sys.shutdown.requested",
-    "sys.usb.config",
-    "sys.usb.configfs",
-    "sys.usb.ffs.mtp.ready",
-    "sys.usb.ffs.ready",
-    "sys.user.0.ce_available",
-    "sys.vdso",
-    "vold.decrypt",
-    "vold.post_fs_data_done",
-    "vts.native_server.on",
-    "wlan.driver.status",
-};
-
-}  // namespace init
-}  // namespace android
-
-#endif
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index 6307993..eae03e3 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -39,7 +39,7 @@
     free(context);
 
     while (state.KeepRunning()) {
-        subcontext.Execute(std::vector<std::string>{"return_success"});
+        subcontext.Execute(std::vector<std::string>{"return_success"}).IgnoreError();
     }
 
     if (subcontext.pid() > 0) {
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 95be6af..66491dd 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -175,7 +175,7 @@
     //
     // When a subprocess crashes, we fatally abort from ueventd.  init will restart ueventd when
     // init reaps it, and the cold boot process will start again.  If this continues to fail, then
-    // since ueventd is marked as a critical service, init will reboot to recovery.
+    // since ueventd is marked as a critical service, init will reboot to bootloader.
     //
     // When a subprocess gets stuck, keep ueventd spinning waiting for it.  init has a timeout for
     // cold boot and will reboot to the bootloader if ueventd does not complete in time.
diff --git a/init/util.cpp b/init/util.cpp
index 105ac87..3781141 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -436,5 +436,21 @@
     return true;
 }
 
+void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function) {
+    // Make stdin/stdout/stderr all point to /dev/null.
+    int fd = open("/dev/null", O_RDWR);
+    if (fd == -1) {
+        int saved_errno = errno;
+        android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+        errno = saved_errno;
+        PLOG(FATAL) << "Couldn't open /dev/null";
+    }
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    if (fd > 2) close(fd);
+    android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index 07e4864..53f4547 100644
--- a/init/util.h
+++ b/init/util.h
@@ -64,6 +64,8 @@
 
 bool IsLegalPropertyName(const std::string& name);
 
+void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function);
+
 }  // namespace init
 }  // namespace android
 
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index c42ae49..7f9a18a 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -27,15 +27,6 @@
             enabled: false,
         },
     },
-
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
-        },
-    },
 }
 
 libbacktrace_sources = [
@@ -108,7 +99,7 @@
     whole_static_libs: ["libdemangle"],
 }
 
-cc_library_shared {
+cc_test_library {
     name: "libbacktrace_test",
     defaults: ["libbacktrace_common"],
     host_supported: true,
@@ -121,6 +112,17 @@
     shared_libs: [
         "libunwindstack",
     ],
+    relative_install_path: "backtrace_test_libs",
+
+    target: {
+        linux_glibc: {
+            // This forces the creation of eh_frame with unwind information
+            // for host.
+            cflags: [
+                "-fcxx-exceptions"
+            ],
+        },
+    },
 }
 
 //-------------------------------------------------------------------------
@@ -128,12 +130,12 @@
 //-------------------------------------------------------------------------
 cc_test {
     name: "backtrace_test",
+    isolated: true,
     defaults: ["libbacktrace_common"],
     host_supported: true,
     srcs: [
         "backtrace_offline_test.cpp",
         "backtrace_test.cpp",
-        "GetPss.cpp",
     ],
 
     cflags: [
@@ -143,7 +145,6 @@
     ],
 
     shared_libs: [
-        "libbacktrace_test",
         "libbacktrace",
         "libbase",
         "liblog",
@@ -152,17 +153,10 @@
 
     group_static_libs: true,
 
-    target: {
-        android: {
-            cflags: ["-DENABLE_PSS_TESTS"],
-            shared_libs: [
-                "libutils",
-            ],
-        },
-        linux_glibc: {
-            static_libs: ["libutils"],
-        },
-    },
+    // So that the dlopen can find the libbacktrace_test.so.
+    ldflags: [
+        "-Wl,--rpath,${ORIGIN}/../backtrace_test_libs",
+    ],
 
     test_suites: ["device-tests"],
     data: [
diff --git a/libbacktrace/BacktraceTest.h b/libbacktrace/BacktraceTest.h
new file mode 100644
index 0000000..c38af04
--- /dev/null
+++ b/libbacktrace/BacktraceTest.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_TEST_H
+#define _LIBBACKTRACE_BACKTRACE_TEST_H
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+class BacktraceTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    dl_handle_ = dlopen("libbacktrace_test.so", RTLD_NOW | RTLD_LOCAL);
+
+    test_level_one_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_one"));
+
+    test_level_two_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_two"));
+
+    test_level_three_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_three"));
+
+    test_level_four_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_level_four"));
+
+    test_recursive_call_ = reinterpret_cast<int (*)(int, void (*)(void*), void*)>(
+        dlsym(dl_handle_, "test_recursive_call"));
+
+    test_get_context_and_wait_ = reinterpret_cast<void (*)(void*, volatile int*)>(
+        dlsym(dl_handle_, "test_get_context_and_wait"));
+
+    test_signal_action_ =
+        reinterpret_cast<void (*)(int, siginfo_t*, void*)>(dlsym(dl_handle_, "test_signal_action"));
+
+    test_signal_handler_ =
+        reinterpret_cast<void (*)(int)>(dlsym(dl_handle_, "test_signal_handler"));
+  }
+
+  void SetUp() override {
+    ASSERT_TRUE(dl_handle_ != nullptr);
+    ASSERT_TRUE(test_level_one_ != nullptr);
+    ASSERT_TRUE(test_level_two_ != nullptr);
+    ASSERT_TRUE(test_level_three_ != nullptr);
+    ASSERT_TRUE(test_level_four_ != nullptr);
+    ASSERT_TRUE(test_recursive_call_ != nullptr);
+    ASSERT_TRUE(test_get_context_and_wait_ != nullptr);
+    ASSERT_TRUE(test_signal_action_ != nullptr);
+    ASSERT_TRUE(test_signal_handler_ != nullptr);
+  }
+
+ public:
+  static void* dl_handle_;
+  static int (*test_level_one_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_two_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_three_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_level_four_)(int, int, int, int, void (*)(void*), void*);
+  static int (*test_recursive_call_)(int, void (*)(void*), void*);
+  static void (*test_get_context_and_wait_)(void*, volatile int*);
+  static void (*test_signal_action_)(int, siginfo_t*, void*);
+  static void (*test_signal_handler_)(int);
+};
+
+#endif  // _LIBBACKTRACE_BACKTRACE_TEST_H
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
deleted file mode 100644
index 6d750ea..0000000
--- a/libbacktrace/GetPss.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-// This is an extremely simplified version of libpagemap.
-
-#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
-
-#define PAGEMAP_PRESENT(x)     (_BITS(x, 63, 1))
-#define PAGEMAP_SWAPPED(x)     (_BITS(x, 62, 1))
-#define PAGEMAP_SHIFT(x)       (_BITS(x, 55, 6))
-#define PAGEMAP_PFN(x)         (_BITS(x, 0, 55))
-#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
-#define PAGEMAP_SWAP_TYPE(x)   (_BITS(x, 0,  5))
-
-static bool ReadData(int fd, off_t place, uint64_t *data) {
-  if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
-    return false;
-  }
-  if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
-    return false;
-  }
-  return true;
-}
-
-size_t GetPssBytes() {
-  FILE* maps = fopen("/proc/self/maps", "r");
-  if (maps == nullptr) {
-    return 0;
-  }
-
-  int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
-  if (pagecount_fd == -1) {
-    fclose(maps);
-    return 0;
-  }
-
-  int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
-  if (pagemap_fd == -1) {
-    fclose(maps);
-    close(pagecount_fd);
-    return 0;
-  }
-
-  char line[4096];
-  size_t total_pss = 0;
-  int pagesize = getpagesize();
-  while (fgets(line, sizeof(line), maps)) {
-    uintptr_t start, end;
-    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
-      total_pss = 0;
-      break;
-    }
-    for (off_t page = static_cast<off_t>(start/pagesize);
-         page < static_cast<off_t>(end/pagesize); page++) {
-      uint64_t data;
-      if (ReadData(pagemap_fd, page, &data)) {
-        if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
-          uint64_t count;
-          if (ReadData(pagecount_fd, static_cast<off_t>(PAGEMAP_PFN(data)), &count)) {
-            total_pss += (count >= 1) ? pagesize / count : 0;
-          }
-        }
-      }
-    }
-  }
-
-  fclose(maps);
-
-  close(pagecount_fd);
-  close(pagemap_fd);
-
-  return total_pss;
-}
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 4e7f761..fe28eba 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -49,6 +49,7 @@
   unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
                                  regs, stack_map->process_memory());
   unwinder.SetResolveNames(stack_map->ResolveNames());
+  stack_map->SetArch(regs->Arch());
   if (stack_map->GetJitDebug() != nullptr) {
     unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
   }
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index d2d6ab8..9d15af2 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -25,6 +25,7 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
 
 #include "UnwindStackMap.h"
 
@@ -106,7 +107,17 @@
     return "";
   }
 
-  unwindstack::Elf* elf = map_info->GetElf(process_memory(), true);
+  if (arch_ == unwindstack::ARCH_UNKNOWN) {
+    if (pid_ == getpid()) {
+      arch_ = unwindstack::Regs::CurrentArch();
+    } else {
+      // Create a remote regs, to figure out the architecture.
+      std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid_));
+      arch_ = regs->Arch();
+    }
+  }
+
+  unwindstack::Elf* elf = map_info->GetElf(process_memory(), arch_);
 
   std::string name;
   uint64_t func_offset;
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 039f4a2..e19b605 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -30,6 +30,7 @@
 #if !defined(NO_LIBDEXFILE_SUPPORT)
 #include <unwindstack/DexFiles.h>
 #endif
+#include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
 
@@ -58,6 +59,8 @@
   unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
 #endif
 
+  void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
+
  protected:
   uint64_t GetLoadBias(size_t index) override;
 
@@ -67,6 +70,8 @@
 #if !defined(NO_LIBDEXFILE_SUPPORT)
   std::unique_ptr<unwindstack::DexFiles> dex_files_;
 #endif
+
+  unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
 };
 
 class UnwindStackOfflineMap : public UnwindStackMap {
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 7d1027e..662fb99 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -37,15 +37,7 @@
 
 #include <gtest/gtest.h>
 
-extern "C" {
-// Prototypes for functions in the test library.
-int test_level_one(int, int, int, int, void (*)(void*), void*);
-int test_level_two(int, int, int, int, void (*)(void*), void*);
-int test_level_three(int, int, int, int, void (*)(void*), void*);
-int test_level_four(int, int, int, int, void (*)(void*), void*);
-int test_recursive_call(int, void (*)(void*), void*);
-void test_get_context_and_wait(void* context, volatile int* exit_flag);
-}
+#include "BacktraceTest.h"
 
 struct FunctionSymbol {
   std::string name;
@@ -56,12 +48,13 @@
 static std::vector<FunctionSymbol> GetFunctionSymbols() {
   std::vector<FunctionSymbol> symbols = {
       {"unknown_start", 0, 0},
-      {"test_level_one", reinterpret_cast<uint64_t>(&test_level_one), 0},
-      {"test_level_two", reinterpret_cast<uint64_t>(&test_level_two), 0},
-      {"test_level_three", reinterpret_cast<uint64_t>(&test_level_three), 0},
-      {"test_level_four", reinterpret_cast<uint64_t>(&test_level_four), 0},
-      {"test_recursive_call", reinterpret_cast<uint64_t>(&test_recursive_call), 0},
-      {"test_get_context_and_wait", reinterpret_cast<uint64_t>(&test_get_context_and_wait), 0},
+      {"test_level_one", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_one_), 0},
+      {"test_level_two", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_two_), 0},
+      {"test_level_three", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_three_), 0},
+      {"test_level_four", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_four_), 0},
+      {"test_recursive_call", reinterpret_cast<uint64_t>(&BacktraceTest::test_recursive_call_), 0},
+      {"test_get_context_and_wait",
+       reinterpret_cast<uint64_t>(&BacktraceTest::test_get_context_and_wait_), 0},
       {"unknown_end", static_cast<uint64_t>(-1), static_cast<uint64_t>(-1)},
   };
   std::sort(
@@ -100,7 +93,7 @@
 static void* OfflineThreadFunc(void* arg) {
   OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
   fn_arg->tid = android::base::GetThreadId();
-  test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
+  BacktraceTest::test_get_context_and_wait_(&fn_arg->ucontext, &fn_arg->exit_flag);
   return nullptr;
 }
 
@@ -109,7 +102,7 @@
 }
 
 // This test is disable because it is for generating test data.
-TEST(libbacktrace, DISABLED_generate_offline_testdata) {
+TEST_F(BacktraceTest, DISABLED_generate_offline_testdata) {
   // Create a thread to generate the needed stack and registers information.
   const size_t stack_size = 16 * 1024;
   void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
@@ -304,22 +297,22 @@
 }
 
 // For now, these tests can only run on the given architectures.
-TEST(libbacktrace, offline_eh_frame) {
+TEST_F(BacktraceTest, offline_eh_frame) {
   BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
   BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
 }
 
-TEST(libbacktrace, offline_debug_frame) {
+TEST_F(BacktraceTest, offline_debug_frame) {
   BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
   BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
 }
 
-TEST(libbacktrace, offline_gnu_debugdata) {
+TEST_F(BacktraceTest, offline_gnu_debugdata) {
   BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
   BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
 }
 
-TEST(libbacktrace, offline_arm_exidx) {
+TEST_F(BacktraceTest, offline_arm_exidx) {
   BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
 }
 
@@ -373,32 +366,32 @@
 
 // This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
 // overlap with each other, which appears in /system/lib/libart.so.
-TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
+TEST_F(BacktraceTest, offline_unwind_mix_eh_frame_and_arm_exidx) {
   LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
 }
 
-TEST(libbacktrace, offline_debug_frame_with_load_bias) {
+TEST_F(BacktraceTest, offline_debug_frame_with_load_bias) {
   LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
 }
 
-TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
+TEST_F(BacktraceTest, offline_try_armexidx_after_debug_frame) {
   LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
 }
 
-TEST(libbacktrace, offline_cie_with_P_augmentation) {
+TEST_F(BacktraceTest, offline_cie_with_P_augmentation) {
   // Make sure we can unwind through functions with CIE entry containing P augmentation, which
   // makes unwinding library reading personality handler from memory. One example is
   // /system/lib64/libskia.so.
   LibUnwindingTest("arm64", "offline_testdata_for_libskia", "libskia.so");
 }
 
-TEST(libbacktrace, offline_empty_eh_frame_hdr) {
+TEST_F(BacktraceTest, offline_empty_eh_frame_hdr) {
   // Make sure we can unwind through libraries with empty .eh_frame_hdr section. One example is
   // /vendor/lib64/egl/eglSubDriverAndroid.so.
   LibUnwindingTest("arm64", "offline_testdata_for_eglSubDriverAndroid", "eglSubDriverAndroid.so");
 }
 
-TEST(libbacktrace, offline_max_frames_limit) {
+TEST_F(BacktraceTest, offline_max_frames_limit) {
   // The length of callchain can reach 256 when recording an application.
   ASSERT_GE(MAX_BACKTRACE_FRAMES, 256);
 }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 06a32c7..f4191b9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <malloc.h>
 #include <pthread.h>
 #include <signal.h>
 #include <stdint.h>
@@ -55,6 +56,7 @@
 
 // For the THREAD_SIGNAL definition.
 #include "BacktraceCurrent.h"
+#include "BacktraceTest.h"
 #include "backtrace_testlib.h"
 
 // Number of microseconds per milliseconds.
@@ -95,6 +97,23 @@
 static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
                           map_create_func_t map_func = nullptr);
 
+void* BacktraceTest::dl_handle_;
+int (*BacktraceTest::test_level_one_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_two_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_three_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_four_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_recursive_call_)(int, void (*)(void*), void*);
+void (*BacktraceTest::test_get_context_and_wait_)(void*, volatile int*);
+void (*BacktraceTest::test_signal_action_)(int, siginfo_t*, void*);
+void (*BacktraceTest::test_signal_handler_)(int);
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+  static const char* initial_args[] = {"--slow_threshold_ms=8000", "--deadline_threshold_ms=15000"};
+  *args = initial_args;
+  *num_args = 2;
+  return true;
+}
+
 static uint64_t NanoTime() {
   struct timespec t = { 0, 0 };
   clock_gettime(CLOCK_MONOTONIC, &t);
@@ -250,7 +269,7 @@
   return false;
 }
 
-TEST(libbacktrace, local_no_unwind_frames) {
+TEST_F(BacktraceTest, local_no_unwind_frames) {
   // Verify that a local unwind does not include any frames within
   // libunwind or libbacktrace.
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -270,7 +289,7 @@
   }
 }
 
-TEST(libbacktrace, local_unwind_frames) {
+TEST_F(BacktraceTest, local_unwind_frames) {
   // Verify that a local unwind with the skip frames disabled does include
   // frames within the backtrace libraries.
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -302,8 +321,8 @@
                                                << DumpFrames(backtrace.get());
 }
 
-TEST(libbacktrace, local_trace) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_trace) {
+  ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
 }
 
 static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
@@ -357,12 +376,12 @@
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
 }
 
-TEST(libbacktrace, local_trace_ignore_frames) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
+TEST_F(BacktraceTest, local_trace_ignore_frames) {
+  ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
 }
 
-TEST(libbacktrace, local_max_trace) {
-  ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_max_trace) {
+  ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxBacktrace, nullptr), 0);
 }
 
 static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
@@ -402,10 +421,10 @@
   ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
 }
 
-TEST(libbacktrace, ptrace_trace) {
+TEST_F(BacktraceTest, ptrace_trace) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
@@ -416,10 +435,10 @@
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-TEST(libbacktrace, ptrace_max_trace) {
+TEST_F(BacktraceTest, ptrace_max_trace) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
+    ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
@@ -446,10 +465,10 @@
   VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
 }
 
-TEST(libbacktrace, ptrace_ignore_frames) {
+TEST_F(BacktraceTest, ptrace_ignore_frames) {
   pid_t pid;
   if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
@@ -462,7 +481,7 @@
 
 // Create a process with multiple threads and dump all of the threads.
 static void* PtraceThreadLevelRun(void*) {
-  EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+  EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
   return nullptr;
 }
 
@@ -483,7 +502,7 @@
   }
 }
 
-TEST(libbacktrace, ptrace_threads) {
+TEST_F(BacktraceTest, ptrace_threads) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
@@ -494,7 +513,7 @@
       pthread_t thread;
       ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
     }
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+    ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
 
@@ -532,8 +551,8 @@
   VerifyLevelDump(backtrace.get());
 }
 
-TEST(libbacktrace, thread_current_level) {
-  ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_level) {
+  ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
 }
 
 static void VerifyMaxThread(void*) {
@@ -545,19 +564,19 @@
   VerifyMaxDump(backtrace.get());
 }
 
-TEST(libbacktrace, thread_current_max) {
-  ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_max) {
+  ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxThread, nullptr), 0);
 }
 
 static void* ThreadLevelRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
   thread->tid = android::base::GetThreadId();
-  EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
+  EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, ThreadSetState, data), 0);
   return nullptr;
 }
 
-TEST(libbacktrace, thread_level_trace) {
+TEST_F(BacktraceTest, thread_level_trace) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -607,7 +626,7 @@
   EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
 }
 
-TEST(libbacktrace, thread_ignore_frames) {
+TEST_F(BacktraceTest, thread_ignore_frames) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -644,11 +663,12 @@
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
   thread->tid = android::base::GetThreadId();
-  EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
+  EXPECT_NE(BacktraceTest::test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, ThreadSetState, data),
+            0);
   return nullptr;
 }
 
-TEST(libbacktrace, thread_max_trace) {
+TEST_F(BacktraceTest, thread_max_trace) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -742,17 +762,17 @@
   }
 }
 
-TEST(libbacktrace, thread_multiple_dump) {
+TEST_F(BacktraceTest, thread_multiple_dump) {
   MultipleThreadDumpTest(false);
 }
 
-TEST(libbacktrace, thread_multiple_dump_same_map) {
+TEST_F(BacktraceTest, thread_multiple_dump_same_map) {
   MultipleThreadDumpTest(true);
 }
 
 // This test is for UnwindMaps that should share the same map cursor when
 // multiple maps are created for the current process at the same time.
-TEST(libbacktrace, simultaneous_maps) {
+TEST_F(BacktraceTest, simultaneous_maps) {
   BacktraceMap* map1 = BacktraceMap::Create(getpid());
   BacktraceMap* map2 = BacktraceMap::Create(getpid());
   BacktraceMap* map3 = BacktraceMap::Create(getpid());
@@ -779,7 +799,7 @@
   delete map3;
 }
 
-TEST(libbacktrace, fillin_erases) {
+TEST_F(BacktraceTest, fillin_erases) {
   BacktraceMap* back_map = BacktraceMap::Create(getpid());
 
   backtrace_map_t map;
@@ -798,7 +818,7 @@
   ASSERT_EQ("", map.name);
 }
 
-TEST(libbacktrace, format_test) {
+TEST_F(BacktraceTest, format_test) {
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
 
@@ -969,7 +989,7 @@
   ASSERT_TRUE(test_it == test_maps.end());
 }
 
-TEST(libbacktrace, verify_map_remote) {
+TEST_F(BacktraceTest, verify_map_remote) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -1069,7 +1089,7 @@
   delete[] expected;
 }
 
-TEST(libbacktrace, thread_read) {
+TEST_F(BacktraceTest, thread_read) {
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -1120,7 +1140,7 @@
   }
 }
 
-TEST(libbacktrace, process_read) {
+TEST_F(BacktraceTest, process_read) {
   g_ready = 0;
   pid_t pid;
   if ((pid = fork()) == 0) {
@@ -1187,29 +1207,23 @@
 }
 
 static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
-  std::string system_dir;
-
-#if defined(__BIONIC__)
-  system_dir = "/system/lib";
-#else
-  const char* host_out_env = getenv("ANDROID_HOST_OUT");
-  ASSERT_TRUE(host_out_env != nullptr);
-  system_dir = std::string(host_out_env) + "/lib";
-#endif
-
-#if defined(__LP64__)
-  system_dir += "64";
-#endif
+  std::string test_lib(testing::internal::GetArgvs()[0]);
+  auto const value = test_lib.find_last_of('/');
+  if (value == std::string::npos) {
+    test_lib = "../backtrace_test_libs/";
+  } else {
+    test_lib = test_lib.substr(0, value + 1) + "../backtrace_test_libs/";
+  }
+  test_lib += "libbacktrace_test.so";
 
   *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
-  std::string cp_cmd =
-      android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+  std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
 
   // Copy the shared so to a tempory directory.
   ASSERT_EQ(0, system(cp_cmd.c_str()));
 }
 
-TEST(libbacktrace, check_unreadable_elf_local) {
+TEST_F(BacktraceTest, check_unreadable_elf_local) {
   TemporaryDir td;
   std::string tmp_so_name;
   ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1251,7 +1265,7 @@
   VerifyFunctionsFound(found_functions);
 }
 
-TEST(libbacktrace, check_unreadable_elf_remote) {
+TEST_F(BacktraceTest, check_unreadable_elf_remote) {
   TemporaryDir td;
   std::string tmp_so_name;
   ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1390,7 +1404,7 @@
 
 typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
 
-TEST(libbacktrace, unwind_through_unreadable_elf_local) {
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_local) {
   TemporaryDir td;
   std::string tmp_so_name;
   ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1405,11 +1419,9 @@
 
   ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace, reinterpret_cast<void*>(test_func)),
             0);
-
-  ASSERT_TRUE(dlclose(lib_handle) == 0);
 }
 
-TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_remote) {
   TemporaryDir td;
   std::string tmp_so_name;
   ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1428,7 +1440,6 @@
     exit(0);
   }
   ASSERT_TRUE(pid > 0);
-  ASSERT_TRUE(dlclose(lib_handle) == 0);
 
   uint64_t start = NanoTime();
   bool done = false;
@@ -1465,7 +1476,7 @@
   ASSERT_TRUE(done) << "Test function never found in unwind.";
 }
 
-TEST(libbacktrace, unwind_thread_doesnt_exist) {
+TEST_F(BacktraceTest, unwind_thread_doesnt_exist) {
   std::unique_ptr<Backtrace> backtrace(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
   ASSERT_TRUE(backtrace.get() != nullptr);
@@ -1473,18 +1484,18 @@
   ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
 }
 
-TEST(libbacktrace, local_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, local_get_function_name_before_unwind) {
   std::unique_ptr<Backtrace> backtrace(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
 
   // Verify that trying to get a function name before doing an unwind works.
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
   uint64_t offset;
   ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
 }
 
-TEST(libbacktrace, remote_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, remote_get_function_name_before_unwind) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -1492,7 +1503,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
 
   // Verify that trying to get a function name before doing an unwind works.
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
   uint64_t offset;
   ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
 
@@ -1579,7 +1590,7 @@
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
 
-  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(BacktraceTest::test_level_one_) + 1;
   // Now verify the device map flag actually causes the function name to be empty.
   backtrace->FillInMap(cur_func_offset, &map);
   ASSERT_TRUE((map.flags & PROT_DEVICE_MAP) == 0);
@@ -1628,7 +1639,7 @@
   ASSERT_EQ(0U, backtrace->NumFrames());
 }
 
-TEST(libbacktrace, unwind_disallow_device_map_local) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_local) {
   void* device_map;
   SetupDeviceMap(&device_map);
 
@@ -1642,7 +1653,7 @@
   munmap(device_map, DEVICE_MAP_SIZE);
 }
 
-TEST(libbacktrace, unwind_disallow_device_map_remote) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_remote) {
   void* device_map;
   SetupDeviceMap(&device_map);
 
@@ -1698,13 +1709,13 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     if (use_action) {
-      ScopedSignalHandler ssh(SIGUSR1, test_signal_action);
+      ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_action_);
 
-      test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+      BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
     } else {
-      ScopedSignalHandler ssh(SIGUSR1, test_signal_handler);
+      ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_handler_);
 
-      test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+      BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
     }
   }
   ASSERT_NE(-1, pid);
@@ -1805,11 +1816,11 @@
   FinishRemoteProcess(pid);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_handler) {
   UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_action) {
   UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
 }
 
@@ -1822,49 +1833,41 @@
   ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
 }
 
-TEST(libbacktrace, unwind_frame_skip_numbering) {
+TEST_F(BacktraceTest, unwind_frame_skip_numbering) {
   TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
 }
 
-#if defined(ENABLE_PSS_TESTS)
-#include "GetPss.h"
-
 #define MAX_LEAK_BYTES (32*1024UL)
 
 static void CheckForLeak(pid_t pid, pid_t tid) {
   std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
 
-  // Do a few runs to get the PSS stable.
-  for (size_t i = 0; i < 100; i++) {
-    Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
-    ASSERT_TRUE(backtrace != nullptr);
-    ASSERT_TRUE(backtrace->Unwind(0));
-    VERIFY_NO_ERROR(backtrace->GetError().error_code);
-    delete backtrace;
-  }
-  size_t stable_pss = GetPssBytes();
-  ASSERT_TRUE(stable_pss != 0);
-
   // Loop enough that even a small leak should be detectable.
+  size_t first_allocated_bytes = 0;
+  size_t last_allocated_bytes = 0;
   for (size_t i = 0; i < 4096; i++) {
     Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
     VERIFY_NO_ERROR(backtrace->GetError().error_code);
     delete backtrace;
-  }
-  size_t new_pss = GetPssBytes();
-  ASSERT_TRUE(new_pss != 0);
-  if (new_pss > stable_pss) {
-    ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+
+    size_t allocated_bytes = mallinfo().uordblks;
+    if (first_allocated_bytes == 0) {
+      first_allocated_bytes = allocated_bytes;
+    } else if (last_allocated_bytes > first_allocated_bytes) {
+      // Check that the memory did not increase too much over the first loop.
+      ASSERT_LE(last_allocated_bytes - first_allocated_bytes, MAX_LEAK_BYTES);
+    }
+    last_allocated_bytes = allocated_bytes;
   }
 }
 
-TEST(libbacktrace, check_for_leak_local) {
+TEST_F(BacktraceTest, check_for_leak_local) {
   CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
 }
 
-TEST(libbacktrace, check_for_leak_local_thread) {
+TEST_F(BacktraceTest, check_for_leak_local_thread) {
   thread_t thread_data = { 0, 0, 0, nullptr };
   pthread_t thread;
   ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0);
@@ -1880,7 +1883,7 @@
   ASSERT_TRUE(pthread_join(thread, nullptr) == 0);
 }
 
-TEST(libbacktrace, check_for_leak_remote) {
+TEST_F(BacktraceTest, check_for_leak_remote) {
   pid_t pid;
   CreateRemoteProcess(&pid);
 
@@ -1888,4 +1891,3 @@
 
   FinishRemoteProcess(pid);
 }
-#endif
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index b24468b..285f150 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef __CUTILS_SOCKETS_H
-#define __CUTILS_SOCKETS_H
+#pragma once
 
 #include <errno.h>
 #include <limits.h>
@@ -141,19 +140,6 @@
                             const cutils_socket_buffer_t* buffers,
                             size_t num_buffers);
 
-/*
- * socket_peer_is_trusted - Takes a socket which is presumed to be a
- * connected local socket (e.g. AF_LOCAL) and returns whether the peer
- * (the userid that owns the process on the other end of that socket)
- * is one of the two trusted userids, root or shell.
- *
- * Note: This only works as advertised on the Android OS and always
- * just returns true when called on other operating systems.
- */
-extern bool socket_peer_is_trusted(int fd);
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* __CUTILS_SOCKETS_H */
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 3be8ad0..845c586 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -131,6 +131,7 @@
 #define AID_SECURE_ELEMENT 1068  /* secure element subsystem */
 #define AID_LMKD 1069            /* low memory killer daemon */
 #define AID_LLKD 1070            /* live lock daemon */
+#define AID_IORAPD 1071          /* input/output readahead and pin daemon */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/partition_utils.cpp b/libcutils/partition_utils.cpp
index 6735d6c..2211ff6 100644
--- a/libcutils/partition_utils.cpp
+++ b/libcutils/partition_utils.cpp
@@ -25,7 +25,7 @@
 
 #include <cutils/properties.h>
 
-static int only_one_char(char *buf, int len, char c)
+static int only_one_char(uint8_t *buf, int len, uint8_t c)
 {
     int i, ret;
 
@@ -41,7 +41,7 @@
 
 int partition_wiped(char *source)
 {
-    char buf[4096];
+    uint8_t buf[4096];
     int fd, ret;
 
     if ((fd = open(source, O_RDONLY)) < 0) {
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 0cb8a4d..2248817 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -32,34 +32,6 @@
 
 #include "android_get_control_env.h"
 
-#if defined(__ANDROID__)
-/* For the socket trust (credentials) check */
-#include <private/android_filesystem_config.h>
-#define __android_unused
-#else
-#define __android_unused __attribute__((__unused__))
-#endif
-
-bool socket_peer_is_trusted(int fd __android_unused) {
-#if defined(__ANDROID__)
-    ucred cr;
-    socklen_t len = sizeof(cr);
-    int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-
-    if (n != 0) {
-        ALOGE("could not get socket credentials: %s\n", strerror(errno));
-        return false;
-    }
-
-    if ((cr.uid != AID_ROOT) && (cr.uid != AID_SHELL)) {
-        ALOGE("untrusted userid on other end of socket: userid %d\n", cr.uid);
-        return false;
-    }
-#endif
-
-    return true;
-}
-
 int socket_close(int sock) {
     return close(sock);
 }
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2e2bf87..574a386 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -72,7 +72,7 @@
   explicit MapString(const std::string& str)
       : alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
   }
-  MapString(MapString&& rval)
+  MapString(MapString&& rval) noexcept
       : alloc(rval.alloc), str(rval.data(), rval.length()) {
     rval.alloc = NULL;
   }
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index adabfd8..b9dead5 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -33,12 +33,12 @@
   }
   ~ScopedPipe() { Close(); }
 
-  ScopedPipe(ScopedPipe&& other) {
+  ScopedPipe(ScopedPipe&& other) noexcept {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
   }
 
-  ScopedPipe& operator=(ScopedPipe&& other) {
+  ScopedPipe& operator=(ScopedPipe&& other) noexcept {
     SetReceiver(other.ReleaseReceiver());
     SetSender(other.ReleaseSender());
     return *this;
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 355679f..f3ab652 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -38,7 +38,7 @@
 
   Node(T* ptr, Allocator<Node> allocator)
       : references_in(allocator), references_out(allocator), ptr(ptr){};
-  Node(Node&& rhs) = default;
+  Node(Node&& rhs) noexcept = default;
   void Edge(Node<T>* ref) {
     references_out.emplace(ref);
     ref->references_in.emplace(this);
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 1551b5b..2d327ee 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -11,7 +11,11 @@
 
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
-    shared_libs: ["liblog"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libstatssocket",
+    ],
     whole_static_libs: ["libgtest_prod"],
 
     cflags: [
@@ -23,17 +27,20 @@
 
 // metricslogger shared library
 // -----------------------------------------------------------------------------
-cc_library_shared {
+cc_library {
     name: "libmetricslogger",
     srcs: metricslogger_lib_src_files,
     defaults: ["metricslogger_defaults"],
+    export_shared_lib_headers: ["libstatssocket"],
 }
 
 // static version of libmetricslogger, needed by a few art static binaries
+// TODO(b/117829226): Remove once dependencies are cleaned up.
 cc_library_static {
     name: "libmetricslogger_static",
     srcs: metricslogger_lib_src_files,
     defaults: ["metricslogger_defaults"],
+    export_shared_lib_headers: ["libstatssocket"],
 }
 
 // metricslogger shared library, debug
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index c305db2..56bd6c4 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -15,6 +15,7 @@
  */
 
 #include <log/log_event_list.h>
+#include <stats_event_list.h>
 #include <cstdint>
 #include <string>
 
@@ -43,6 +44,7 @@
 class ComplexEventLogger {
   private:
     android_log_event_list logger;
+    stats_event_list stats_logger;
 
   public:
     // Create a complex event with category|category|.
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 6a32153..2a1b137 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -18,11 +18,15 @@
 
 #include <cstdlib>
 
+#include <android-base/chrono_utils.h>
 #include <log/event_tag_map.h>
-#include <log/log_event_list.h>
+
+using namespace android;
 
 namespace {
 
+const static int kStatsEventTag = 1937006964;
+const static int kKeyValuePairAtomId = 83;
 #ifdef __ANDROID__
 EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
 const int kSysuiMultiActionTag = android_lookupEventTagNum(
@@ -32,6 +36,12 @@
 const int kSysuiMultiActionTag = 0;
 #endif
 
+int64_t getElapsedTimeNanoSinceBoot() {
+    return std::chrono::duration_cast<std::chrono::nanoseconds>(
+                   android::base::boot_clock::now().time_since_epoch())
+            .count();
+}
+
 }  // namespace
 
 namespace android {
@@ -42,6 +52,12 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
         << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event << LOGBUILDER_BUCKET << data
+              << LOGBUILDER_VALUE << 1;
+    stats_log.write(LOG_ID_STATS);
 }
 
 // Mirror com.android.internal.logging.MetricsLogger#count().
@@ -49,6 +65,11 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
         << val << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE << val;
+    stats_log.write(LOG_ID_STATS);
 }
 
 // Mirror com.android.internal.logging.MetricsLogger#action().
@@ -56,34 +77,48 @@
     android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
         << field << value << LOG_ID_EVENTS;
+
+    stats_event_list stats_log(kStatsEventTag);
+    stats_log << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+              << category << LOGBUILDER_TYPE << TYPE_ACTION << field << value;
+    stats_log.write(LOG_ID_STATS);
 }
 
-ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionTag) {
+ComplexEventLogger::ComplexEventLogger(int category)
+    : logger(kSysuiMultiActionTag), stats_logger(kStatsEventTag) {
     logger << LOGBUILDER_CATEGORY << category;
+    stats_logger << getElapsedTimeNanoSinceBoot() << kKeyValuePairAtomId << LOGBUILDER_CATEGORY
+                 << category;
 }
 
 void ComplexEventLogger::SetPackageName(const std::string& package_name) {
     logger << LOGBUILDER_PACKAGENAME << package_name;
+    stats_logger << LOGBUILDER_PACKAGENAME << package_name;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::AddTaggedData(int tag, float value) {
     logger << tag << value;
+    stats_logger << tag << value;
 }
 
 void ComplexEventLogger::Record() {
     logger << LOG_ID_EVENTS;
+    stats_logger.write(LOG_ID_STATS);
 }
 
 }  // namespace metricslogger
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 6e63b74..92fd2a2 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -10,9 +10,11 @@
 
     host_supported: true,
     srcs: ["native_bridge.cc"],
+    header_libs: [
+        "libbase_headers",
+    ],
     shared_libs: [
         "liblog",
-        "libbase",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 5b9ba1c..4ed6e20 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -29,10 +29,12 @@
 
 shared_libraries := \
     liblog \
-    libbase \
     libnativebridge \
     libnativebridge-dummy
 
+header_libraries := \
+    libbase_headers
+
 libnativebridge_tests_common_cflags := \
     -Wall \
     -Werror \
@@ -40,6 +42,7 @@
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
@@ -49,6 +52,7 @@
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index ac009a9..f47b6e4 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -171,7 +171,7 @@
     }
 
     mAssembly->resize( int(pc()-base())*4 );
-    
+
     // the instruction cache is flushed by CodeCache
     const int64_t duration = ggl_system_time() - mDuration;
     const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
@@ -183,8 +183,8 @@
         printf(format, name, int(pc()-base()), base(), pc(), duration);
         disassemble(name);
     }
-    
-    return NO_ERROR;
+
+    return OK;
 }
 
 uint32_t* ARMAssembler::pcForLabel(const char* label)
@@ -213,14 +213,14 @@
 // multiply...
 void ARMAssembler::MLA(int cc, int s,
         int Rd, int Rm, int Rs, int Rn) {
-    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; } 
+    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
     LOG_FATAL_IF(Rd==Rm, "MLA(r%u,r%u,r%u,r%u)", Rd,Rm,Rs,Rn);
     *mPC++ =    (cc<<28) | (1<<21) | (s<<20) |
                 (Rd<<16) | (Rn<<12) | (Rs<<8) | 0x90 | Rm;
 }
 void ARMAssembler::MUL(int cc, int s,
         int Rd, int Rm, int Rs) {
-    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; } 
+    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; }
     LOG_FATAL_IF(Rd==Rm, "MUL(r%u,r%u,r%u)", Rd,Rm,Rs);
     *mPC++ = (cc<<28) | (s<<20) | (Rd<<16) | (Rs<<8) | 0x90 | Rm;
 }
@@ -577,4 +577,3 @@
 }
 
 }; // namespace android
-
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index aebc129..8926776 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -325,7 +325,7 @@
         printf(format, name, int(pc()-base()), base(), pc(), duration);
         disassemble(name);
     }
-    return NO_ERROR;
+    return OK;
 }
 
 uint32_t* ArmToArm64Assembler::pcForLabel(const char* label)
@@ -1238,4 +1238,3 @@
 }
 
 }; // namespace android
-
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index 039a725..7de8cc1 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -1421,7 +1421,7 @@
         disassemble(name);
     }
 
-    return NO_ERROR;
+    return OK;
 }
 
 uint32_t* MIPSAssembler::pcForLabel(const char* label)
@@ -1953,5 +1953,3 @@
 
 
 }; // namespace android:
-
-
diff --git a/libstats/Android.bp b/libstats/Android.bp
index d58f294..f5ee1da 100644
--- a/libstats/Android.bp
+++ b/libstats/Android.bp
@@ -17,12 +17,13 @@
 // ==========================================================
 // Native library to write stats log to statsd socket
 // ==========================================================
-cc_library_static {
+cc_library {
     name: "libstatssocket",
     srcs: [
         "stats_event_list.c",
         "statsd_writer.c",
     ],
+    host_supported: true,
     cflags: [
         "-Wall",
         "-Werror",
@@ -32,6 +33,7 @@
     ],
     export_include_dirs: ["include"],
     shared_libs: [
+        "libcutils",
         "liblog",
     ],
 }
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index 5d174ae..a9832db 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -24,6 +24,8 @@
 #endif
 void reset_log_context(android_log_context ctx);
 int write_to_logger(android_log_context context, log_id_t id);
+void note_log_drop();
+void stats_log_close();
 
 #ifdef __cplusplus
 }
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 3d746db..72770d4 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -17,6 +17,7 @@
 #include "include/stats_event_list.h"
 
 #include <string.h>
+#include <sys/time.h>
 #include "statsd_writer.h"
 
 #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
@@ -119,6 +120,19 @@
     return retValue;
 }
 
+void note_log_drop() {
+    statsdLoggerWrite.noteDrop();
+}
+
+void stats_log_close() {
+    statsd_writer_init_lock();
+    write_to_statsd = __write_to_statsd_init;
+    if (statsdLoggerWrite.close) {
+        (*statsdLoggerWrite.close)();
+    }
+    statsd_writer_init_unlock();
+}
+
 /* log_init_lock assumed */
 static int __write_to_statsd_initialize_locked() {
     if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
@@ -143,7 +157,14 @@
     }
 
     save_errno = errno;
+#if defined(__ANDROID__)
     clock_gettime(CLOCK_REALTIME, &ts);
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ts.tv_sec = tv.tv_sec;
+    ts.tv_nsec = tv.tv_usec * 1000;
+#endif
 
     int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
     errno = save_errno;
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 9953bba..88f7d44 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -15,8 +15,9 @@
  */
 #include "statsd_writer.h"
 
+#include <cutils/fs.h>
 #include <cutils/sockets.h>
-#include <endian.h>
+#include <cutils/threads.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -37,7 +38,16 @@
 /* branchless on many architectures. */
 #define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
 
+#ifndef htole32
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole32(x) (x)
+#else
+#define htole32(x) __bswap_32(x)
+#endif
+#endif
+
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static atomic_int dropped = 0;
 
 void statsd_writer_init_lock() {
     /*
@@ -59,14 +69,16 @@
 static int statsdOpen();
 static void statsdClose();
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+static void statsdNoteDrop();
 
 struct android_log_transport_write statsdLoggerWrite = {
-    .name = "statsd",
-    .sock = -EBADF,
-    .available = statsdAvailable,
-    .open = statsdOpen,
-    .close = statsdClose,
-    .write = statsdWrite,
+        .name = "statsd",
+        .sock = -EBADF,
+        .available = statsdAvailable,
+        .open = statsdOpen,
+        .close = statsdClose,
+        .write = statsdWrite,
+        .noteDrop = statsdNoteDrop,
 };
 
 /* log_init_lock assumed */
@@ -75,7 +87,14 @@
 
     i = atomic_load(&statsdLoggerWrite.sock);
     if (i < 0) {
-        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+        int flags = SOCK_DGRAM;
+#ifdef SOCK_CLOEXEC
+        flags |= SOCK_CLOEXEC;
+#endif
+#ifdef SOCK_NONBLOCK
+        flags |= SOCK_NONBLOCK;
+#endif
+        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
         if (sock < 0) {
             ret = -errno;
         } else {
@@ -131,6 +150,10 @@
     return 1;
 }
 
+static void statsdNoteDrop() {
+    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+}
+
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
     ssize_t ret;
     int sock;
@@ -138,7 +161,6 @@
     struct iovec newVec[nr + headerLength];
     android_log_header_t header;
     size_t i, payloadSize;
-    static atomic_int dropped;
 
     sock = atomic_load(&statsdLoggerWrite.sock);
     if (sock < 0) switch (sock) {
@@ -252,8 +274,6 @@
 
     if (ret > (ssize_t)sizeof(header)) {
         ret -= sizeof(header);
-    } else if (ret == -EAGAIN) {
-        atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
     }
 
     return ret;
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 82e14e0..7289441 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -38,6 +38,8 @@
     void (*close)();    /* free up resources */
     /* write log to transport, returns number of bytes propagated, or -errno */
     int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+    /* note one log drop */
+    void (*noteDrop)();
 };
 
 #endif  // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsystem/include/system/camera.h b/libsystem/include/system/camera.h
index 7d79673..2ca90c3 100644
--- a/libsystem/include/system/camera.h
+++ b/libsystem/include/system/camera.h
@@ -158,8 +158,8 @@
      *
      * When any camera method returns error, the client can use ping command
      * to see if the camera has been taken away by other clients. If the result
-     * is NO_ERROR, it means the camera hardware is not released. If the result
-     * is not NO_ERROR, the camera has been released and the existing client
+     * is OK, it means the camera hardware is not released. If the result
+     * is not OK, the camera has been released and the existing client
      * can silently finish itself or show a dialog.
      */
     CAMERA_CMD_PING = 9,
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 24ea7aa..9dc2699 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -374,6 +374,7 @@
                     continue;
                 if (!if_indextoname(* (int *) RTA_DATA(rta), dev))
                     return false;
+                continue;
             default:
                 continue;
         }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index be2145d..14f82c7 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -58,6 +58,7 @@
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
+        "Global.cpp",
         "JitDebug.cpp",
         "Log.cpp",
         "MapInfo.cpp",
@@ -178,6 +179,7 @@
         "tests/JitDebugTest.cpp",
         "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
+        "tests/MapInfoCreateMemoryTest.cpp",
         "tests/MapInfoGetElfTest.cpp",
         "tests/MapInfoGetLoadBiasTest.cpp",
         "tests/MapsTest.cpp",
@@ -188,6 +190,7 @@
         "tests/MemoryOfflineBufferTest.cpp",
         "tests/MemoryOfflineTest.cpp",
         "tests/MemoryRangeTest.cpp",
+        "tests/MemoryRangesTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
         "tests/RegsInfoTest.cpp",
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index 430f6c5..451a0b9 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -43,10 +43,10 @@
   uint64_t dex_file;
 };
 
-DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : Global(memory) {}
 
 DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
-    : memory_(memory), search_libs_(search_libs) {}
+    : Global(memory, search_libs) {}
 
 DexFiles::~DexFiles() {
   for (auto& entry : files_) {
@@ -54,8 +54,8 @@
   }
 }
 
-void DexFiles::SetArch(ArchEnum arch) {
-  switch (arch) {
+void DexFiles::ProcessArch() {
+  switch (arch()) {
     case ARCH_ARM:
     case ARCH_MIPS:
     case ARCH_X86:
@@ -117,6 +117,11 @@
   return true;
 }
 
+bool DexFiles::ReadVariableData(uint64_t ptr_offset) {
+  entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset);
+  return entry_addr_ != 0;
+}
+
 void DexFiles::Init(Maps* maps) {
   if (initialized_) {
     return;
@@ -124,36 +129,7 @@
   initialized_ = true;
   entry_addr_ = 0;
 
-  const std::string dex_debug_name("__dex_debug_descriptor");
-  for (MapInfo* info : *maps) {
-    if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
-      continue;
-    }
-
-    if (!search_libs_.empty()) {
-      bool found = false;
-      const char* lib = basename(info->name.c_str());
-      for (const std::string& name : search_libs_) {
-        if (name == lib) {
-          found = true;
-          break;
-        }
-      }
-      if (!found) {
-        continue;
-      }
-    }
-
-    Elf* elf = info->GetElf(memory_, true);
-    uint64_t ptr;
-    // Find first non-empty list (libart might be loaded multiple times).
-    if (elf->GetGlobalVariable(dex_debug_name, &ptr) && ptr != 0) {
-      entry_addr_ = (this->*read_entry_ptr_func_)(ptr + info->start);
-      if (entry_addr_ != 0) {
-        break;
-      }
-    }
-  }
+  FindAndReadVariable(maps, "__dex_debug_descriptor");
 }
 
 DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4723606..4d72ead 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -40,7 +40,7 @@
 std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
 std::mutex* Elf::cache_lock_;
 
-bool Elf::Init(bool init_gnu_debugdata) {
+bool Elf::Init() {
   load_bias_ = 0;
   if (!memory_) {
     return false;
@@ -54,11 +54,7 @@
   valid_ = interface_->Init(&load_bias_);
   if (valid_) {
     interface_->InitHeaders(load_bias_);
-    if (init_gnu_debugdata) {
-      InitGnuDebugdata();
-    } else {
-      gnu_debugdata_interface_.reset(nullptr);
-    }
+    InitGnuDebugdata();
   } else {
     interface_.reset(nullptr);
   }
@@ -92,6 +88,11 @@
   }
 }
 
+void Elf::Invalidate() {
+  interface_.reset(nullptr);
+  valid_ = false;
+}
+
 bool Elf::GetSoname(std::string* name) {
   std::lock_guard<std::mutex> guard(lock_);
   return valid_ && interface_->GetSoname(name);
@@ -194,26 +195,26 @@
   return true;
 }
 
-void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+bool Elf::GetInfo(Memory* memory, uint64_t* size) {
   if (!IsValidElf(memory)) {
-    *valid = false;
-    return;
+    return false;
   }
   *size = 0;
-  *valid = true;
 
-  // Now read the section header information.
   uint8_t class_type;
   if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
-    return;
+    return false;
   }
+
+  // Get the maximum size of the elf data from the header.
   if (class_type == ELFCLASS32) {
     ElfInterface32::GetMaxSize(memory, size);
   } else if (class_type == ELFCLASS64) {
     ElfInterface64::GetMaxSize(memory, size);
   } else {
-    *valid = false;
+    return false;
   }
+  return true;
 }
 
 bool Elf::IsValidPc(uint64_t pc) {
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
new file mode 100644
index 0000000..7a3de01
--- /dev/null
+++ b/libunwindstack/Global.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 <sys/mman.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Global.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+Global::Global(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+Global::Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : memory_(memory), search_libs_(search_libs) {}
+
+void Global::SetArch(ArchEnum arch) {
+  if (arch_ == ARCH_UNKNOWN) {
+    arch_ = arch;
+    ProcessArch();
+  }
+}
+
+uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) {
+  if (!search_libs_.empty()) {
+    bool found = false;
+    const char* lib = basename(info->name.c_str());
+    for (const std::string& name : search_libs_) {
+      if (name == lib) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      return 0;
+    }
+  }
+
+  Elf* elf = info->GetElf(memory_, arch());
+  uint64_t ptr;
+  // Find first non-empty list (libraries might be loaded multiple times).
+  if (elf->GetGlobalVariable(variable, &ptr) && ptr != 0) {
+    return ptr + info->start;
+  }
+  return 0;
+}
+
+void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
+  std::string variable(var_str);
+  // When looking for global variables, do not arbitrarily search every
+  // readable map. Instead look for a specific pattern that must exist.
+  // The pattern should be a readable map, followed by a read-write
+  // map with a non-zero offset.
+  // For example:
+  //   f0000-f1000 0 r-- /system/lib/libc.so
+  //   f1000-f2000 1000 r-x /system/lib/libc.so
+  //   f2000-f3000 2000 rw- /system/lib/libc.so
+  // This also works:
+  //   f0000-f2000 0 r-- /system/lib/libc.so
+  //   f2000-f3000 2000 rw- /system/lib/libc.so
+  MapInfo* map_start = nullptr;
+  for (MapInfo* info : *maps) {
+    if (map_start != nullptr) {
+      if (map_start->name == info->name) {
+        if (info->offset != 0 &&
+            (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
+          uint64_t ptr = GetVariableOffset(map_start, variable);
+          if (ptr != 0 && ReadVariableData(ptr)) {
+            break;
+          } else {
+            // Failed to find the global variable, do not bother trying again.
+            map_start = nullptr;
+          }
+        }
+      } else {
+        map_start = nullptr;
+      }
+    }
+    if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
+        !info->name.empty()) {
+      map_start = info;
+    }
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index 0c319ec..20bc4b9 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -69,10 +69,10 @@
   uint64_t first_entry;
 };
 
-JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {}
 
 JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
-    : memory_(memory), search_libs_(search_libs) {}
+    : Global(memory, search_libs) {}
 
 JitDebug::~JitDebug() {
   for (auto* elf : elf_list_) {
@@ -141,8 +141,8 @@
   return code.next;
 }
 
-void JitDebug::SetArch(ArchEnum arch) {
-  switch (arch) {
+void JitDebug::ProcessArch() {
+  switch (arch()) {
     case ARCH_X86:
       read_descriptor_func_ = &JitDebug::ReadDescriptor32;
       read_entry_func_ = &JitDebug::ReadEntry32Pack;
@@ -165,6 +165,11 @@
   }
 }
 
+bool JitDebug::ReadVariableData(uint64_t ptr) {
+  entry_addr_ = (this->*read_descriptor_func_)(ptr);
+  return entry_addr_ != 0;
+}
+
 void JitDebug::Init(Maps* maps) {
   if (initialized_) {
     return;
@@ -172,37 +177,7 @@
   // Regardless of what happens below, consider the init finished.
   initialized_ = true;
 
-  const std::string descriptor_name("__jit_debug_descriptor");
-  for (MapInfo* info : *maps) {
-    if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
-      continue;
-    }
-
-    if (!search_libs_.empty()) {
-      bool found = false;
-      const char* lib = basename(info->name.c_str());
-      for (std::string& name : search_libs_) {
-        if (strcmp(name.c_str(), lib) == 0) {
-          found = true;
-          break;
-        }
-      }
-      if (!found) {
-        continue;
-      }
-    }
-
-    Elf* elf = info->GetElf(memory_, true);
-    uint64_t descriptor_addr;
-    if (elf->GetGlobalVariable(descriptor_name, &descriptor_addr)) {
-      // Search for the first non-zero entry.
-      descriptor_addr += info->start;
-      entry_addr_ = (this->*read_descriptor_func_)(descriptor_addr);
-      if (entry_addr_ != 0) {
-        break;
-      }
-    }
-  }
+  FindAndReadVariable(maps, "__jit_debug_descriptor");
 }
 
 Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
@@ -226,7 +201,7 @@
     entry_addr_ = (this->*read_entry_func_)(&start, &size);
 
     Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
-    elf->Init(true);
+    elf->Init();
     if (!elf->valid()) {
       // The data is not formatted in a way we understand, do not attempt
       // to process any other entries.
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 952b332..5b2fadf 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -88,6 +88,7 @@
 bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
   std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
   unwindstack::RegsGetLocal(regs.get());
+  ArchEnum arch = regs->Arch();
 
   size_t num_frames = 0;
   bool adjust_pc = false;
@@ -100,7 +101,7 @@
       break;
     }
 
-    Elf* elf = map_info->GetElf(process_memory_, true);
+    Elf* elf = map_info->GetElf(process_memory_, arch);
     uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
     uint64_t step_pc = rel_pc;
     uint64_t pc_adjustment;
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 39378a3..fe32b5e 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -53,10 +53,8 @@
     return nullptr;
   }
 
-  bool valid;
   uint64_t max_size;
-  Elf::GetInfo(memory.get(), &valid, &max_size);
-  if (!valid) {
+  if (!Elf::GetInfo(memory.get(), &max_size)) {
     // Init as if the whole file is an elf.
     if (memory->Init(name, 0)) {
       elf_offset = offset;
@@ -102,10 +100,53 @@
   if (!(flags & PROT_READ)) {
     return nullptr;
   }
-  return new MemoryRange(process_memory, start, end - start, 0);
+
+  // 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
+  // first part of the elf file. This is done if the linker rosegment
+  // option is used.
+  std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
+  if (Elf::IsValidElf(memory.get())) {
+    return memory.release();
+  }
+
+  if (name.empty() || maps_ == nullptr) {
+    return nullptr;
+  }
+
+  // Find the read-only map that has the same name and has an offset closest
+  // to the current offset but less than the offset of the current map.
+  // For shared libraries, there should be a r-x map that has a non-zero
+  // offset and then a r-- map that has a zero offset.
+  // For shared libraries loaded from an apk, there should be a r-x map that
+  // has a non-zero offset and then a r-- map that has a non-zero offset less
+  // than the offset from the r-x map.
+  uint64_t closest_offset = 0;
+  MapInfo* ro_map_info = nullptr;
+  for (auto map_info : *maps_) {
+    if (map_info->flags == PROT_READ && map_info->name == name && map_info->offset < offset &&
+        map_info->offset >= closest_offset) {
+      ro_map_info = map_info;
+      closest_offset = ro_map_info->offset;
+    }
+  }
+
+  if (ro_map_info != nullptr) {
+    // Make sure that relative pc values are corrected properly.
+    elf_offset = offset - closest_offset;
+
+    MemoryRanges* ranges = new MemoryRanges;
+    ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
+                                   ro_map_info->end - ro_map_info->start, 0));
+    ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
+
+    return ranges;
+  }
+  return nullptr;
 }
 
-Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
   // Make sure no other thread is trying to add the elf to this map.
   std::lock_guard<std::mutex> guard(mutex_);
 
@@ -134,7 +175,11 @@
   elf.reset(new Elf(memory));
   // If the init fails, keep the elf around as an invalid object so we
   // don't try to reinit the object.
-  elf->Init(init_gnu_debugdata);
+  elf->Init();
+  if (elf->valid() && expected_arch != elf->arch()) {
+    // Make the elf invalid, mismatch between arch and expected arch.
+    elf->Invalidate();
+  }
 
   if (locked) {
     Elf::CacheAdd(this);
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index e676a5a..8729871 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -66,13 +66,13 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+        maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
       });
 }
 
 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
                const std::string& name, uint64_t load_bias) {
-  MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
+  MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
   map_info->load_bias = load_bias;
   maps_.push_back(map_info);
 }
@@ -97,7 +97,7 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+        maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
       });
 }
 
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index beb2aad..cfa8c6d 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -316,6 +316,18 @@
   return memory_->Read(read_addr, dst, read_length);
 }
 
+void MemoryRanges::Insert(MemoryRange* memory) {
+  maps_.emplace(memory->offset() + memory->length(), memory);
+}
+
+size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
+  auto entry = maps_.upper_bound(addr);
+  if (entry != maps_.end()) {
+    return entry->second->Read(addr, dst, size);
+  }
+  return 0;
+}
+
 bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
   auto memory_file = std::make_shared<MemoryFileAtOffset>();
   if (!memory_file->Init(file, offset)) {
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
index e6dd33c..e445a91 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -41,7 +41,7 @@
   }
 
   inline AddressType* Save(uint32_t reg) {
-    if (reg > MAX_REGISTERS) {
+    if (reg >= MAX_REGISTERS) {
       // This should never happen since all currently supported
       // architectures have < 64 total registers.
       abort();
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 099cc9e..b3c5549 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -135,6 +135,8 @@
   last_error_.code = ERROR_NONE;
   last_error_.address = 0;
 
+  ArchEnum arch = regs_->Arch();
+
   bool return_address_attempt = false;
   bool adjust_pc = false;
   std::unique_ptr<JitDebug> jit_debug;
@@ -155,7 +157,7 @@
       if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
         break;
       }
-      elf = map_info->GetElf(process_memory_, true);
+      elf = map_info->GetElf(process_memory_, arch);
       step_pc = regs_->pc();
       rel_pc = elf->GetRelPc(step_pc, map_info);
       // Everyone except elf data in gdb jit debug maps uses the relative pc.
@@ -190,6 +192,12 @@
         FillInDexFrame();
         // Clear the dex pc so that we don't repeat this frame later.
         regs_->set_dex_pc(0);
+
+        // Make sure there is enough room for the real frame.
+        if (frames_.size() == max_frames_) {
+          last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+          break;
+        }
       }
 
       FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index 26f5d35..c202a33 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -25,16 +25,18 @@
 #include <unordered_map>
 #include <vector>
 
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
 namespace unwindstack {
 
 // Forward declarations.
 class DexFile;
 class Maps;
 struct MapInfo;
-class Memory;
 enum ArchEnum : uint8_t;
 
-class DexFiles {
+class DexFiles : public Global {
  public:
   explicit DexFiles(std::shared_ptr<Memory>& memory);
   DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
@@ -45,8 +47,6 @@
   void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
                             uint64_t* method_offset);
 
-  void SetArch(ArchEnum arch);
-
  private:
   void Init(Maps* maps);
 
@@ -60,8 +60,9 @@
 
   bool ReadEntry64();
 
-  std::shared_ptr<Memory> memory_;
-  std::vector<std::string> search_libs_;
+  bool ReadVariableData(uint64_t ptr_offset) override;
+
+  void ProcessArch() override;
 
   std::mutex lock_;
   bool initialized_ = false;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index f4cdbda..e5b0a89 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -53,10 +53,12 @@
   Elf(Memory* memory) : memory_(memory) {}
   virtual ~Elf() = default;
 
-  bool Init(bool init_gnu_debugdata);
+  bool Init();
 
   void InitGnuDebugdata();
 
+  void Invalidate();
+
   bool GetSoname(std::string* name);
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
@@ -94,7 +96,7 @@
 
   static bool IsValidElf(Memory* memory);
 
-  static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+  static bool GetInfo(Memory* memory, uint64_t* size);
 
   static uint64_t GetLoadBias(Memory* memory);
 
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
new file mode 100644
index 0000000..a7e6c15
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_GLOBAL_H
+#define _LIBUNWINDSTACK_GLOBAL_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Maps;
+struct MapInfo;
+
+class Global {
+ public:
+  explicit Global(std::shared_ptr<Memory>& memory);
+  Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+  virtual ~Global() = default;
+
+  void SetArch(ArchEnum arch);
+
+  ArchEnum arch() { return arch_; }
+
+ protected:
+  uint64_t GetVariableOffset(MapInfo* info, const std::string& variable);
+  void FindAndReadVariable(Maps* maps, const char* variable);
+
+  virtual bool ReadVariableData(uint64_t offset) = 0;
+
+  virtual void ProcessArch() = 0;
+
+  ArchEnum arch_ = ARCH_UNKNOWN;
+
+  std::shared_ptr<Memory> memory_;
+  std::vector<std::string> search_libs_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_GLOBAL_H
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index 0bcd0b0..f64b04f 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -24,15 +24,17 @@
 #include <string>
 #include <vector>
 
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
 namespace unwindstack {
 
 // Forward declarations.
 class Elf;
 class Maps;
-class Memory;
 enum ArchEnum : uint8_t;
 
-class JitDebug {
+class JitDebug : public Global {
  public:
   explicit JitDebug(std::shared_ptr<Memory>& memory);
   JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
@@ -40,19 +42,9 @@
 
   Elf* GetElf(Maps* maps, uint64_t pc);
 
-  void SetArch(ArchEnum arch);
-
  private:
   void Init(Maps* maps);
 
-  std::shared_ptr<Memory> memory_;
-  uint64_t entry_addr_ = 0;
-  bool initialized_ = false;
-  std::vector<Elf*> elf_list_;
-  std::vector<std::string> search_libs_;
-
-  std::mutex lock_;
-
   uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
   uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
 
@@ -62,6 +54,16 @@
   uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
   uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
   uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
+
+  bool ReadVariableData(uint64_t ptr_offset) override;
+
+  void ProcessArch() override;
+
+  uint64_t entry_addr_ = 0;
+  bool initialized_ = false;
+  std::vector<Elf*> elf_list_;
+
+  std::mutex lock_;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index ac0df41..9c6b552 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -29,20 +29,25 @@
 namespace unwindstack {
 
 // Forward declarations.
+class Maps;
 class Memory;
 
 struct MapInfo {
-  MapInfo() = default;
-  MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
-  MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name)
-      : start(start),
+  MapInfo(Maps* maps) : maps_(maps) {}
+  MapInfo(Maps* maps, uint64_t start, uint64_t end) : maps_(maps), start(start), end(end) {}
+  MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+          const char* name)
+      : maps_(maps),
+        start(start),
         end(end),
         offset(offset),
         flags(flags),
         name(name),
         load_bias(static_cast<uint64_t>(-1)) {}
-  MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
-      : start(start),
+  MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+          const std::string& name)
+      : maps_(maps),
+        start(start),
         end(end),
         offset(offset),
         flags(flags),
@@ -50,6 +55,8 @@
         load_bias(static_cast<uint64_t>(-1)) {}
   ~MapInfo() = default;
 
+  Maps* maps_ = nullptr;
+
   uint64_t start = 0;
   uint64_t end = 0;
   uint64_t offset = 0;
@@ -65,18 +72,18 @@
   std::atomic_uint64_t load_bias;
 
   // This function guarantees it will never return nullptr.
-  Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
+  Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
 
   uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
 
+  Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
  private:
   MapInfo(const MapInfo&) = delete;
   void operator=(const MapInfo&) = delete;
 
   Memory* GetFileMemory();
 
-  Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
-
   // Protect the creation of the elf object.
   std::mutex mutex_;
 };
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dee5e98..9c425cb 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -22,6 +22,7 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -119,6 +120,9 @@
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
 
+  uint64_t offset() { return offset_; }
+  uint64_t length() { return length_; }
+
  private:
   std::shared_ptr<Memory> memory_;
   uint64_t begin_;
@@ -126,6 +130,19 @@
   uint64_t offset_;
 };
 
+class MemoryRanges : public Memory {
+ public:
+  MemoryRanges() = default;
+  virtual ~MemoryRanges() = default;
+
+  void Insert(MemoryRange* memory);
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
+};
+
 class MemoryOffline : public Memory {
  public:
   MemoryOffline() = default;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 4dd8cb0..40f9f8e 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -120,7 +120,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(0, 0x10000, 0, 0x5, tf.path);
+  MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -134,7 +134,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
+  MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -148,7 +148,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -156,7 +156,7 @@
 TEST(DexFileTest, create_using_memory_empty_file) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -164,7 +164,7 @@
 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 }
@@ -178,7 +178,7 @@
 
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+  MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -200,7 +200,7 @@
 TEST(DexFileTest, get_method) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -227,7 +227,7 @@
 TEST(DexFileTest, get_method_empty) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index d029bb0..1ea9e5c 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -36,57 +36,54 @@
 
 class DexFilesTest : public ::testing::Test {
  protected:
-  void SetUp() override {
-    memory_ = new MemoryFake;
-    process_memory_.reset(memory_);
-
-    dex_files_.reset(new DexFiles(process_memory_));
-    dex_files_->SetArch(ARCH_ARM);
-
-    maps_.reset(
-        new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
-                       "4000-6000 r--s 00000000 00:00 0\n"
-                       "6000-8000 -w-s 00000000 00:00 0\n"
-                       "a000-c000 r-xp 00000000 00:00 0\n"
-                       "c000-f000 rwxp 00000000 00:00 0\n"
-                       "f000-11000 r-xp 00000000 00:00 0\n"
-                       "100000-110000 rw-p 0000000 00:00 0\n"
-                       "200000-210000 rw-p 0000000 00:00 0\n"
-                       "300000-400000 rw-p 0000000 00:00 0\n"));
-    ASSERT_TRUE(maps_->Parse());
-
-    // Global variable in a section that is not readable/executable.
-    MapInfo* map_info = maps_->Get(kMapGlobalNonReadableExectable);
-    ASSERT_TRUE(map_info != nullptr);
+  void CreateFakeElf(MapInfo* map_info) {
     MemoryFake* memory = new MemoryFake;
     ElfFake* elf = new ElfFake(memory);
     elf->FakeSetValid(true);
     ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
     elf->FakeSetInterface(interface);
+
     interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
+  }
+
+  void Init(ArchEnum arch) {
+    dex_files_.reset(new DexFiles(process_memory_));
+    dex_files_->SetArch(arch);
+
+    maps_.reset(
+        new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
+                       "4000-6000 r--s 00000000 00:00 0 /fake/elf\n"
+                       "6000-8000 -wxs 00000000 00:00 0 /fake/elf\n"
+                       "a000-c000 r--p 00000000 00:00 0 /fake/elf2\n"
+                       "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n"
+                       "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+                       "100000-110000 rw-p 0001000 00:00 0 /fake/elf3\n"
+                       "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
+                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
+    ASSERT_TRUE(maps_->Parse());
+
+    // Global variable in a section that is not readable.
+    MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
+    ASSERT_TRUE(map_info != nullptr);
+    CreateFakeElf(map_info);
 
     // Global variable not set by default.
     map_info = maps_->Get(kMapGlobalSetToZero);
     ASSERT_TRUE(map_info != nullptr);
-    memory = new MemoryFake;
-    elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
 
     // Global variable set in this map.
     map_info = maps_->Get(kMapGlobal);
     ASSERT_TRUE(map_info != nullptr);
-    memory = new MemoryFake;
-    elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
+  }
+
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    Init(ARCH_ARM);
   }
 
   void WriteDescriptor32(uint64_t addr, uint32_t head);
@@ -95,9 +92,10 @@
   void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
   void WriteDex(uint64_t dex_file);
 
-  static constexpr size_t kMapGlobalNonReadableExectable = 3;
-  static constexpr size_t kMapGlobalSetToZero = 4;
+  static constexpr size_t kMapGlobalNonReadable = 2;
+  static constexpr size_t kMapGlobalSetToZero = 3;
   static constexpr size_t kMapGlobal = 5;
+  static constexpr size_t kMapGlobalRw = 6;
   static constexpr size_t kMapDexFileEntries = 7;
   static constexpr size_t kMapDexFiles = 8;
 
@@ -168,11 +166,12 @@
 }
 
 TEST_F(DexFilesTest, get_method_information_64) {
+  Init(ARCH_ARM64);
+
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  dex_files_->SetArch(ARCH_ARM64);
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x301000);
   WriteDex(0x301000);
@@ -198,11 +197,12 @@
 }
 
 TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+  Init(ARCH_ARM64);
+
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
-  dex_files_->SetArch(ARCH_ARM64);
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0x200100, 0, 0x100000);
   WriteEntry64(0x200100, 0, 0x200000, 0x300000);
@@ -256,6 +256,9 @@
   map_info->name = "/system/lib/libart.so";
   dex_files_.reset(new DexFiles(process_memory_, libs));
   dex_files_->SetArch(ARCH_ARM);
+  // Set the rw map to the same name or this will not scan this entry.
+  map_info = maps_->Get(kMapGlobalRw);
+  map_info->name = "/system/lib/libart.so";
   // Make sure that clearing out copy of the libs doesn't affect the
   // DexFiles object.
   libs.clear();
@@ -271,7 +274,7 @@
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   // First global variable found, but value is zero.
-  WriteDescriptor32(0xc800, 0);
+  WriteDescriptor32(0xa800, 0);
 
   WriteDescriptor32(0xf800, 0x200000);
   WriteEntry32(0x200000, 0, 0, 0x300000);
@@ -286,25 +289,26 @@
   dex_files_->SetArch(ARCH_ARM);
   method_name = "fail";
   method_offset = 0x123;
-  WriteDescriptor32(0xc800, 0x100000);
+  WriteDescriptor32(0xa800, 0x100000);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
 }
 
 TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+  Init(ARCH_ARM64);
+
   std::string method_name = "nothing";
   uint64_t method_offset = 0x124;
   MapInfo* info = maps_->Get(kMapDexFiles);
 
   // First global variable found, but value is zero.
-  WriteDescriptor64(0xc800, 0);
+  WriteDescriptor64(0xa800, 0);
 
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x300000);
   WriteDex(0x300000);
 
-  dex_files_->SetArch(ARCH_ARM64);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("Main.<init>", method_name);
   EXPECT_EQ(0U, method_offset);
@@ -314,7 +318,7 @@
   dex_files_->SetArch(ARCH_ARM64);
   method_name = "fail";
   method_offset = 0x123;
-  WriteDescriptor64(0xc800, 0x100000);
+  WriteDescriptor64(0xa800, 0x100000);
   dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
   EXPECT_EQ("fail", method_name);
   EXPECT_EQ(0x123U, method_offset);
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 89331ea..d9acdec 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -79,12 +79,12 @@
 
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
-  MapInfo info1(start, end, 0, 0x5, tf.path);
-  MapInfo info2(start, end, 0, 0x5, tf.path);
+  MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
 
-  Elf* elf1 = info1.GetElf(memory_, true);
+  Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf1->valid());
-  Elf* elf2 = info2.GetElf(memory_, true);
+  Elf* elf2 = info2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf2->valid());
 
   if (cache_enabled) {
@@ -120,22 +120,22 @@
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
   // Will have an elf at offset 0 in file.
-  MapInfo info0_1(start, end, 0, 0x5, tf.path);
-  MapInfo info0_2(start, end, 0, 0x5, tf.path);
+  MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info0_2(nullptr, start, end, 0, 0x5, tf.path);
   // Will have an elf at offset 0x100 in file.
-  MapInfo info100_1(start, end, 0x100, 0x5, tf.path);
-  MapInfo info100_2(start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_2(nullptr, start, end, 0x100, 0x5, tf.path);
   // Will have an elf at offset 0x200 in file.
-  MapInfo info200_1(start, end, 0x200, 0x5, tf.path);
-  MapInfo info200_2(start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_2(nullptr, start, end, 0x200, 0x5, tf.path);
   // Will have an elf at offset 0 in file.
-  MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
 
-  Elf* elf0_1 = info0_1.GetElf(memory_, true);
+  Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf0_1->valid());
   EXPECT_EQ(ARCH_ARM, elf0_1->arch());
-  Elf* elf0_2 = info0_2.GetElf(memory_, true);
+  Elf* elf0_2 = info0_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf0_2->valid());
   EXPECT_EQ(ARCH_ARM, elf0_2->arch());
   EXPECT_EQ(0U, info0_1.elf_offset);
@@ -146,10 +146,10 @@
     EXPECT_NE(elf0_1, elf0_2);
   }
 
-  Elf* elf100_1 = info100_1.GetElf(memory_, true);
+  Elf* elf100_1 = info100_1.GetElf(memory_, ARCH_X86);
   ASSERT_TRUE(elf100_1->valid());
   EXPECT_EQ(ARCH_X86, elf100_1->arch());
-  Elf* elf100_2 = info100_2.GetElf(memory_, true);
+  Elf* elf100_2 = info100_2.GetElf(memory_, ARCH_X86);
   ASSERT_TRUE(elf100_2->valid());
   EXPECT_EQ(ARCH_X86, elf100_2->arch());
   EXPECT_EQ(0U, info100_1.elf_offset);
@@ -160,10 +160,10 @@
     EXPECT_NE(elf100_1, elf100_2);
   }
 
-  Elf* elf200_1 = info200_1.GetElf(memory_, true);
+  Elf* elf200_1 = info200_1.GetElf(memory_, ARCH_X86_64);
   ASSERT_TRUE(elf200_1->valid());
   EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
-  Elf* elf200_2 = info200_2.GetElf(memory_, true);
+  Elf* elf200_2 = info200_2.GetElf(memory_, ARCH_X86_64);
   ASSERT_TRUE(elf200_2->valid());
   EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
   EXPECT_EQ(0U, info200_1.elf_offset);
@@ -174,10 +174,10 @@
     EXPECT_NE(elf200_1, elf200_2);
   }
 
-  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_1->valid());
   EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_2->valid());
   EXPECT_EQ(ARCH_ARM, elf300_2->arch());
   EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -217,15 +217,15 @@
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
   // Multiple info sections at different offsets will have non-zero elf offsets.
-  MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
-  MapInfo info400_1(start, end, 0x400, 0x5, tf.path);
-  MapInfo info400_2(start, end, 0x400, 0x5, tf.path);
+  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
+  MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
 
-  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_1->valid());
   EXPECT_EQ(ARCH_ARM, elf300_1->arch());
-  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_2->valid());
   EXPECT_EQ(ARCH_ARM, elf300_2->arch());
   EXPECT_EQ(0x300U, info300_1.elf_offset);
@@ -236,10 +236,10 @@
     EXPECT_NE(elf300_1, elf300_2);
   }
 
-  Elf* elf400_1 = info400_1.GetElf(memory_, true);
+  Elf* elf400_1 = info400_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf400_1->valid());
   EXPECT_EQ(ARCH_ARM, elf400_1->arch());
-  Elf* elf400_2 = info400_2.GetElf(memory_, true);
+  Elf* elf400_2 = info400_2.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf400_2->valid());
   EXPECT_EQ(ARCH_ARM, elf400_2->arch());
   EXPECT_EQ(0x400U, info400_1.elf_offset);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 55fe16f..ccf8927 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -110,7 +110,7 @@
 TEST_F(ElfTest, invalid_memory) {
   Elf elf(memory_);
 
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
   ASSERT_FALSE(elf.valid());
 }
 
@@ -122,7 +122,7 @@
   // Corrupt the ELF signature.
   memory_->SetData32(0, 0x7f000000);
 
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
   ASSERT_FALSE(elf.valid());
   ASSERT_TRUE(elf.interface() == nullptr);
 
@@ -142,7 +142,7 @@
   InitElf32(EM_PPC);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
@@ -155,7 +155,7 @@
   InitElf64(EM_PPC64);
 
   ResetLogs();
-  ASSERT_FALSE(elf.Init(false));
+  ASSERT_FALSE(elf.Init());
 
   ASSERT_EQ("", GetFakeLogBuf());
   ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
@@ -167,7 +167,7 @@
 
   InitElf32(EM_ARM);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_ARM), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -179,7 +179,7 @@
 
   InitElf32(EM_MIPS);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -191,7 +191,7 @@
 
   InitElf32(EM_386);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_386), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -203,7 +203,7 @@
 
   InitElf64(EM_AARCH64);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -215,7 +215,7 @@
 
   InitElf64(EM_X86_64);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_X86_64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -227,41 +227,13 @@
 
   InitElf64(EM_MIPS);
 
-  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
   ASSERT_TRUE(elf.interface() != nullptr);
 }
 
-TEST_F(ElfTest, gnu_debugdata_init_fail32) {
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(offset, ptr, size);
-                                               });
-
-  Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(false));
-  ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
-  EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
-  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
-TEST_F(ElfTest, gnu_debugdata_init_fail64) {
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(offset, ptr, size);
-                                               });
-
-  Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(false));
-  ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
-  EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
-  EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
-}
-
 TEST_F(ElfTest, gnu_debugdata_init32) {
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -269,7 +241,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(true));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
@@ -283,7 +255,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init(true));
+  ASSERT_TRUE(elf.Init());
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
@@ -297,7 +269,7 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  MapInfo map_info(0x1000, 0x2000);
+  MapInfo map_info(nullptr, 0x1000, 0x2000);
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index c1c45f8..b1ca111 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -35,27 +35,7 @@
 
 class JitDebugTest : public ::testing::Test {
  protected:
-  void SetUp() override {
-    memory_ = new MemoryFake;
-    process_memory_.reset(memory_);
-
-    jit_debug_.reset(new JitDebug(process_memory_));
-    jit_debug_->SetArch(ARCH_ARM);
-
-    maps_.reset(
-        new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
-                       "4000-6000 r--s 00000000 00:00 0\n"
-                       "6000-8000 -w-s 00000000 00:00 0\n"
-                       "a000-c000 --xp 00000000 00:00 0\n"
-                       "c000-f000 rwxp 00000000 00:00 0\n"
-                       "f000-11000 r-xp 00000000 00:00 0\n"
-                       "12000-14000 r-xp 00000000 00:00 0\n"
-                       "100000-110000 rw-p 0000000 00:00 0\n"
-                       "200000-210000 rw-p 0000000 00:00 0\n"));
-    ASSERT_TRUE(maps_->Parse());
-
-    MapInfo* map_info = maps_->Get(3);
-    ASSERT_TRUE(map_info != nullptr);
+  void CreateFakeElf(MapInfo* map_info) {
     MemoryFake* memory = new MemoryFake;
     ElfFake* elf = new ElfFake(memory);
     elf->FakeSetValid(true);
@@ -63,26 +43,43 @@
     elf->FakeSetInterface(interface);
     interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
     map_info->elf.reset(elf);
+  }
+
+  void Init(ArchEnum arch) {
+    jit_debug_.reset(new JitDebug(process_memory_));
+    jit_debug_->SetArch(arch);
+
+    maps_.reset(
+        new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
+                       "4000-6000 r--s 00000000 00:00 0 /fake/elf1\n"
+                       "6000-8000 -wxs 00000000 00:00 0 /fake/elf1\n"
+                       "a000-c000 --xp 00000000 00:00 0 /fake/elf2\n"
+                       "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n"
+                       "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+                       "11000-12000 rw-p 00001000 00:00 0 /fake/elf3\n"
+                       "12000-14000 r--p 00000000 00:00 0 /fake/elf4\n"
+                       "100000-110000 rw-p 0001000 00:00 0 /fake/elf4\n"
+                       "200000-210000 rw-p 0002000 00:00 0 /fake/elf4\n"));
+    ASSERT_TRUE(maps_->Parse());
+
+    MapInfo* map_info = maps_->Get(3);
+    ASSERT_TRUE(map_info != nullptr);
+    CreateFakeElf(map_info);
 
     map_info = maps_->Get(5);
     ASSERT_TRUE(map_info != nullptr);
-    memory = new MemoryFake;
-    elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
 
-    map_info = maps_->Get(6);
+    map_info = maps_->Get(7);
     ASSERT_TRUE(map_info != nullptr);
-    memory = new MemoryFake;
-    elf = new ElfFake(memory);
-    elf->FakeSetValid(true);
-    interface = new ElfInterfaceFake(memory);
-    elf->FakeSetInterface(interface);
-    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
-    map_info->elf.reset(elf);
+    CreateFakeElf(map_info);
+  }
+
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    Init(ARCH_ARM);
   }
 
   template <typename EhdrType, typename ShdrType>
@@ -325,6 +322,8 @@
 }
 
 TEST_F(JitDebugTest, get_elf_x86) {
+  Init(ARCH_X86);
+
   CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
 
   WriteDescriptor32(0xf800, 0x200000);
@@ -342,12 +341,13 @@
 }
 
 TEST_F(JitDebugTest, get_elf_64) {
+  Init(ARCH_ARM64);
+
   CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
 
   WriteDescriptor64(0xf800, 0x200000);
   WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
 
-  jit_debug_->SetArch(ARCH_ARM64);
   Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
   ASSERT_TRUE(elf != nullptr);
 
@@ -397,6 +397,8 @@
   // Change the name of the map that includes the value and verify this works.
   MapInfo* map_info = maps_->Get(5);
   map_info->name = "/system/lib/libart.so";
+  map_info = maps_->Get(6);
+  map_info->name = "/system/lib/libart.so";
   jit_debug_.reset(new JitDebug(process_memory_, libs));
   // Make sure that clearing our copy of the libs doesn't affect the
   // JitDebug object.
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 866b5b4..2a73c7e 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -32,8 +32,10 @@
 
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 
+#include "ElfTestUtils.h"
 #include "MemoryFake.h"
 
 namespace unwindstack {
@@ -94,7 +96,7 @@
 TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
-  MapInfo info(0x100, 0x100, 0, 0, elf_.path);
+  MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
@@ -112,7 +114,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(0x100, 0x200, 0x100, 0, elf_.path);
+  MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -133,7 +135,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(0x100, 0x200, 0x100, 0, elf_at_100_.path);
+  MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -156,7 +158,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -172,7 +174,7 @@
 }
 
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -192,27 +194,24 @@
   // Set up some memory so that a valid local memory object would
   // be returned if the file mapping fails, but the device check is incorrect.
   std::vector<uint8_t> buffer(1024);
-  MapInfo info;
-  info.start = reinterpret_cast<uint64_t>(buffer.data());
-  info.end = info.start + buffer.size();
-  info.offset = 0;
+  uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
+  MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
 
-  info.flags = 0x8000;
-  info.name = "/dev/something";
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 }
 
 TEST_F(MapInfoCreateMemoryTest, process_memory) {
-  MapInfo info;
-  info.start = 0x2000;
-  info.end = 0x3000;
-  info.offset = 0;
+  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  std::vector<uint8_t> buffer(1024);
+  memcpy(buffer.data(), &ehdr, sizeof(ehdr));
 
   // Verify that the the process_memory object is used, so seed it
   // with memory.
-  std::vector<uint8_t> buffer(1024);
-  for (size_t i = 0; i < buffer.size(); i++) {
+  for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
     buffer[i] = i % 256;
   }
   memory_->SetMemory(info.start, buffer.data(), buffer.size());
@@ -222,7 +221,8 @@
 
   memset(buffer.data(), 0, buffer.size());
   ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
-  for (size_t i = 0; i < buffer.size(); i++) {
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
     ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
   }
 
@@ -230,4 +230,87 @@
   ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
 }
 
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2600, 0, PROT_READ, "/only/in/memory.so", 0);
+  maps.Add(0x3000, 0x5000, 0x4000, PROT_READ | PROT_EXEC, "/only/in/memory.so", 0);
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x1600 - sizeof(ehdr), 0xab);
+
+  // Set the memory in the r-x map.
+  memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
+
+  MapInfo* map_info = maps.Find(0x3000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(mem.get() != nullptr);
+  EXPECT_EQ(0x4000UL, map_info->elf_offset);
+  EXPECT_EQ(0x4000UL, map_info->offset);
+
+  // Verify that reading values from this memory works properly.
+  std::vector<uint8_t> buffer(0x4000);
+  size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1600UL, bytes);
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < bytes; i++) {
+    ASSERT_EQ(0xab, buffer[i]) << "Failed at byte " << i;
+  }
+
+  bytes = mem->Read(0x4000, buffer.data(), buffer.size());
+  ASSERT_EQ(0x2000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x5d, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2000, 0, PROT_READ, "/only/in/memory.apk", 0);
+  maps.Add(0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+  maps.Add(0x3000, 0x4000, 0xa000, PROT_READ, "/only/in/memory.apk", 0);
+  maps.Add(0x4000, 0x5000, 0xb000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+  Elf32_Ehdr ehdr = {};
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+  // Setup an elf at offset 0x1000 in memory.
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x2000 - sizeof(ehdr), 0x12);
+  memory_->SetMemoryBlock(0x2000, 0x1000, 0x23);
+
+  // Setup an elf at offset 0x3000 in memory..
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+  memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
+  memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
+
+  MapInfo* map_info = maps.Find(0x4000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(mem.get() != nullptr);
+  EXPECT_EQ(0x1000UL, map_info->elf_offset);
+  EXPECT_EQ(0xb000UL, map_info->offset);
+
+  // Verify that reading values from this memory works properly.
+  std::vector<uint8_t> buffer(0x4000);
+  size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1000UL, bytes);
+  ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < bytes; i++) {
+    ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+  }
+
+  bytes = mem->Read(0x1000, buffer.data(), buffer.size());
+  ASSERT_EQ(0x1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x43, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 861b82f..4d74696 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -69,22 +69,22 @@
 };
 
 TEST_F(MapInfoGetElfTest, invalid) {
-  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid32) {
-  MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -92,60 +92,40 @@
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
-  MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
 
   Elf64_Ehdr ehdr;
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
   memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
 }
 
-TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
-  MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
+TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
+  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(0x4000 + offset, ptr, size);
-                                               });
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_X86);
   ASSERT_TRUE(elf != nullptr);
-  ASSERT_TRUE(elf->valid());
-  EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
-  EXPECT_EQ(ELFCLASS32, elf->class_type());
-  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
-}
-
-TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
-  MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
-
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
-                                               [&](uint64_t offset, const void* ptr, size_t size) {
-                                                 memory_->SetMemory(0x6000 + offset, ptr, size);
-                                               });
-
-  Elf* elf = info.GetElf(process_memory_, false);
-  ASSERT_TRUE(elf != nullptr);
-  ASSERT_TRUE(elf->valid());
-  EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
-  EXPECT_EQ(ELFCLASS64, elf->class_type());
-  EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+  ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
-  MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
                                                  memory_->SetMemory(0x2000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_, true);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -154,14 +134,14 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
-  MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
                                                  memory_->SetMemory(0x5000 + offset, ptr, size);
                                                });
 
-  Elf* elf = info.GetElf(process_memory_, true);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -170,26 +150,26 @@
 }
 
 TEST_F(MapInfoGetElfTest, end_le_start) {
-  MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   info.elf.reset();
   info.end = 0xfff;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Make sure this test is valid.
   info.elf.reset();
   info.end = 0x2000;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
 }
@@ -197,7 +177,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x1000);
   memset(buffer.data(), 0, buffer.size());
@@ -206,7 +186,7 @@
   memcpy(buffer.data(), &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -226,7 +206,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -235,7 +215,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -256,7 +236,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -268,7 +248,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -284,7 +264,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -296,7 +276,7 @@
   memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
   ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   ASSERT_TRUE(elf->memory() != nullptr);
@@ -312,7 +292,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
-  MapInfo info(0x9000, 0xa000, 0x1000, 0, "");
+  MapInfo info(nullptr, 0x9000, 0xa000, 0x1000, 0, "");
 
   // Create valid elf data in process memory only.
   Elf64_Ehdr ehdr;
@@ -322,18 +302,19 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   info.elf.reset();
   info.flags = PROT_READ;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_, ARCH_ARM64);
   ASSERT_TRUE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, check_device_maps) {
-  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+               "/dev/something");
 
   // Create valid elf data in process memory for this to verify that only
   // the name is causing invalid elf data.
@@ -344,20 +325,20 @@
   ehdr.e_shnum = 0;
   memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
 
-  Elf* elf = info.GetElf(process_memory_, false);
+  Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
   ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 
   // Set the name to nothing to verify that it still fails.
   info.elf.reset();
   info.name = "";
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_, ARCH_X86_64);
   ASSERT_FALSE(elf->valid());
 
   // Change the flags and verify the elf is valid now.
   info.elf.reset();
   info.flags = PROT_READ;
-  elf = info.GetElf(process_memory_, false);
+  elf = info.GetElf(process_memory_, ARCH_X86_64);
   ASSERT_TRUE(elf->valid());
 }
 
@@ -378,12 +359,12 @@
   wait = true;
   // Create all of the threads and have them do the GetElf at the same time
   // to make it likely that a race will occur.
-  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, "");
+  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
   for (size_t i = 0; i < kNumConcurrentThreads; i++) {
     std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
       while (wait)
         ;
-      Elf* elf = info.GetElf(process_memory_, false);
+      Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
       elf_in_threads[i] = elf;
     });
     threads.push_back(thread);
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 7e64a8a..f5ac6cb 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@
     process_memory_.reset(memory_);
     elf_ = new ElfFake(new MemoryFake);
     elf_container_.reset(elf_);
-    map_info_.reset(new MapInfo(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
   }
 
   void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@
 };
 
 TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
 }
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9622ba5..6bdd0b2 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -63,7 +63,7 @@
 }
 
 TEST(MapsTest, verify_parse_line) {
-  MapInfo info;
+  MapInfo info(nullptr);
 
   VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
   EXPECT_EQ(1U, info.start);
@@ -136,7 +136,7 @@
 }
 
 TEST(MapsTest, verify_large_values) {
-  MapInfo info;
+  MapInfo info(nullptr);
 #if defined(__LP64__)
   VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
   EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index 60936cd..5695dfc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -23,6 +23,17 @@
 
 namespace unwindstack {
 
+void MemoryFake::SetMemoryBlock(uint64_t addr, size_t length, uint8_t value) {
+  for (size_t i = 0; i < length; i++, addr++) {
+    auto entry = data_.find(addr);
+    if (entry != data_.end()) {
+      entry->second = value;
+    } else {
+      data_.insert({addr, value});
+    }
+  }
+}
+
 void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
   const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
   for (size_t i = 0; i < length; i++, addr++) {
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 764a6c3..20610a5 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -36,6 +36,8 @@
 
   void SetMemory(uint64_t addr, const void* memory, size_t length);
 
+  void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
+
   void SetData8(uint64_t addr, uint8_t value) {
     SetMemory(addr, &value, sizeof(value));
   }
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index cb1a0c9..2bac95b 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <stdint.h>
-#include <string.h>
 
 #include <memory>
 #include <vector>
@@ -28,30 +27,34 @@
 
 namespace unwindstack {
 
-TEST(MemoryRangeTest, read) {
-  std::vector<uint8_t> src(1024);
-  memset(src.data(), 0x4c, 1024);
-  MemoryFake* memory_fake = new MemoryFake;
-  std::shared_ptr<Memory> process_memory(memory_fake);
-  memory_fake->SetMemory(9001, src);
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    process_memory_.reset();
+    memory_fake_ = new MemoryFake;
+    process_memory_.reset(memory_fake_);
+  }
 
-  MemoryRange range(process_memory, 9001, src.size(), 0);
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_fake_ = nullptr;
+};
+
+TEST_F(MemoryRangeTest, read_fully) {
+  memory_fake_->SetMemoryBlock(9000, 2048, 0x4c);
+
+  MemoryRange range(process_memory_, 9001, 1024, 0);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size()));
-  for (size_t i = 0; i < 1024; i++) {
+  ASSERT_TRUE(range.ReadFully(0, dst.data(), dst.size()));
+  for (size_t i = 0; i < dst.size(); i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 }
 
-TEST(MemoryRangeTest, read_near_limit) {
-  std::vector<uint8_t> src(4096);
-  memset(src.data(), 0x4c, 4096);
-  MemoryFake* memory_fake = new MemoryFake;
-  std::shared_ptr<Memory> process_memory(memory_fake);
-  memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read_fully_near_limit) {
+  memory_fake_->SetMemoryBlock(0, 8192, 0x4c);
 
-  MemoryRange range(process_memory, 1000, 1024, 0);
+  MemoryRange range(process_memory_, 1000, 1024, 0);
 
   std::vector<uint8_t> dst(1024);
   ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
@@ -68,7 +71,7 @@
   ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
 }
 
-TEST(MemoryRangeTest, read_overflow) {
+TEST_F(MemoryRangeTest, read_fully_overflow) {
   std::vector<uint8_t> buffer(100);
 
   std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
@@ -76,19 +79,28 @@
   ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100));
 }
 
-TEST(MemoryRangeTest, Read) {
-  std::vector<uint8_t> src(4096);
-  memset(src.data(), 0x4c, 4096);
-  MemoryFake* memory_fake = new MemoryFake;
-  std::shared_ptr<Memory> process_memory(memory_fake);
-  memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read) {
+  memory_fake_->SetMemoryBlock(0, 4096, 0x4c);
 
-  MemoryRange range(process_memory, 1000, 1024, 0);
+  MemoryRange range(process_memory_, 1000, 1024, 0);
+
   std::vector<uint8_t> dst(1024);
-  ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024));
+  ASSERT_EQ(4U, range.Read(1020, dst.data(), dst.size()));
   for (size_t i = 0; i < 4; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 }
 
+TEST_F(MemoryRangeTest, read_non_zero_offset) {
+  memory_fake_->SetMemoryBlock(1000, 1024, 0x12);
+
+  MemoryRange range(process_memory_, 1000, 1024, 400);
+
+  std::vector<uint8_t> dst(1024);
+  ASSERT_EQ(1024U, range.Read(400, dst.data(), dst.size()));
+  for (size_t i = 0; i < dst.size(); i++) {
+    ASSERT_EQ(0x12U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
new file mode 100644
index 0000000..d24fcd2
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryRangesTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    MemoryFake* memory = new MemoryFake;
+    process_memory_.reset(memory);
+    memory->SetMemoryBlock(1000, 5000, 0x15);
+    memory->SetMemoryBlock(6000, 12000, 0x26);
+    memory->SetMemoryBlock(14000, 20000, 0x37);
+    memory->SetMemoryBlock(20000, 22000, 0x48);
+
+    ranges_.reset(new MemoryRanges);
+    ranges_->Insert(new MemoryRange(process_memory_, 15000, 100, 4000));
+    ranges_->Insert(new MemoryRange(process_memory_, 10000, 2000, 2000));
+    ranges_->Insert(new MemoryRange(process_memory_, 3000, 1000, 0));
+    ranges_->Insert(new MemoryRange(process_memory_, 19000, 1000, 6000));
+    ranges_->Insert(new MemoryRange(process_memory_, 20000, 1000, 7000));
+  }
+
+  std::shared_ptr<Memory> process_memory_;
+  std::unique_ptr<MemoryRanges> ranges_;
+};
+
+TEST_F(MemoryRangesTest, read) {
+  std::vector<uint8_t> dst(2000);
+  size_t bytes = ranges_->Read(0, dst.data(), dst.size());
+  ASSERT_EQ(1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x15U, dst[i]) << "Failed at byte " << i;
+  }
+
+  bytes = ranges_->Read(2000, dst.data(), dst.size());
+  ASSERT_EQ(2000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x26U, dst[i]) << "Failed at byte " << i;
+  }
+
+  bytes = ranges_->Read(4000, dst.data(), dst.size());
+  ASSERT_EQ(100UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryRangesTest, read_fail) {
+  std::vector<uint8_t> dst(4096);
+  ASSERT_EQ(0UL, ranges_->Read(1000, dst.data(), dst.size()));
+  ASSERT_EQ(0UL, ranges_->Read(5000, dst.data(), dst.size()));
+  ASSERT_EQ(0UL, ranges_->Read(8000, dst.data(), dst.size()));
+}
+
+TEST_F(MemoryRangesTest, read_across_ranges) {
+  // The MemoryRanges object does not support reading across a range,
+  // so this will only read in the first range.
+  std::vector<uint8_t> dst(4096);
+  size_t bytes = ranges_->Read(6000, dst.data(), dst.size());
+  ASSERT_EQ(1000UL, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsInfoTest.cpp b/libunwindstack/tests/RegsInfoTest.cpp
index 052b5bf..a6bc2c5 100644
--- a/libunwindstack/tests/RegsInfoTest.cpp
+++ b/libunwindstack/tests/RegsInfoTest.cpp
@@ -82,4 +82,11 @@
   }
 }
 
+TEST(RegsInfoTest, invalid_register) {
+  RegsImplFake<uint64_t> regs(64);
+  RegsInfo<uint64_t> info(&regs);
+
+  EXPECT_DEATH(info.Save(RegsInfo<uint64_t>::MAX_REGISTERS), "");
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 90c3fe6..00264c2 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
   RegsX86_64 regs_x86_64;
   RegsMips regs_mips;
   RegsMips64 regs_mips64;
-  MapInfo map_info(0x1000, 0x2000);
+  MapInfo map_info(nullptr, 0x1000, 0x2000);
   Elf* invalid_elf = new Elf(nullptr);
   map_info.elf.reset(invalid_elf);
 
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2428f68..831d3b5 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -58,51 +58,54 @@
  protected:
   static void SetUpTestCase() {
     maps_.FakeClear();
-    MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+    MapInfo* info =
+        new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
     ElfFake* elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
+    info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+    info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
                        "/dev/fake_device");
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so");
+    info = new MapInfo(&maps_, 0x20000, 0x22000, 0, PROT_READ | PROT_WRITE,
+                       "/system/fake/libunwind.so");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
+    info = new MapInfo(&maps_, 0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
+    info = new MapInfo(&maps_, 0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
+    info = new MapInfo(&maps_, 0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
     elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
+    info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+    info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                       "/fake/fake.vdex");
     info->load_bias = 0;
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+    info = new MapInfo(&maps_, 0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
                        "/fake/fake_load_bias.so");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
@@ -110,7 +113,7 @@
     elf->FakeSetLoadBias(0x5000);
     maps_.FakeAddMapInfo(info);
 
-    info = new MapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+    info = new MapInfo(&maps_, 0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
                        "/fake/fake_offset.oat");
     elf = new ElfFake(new MemoryFake);
     info->elf.reset(elf);
@@ -943,6 +946,33 @@
   EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
+TEST_F(UnwinderTest, dex_pc_max_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetDexPc(0xa3400);
+
+  Unwinder unwinder(1, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0xa3400U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa3000U, frame->map_start);
+  EXPECT_EQ(0xa4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
 // Verify format frame code.
 TEST_F(UnwinderTest, format_frame_static) {
   FrameData frame;
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
index 55aaaf6..5657373 100644
--- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
@@ -1,3 +1,4 @@
 d0250000-d2600000 r-xp 0 00:00 0 <anonymous:d0250000>
 e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e4ae8000-e4ae9000 rw-p 1000 00:00 0 libart.so
 e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
index f25c781..3cd9d40 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -2,7 +2,9 @@
 dfe4e000-dfe7b000 r-xp 0 00:00 0   libarttestd.so
 e0447000-e0448000 r-xp 2000 00:00 0   137-cfi.odex
 e2796000-e4796000 r-xp 0 00:00 0   anonymous:e2796000
-e648e000-e690f000 r-xp 00000000 00:00 0  libart.so
+e648e000-e690f000 r-xp 0 00:00 0  libart.so
+e690f000-e6910000 rw-p 1000 00:00 0  libart.so
 ed306000-ed801000 r-xp 0 00:00 0   libartd.so
+ed801000-ed802000 rw-p 1000 00:00 0   libartd.so
 eda88000-edb23000 r-xp 0 00:00 0   libc.so
 ede4e000-ede50000 r-xp 0 00:00 0   anonymous:ede4e000
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
index db4f9f7..a8d215c 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -3,4 +3,5 @@
 ec606000-ec607000 r-xp 2000 00:00 0   137-cfi.odex
 ee74c000-f074c000 r-xp 0 00:00 0   anonymous:ee74c000
 f6be1000-f732b000 r-xp 0 00:00 0   libartd.so
+f732b000-f732c000 rw-p 1000 00:00 0   libartd.so
 f734b000-f74fc000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 266a6db..aebeb95 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -113,7 +113,7 @@
   }
 
   Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 0f01566..4b6f49a 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -173,7 +173,7 @@
   }
 
   Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
   }
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index f8e3e92..9128430 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -66,7 +66,7 @@
   }
 
   unwindstack::Elf elf(memory);
-  if (!elf.Init(true) || !elf.valid()) {
+  if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", argv[1]);
     return 1;
   }
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 583c6b9..5feb2aa 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -62,11 +62,17 @@
 }
 
 // Move Constructor.
-FileMap::FileMap(FileMap&& other)
-    : mFileName(other.mFileName), mBasePtr(other.mBasePtr), mBaseLength(other.mBaseLength),
-      mDataOffset(other.mDataOffset), mDataPtr(other.mDataPtr), mDataLength(other.mDataLength)
+FileMap::FileMap(FileMap&& other) noexcept
+    : mFileName(other.mFileName),
+      mBasePtr(other.mBasePtr),
+      mBaseLength(other.mBaseLength),
+      mDataOffset(other.mDataOffset),
+      mDataPtr(other.mDataPtr),
+      mDataLength(other.mDataLength)
 #if defined(__MINGW32__)
-      , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
+      ,
+      mFileHandle(other.mFileHandle),
+      mFileMapping(other.mFileMapping)
 #endif
 {
     other.mFileName = nullptr;
@@ -79,7 +85,7 @@
 }
 
 // Move assign operator.
-FileMap& FileMap::operator=(FileMap&& other) {
+FileMap& FileMap::operator=(FileMap&& other) noexcept {
     mFileName = other.mFileName;
     mBasePtr = other.mBasePtr;
     mBaseLength = other.mBaseLength;
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
index b8c065d..f00272a 100644
--- a/libutils/PropertyMap.cpp
+++ b/libutils/PropertyMap.cpp
@@ -208,7 +208,7 @@
 
         mTokenizer->nextLine();
     }
-    return NO_ERROR;
+    return OK;
 }
 
 } // namespace android
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 5c0b406..818b171 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -157,12 +157,12 @@
     if (begin >= N) {
         SharedBuffer::bufferFromData(mString)->release();
         mString = getEmptyString();
-        return NO_ERROR;
+        return OK;
     }
     if ((begin+len) > N) len = N-begin;
     if (begin == 0 && len == N) {
         setTo(other);
-        return NO_ERROR;
+        return OK;
     }
 
     if (&other == this) {
@@ -191,7 +191,7 @@
         memmove(str, other, len*sizeof(char16_t));
         str[len] = 0;
         mString = str;
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -202,9 +202,9 @@
     const size_t otherLen = other.size();
     if (myLen == 0) {
         setTo(other);
-        return NO_ERROR;
+        return OK;
     } else if (otherLen == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
@@ -218,7 +218,7 @@
         char16_t* str = (char16_t*)buf->data();
         memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
         mString = str;
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -228,9 +228,9 @@
     const size_t myLen = size();
     if (myLen == 0) {
         setTo(chrs, otherLen);
-        return NO_ERROR;
+        return OK;
     } else if (otherLen == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
@@ -245,7 +245,7 @@
         memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
         str[myLen+otherLen] = 0;
         mString = str;
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -260,9 +260,9 @@
     const size_t myLen = size();
     if (myLen == 0) {
         return setTo(chrs, len);
-        return NO_ERROR;
+        return OK;
     } else if (len == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     if (pos > myLen) pos = myLen;
@@ -286,7 +286,7 @@
         #if 0
         printf("Result (%d chrs): %s\n", size(), String8(*this).string());
         #endif
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -357,7 +357,7 @@
             edit[i] = tolower((char)v);
         }
     }
-    return NO_ERROR;
+    return OK;
 }
 
 status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
@@ -378,7 +378,7 @@
             edit[i] = withThis;
         }
     }
-    return NO_ERROR;
+    return OK;
 }
 
 status_t String16::remove(size_t len, size_t begin)
@@ -387,11 +387,11 @@
     if (begin >= N) {
         SharedBuffer::bufferFromData(mString)->release();
         mString = getEmptyString();
-        return NO_ERROR;
+        return OK;
     }
     if ((begin+len) > N) len = N-begin;
     if (begin == 0 && len == N) {
-        return NO_ERROR;
+        return OK;
     }
 
     if (begin > 0) {
@@ -410,7 +410,7 @@
         char16_t* str = (char16_t*)buf->data();
         str[len] = 0;
         mString = str;
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 8d318f7..0025c56 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -230,7 +230,7 @@
     const char *newString = allocFromUTF8(other, strlen(other));
     SharedBuffer::bufferFromData(mString)->release();
     mString = newString;
-    if (mString) return NO_ERROR;
+    if (mString) return OK;
 
     mString = getEmptyString();
     return NO_MEMORY;
@@ -241,7 +241,7 @@
     const char *newString = allocFromUTF8(other, len);
     SharedBuffer::bufferFromData(mString)->release();
     mString = newString;
-    if (mString) return NO_ERROR;
+    if (mString) return OK;
 
     mString = getEmptyString();
     return NO_MEMORY;
@@ -252,7 +252,7 @@
     const char *newString = allocFromUTF16(other, len);
     SharedBuffer::bufferFromData(mString)->release();
     mString = newString;
-    if (mString) return NO_ERROR;
+    if (mString) return OK;
 
     mString = getEmptyString();
     return NO_MEMORY;
@@ -263,7 +263,7 @@
     const char *newString = allocFromUTF32(other, len);
     SharedBuffer::bufferFromData(mString)->release();
     mString = newString;
-    if (mString) return NO_ERROR;
+    if (mString) return OK;
 
     mString = getEmptyString();
     return NO_MEMORY;
@@ -274,9 +274,9 @@
     const size_t otherLen = other.bytes();
     if (bytes() == 0) {
         setTo(other);
-        return NO_ERROR;
+        return OK;
     } else if (otherLen == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     return real_append(other.string(), otherLen);
@@ -292,7 +292,7 @@
     if (bytes() == 0) {
         return setTo(other, otherLen);
     } else if (otherLen == 0) {
-        return NO_ERROR;
+        return OK;
     }
 
     return real_append(other, otherLen);
@@ -311,7 +311,7 @@
 
 status_t String8::appendFormatV(const char* fmt, va_list args)
 {
-    int n, result = NO_ERROR;
+    int n, result = OK;
     va_list tmp_args;
 
     /* args is undefined after vsnprintf.
@@ -346,7 +346,7 @@
         str += myLen;
         memcpy(str, other, otherLen);
         str[otherLen] = '\0';
-        return NO_ERROR;
+        return OK;
     }
     return NO_MEMORY;
 }
@@ -382,7 +382,7 @@
         mString = str;
     }
 
-    return NO_ERROR;
+    return OK;
 }
 
 ssize_t String8::find(const char* other, size_t start) const
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 43ec6c1..64bc402 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -379,7 +379,7 @@
 {
     DWORD dwWaitResult;
     dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE);
-    return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR;
+    return dwWaitResult != WAIT_OBJECT_0 ? -1 : OK;
 }
 
 void Mutex::unlock()
@@ -506,7 +506,7 @@
         ReleaseMutex(condState->internalMutex);
         WaitForSingleObject(hMutex, INFINITE);
 
-        return res == WAIT_OBJECT_0 ? NO_ERROR : -1;
+        return res == WAIT_OBJECT_0 ? OK : -1;
     }
 } WinCondition;
 
@@ -639,13 +639,15 @@
  */
 
 Thread::Thread(bool canCallJava)
-    :   mCanCallJava(canCallJava),
-        mThread(thread_id_t(-1)),
-        mLock("Thread::mLock"),
-        mStatus(NO_ERROR),
-        mExitPending(false), mRunning(false)
+    : mCanCallJava(canCallJava),
+      mThread(thread_id_t(-1)),
+      mLock("Thread::mLock"),
+      mStatus(OK),
+      mExitPending(false),
+      mRunning(false)
 #if defined(__ANDROID__)
-        , mTid(-1)
+      ,
+      mTid(-1)
 #endif
 {
 }
@@ -656,7 +658,7 @@
 
 status_t Thread::readyToRun()
 {
-    return NO_ERROR;
+    return OK;
 }
 
 status_t Thread::run(const char* name, int32_t priority, size_t stack)
@@ -672,7 +674,7 @@
 
     // reset status and exitPending to their default value, so we can
     // try again after an error happened (either below, or in readyToRun())
-    mStatus = NO_ERROR;
+    mStatus = OK;
     mExitPending = false;
     mThread = thread_id_t(-1);
 
@@ -700,10 +702,10 @@
     }
 
     // Do not refer to mStatus here: The thread is already running (may, in fact
-    // already have exited with a valid mStatus result). The NO_ERROR indication
+    // already have exited with a valid mStatus result). The OK indication
     // here merely indicates successfully starting the thread and does not
     // imply successful termination/execution.
-    return NO_ERROR;
+    return OK;
 
     // Exiting scope of mLock is a memory barrier and allows new thread to run
 }
@@ -728,7 +730,7 @@
         if (first) {
             first = false;
             self->mStatus = self->readyToRun();
-            result = (self->mStatus == NO_ERROR);
+            result = (self->mStatus == OK);
 
             if (result && !self->exitPending()) {
                 // Binder threads (and maybe others) rely on threadLoop
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index f73d699..98dd2fd 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -48,7 +48,7 @@
 status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
     *outTokenizer = nullptr;
 
-    int result = NO_ERROR;
+    int result = OK;
     int fd = ::open(filename.string(), O_RDONLY);
     if (fd < 0) {
         result = -errno;
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index e16f88d..c97a19b 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -61,7 +61,7 @@
         "[%p] subclasses of VectorImpl must call finish_vector()"
         " in their destructor. Leaking %d bytes.",
         this, (int)(mCount*mItemSize));
-    // We can't call _do_destroy() here because the vtable is already gone. 
+    // We can't call _do_destroy() here because the vtable is already gone.
 }
 
 VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
@@ -197,7 +197,7 @@
                 _do_copy(temp, item, 1);
 
                 ssize_t j = i-1;
-                void* next = reinterpret_cast<char*>(array) + mItemSize*(i);                    
+                void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
                 do {
                     _do_destroy(next, 1);
                     _do_copy(next, curr, 1);
@@ -214,13 +214,13 @@
             }
             i++;
         }
-        
+
         if (temp) {
             _do_destroy(temp, 1);
             free(temp);
         }
     }
-    return NO_ERROR;
+    return OK;
 }
 
 void VectorImpl::pop()
@@ -354,7 +354,7 @@
 }
 
 ssize_t VectorImpl::resize(size_t size) {
-    ssize_t result = NO_ERROR;
+    ssize_t result = OK;
     if (size > mCount) {
         result = insertAt(mCount, size - mCount);
     } else if (size < mCount) {
@@ -370,7 +370,7 @@
         if (sb->release(SharedBuffer::eKeepStorage) == 1) {
             _do_destroy(mStorage, mCount);
             SharedBuffer::dealloc(sb);
-        } 
+        }
     }
 }
 
@@ -644,13 +644,13 @@
             }
         }
     }
-    return NO_ERROR;
+    return OK;
 }
 
 ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
 {
     // we've merging a sorted vector... nice!
-    ssize_t err = NO_ERROR;
+    ssize_t err = OK;
     if (!vector.isEmpty()) {
         // first take care of the case where the vectors are sorted together
         if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
@@ -677,4 +677,3 @@
 /*****************************************************************************/
 
 }; // namespace android
-
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index 7093a20..1e03677 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -14,22 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_ERRORS_H
-#define ANDROID_ERRORS_H
+#pragma once
 
-#include <sys/types.h>
 #include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
 
 namespace android {
 
-// use this type to return error codes
-#ifdef _WIN32
-typedef int         status_t;
-#else
-typedef int32_t     status_t;
-#endif
-
-/* the MS C runtime lacks a few error codes */
+/**
+ * The type used to return success/failure from frameworks APIs.
+ * See the anonymous enum below for valid values.
+ */
+typedef int32_t status_t;
 
 /*
  * Error codes. 
@@ -43,8 +40,8 @@
 #endif
 
 enum {
-    OK                = 0,    // Everything's swell.
-    NO_ERROR          = 0,    // No errors.
+    OK                = 0,    // Preferred constant for checking success.
+    NO_ERROR          = OK,   // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
 
     UNKNOWN_ERROR       = (-2147483647-1), // INT32_MIN value
 
@@ -82,7 +79,3 @@
 #endif
 
 }  // namespace android
-
-// ---------------------------------------------------------------------------
-    
-#endif // ANDROID_ERRORS_H
diff --git a/libutils/include/utils/FileMap.h b/libutils/include/utils/FileMap.h
index 8d402a3..f9f8f3c 100644
--- a/libutils/include/utils/FileMap.h
+++ b/libutils/include/utils/FileMap.h
@@ -52,8 +52,8 @@
 public:
     FileMap(void);
 
-    FileMap(FileMap&& f);
-    FileMap& operator=(FileMap&& f);
+    FileMap(FileMap&& f) noexcept;
+    FileMap& operator=(FileMap&& f) noexcept;
 
     /*
      * Create a new mapping on an open file.
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 0a19019..9d00602 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -190,11 +190,11 @@
     inline status_t flatten(void* buffer, size_t size) const {
         if (size < sizeof(T)) return NO_MEMORY;
         memcpy(buffer, static_cast<T const*>(this), sizeof(T));
-        return NO_ERROR;
+        return OK;
     }
     inline status_t unflatten(void const* buffer, size_t) {
         memcpy(static_cast<T*>(this), buffer, sizeof(T));
-        return NO_ERROR;
+        return OK;
     }
 };
 
diff --git a/libutils/include/utils/Functor.h b/libutils/include/utils/Functor.h
index c0c8d57..c458699 100644
--- a/libutils/include/utils/Functor.h
+++ b/libutils/include/utils/Functor.h
@@ -29,7 +29,7 @@
 public:
     Functor() {}
     virtual ~Functor() {}
-    virtual status_t operator ()(int /*what*/, void* /*data*/) { return NO_ERROR; }
+    virtual status_t operator()(int /*what*/, void* /*data*/) { return OK; }
 };
 
 }  // namespace android
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 3abce17..1571129 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -56,7 +56,7 @@
 
     sp(T* other);  // NOLINT(implicit)
     sp(const sp<T>& other);
-    sp(sp<T>&& other);
+    sp(sp<T>&& other) noexcept;
     template<typename U> sp(U* other);  // NOLINT(implicit)
     template<typename U> sp(const sp<U>& other);  // NOLINT(implicit)
     template<typename U> sp(sp<U>&& other);  // NOLINT(implicit)
@@ -67,7 +67,7 @@
 
     sp& operator = (T* other);
     sp& operator = (const sp<T>& other);
-    sp& operator = (sp<T>&& other);
+    sp& operator=(sp<T>&& other) noexcept;
 
     template<typename U> sp& operator = (const sp<U>& other);
     template<typename U> sp& operator = (sp<U>&& other);
@@ -125,9 +125,8 @@
         m_ptr->incStrong(this);
 }
 
-template<typename T>
-sp<T>::sp(sp<T>&& other)
-        : m_ptr(other.m_ptr) {
+template <typename T>
+sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
     other.m_ptr = nullptr;
 }
 
@@ -169,8 +168,8 @@
     return *this;
 }
 
-template<typename T>
-sp<T>& sp<T>::operator =(sp<T>&& other) {
+template <typename T>
+sp<T>& sp<T>::operator=(sp<T>&& other) noexcept {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
     if (oldPtr) oldPtr->decStrong(this);
     if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
diff --git a/libutils/include/utils/Tokenizer.h b/libutils/include/utils/Tokenizer.h
index bb25f37..61c5ff7 100644
--- a/libutils/include/utils/Tokenizer.h
+++ b/libutils/include/utils/Tokenizer.h
@@ -37,7 +37,7 @@
     /**
      * Opens a file and maps it into memory.
      *
-     * Returns NO_ERROR and a tokenizer for the file, if successful.
+     * Returns OK and a tokenizer for the file, if successful.
      * Otherwise returns an error and sets outTokenizer to NULL.
      */
     static status_t open(const String8& filename, Tokenizer** outTokenizer);
@@ -45,7 +45,7 @@
     /**
      * Prepares to tokenize the contents of a string.
      *
-     * Returns NO_ERROR and a tokenizer for the string, if successful.
+     * Returns OK and a tokenizer for the string, if successful.
      * Otherwise returns an error and sets outTokenizer to NULL.
      */
     static status_t fromContents(const String8& filename,
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index fd3f602..2095189 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -23,11 +23,21 @@
         "-D_FILE_OFFSET_BITS=64",
     ],
     cppflags: [
-        "-Wold-style-cast",
         // Incorrectly warns when C++11 empty brace {} initializer is used.
         // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
         "-Wno-missing-field-initializers",
     ],
+
+    // Enable -Wold-style-cast only for non-Windows targets.  _islower_l,
+    // _isupper_l etc. in MinGW locale_win32.h (included from
+    // libcxx/include/__locale) has an old-style-cast.
+    target: {
+        not_windows: {
+            cppflags: [
+                "-Wold-style-cast",
+            ],
+        },
+    },
 }
 
 cc_defaults {
@@ -63,6 +73,7 @@
         enabled: true,
     },
     double_loadable: true,
+    export_shared_lib_headers: ["libbase"],
 
     defaults: [
         "libziparchive_defaults",
@@ -74,14 +85,6 @@
         "libz",
     ],
     target: {
-        android: {
-            shared_libs: [
-                "libutils",
-            ],
-        },
-        host: {
-            static_libs: ["libutils"],
-        },
         linux_bionic: {
             enabled: true,
         },
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 018b1a9..ab38dfd 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
+#pragma once
+
 /*
  * Read-only access to Zip archives, with minimal heap allocation.
  */
-#ifndef LIBZIPARCHIVE_ZIPARCHIVE_H_
-#define LIBZIPARCHIVE_ZIPARCHIVE_H_
 
 #include <stdint.h>
 #include <string.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
-#include <utils/Compat.h>
+
+#include "android-base/off64_t.h"
 
 /* Zip compression methods we support */
 enum {
@@ -103,7 +104,8 @@
   off64_t offset;
 };
 
-typedef void* ZipArchiveHandle;
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
 
 /*
  * Open a Zip archive, and sets handle to the value of the opaque
@@ -144,7 +146,7 @@
  * this handle for any further operations without an intervening
  * call to one of the OpenArchive variants.
  */
-void CloseArchive(ZipArchiveHandle handle);
+void CloseArchive(ZipArchiveHandle archive);
 
 /*
  * Find an entry in the Zip archive, by name. |entryName| must be a null
@@ -162,7 +164,7 @@
  * On non-Windows platforms this method does not modify internal state and
  * can be called concurrently.
  */
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
 
 /*
  * Start iterating over all entries of a zip file. The order of iteration
@@ -177,8 +179,8 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
-                       const ZipString* optional_suffix);
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+                       const ZipString* optional_prefix, const ZipString* optional_suffix);
 
 /*
  * Advance to the next element in the zipfile in iteration order.
@@ -203,7 +205,7 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd);
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd);
 
 /**
  * Uncompress a given zip entry to the memory region at |begin| and of
@@ -213,9 +215,9 @@
  *
  * Returns 0 on success and negative values on failure.
  */
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size);
 
-int GetFileDescriptor(const ZipArchiveHandle handle);
+int GetFileDescriptor(const ZipArchiveHandle archive);
 
 const char* ErrorCodeString(int32_t error_code);
 
@@ -226,7 +228,7 @@
  * Stream the uncompressed data through the supplied function,
  * passing cookie to it each time it gets called.
  */
-int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
                                 ProcessZipEntryFunction func, void* cookie);
 #endif
 
@@ -272,5 +274,3 @@
 int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
                 const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
 }  // namespace zip_archive
-
-#endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
index b4766f8..8c6ca79 100644
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -15,12 +15,13 @@
  */
 
 // Read-only stream access to Zip archives entries.
-#ifndef LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
-#define LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
 
 #include <vector>
 
-#include <ziparchive/zip_archive.h>
+#include "android-base/off64_t.h"
 
 class ZipArchiveStreamEntry {
  public:
@@ -43,5 +44,3 @@
   off64_t offset_ = 0;
   uint32_t crc32_ = 0u;
 };
-
-#endif  // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index c350a27..f6c8427 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
-#define LIBZIPARCHIVE_ZIPWRITER_H_
+#pragma once
 
 #include <cstdio>
 #include <ctime>
@@ -25,7 +24,7 @@
 #include <vector>
 
 #include "android-base/macros.h"
-#include "utils/Compat.h"
+#include "android-base/off64_t.h"
 
 struct z_stream_s;
 typedef struct z_stream_s z_stream;
@@ -91,10 +90,10 @@
   explicit ZipWriter(FILE* f);
 
   // Move constructor.
-  ZipWriter(ZipWriter&& zipWriter);
+  ZipWriter(ZipWriter&& zipWriter) noexcept;
 
   // Move assignment.
-  ZipWriter& operator=(ZipWriter&& zipWriter);
+  ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
 
   /**
    * Starts a new zip entry with the given path and flags.
@@ -183,5 +182,3 @@
   std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
   std::vector<uint8_t> buffer_;
 };
-
-#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f8d1356..9eb7f2c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -20,7 +20,8 @@
 
 #define LOG_TAG "ziparchive"
 
-#include <assert.h>
+#include "ziparchive/zip_archive.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -33,6 +34,10 @@
 #include <memory>
 #include <vector>
 
+#if defined(__APPLE__)
+#define lseek64 lseek
+#endif
+
 #if defined(__BIONIC__)
 #include <android/fdsan.h>
 #endif
@@ -40,12 +45,10 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
+#include <android-base/mapped_file.h>
 #include <android-base/memory.h>
 #include <android-base/utf8.h>
 #include <log/log.h>
-#include <utils/Compat.h>
-#include <utils/FileMap.h>
-#include "ziparchive/zip_archive.h"
 #include "zlib.h"
 
 #include "entry_name_utils-inl.h"
@@ -58,12 +61,6 @@
 // specified in the local file header and the central directory.
 static const bool kCrcChecksEnabled = false;
 
-// This is for windows. If we don't open a file in binary mode, weird
-// things will happen.
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
 // The maximum number of bytes to scan backwards for the EOCD start.
 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
 
@@ -196,7 +193,7 @@
       close_file(assume_ownership),
       directory_offset(0),
       central_directory(),
-      directory_map(new android::FileMap()),
+      directory_map(),
       num_entries(0),
       hash_table_size(0),
       hash_table(nullptr) {
@@ -212,7 +209,7 @@
       close_file(false),
       directory_offset(0),
       central_directory(),
-      directory_map(new android::FileMap()),
+      directory_map(),
       num_entries(0),
       hash_table_size(0),
       hash_table(nullptr) {}
@@ -303,8 +300,7 @@
    * in archive.
    */
 
-  if (!archive->InitializeCentralDirectory(debug_file_name,
-                                           static_cast<off64_t>(eocd->cd_start_offset),
+  if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset),
                                            static_cast<size_t>(eocd->cd_size))) {
     ALOGE("Zip: failed to intialize central directory.\n");
     return kMmapFailed;
@@ -517,8 +513,7 @@
 /*
  * Close a ZipArchive, closing the file and freeing the contents.
  */
-void CloseArchive(ZipArchiveHandle handle) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+void CloseArchive(ZipArchiveHandle archive) {
   ALOGV("Closing archive %p", archive);
   delete archive;
 }
@@ -745,10 +740,8 @@
   }
 };
 
-int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const ZipString* optional_prefix,
-                       const ZipString* optional_suffix) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
-
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+                       const ZipString* optional_prefix, const ZipString* optional_suffix) {
   if (archive == NULL || archive->hash_table == NULL) {
     ALOGW("Zip: Invalid ZipArchiveHandle");
     return kInvalidHandle;
@@ -766,8 +759,7 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName, ZipEntry* data) {
-  const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data) {
   if (entryName.name_length == 0) {
     ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
     return kInvalidEntryName;
@@ -827,7 +819,7 @@
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (bytes_written_ + buf_size > size_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", size_,
+      ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
             bytes_written_ + buf_size);
       return false;
     }
@@ -903,7 +895,7 @@
     return FileWriter(fd, declared_length);
   }
 
-  FileWriter(FileWriter&& other)
+  FileWriter(FileWriter&& other) noexcept
       : fd_(other.fd_),
         declared_length_(other.declared_length_),
         total_bytes_written_(other.total_bytes_written_) {
@@ -914,7 +906,7 @@
 
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (total_bytes_written_ + buf_size > declared_length_) {
-      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_,
+      ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
             total_bytes_written_ + buf_size);
       return false;
     }
@@ -923,7 +915,7 @@
     if (result) {
       total_bytes_written_ += buf_size;
     } else {
-      ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
+      ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
     }
 
     return result;
@@ -1052,7 +1044,7 @@
     }
   } while (zerr == Z_OK);
 
-  assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+  CHECK_EQ(zerr, Z_STREAM_END); /* other errors should've been caught */
 
   // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
   // "feature" of zlib to tell it there won't be a zlib file header. zlib
@@ -1116,8 +1108,7 @@
   return 0;
 }
 
-int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
+int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive::Writer* writer) {
   const uint16_t method = entry->method;
 
   // this should default to kUnknownCompressionMethod.
@@ -1145,18 +1136,18 @@
   return return_value;
 }
 
-int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size) {
   MemoryWriter writer(begin, size);
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
-int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd) {
   auto writer = FileWriter::Create(fd, entry);
   if (!writer.IsValid()) {
     return kIoError;
   }
 
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
 const char* ErrorCodeString(int32_t error_code) {
@@ -1173,8 +1164,8 @@
   return "Unknown return code";
 }
 
-int GetFileDescriptor(const ZipArchiveHandle handle) {
-  return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
+int GetFileDescriptor(const ZipArchiveHandle archive) {
+  return archive->mapped_zip.GetFileDescriptor();
 }
 
 ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
@@ -1198,10 +1189,10 @@
   void* cookie_;
 };
 
-int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
                                 ProcessZipEntryFunction func, void* cookie) {
   ProcessWriter writer(func, cookie);
-  return ExtractToWriter(handle, entry, &writer);
+  return ExtractToWriter(archive, entry, &writer);
 }
 
 #endif  //! defined(_WIN32)
@@ -1260,16 +1251,14 @@
   length_ = cd_size;
 }
 
-bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
-                                            size_t cd_size) {
+bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
   if (mapped_zip.HasFd()) {
-    if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(), cd_start_offset,
-                               cd_size, true /* read only */)) {
-      return false;
-    }
+    directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
+                                                      cd_start_offset, cd_size, PROT_READ);
+    if (!directory_map) return false;
 
-    CHECK_EQ(directory_map->getDataLength(), cd_size);
-    central_directory.Initialize(directory_map->getDataPtr(), 0 /*offset*/, cd_size);
+    CHECK_EQ(directory_map->size(), cd_size);
+    central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
   } else {
     if (mapped_zip.GetBasePtr() == nullptr) {
       ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 83cb11f..330a02a 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
-#define LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+#pragma once
+
+#include <ziparchive/zip_archive.h>
 
 #include <stdint.h>
 #include <stdlib.h>
@@ -24,9 +25,8 @@
 #include <memory>
 #include <vector>
 
-#include <utils/FileMap.h>
-#include <ziparchive/zip_archive.h>
 #include "android-base/macros.h"
+#include "android-base/mapped_file.h"
 
 static const char* kErrorMessages[] = {
     "Success",
@@ -164,7 +164,7 @@
   // mapped central directory area
   off64_t directory_offset;
   CentralDirectory central_directory;
-  std::unique_ptr<android::FileMap> directory_map;
+  std::unique_ptr<android::base::MappedFile> directory_map;
 
   // number of entries in the Zip archive
   uint16_t num_entries;
@@ -180,8 +180,5 @@
   ZipArchive(void* address, size_t length);
   ~ZipArchive();
 
-  bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
-                                  size_t cd_size);
+  bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
 };
-
-#endif  // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 23dd5dc..0ea7d5d 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -27,10 +27,10 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/mapped_file.h>
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
-#include <utils/FileMap.h>
 #include <ziparchive/zip_archive.h>
 #include <ziparchive/zip_archive_stream_entry.h>
 
@@ -348,7 +348,7 @@
   // Read the file back to a buffer and make sure the contents are
   // the same as the memory buffer we extracted directly to.
   std::vector<uint8_t> file_contents(kAbUncompressedSize);
-  ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+  ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
   ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
   ASSERT_EQ(file_contents, buffer);
 
@@ -392,7 +392,7 @@
 
   // Assert that the first 8 bytes of the file haven't been clobbered.
   uint8_t read_buffer[data_size];
-  ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+  ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
 
@@ -404,7 +404,7 @@
 
   // Assert that the total length of the file is sane
   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
-            lseek64(tmp_file.fd, 0, SEEK_END));
+            lseek(tmp_file.fd, 0, SEEK_END));
 }
 
 #if !defined(_WIN32)
@@ -416,11 +416,10 @@
   ASSERT_EQ(0, fstat(fd, &sb));
 
   // Memory map the file first and open the archive from the memory region.
-  android::FileMap file_map;
-  file_map.create(zip_path.c_str(), fd, 0 /*offset*/, sb.st_size, true);
+  auto file_map{android::base::MappedFile::FromFd(fd, 0, sb.st_size, PROT_READ)};
   ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFromMemory(file_map.getDataPtr(), file_map.getDataLength(),
-                                     zip_path.c_str(), &handle));
+  ASSERT_EQ(0,
+            OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
 
   // Assert one entry can be found and extracted correctly.
   std::string BINARY_PATH("META-INF/com/google/android/update-binary");
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6ad3366..981df3a 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -26,8 +26,6 @@
 #include <vector>
 
 #include "android-base/logging.h"
-#include "utils/Compat.h"
-#include "utils/Log.h"
 
 #include "entry_name_utils-inl.h"
 #include "zip_archive_common.h"
@@ -97,7 +95,7 @@
   }
 }
 
-ZipWriter::ZipWriter(ZipWriter&& writer)
+ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
     : file_(writer.file_),
       seekable_(writer.seekable_),
       current_offset_(writer.current_offset_),
@@ -109,7 +107,7 @@
   writer.state_ = State::kError;
 }
 
-ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
   file_ = writer.file_;
   seekable_ = writer.seekable_;
   current_offset_ = writer.current_offset_;
@@ -303,10 +301,10 @@
 
   if (zerr != Z_OK) {
     if (zerr == Z_VERSION_ERROR) {
-      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+      LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
       return HandleError(kZlibError);
     } else {
-      ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+      LOG(ERROR) << "deflateInit2 failed (zerr=" << zerr << ")";
       return HandleError(kZlibError);
     }
   }
diff --git a/llkd/README.md b/llkd/README.md
index 1f69718..e5be850 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -127,7 +127,7 @@
 default 2 minutes samples of threads for D or Z.
 
 #### ro.llk.stack
-default *empty* or false, comma separated list of kernel symbols.
+default cma_alloc,__get_user_pages, comma separated list of kernel symbols.
 The string "*false*" is the equivalent to an *empty* list.
 Look for kernel stack symbols that if ever persistently present can
 indicate a subsystem is locked up.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index d0188ec..2c62fca 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -48,16 +48,16 @@
 /* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
 #define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
 #define LLK_CHECK_STACK_PROPERTY       "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT        ""
+#define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages"
 #define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
 #define LLK_BLACKLIST_PROCESS_DEFAULT  \
-    "0,1,2,init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
+    "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
 #define LLK_BLACKLIST_PARENT_PROPERTY  "ro.llk.blacklist.parent"
 #define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd]"
 #define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
 #define LLK_BLACKLIST_UID_DEFAULT      ""
 #define LLK_BLACKLIST_STACK_PROPERTY   "ro.llk.blacklist.process.stack"
-#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,/system/bin/keystore"
+#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,ueventd,apexd"
 /* clang-format on */
 
 __END_DECLS
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 58c2ba8..2727aab 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -265,7 +265,7 @@
     }
     content.erase(pos);
     uid_t ret;
-    if (!android::base::ParseInt(content, &ret, uid_t(0))) {
+    if (!android::base::ParseUint(content, &ret, uid_t(0))) {
         return -1;
     }
     return ret;
@@ -712,6 +712,7 @@
     if (llkSkipName(std::to_string(procp->pid), llkBlacklistStack)) return false;
     if (llkSkipName(procp->getComm(), llkBlacklistStack)) return false;
     if (llkSkipName(procp->getCmdline(), llkBlacklistStack)) return false;
+    if (llkSkipName(android::base::Basename(procp->getCmdline()), llkBlacklistStack)) return false;
 
     auto kernel_stack = ReadFile(piddir + "/stack");
     if (kernel_stack.empty()) {
@@ -995,13 +996,18 @@
             if (llkSkipName(procp->getCmdline())) {
                 break;
             }
+            if (llkSkipName(android::base::Basename(procp->getCmdline()))) {
+                break;
+            }
 
             auto pprocp = llkTidLookup(ppid);
             if (pprocp == nullptr) {
                 pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
             }
-            if ((pprocp != nullptr) && (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
-                                        llkSkipName(pprocp->getCmdline(), llkBlacklistParent))) {
+            if ((pprocp != nullptr) &&
+                (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
+                 llkSkipName(pprocp->getCmdline(), llkBlacklistParent) ||
+                 llkSkipName(android::base::Basename(pprocp->getCmdline()), llkBlacklistParent))) {
                 break;
             }
 
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index fe6364d..e72d159 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -30,6 +30,8 @@
     LMK_TARGET = 0,  /* Associate minfree with oom_adj_score */
     LMK_PROCPRIO,    /* Register a process and set its oom_adj_score */
     LMK_PROCREMOVE,  /* Unregister a process */
+    LMK_PROCPURGE,   /* Purge all registered processes */
+    LMK_GETKILLCNT,  /* Get number of kills */
 };
 
 /*
@@ -142,6 +144,53 @@
     return 2 * sizeof(int);
 }
 
+/*
+ * Prepare LMK_PROCPURGE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
+    packet[0] = htonl(LMK_PROCPURGE);
+    return sizeof(int);
+}
+
+/* LMK_GETKILLCNT packet payload */
+struct lmk_getkillcnt {
+    int min_oomadj;
+    int max_oomadj;
+};
+
+/*
+ * For LMK_GETKILLCNT packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
+                                   struct lmk_getkillcnt *params) {
+    params->min_oomadj = ntohl(packet[1]);
+    params->max_oomadj = ntohl(packet[2]);
+}
+
+/*
+ * Prepare LMK_GETKILLCNT packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
+                                       struct lmk_getkillcnt *params) {
+    packet[0] = htonl(LMK_GETKILLCNT);
+    packet[1] = htonl(params->min_oomadj);
+    packet[2] = htonl(params->max_oomadj);
+    return 3 * sizeof(int);
+}
+
+/*
+ * Prepare LMK_GETKILLCNT reply packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
+    packet[0] = htonl(LMK_GETKILLCNT);
+    packet[1] = htonl(kill_cnt);
+    return 2 * sizeof(int);
+}
+
 __END_DECLS
 
 #endif /* _LMKD_H_ */
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 1980dc6..c9c9e8e 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -313,7 +313,20 @@
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
 
 #define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
-static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
+#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)
+static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];
+
+#define MAX_DISTINCT_OOM_ADJ 32
+#define KILLCNT_INVALID_IDX 0xFF
+/*
+ * Because killcnt array is sparse a two-level indirection is used
+ * to keep the size small. killcnt_idx stores index of the element in
+ * killcnt array. Index KILLCNT_INVALID_IDX indicates an unused slot.
+ */
+static uint8_t killcnt_idx[ADJTOSLOT_COUNT];
+static uint16_t killcnt[MAX_DISTINCT_OOM_ADJ];
+static int killcnt_free_idx = 0;
+static uint32_t killcnt_total = 0;
 
 /* PAGE_SIZE / 1024 */
 static long page_k;
@@ -559,7 +572,7 @@
         } else if (params.oomadj >= 300) {
             soft_limit_mult = 1;
         } else if (params.oomadj >= 200) {
-            soft_limit_mult = 2;
+            soft_limit_mult = 8;
         } else if (params.oomadj >= 100) {
             soft_limit_mult = 10;
         } else if (params.oomadj >=   0) {
@@ -612,9 +625,99 @@
     }
 
     lmkd_pack_get_procremove(packet, &params);
+    /*
+     * WARNING: After pid_remove() procp is freed and can't be used!
+     * Therefore placed at the end of the function.
+     */
     pid_remove(params.pid);
 }
 
+static void cmd_procpurge() {
+    int i;
+    struct proc *procp;
+    struct proc *next;
+
+    if (use_inkernel_interface) {
+        return;
+    }
+
+    for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
+        procadjslot_list[i].next = &procadjslot_list[i];
+        procadjslot_list[i].prev = &procadjslot_list[i];
+    }
+
+    for (i = 0; i < PIDHASH_SZ; i++) {
+        procp = pidhash[i];
+        while (procp) {
+            next = procp->pidhash_next;
+            free(procp);
+            procp = next;
+        }
+    }
+    memset(&pidhash[0], 0, sizeof(pidhash));
+}
+
+static void inc_killcnt(int oomadj) {
+    int slot = ADJTOSLOT(oomadj);
+    uint8_t idx = killcnt_idx[slot];
+
+    if (idx == KILLCNT_INVALID_IDX) {
+        /* index is not assigned for this oomadj */
+        if (killcnt_free_idx < MAX_DISTINCT_OOM_ADJ) {
+            killcnt_idx[slot] = killcnt_free_idx;
+            killcnt[killcnt_free_idx] = 1;
+            killcnt_free_idx++;
+        } else {
+            ALOGW("Number of distinct oomadj levels exceeds %d",
+                MAX_DISTINCT_OOM_ADJ);
+        }
+    } else {
+        /*
+         * wraparound is highly unlikely and is detectable using total
+         * counter because it has to be equal to the sum of all counters
+         */
+        killcnt[idx]++;
+    }
+    /* increment total kill counter */
+    killcnt_total++;
+}
+
+static int get_killcnt(int min_oomadj, int max_oomadj) {
+    int slot;
+    int count = 0;
+
+    if (min_oomadj > max_oomadj)
+        return 0;
+
+    /* special case to get total kill count */
+    if (min_oomadj > OOM_SCORE_ADJ_MAX)
+        return killcnt_total;
+
+    while (min_oomadj <= max_oomadj &&
+           (slot = ADJTOSLOT(min_oomadj)) < ADJTOSLOT_COUNT) {
+        uint8_t idx = killcnt_idx[slot];
+        if (idx != KILLCNT_INVALID_IDX) {
+            count += killcnt[idx];
+        }
+        min_oomadj++;
+    }
+
+    return count;
+}
+
+static int cmd_getkillcnt(LMKD_CTRL_PACKET packet) {
+    struct lmk_getkillcnt params;
+
+    if (use_inkernel_interface) {
+        /* kernel driver does not expose this information */
+        return 0;
+    }
+
+    lmkd_pack_get_getkillcnt(packet, &params);
+
+    return get_killcnt(params.min_oomadj, params.max_oomadj);
+}
+
 static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
     int i;
     struct lmk_target target;
@@ -719,12 +822,28 @@
     return ret;
 }
 
+static int ctrl_data_write(int dsock_idx, char *buf, size_t bufsz) {
+    int ret = 0;
+
+    ret = TEMP_FAILURE_RETRY(write(data_sock[dsock_idx].sock, buf, bufsz));
+
+    if (ret == -1) {
+        ALOGE("control data socket write failed; errno=%d", errno);
+    } else if (ret == 0) {
+        ALOGE("Got EOF on control data socket");
+        ret = -1;
+    }
+
+    return ret;
+}
+
 static void ctrl_command_handler(int dsock_idx) {
     LMKD_CTRL_PACKET packet;
     int len;
     enum lmk_cmd cmd;
     int nargs;
     int targets;
+    int kill_cnt;
 
     len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
     if (len <= 0)
@@ -757,6 +876,19 @@
             goto wronglen;
         cmd_procremove(packet);
         break;
+    case LMK_PROCPURGE:
+        if (nargs != 0)
+            goto wronglen;
+        cmd_procpurge();
+        break;
+    case LMK_GETKILLCNT:
+        if (nargs != 2)
+            goto wronglen;
+        kill_cnt = cmd_getkillcnt(packet);
+        len = lmkd_pack_set_getkillcnt_repl(packet, kill_cnt);
+        if (ctrl_data_write(dsock_idx, (char *)packet, len) != len)
+            return;
+        break;
     default:
         ALOGE("Received unknown command code %d", cmd);
         return;
@@ -822,7 +954,7 @@
 }
 
 #ifdef LMKD_LOG_STATS
-static void memory_stat_parse_line(char *line, struct memory_stat *mem_st) {
+static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
     char key[LINE_MAX + 1];
     int64_t value;
 
@@ -844,17 +976,10 @@
         mem_st->swap_in_bytes = value;
 }
 
-static int memory_stat_parse(struct memory_stat *mem_st,  int pid, uid_t uid) {
+static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
     FILE *fp;
     char buf[PATH_MAX];
 
-    /*
-     * Per-application memory.stat files are available only when
-     * per-application memcgs are enabled.
-     */
-    if (!per_app_memcg)
-        return -1;
-
     snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
 
     fp = fopen(buf, "r");
@@ -864,13 +989,50 @@
         return -1;
     }
 
-    while (fgets(buf, PAGE_SIZE, fp) != NULL ) {
+    while (fgets(buf, PAGE_SIZE, fp) != NULL) {
         memory_stat_parse_line(buf, mem_st);
     }
     fclose(fp);
 
     return 0;
 }
+
+static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
+    char path[PATH_MAX];
+    char buffer[PROC_STAT_BUFFER_SIZE];
+    int fd, ret;
+
+    snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
+    if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
+        ALOGE("%s open failed: %s", path, strerror(errno));
+        return -1;
+    }
+
+    ret = read(fd, buffer, sizeof(buffer));
+    if (ret < 0) {
+        ALOGE("%s read failed: %s", path, strerror(errno));
+        close(fd);
+        return -1;
+    }
+    close(fd);
+
+    // field 10 is pgfault
+    // field 12 is pgmajfault
+    // field 24 is rss_in_pages
+    int64_t pgfault = 0, pgmajfault = 0, rss_in_pages = 0;
+    if (sscanf(buffer,
+               "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
+               "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
+               "%*d %*d %" SCNd64 "",
+               &pgfault, &pgmajfault, &rss_in_pages) != 3) {
+        return -1;
+    }
+    mem_st->pgfault = pgfault;
+    mem_st->pgmajfault = pgmajfault;
+    mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
+
+    return 0;
+}
 #endif
 
 /* /prop/zoneinfo parsing routines */
@@ -1098,6 +1260,8 @@
     return maxprocp;
 }
 
+static int last_killed_pid = -1;
+
 /* Kill one process specified by procp.  Returns the size of the process killed */
 static int kill_one_process(struct proc* procp) {
     int pid = procp->pid;
@@ -1105,6 +1269,7 @@
     char *taskname;
     int tasksize;
     int r;
+    int result = -1;
 
 #ifdef LMKD_LOG_STATS
     struct memory_stat mem_st = {};
@@ -1113,19 +1278,21 @@
 
     taskname = proc_get_name(pid);
     if (!taskname) {
-        pid_remove(pid);
-        return -1;
+        goto out;
     }
 
     tasksize = proc_get_size(pid);
     if (tasksize <= 0) {
-        pid_remove(pid);
-        return -1;
+        goto out;
     }
 
 #ifdef LMKD_LOG_STATS
     if (enable_stats_log) {
-        memory_stat_parse_result = memory_stat_parse(&mem_st, pid, uid);
+        if (per_app_memcg) {
+            memory_stat_parse_result = memory_stat_from_cgroup(&mem_st, pid, uid);
+        } else {
+            memory_stat_parse_result = memory_stat_from_procfs(&mem_st, pid);
+        }
     }
 #endif
 
@@ -1133,38 +1300,47 @@
 
     /* CAP_KILL required */
     r = kill(pid, SIGKILL);
+    inc_killcnt(procp->oomadj);
     ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
         taskname, pid, uid, procp->oomadj, tasksize * page_k);
-    pid_remove(pid);
 
     TRACE_KILL_END();
 
+    last_killed_pid = pid;
+
     if (r) {
         ALOGE("kill(%d): errno=%d", pid, errno);
-        return -1;
+        goto out;
     } else {
 #ifdef LMKD_LOG_STATS
         if (memory_stat_parse_result == 0) {
             stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
                     procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
                     mem_st.cache_in_bytes, mem_st.swap_in_bytes);
+        } else if (enable_stats_log) {
+            stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
+                                          -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1);
         }
 #endif
-        return tasksize;
+        result = tasksize;
     }
 
-    return tasksize;
+out:
+    /*
+     * WARNING: After pid_remove() procp is freed and can't be used!
+     * Therefore placed at the end of the function.
+     */
+    pid_remove(pid);
+    return result;
 }
 
 /*
- * Find processes to kill to free required number of pages.
- * If pages_to_free is set to 0 only one process will be killed.
- * Returns the size of the killed processes.
+ * Find one process to kill at or above the given oom_adj level.
+ * Returns size of the killed process.
  */
-static int find_and_kill_processes(int min_score_adj, int pages_to_free) {
+static int find_and_kill_process(int min_score_adj) {
     int i;
-    int killed_size;
-    int pages_freed = 0;
+    int killed_size = 0;
 
 #ifdef LMKD_LOG_STATS
     bool lmk_state_change_start = false;
@@ -1189,20 +1365,12 @@
                                                   LMK_STATE_CHANGE_START);
                 }
 #endif
-
-                pages_freed += killed_size;
-                if (pages_freed >= pages_to_free) {
-
-#ifdef LMKD_LOG_STATS
-                    if (enable_stats_log && lmk_state_change_start) {
-                        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
-                                LMK_STATE_CHANGE_STOP);
-                    }
-#endif
-                    return pages_freed;
-                }
+                break;
             }
         }
+        if (killed_size) {
+            break;
+        }
     }
 
 #ifdef LMKD_LOG_STATS
@@ -1211,7 +1379,7 @@
     }
 #endif
 
-    return pages_freed;
+    return killed_size;
 }
 
 static int64_t get_memory_usage(struct reread_data *file_data) {
@@ -1271,6 +1439,23 @@
         level - 1 : level);
 }
 
+static bool is_kill_pending(void) {
+    char buf[24];
+
+    if (last_killed_pid < 0) {
+        return false;
+    }
+
+    snprintf(buf, sizeof(buf), "/proc/%d/", last_killed_pid);
+    if (access(buf, F_OK) == 0) {
+        return true;
+    }
+
+    // reset last killed PID because there's nothing pending
+    last_killed_pid = -1;
+    return false;
+}
+
 static void mp_event_common(int data, uint32_t events __unused) {
     int ret;
     unsigned long long evcount;
@@ -1285,7 +1470,6 @@
     enum vmpressure_level level = (enum vmpressure_level)data;
     long other_free = 0, other_file = 0;
     int min_score_adj;
-    int pages_to_free = 0;
     int minfree = 0;
     static struct reread_data mem_usage_file_data = {
         .filename = MEMCG_MEMORY_USAGE,
@@ -1316,7 +1500,11 @@
     }
 
     if (kill_timeout_ms) {
-        if (get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
+        // If we're within the timeout, see if there's pending reclaim work
+        // from the last killed process. If there is (as evidenced by
+        // /proc/<pid> continuing to exist), skip killing for now.
+        if ((get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) &&
+            (low_ram_device || is_kill_pending())) {
             kill_skip_count++;
             return;
         }
@@ -1363,9 +1551,6 @@
             return;
         }
 
-        /* Free up enough pages to push over the highest minfree level */
-        pages_to_free = lowmem_minfree[lowmem_targets_size - 1] -
-            ((other_free < other_file) ? other_free : other_file);
         goto do_kill;
     }
 
@@ -1421,7 +1606,7 @@
 do_kill:
     if (low_ram_device) {
         /* For Go devices kill only one task */
-        if (find_and_kill_processes(level_oomadj[level], 0) == 0) {
+        if (find_and_kill_process(level_oomadj[level]) == 0) {
             if (debug_process_killing) {
                 ALOGI("Nothing to kill");
             }
@@ -1445,10 +1630,7 @@
                 return;
             }
             /* Free up enough memory to downgrate the memory pressure to low level */
-            if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) {
-                pages_to_free = low_pressure_mem.max_nr_free_pages -
-                    mi.field.nr_free_pages;
-            } else {
+            if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
                 if (debug_process_killing) {
                     ALOGI("Ignoring pressure since more memory is "
                         "available (%" PRId64 ") than watermark (%" PRId64 ")",
@@ -1459,7 +1641,7 @@
             min_score_adj = level_oomadj[level];
         }
 
-        pages_freed = find_and_kill_processes(min_score_adj, pages_to_free);
+        pages_freed = find_and_kill_process(min_score_adj);
 
         if (pages_freed == 0) {
             /* Rate limit kill reports when nothing was reclaimed */
@@ -1467,25 +1649,24 @@
                 report_skip_count++;
                 return;
             }
+        } else {
+            /* If we killed anything, update the last killed timestamp. */
+            last_kill_tm = curr_tm;
         }
 
         /* Log meminfo whenever we kill or when report rate limit allows */
         meminfo_log(&mi);
-        if (pages_freed >= pages_to_free) {
-            /* Reset kill time only if reclaimed enough memory */
-            last_kill_tm = curr_tm;
-        }
 
         if (use_minfree_levels) {
-            ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB, cache(%ldkB) and "
+            ALOGI("Reclaimed %ldkB, cache(%ldkB) and "
                 "free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
-                pages_to_free * page_k, pages_freed * page_k,
+                pages_freed * page_k,
                 other_file * page_k, mi.field.nr_free_pages * page_k,
                 zi.field.totalreserve_pages * page_k,
                 minfree * page_k, min_score_adj);
         } else {
-            ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB at oom_adj %d",
-                pages_to_free * page_k, pages_freed * page_k, min_score_adj);
+            ALOGI("Reclaimed %ldkB at oom_adj %d",
+                pages_freed * page_k, min_score_adj);
         }
 
         if (report_skip_count > 0) {
@@ -1625,6 +1806,8 @@
         procadjslot_list[i].prev = &procadjslot_list[i];
     }
 
+    memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx));
+
     return 0;
 }
 
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index edebb19..8458480 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -67,6 +67,9 @@
 };
 
 #define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
+#define PROC_STAT_FILE_PATH "/proc/%d/stat"
+#define PROC_STAT_BUFFER_SIZE 1024
+#define BYTES_IN_KILOBYTE 1024
 
 /**
  * Logs the change in LMKD state which is used as start/stop boundaries for logging
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 115b1a3..87bc6ae 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -478,10 +478,10 @@
                     "  -G <size>, --buffer-size=<size>\n"
                     "                  Set size of log ring buffer, may suffix with K or M.\n"
                     "  -L, --last      Dump logs from prior to last reboot\n"
-                    // Leave security (Device Owner only installations) and
-                    // kernel (userdebug and eng) buffers undocumented.
                     "  -b <buffer>, --buffer=<buffer>         Request alternate ring buffer, 'main',\n"
                     "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
+                    "                  Additionally, 'kernel' for userdebug and eng builds, and\n"
+                    "                  'security' for Device Owner installations.\n"
                     "                  Multiple -b parameters or comma separated list of buffers are\n"
                     "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
                     "  -B, --binary    Output the log in binary.\n"
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index 658e079..bd17555 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -42,7 +42,7 @@
     LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
-        entry = (*it);
+        entry = it->get();
         if (entry->mClient == client) {
             if (!entry->isWatchingMultiple(mLogMask)) {
                 LogTimeEntry::unlock();
@@ -63,31 +63,12 @@
                 }
             }
             entry->triggerReader_Locked();
-            if (entry->runningReader_Locked()) {
-                LogTimeEntry::unlock();
-                return;
-            }
-            entry->incRef_Locked();
-            break;
+            LogTimeEntry::unlock();
+            return;
         }
         it++;
     }
 
-    if (it == times.end()) {
-        // Create LogTimeEntry in notifyNewLog() ?
-        if (mTail == (unsigned long)-1) {
-            LogTimeEntry::unlock();
-            return;
-        }
-        entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask,
-                                 mPid, mStart, mTimeout);
-        times.push_front(entry);
-    }
-
-    client->incRef();
-
-    // release client and entry reference counts once done
-    entry->startReader_Locked();
     LogTimeEntry::unlock();
 }
 
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 543dfc3..ceaf393 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -27,36 +27,11 @@
 
 class FlushCommand : public SocketClientCommand {
     LogReader& mReader;
-    bool mNonBlock;
-    unsigned long mTail;
     log_mask_t mLogMask;
-    pid_t mPid;
-    log_time mStart;
-    uint64_t mTimeout;
 
    public:
-    // for opening a reader
-    explicit FlushCommand(LogReader& reader, bool nonBlock, unsigned long tail,
-                          log_mask_t logMask, pid_t pid, log_time start,
-                          uint64_t timeout)
-        : mReader(reader),
-          mNonBlock(nonBlock),
-          mTail(tail),
-          mLogMask(logMask),
-          mPid(pid),
-          mStart(start),
-          mTimeout((start != log_time::EPOCH) ? timeout : 0) {
-    }
-
-    // for notification of an update
     explicit FlushCommand(LogReader& reader, log_mask_t logMask)
-        : mReader(reader),
-          mNonBlock(false),
-          mTail(-1),
-          mLogMask(logMask),
-          mPid(0),
-          mStart(log_time::EPOCH),
-          mTimeout(0) {
+        : mReader(reader), mLogMask(logMask) {
     }
 
     virtual void runSocketCommand(SocketClient* client);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index fd1b8b2..fbdbf79 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -105,10 +105,8 @@
 
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
-        LogTimeEntry* entry = (*times);
-        if (entry->owned_Locked()) {
-            entry->triggerReader_Locked();
-        }
+        LogTimeEntry* entry = times->get();
+        entry->triggerReader_Locked();
         times++;
     }
 
@@ -409,17 +407,15 @@
 
         LastLogTimes::iterator times = mTimes.begin();
         while (times != mTimes.end()) {
-            LogTimeEntry* entry = (*times);
-            if (entry->owned_Locked()) {
-                if (!entry->mNonBlock) {
-                    end_always = true;
-                    break;
-                }
-                // it passing mEnd is blocked by the following checks.
-                if (!end_set || (end <= entry->mEnd)) {
-                    end = entry->mEnd;
-                    end_set = true;
-                }
+            LogTimeEntry* entry = times->get();
+            if (!entry->mNonBlock) {
+                end_always = true;
+                break;
+            }
+            // it passing mEnd is blocked by the following checks.
+            if (!end_set || (end <= entry->mEnd)) {
+                end = entry->mEnd;
+                end_set = true;
             }
             times++;
         }
@@ -710,8 +706,8 @@
     // Region locked?
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
-        LogTimeEntry* entry = (*times);
-        if (entry->owned_Locked() && entry->isWatching(id) &&
+        LogTimeEntry* entry = times->get();
+        if (entry->isWatching(id) &&
             (!oldest || (oldest->mStart > entry->mStart) ||
              ((oldest->mStart == entry->mStart) &&
               (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
@@ -1052,9 +1048,9 @@
                 LogTimeEntry::wrlock();
                 LastLogTimes::iterator times = mTimes.begin();
                 while (times != mTimes.end()) {
-                    LogTimeEntry* entry = (*times);
+                    LogTimeEntry* entry = times->get();
                     // Killer punch
-                    if (entry->owned_Locked() && entry->isWatching(id)) {
+                    if (entry->isWatching(id)) {
                         entry->release_Locked();
                     }
                     times++;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 2b6556d..9db8c00 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -41,6 +41,7 @@
     runOnEachSocket(&command);
 }
 
+// Note returning false will release the SocketClient instance.
 bool LogReader::onDataAvailable(SocketClient* cli) {
     static bool name_set;
     if (!name_set) {
@@ -57,6 +58,18 @@
     }
     buffer[len] = '\0';
 
+    // Clients are only allowed to send one command, disconnect them if they
+    // send another.
+    LogTimeEntry::wrlock();
+    for (const auto& entry : mLogbuf.mTimes) {
+        if (entry->mClient == cli) {
+            entry->release_Locked();
+            LogTimeEntry::unlock();
+            return false;
+        }
+    }
+    LogTimeEntry::unlock();
+
     unsigned long tail = 0;
     static const char _tail[] = " tail=";
     char* cp = strstr(buffer, _tail);
@@ -199,14 +212,29 @@
         cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
         logMask, (int)pid, sequence.nsec(), timeout);
 
-    FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+    if (sequence == log_time::EPOCH) {
+        timeout = 0;
+    }
+
+    LogTimeEntry::wrlock();
+    auto entry = std::make_unique<LogTimeEntry>(
+        *this, cli, nonBlock, tail, logMask, pid, sequence, timeout);
+    if (!entry->startReader_Locked()) {
+        LogTimeEntry::unlock();
+        return false;
+    }
+
+    // release client and entry reference counts once done
+    cli->incRef();
+    mLogbuf.mTimes.emplace_front(std::move(entry));
 
     // Set acceptable upper limit to wait for slow reader processing b/27242723
     struct timeval t = { LOGD_SNDTIMEO, 0 };
     setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
                sizeof(t));
 
-    command.runSocketCommand(cli);
+    LogTimeEntry::unlock();
+
     return true;
 }
 
@@ -215,9 +243,8 @@
     LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
-        LogTimeEntry* entry = (*it);
+        LogTimeEntry* entry = it->get();
         if (entry->mClient == cli) {
-            times.erase(it);
             entry->release_Locked();
             break;
         }
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index d6b8ab3..469f6dc 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -531,7 +531,7 @@
         name = std::string_view(alloc->c_str(), alloc->size());
     }
 
-    explicit TagNameKey(TagNameKey&& rval)
+    explicit TagNameKey(TagNameKey&& rval) noexcept
         : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
         rval.alloc = nullptr;
     }
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 7a6f84b..1715501 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -30,11 +30,7 @@
 LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
                            bool nonBlock, unsigned long tail, log_mask_t logMask,
                            pid_t pid, log_time start, uint64_t timeout)
-    : mRefCount(1),
-      mRelease(false),
-      mError(false),
-      threadRunning(false),
-      leadingDropped(false),
+    : leadingDropped(false),
       mReader(reader),
       mLogMask(logMask),
       mPid(pid),
@@ -52,65 +48,21 @@
     cleanSkip_Locked();
 }
 
-void LogTimeEntry::startReader_Locked(void) {
+bool LogTimeEntry::startReader_Locked() {
     pthread_attr_t attr;
 
-    threadRunning = true;
-
     if (!pthread_attr_init(&attr)) {
         if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
             if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
                                 this)) {
                 pthread_attr_destroy(&attr);
-                return;
+                return true;
             }
         }
         pthread_attr_destroy(&attr);
     }
-    threadRunning = false;
-    if (mClient) {
-        mClient->decRef();
-    }
-    decRef_Locked();
-}
 
-void LogTimeEntry::threadStop(void* obj) {
-    LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
-    wrlock();
-
-    if (me->mNonBlock) {
-        me->error_Locked();
-    }
-
-    SocketClient* client = me->mClient;
-
-    if (me->isError_Locked()) {
-        LogReader& reader = me->mReader;
-        LastLogTimes& times = reader.logbuf().mTimes;
-
-        LastLogTimes::iterator it = times.begin();
-        while (it != times.end()) {
-            if (*it == me) {
-                times.erase(it);
-                me->release_nodelete_Locked();
-                break;
-            }
-            it++;
-        }
-
-        me->mClient = nullptr;
-        reader.release(client);
-    }
-
-    if (client) {
-        client->decRef();
-    }
-
-    me->threadRunning = false;
-    me->decRef_Locked();
-
-    unlock();
+    return false;
 }
 
 void* LogTimeEntry::threadStart(void* obj) {
@@ -118,13 +70,7 @@
 
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    pthread_cleanup_push(threadStop, obj);
-
     SocketClient* client = me->mClient;
-    if (!client) {
-        me->error();
-        return nullptr;
-    }
 
     LogBuffer& logbuf = me->mReader.logbuf();
 
@@ -137,14 +83,14 @@
 
     log_time start = me->mStart;
 
-    while (me->threadRunning && !me->isError_Locked()) {
+    while (!me->mRelease) {
         if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
             if (pthread_cond_timedwait(&me->threadTriggeredCondition,
                                        &timesLock, &me->mTimeout) == ETIMEDOUT) {
                 me->mTimeout.tv_sec = 0;
                 me->mTimeout.tv_nsec = 0;
             }
-            if (!me->threadRunning || me->isError_Locked()) {
+            if (me->mRelease) {
                 break;
             }
         }
@@ -162,13 +108,12 @@
         wrlock();
 
         if (start == LogBufferElement::FLUSH_ERROR) {
-            me->error_Locked();
             break;
         }
 
         me->mStart = start + log_time(0, 1);
 
-        if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
+        if (me->mNonBlock || me->mRelease) {
             break;
         }
 
@@ -179,9 +124,21 @@
         }
     }
 
-    unlock();
+    LogReader& reader = me->mReader;
+    reader.release(client);
 
-    pthread_cleanup_pop(true);
+    client->decRef();
+
+    LastLogTimes& times = reader.logbuf().mTimes;
+    auto it =
+        std::find_if(times.begin(), times.end(),
+                     [&me](const auto& other) { return other.get() == me; });
+
+    if (it != times.end()) {
+        times.erase(it);
+    }
+
+    unlock();
 
     return nullptr;
 }
@@ -247,10 +204,6 @@
         goto skip;
     }
 
-    if (me->isError_Locked()) {
-        goto stop;
-    }
-
     if (!me->mTail) {
         goto ok;
     }
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 76d016c..f4e165f 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -22,6 +22,7 @@
 #include <time.h>
 
 #include <list>
+#include <memory>
 
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
@@ -33,16 +34,12 @@
 
 class LogTimeEntry {
     static pthread_mutex_t timesLock;
-    unsigned int mRefCount;
-    bool mRelease;
-    bool mError;
-    bool threadRunning;
+    bool mRelease = false;
     bool leadingDropped;
     pthread_cond_t threadTriggeredCondition;
     pthread_t mThread;
     LogReader& mReader;
     static void* threadStart(void* me);
-    static void threadStop(void* me);
     const log_mask_t mLogMask;
     const pid_t mPid;
     unsigned int skipAhead[LOG_ID_MAX];
@@ -73,11 +70,8 @@
         pthread_mutex_unlock(&timesLock);
     }
 
-    void startReader_Locked(void);
+    bool startReader_Locked();
 
-    bool runningReader_Locked(void) const {
-        return threadRunning || mRelease || mError || mNonBlock;
-    }
     void triggerReader_Locked(void) {
         pthread_cond_signal(&threadTriggeredCondition);
     }
@@ -87,54 +81,11 @@
     }
     void cleanSkip_Locked(void);
 
-    // These called after LogTimeEntry removed from list, lock implicitly held
-    void release_nodelete_Locked(void) {
-        mRelease = true;
-        pthread_cond_signal(&threadTriggeredCondition);
-        // assumes caller code path will call decRef_Locked()
-    }
-
     void release_Locked(void) {
         mRelease = true;
         pthread_cond_signal(&threadTriggeredCondition);
-        if (mRefCount || threadRunning) {
-            return;
-        }
-        // No one else is holding a reference to this
-        delete this;
     }
 
-    // Called to mark socket in jeopardy
-    void error_Locked(void) {
-        mError = true;
-    }
-    void error(void) {
-        wrlock();
-        error_Locked();
-        unlock();
-    }
-
-    bool isError_Locked(void) const {
-        return mRelease || mError;
-    }
-
-    // Mark Used
-    //  Locking implied, grabbed when protection around loop iteration
-    void incRef_Locked(void) {
-        ++mRefCount;
-    }
-
-    bool owned_Locked(void) const {
-        return mRefCount != 0;
-    }
-
-    void decRef_Locked(void) {
-        if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
-            return;
-        }
-        // No one else is holding a reference to this
-        delete this;
-    }
     bool isWatching(log_id_t id) const {
         return mLogMask & (1 << id);
     }
@@ -146,6 +97,6 @@
     static int FilterSecondPass(const LogBufferElement* element, void* me);
 };
 
-typedef std::list<LogTimeEntry*> LastLogTimes;
+typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
 
 #endif  // _LOGD_LOG_TIMES_H__
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index b68dc34..aad00ad 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -67,6 +67,13 @@
   LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
 endif
 
+EXPORT_GLOBAL_HWASAN_OPTIONS :=
+ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)
+  ifneq ($(HWADDRESS_SANITIZER_GLOBAL_OPTIONS),)
+    EXPORT_GLOBAL_HWASAN_OPTIONS := export HWASAN_OPTIONS $(HWADDRESS_SANITIZER_GLOBAL_OPTIONS)
+  endif
+endif
+
 EXPORT_GLOBAL_GCOV_OPTIONS :=
 ifeq ($(NATIVE_COVERAGE),true)
   EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
@@ -77,7 +84,7 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data odm oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    sbin dev proc sys system data odm oem acct config storage mnt apex $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
@@ -117,6 +124,7 @@
 LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib64 $(TARGET_ROOT_OUT)/odm/lib64
 LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/overlay $(TARGET_ROOT_OUT)/odm/overlay
 LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
 
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
@@ -138,22 +146,14 @@
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
-# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed.
-bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM)))
-bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep
-$(bcp_dep) :
-	$(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.bcp.dep && touch $@
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
 	@echo "Generate: $< -> $@"
 	@mkdir -p $(dir $@)
 	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
-
-bcp_md5 :=
-bcp_dep :=
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
 
 # Append PLATFORM_VNDK_VERSION to base name.
 define append_vndk_version
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
index 6029ae7..ca22eb8 100644
--- a/rootdir/OWNERS
+++ b/rootdir/OWNERS
@@ -1,3 +1,4 @@
+ccross@google.com
 jeffv@google.com
 jiyong@google.com
 smoreland@google.com
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 2e2ab74..4576776 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -11,3 +11,4 @@
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
     %EXPORT_GLOBAL_GCOV_OPTIONS%
+    %EXPORT_GLOBAL_HWASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index f39ea7c..025e3c3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -269,6 +269,19 @@
     # that they can be chown'd to system:system later on boot
     write /sys/class/leds/vibrator/trigger "transient"
 
+    # Setup APEX mount point and its security context
+    mount tmpfs tmpfs /apex nodev noexec nosuid
+    chmod 0755 /apex
+    chown root root /apex
+    restorecon /apex
+
+    # Start logd before any other services run to ensure we capture all of their logs.
+    start logd
+    # Start essential services.
+    start servicemanager
+    start hwservicemanager
+    start vndservicemanager
+
 # Healthd can trigger a full boot from charger mode by signaling this
 # property when the power button is held.
 on property:sys.boot_from_charger_mode=1
@@ -325,11 +338,8 @@
     #     /vendor/build.prop and
     #     /factory/factory.prop
     load_system_props
-    # start essential services
-    start logd
-    start servicemanager
-    start hwservicemanager
-    start vndservicemanager
+    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.
@@ -392,6 +402,10 @@
     class_start early_hal
 
 on post-fs-data
+    # Start checkpoint before we touch data
+    start vold
+    exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
+
     # We chown/chmod /data again so because mount is run as root + defaults
     chown system system /data
     chmod 0771 /data
@@ -399,7 +413,6 @@
     restorecon /data
 
     # Make sure we have the device encryption key.
-    start vold
     installkey /data
 
     # Start bootcharting as soon as possible after the data partition is
@@ -476,6 +489,8 @@
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
 
+    mkdir /data/preloads 0775 system system
+
     mkdir /data/vendor 0771 root root
     mkdir /data/vendor_ce 0771 root root
     mkdir /data/vendor_de 0771 root root
@@ -523,6 +538,8 @@
 
     mkdir /data/anr 0775 system system
 
+    mkdir /data/apex 0770 root root
+
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc
     mkdir /data/nfc/param 0770 nfc nfc
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index 17ea25b..45f1d4d 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -161,7 +161,7 @@
         storaged_sp->update_uid_io_interval(time_window);
     }
 
-    return NO_ERROR;
+    return OK;
 }
 
 binder::Status StoragedService::onUserStarted(int32_t userId) {
diff --git a/storaged/uid_info.cpp b/storaged/uid_info.cpp
index 58e3fd2..0f718de 100644
--- a/storaged/uid_info.cpp
+++ b/storaged/uid_info.cpp
@@ -32,7 +32,7 @@
         parcel->writeCString(task_it.second.comm.c_str());
         parcel->write(&task_it.second.io, sizeof(task_it.second.io));
     }
-    return NO_ERROR;
+    return OK;
 }
 
 status_t UidInfo::readFromParcel(const Parcel* parcel) {
@@ -48,5 +48,5 @@
         parcel->read(&task.io, sizeof(task.io));
         tasks[task.pid] = task;
     }
-    return NO_ERROR;
+    return OK;
 }
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index 0849ee9..98cbcc3 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -112,7 +112,8 @@
             }
         }
     }
-    KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} {
+    KmParamSet(KmParamSet&& other) noexcept
+        : keymaster_key_param_set_t{other.params, other.length} {
         other.length = 0;
         other.params = nullptr;
     }