Merge "fastbootd: Only flash slots listed by the boot control HAL."
diff --git a/adb/Android.bp b/adb/Android.bp
index e994075..6234af1 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -24,8 +24,7 @@
         "-Wno-missing-field-initializers",
         "-Wvla",
     ],
-    rtti: true,
-    cpp_std: "gnu++17",
+    cpp_std: "experimental",
 
     use_version_lib: true,
 
@@ -72,7 +71,7 @@
                 "-DUNICODE=1",
                 "-D_UNICODE=1",
 
-                // -std=gnu++11 doesn't set _GNU_SOURCE on Windows.
+                // Unlike on Linux, -std=gnu++ doesn't set _GNU_SOURCE on Windows.
                 "-D_GNU_SOURCE",
 
                 // MinGW hides some things behind _POSIX_SOURCE.
diff --git a/adb/NOTICE b/adb/NOTICE
index ff47c95..9ffcc08 100644
--- a/adb/NOTICE
+++ b/adb/NOTICE
@@ -189,63 +189,3 @@
 
    END OF TERMS AND CONDITIONS
 
-------------------------------------------------------------
-libwinpthread license:
-------------------------------------------------------------
-Copyright (c) 2011 mingw-w64 project
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
-
-/*
- * Parts of this library are derived by:
- *
- * Posix Threads library for Microsoft Windows
- *
- * Use at own risk, there is no implied warranty to this code.
- * It uses undocumented features of Microsoft Windows that can change
- * at any time in the future.
- *
- * (C) 2010 Lockless Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- *
- *  * Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *  * Neither the name of Lockless Inc. nor the names of its contributors may be
- *    used to endorse or promote products derived from this software without
- *    specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
diff --git a/adb/adb.h b/adb/adb.h
index e2911e8..cdd6346 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -149,6 +149,10 @@
 #endif
 
 #if !ADB_HOST
+asocket* daemon_service_to_socket(std::string_view name);
+#endif
+
+#if !ADB_HOST
 int init_jdwp(void);
 asocket* create_jdwp_service_socket();
 asocket* create_jdwp_tracker_service_socket();
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 7f37c45..054cbac 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -226,18 +226,8 @@
 
 static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
                               bool use_localagent) {
-    static const char* const DATA_DEST = "/data/local/tmp/%s";
-    static const char* const SD_DEST = "/sdcard/tmp/%s";
-    const char* where = DATA_DEST;
-
     printf("Performing Push Install\n");
 
-    for (int i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-s")) {
-            where = SD_DEST;
-        }
-    }
-
     // Find last APK argument.
     // All other arguments passed through verbatim.
     int last_apk = -1;
@@ -256,7 +246,7 @@
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
     std::string apk_dest =
-            android::base::StringPrintf(where, android::base::Basename(argv[last_apk]).c_str());
+            "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
 
     if (use_fastdeploy == true) {
         TemporaryFile metadataTmpFile;
@@ -341,11 +331,6 @@
         error_exit("Attempting to use streaming install on unsupported device");
     }
 
-    if (use_fastdeploy == true && is_reinstall == false) {
-        printf("Fast Deploy is only available with -r.\n");
-        use_fastdeploy = false;
-    }
-
     if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
         printf("Fast Deploy is only compatible with devices of API version %d or higher, "
                "ignoring.\n",
@@ -360,10 +345,17 @@
             passthrough_argv.push_back(argv[i]);
         }
     }
+    if (passthrough_argv.size() < 2) {
+        error_exit("install requires an apk argument");
+    }
 
     if (use_fastdeploy == true) {
         fastdeploy_set_local_agent(use_localagent);
         update_agent(agent_update_strategy);
+
+        // The last argument must be the APK file
+        const char* file = passthrough_argv.back();
+        use_fastdeploy = find_package(file);
     }
 
     switch (installMode) {
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index c11052d..8676214 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -146,18 +146,16 @@
         " install [-lrtsdg] [--instant] PACKAGE\n"
         " install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
         "     push package(s) to the device and install them\n"
-        "     -l: forward lock application\n"
         "     -r: replace existing application\n"
         "     -t: allow test packages\n"
-        "     -s: install application on sdcard\n"
         "     -d: allow version code downgrade (debuggable packages only)\n"
         "     -p: partial application install (install-multiple only)\n"
         "     -g: grant all runtime permissions\n"
         "     --instant: cause the app to be installed as an ephemeral install app\n"
         "     --no-streaming: always push APK to device and invoke Package Manager as separate steps\n"
         "     --streaming: force streaming APK directly into Package Manager\n"
-        "     --fastdeploy: use fast deploy (only valid with -r)\n"
-        "     --no-fastdeploy: prevent use of fast deploy (only valid with -r)\n"
+        "     --fastdeploy: use fast deploy\n"
+        "     --no-fastdeploy: prevent use of fast deploy\n"
         "     --force-agent: force update of deployment agent when using fast deploy\n"
         "     --date-check-agent: update deployment agent when local version is newer and using fast deploy\n"
         "     --version-check-agent: update deployment agent when local version has different version code and using fast deploy\n"
@@ -1277,6 +1275,42 @@
     return 0;
 }
 
+static int adb_connect_command_bidirectional(const std::string& command) {
+    std::string error;
+    int fd = adb_connect(command, &error);
+    if (fd < 0) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return 1;
+    }
+
+    static constexpr auto forward = [](int src, int sink, bool exit_on_end) {
+        char buf[4096];
+        while (true) {
+            int rc = adb_read(src, buf, sizeof(buf));
+            if (rc == 0) {
+                if (exit_on_end) {
+                    exit(0);
+                } else {
+                    adb_shutdown(sink, SHUT_WR);
+                }
+                return;
+            } else if (rc < 0) {
+                perror_exit("read failed");
+            }
+            if (!WriteFdExactly(sink, buf, rc)) {
+                perror_exit("write failed");
+            }
+        }
+    };
+
+    std::thread read(forward, fd, STDOUT_FILENO, true);
+    std::thread write(forward, STDIN_FILENO, fd, false);
+    read.join();
+    write.join();
+    adb_close(fd);
+    return 0;
+}
+
 static int adb_query_command(const std::string& command) {
     std::string result;
     std::string error;
@@ -1768,7 +1802,7 @@
         if (argc != 2) {
             error_exit("usage: adb raw SERVICE");
         }
-        return adb_connect_command(argv[1]);
+        return adb_connect_command_bidirectional(argv[1]);
     }
 
     /* "adb /?" is a common idiom under Windows */
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index 45f3cca..f4e8664 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -16,6 +16,7 @@
 
 #include "fastdeploy.h"
 
+#include <string.h>
 #include <algorithm>
 #include <array>
 #include <memory>
@@ -31,7 +32,7 @@
 
 #include "adb_utils.h"
 
-static constexpr long kRequiredAgentVersion = 0x00000001;
+static constexpr long kRequiredAgentVersion = 0x00000002;
 
 static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
 
@@ -228,11 +229,12 @@
             android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
 
     std::vector<char> extractErrorBuffer;
-    int statusCode;
-    DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
+    DeployAgentFileCallback cb(outputFp, &extractErrorBuffer);
     int returnCode = send_shell_command(extractCommand, false, &cb);
     if (returnCode != 0) {
-        error_exit("Executing %s returned %d", extractCommand.c_str(), returnCode);
+        fprintf(stderr, "Executing %s returned %d\n", extractCommand.c_str(), returnCode);
+        fprintf(stderr, "%*s\n", int(extractErrorBuffer.size()), extractErrorBuffer.data());
+        error_exit("Aborting");
     }
 }
 
@@ -312,9 +314,16 @@
     std::vector<unsigned char> applyErrorBuffer;
     std::string argsString;
 
+    bool rSwitchPresent = false;
     for (int i = 0; i < argc; i++) {
         argsString.append(argv[i]);
         argsString.append(" ");
+        if (!strcmp(argv[i], "-r")) {
+            rSwitchPresent = true;
+        }
+    }
+    if (!rSwitchPresent) {
+        argsString.append("-r");
     }
 
     std::string applyPatchCommand =
@@ -325,3 +334,9 @@
         error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
     }
 }
+
+bool find_package(const char* apkPath) {
+    const std::string findCommand =
+            "/data/local/tmp/deployagent find " + get_packagename_from_apk(apkPath);
+    return !send_shell_command(findCommand);
+}
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
index a6b10d3..7b7f2ec 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -32,3 +32,4 @@
 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);
+bool find_package(const char* apkPath);
diff --git a/adb/client/fastdeploycallbacks.cpp b/adb/client/fastdeploycallbacks.cpp
index 6c9a21f..23a0aca 100644
--- a/adb/client/fastdeploycallbacks.cpp
+++ b/adb/client/fastdeploycallbacks.cpp
@@ -35,8 +35,7 @@
 
 class DeployAgentBufferCallback : public StandardStreamsCallbackInterface {
   public:
-    DeployAgentBufferCallback(std::vector<char>* outBuffer, std::vector<char>* errBuffer,
-                              int* statusCode);
+    DeployAgentBufferCallback(std::vector<char>* outBuffer, std::vector<char>* errBuffer);
 
     virtual void OnStdout(const char* buffer, int length);
     virtual void OnStderr(const char* buffer, int length);
@@ -45,27 +44,17 @@
   private:
     std::vector<char>* mpOutBuffer;
     std::vector<char>* mpErrBuffer;
-    int* mpStatusCode;
 };
 
 int capture_shell_command(const char* command, std::vector<char>* outBuffer,
                           std::vector<char>* errBuffer) {
-    int statusCode;
-    DeployAgentBufferCallback cb(outBuffer, errBuffer, &statusCode);
-    int ret = send_shell_command(command, false, &cb);
-
-    if (ret == 0) {
-        return statusCode;
-    } else {
-        return ret;
-    }
+    DeployAgentBufferCallback cb(outBuffer, errBuffer);
+    return send_shell_command(command, false, &cb);
 }
 
-DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer,
-                                                 int* statusCode) {
+DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer) {
     mpOutFile = outputFile;
     mpErrBuffer = errBuffer;
-    mpStatusCode = statusCode;
     mBytesWritten = 0;
 }
 
@@ -84,10 +73,7 @@
 }
 
 int DeployAgentFileCallback::Done(int status) {
-    if (mpStatusCode != NULL) {
-        *mpStatusCode = status;
-    }
-    return 0;
+    return status;
 }
 
 int DeployAgentFileCallback::getBytesWritten() {
@@ -95,11 +81,9 @@
 }
 
 DeployAgentBufferCallback::DeployAgentBufferCallback(std::vector<char>* outBuffer,
-                                                     std::vector<char>* errBuffer,
-                                                     int* statusCode) {
+                                                     std::vector<char>* errBuffer) {
     mpOutBuffer = outBuffer;
     mpErrBuffer = errBuffer;
-    mpStatusCode = statusCode;
 }
 
 void DeployAgentBufferCallback::OnStdout(const char* buffer, int length) {
@@ -111,8 +95,5 @@
 }
 
 int DeployAgentBufferCallback::Done(int status) {
-    if (mpStatusCode != NULL) {
-        *mpStatusCode = status;
-    }
-    return 0;
+    return status;
 }
diff --git a/adb/client/fastdeploycallbacks.h b/adb/client/fastdeploycallbacks.h
index b428b50..7e049c5 100644
--- a/adb/client/fastdeploycallbacks.h
+++ b/adb/client/fastdeploycallbacks.h
@@ -21,7 +21,7 @@
 
 class DeployAgentFileCallback : public StandardStreamsCallbackInterface {
   public:
-    DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer, int* statusCode);
+    DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer);
 
     virtual void OnStdout(const char* buffer, int length);
     virtual void OnStderr(const char* buffer, int length);
@@ -33,7 +33,6 @@
     FILE* mpOutFile;
     std::vector<char>* mpErrBuffer;
     int mBytesWritten;
-    int* mpStatusCode;
 };
 
 int capture_shell_command(const char* command, std::vector<char>* outBuffer,
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index b72ed16..1a92317 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -245,9 +245,8 @@
     // If we can use overlayfs, lets get it in place first
     // before we struggle with determining deduplication operations.
     if (!verity_enabled && fs_mgr_overlayfs_setup()) {
-        std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                                   fs_mgr_free_fstab);
-        if (fs_mgr_overlayfs_mount_all(fstab.get())) {
+        Fstab fstab;
+        if (ReadDefaultFstab(&fstab) && fs_mgr_overlayfs_mount_all(&fstab)) {
             WriteFdExactly(fd.get(), "overlayfs mounted\n");
         }
     }
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index b300fac..3182ddd 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -33,6 +33,7 @@
 #include <thread>
 
 #include <android-base/file.h>
+#include <android-base/parseint.h>
 #include <android-base/parsenetaddress.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -223,6 +224,105 @@
     WriteFdExactly(fd.get(), "spinning\n");
 }
 
+struct ServiceSocket : public asocket {
+    ServiceSocket() {
+        install_local_socket(this);
+        this->enqueue = [](asocket* self, apacket::payload_type data) {
+            return static_cast<ServiceSocket*>(self)->Enqueue(std::move(data));
+        };
+        this->ready = [](asocket* self) { return static_cast<ServiceSocket*>(self)->Ready(); };
+        this->close = [](asocket* self) { return static_cast<ServiceSocket*>(self)->Close(); };
+    }
+    virtual ~ServiceSocket() = default;
+
+    virtual int Enqueue(apacket::payload_type data) { return -1; }
+    virtual void Ready() {}
+    virtual void Close() {
+        if (peer) {
+            peer->peer = nullptr;
+            if (peer->shutdown) {
+                peer->shutdown(peer);
+            }
+            peer->close(peer);
+        }
+
+        remove_socket(this);
+        delete this;
+    }
+};
+
+struct SinkSocket : public ServiceSocket {
+    explicit SinkSocket(size_t byte_count) {
+        LOG(INFO) << "Creating new SinkSocket with capacity " << byte_count;
+        bytes_left_ = byte_count;
+    }
+
+    virtual ~SinkSocket() { LOG(INFO) << "SinkSocket destroyed"; }
+
+    virtual int Enqueue(apacket::payload_type data) override final {
+        if (bytes_left_ <= data.size()) {
+            // Done reading.
+            Close();
+            return -1;
+        }
+
+        bytes_left_ -= data.size();
+        return 0;
+    }
+
+    size_t bytes_left_;
+};
+
+struct SourceSocket : public ServiceSocket {
+    explicit SourceSocket(size_t byte_count) {
+        LOG(INFO) << "Creating new SourceSocket with capacity " << byte_count;
+        bytes_left_ = byte_count;
+    }
+
+    virtual ~SourceSocket() { LOG(INFO) << "SourceSocket destroyed"; }
+
+    void Ready() {
+        size_t len = std::min(bytes_left_, get_max_payload());
+        if (len == 0) {
+            Close();
+            return;
+        }
+
+        Block block(len);
+        memset(block.data(), 0, block.size());
+        peer->enqueue(peer, std::move(block));
+        bytes_left_ -= len;
+    }
+
+    int Enqueue(apacket::payload_type data) { return -1; }
+
+    size_t bytes_left_;
+};
+
+asocket* daemon_service_to_socket(std::string_view name) {
+    if (name == "jdwp") {
+        return create_jdwp_service_socket();
+    } else if (name == "track-jdwp") {
+        return create_jdwp_tracker_service_socket();
+    } else if (name.starts_with("sink:")) {
+        name.remove_prefix(strlen("sink:"));
+        uint64_t byte_count = 0;
+        if (!android::base::ParseUint(name.data(), &byte_count)) {
+            return nullptr;
+        }
+        return new SinkSocket(byte_count);
+    } else if (name.starts_with("source:")) {
+        name.remove_prefix(strlen("source:"));
+        uint64_t byte_count = 0;
+        if (!android::base::ParseUint(name.data(), &byte_count)) {
+            return nullptr;
+        }
+        return new SourceSocket(byte_count);
+    }
+
+    return nullptr;
+}
+
 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)};
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 01097ac..8805fc1 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -273,7 +273,7 @@
     }
 
     std::vector<std::string> joined_env;
-    for (auto it : env) {
+    for (const auto& it : env) {
         const char* key = it.first.c_str();
         const char* value = it.second.c_str();
         joined_env.push_back(android::base::StringPrintf("%s=%s", key, value));
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
index cd6f168..2d3b135 100644
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -35,7 +35,7 @@
 
 public final class DeployAgent {
     private static final int BUFFER_SIZE = 128 * 1024;
-    private static final int AGENT_VERSION = 0x00000001;
+    private static final int AGENT_VERSION = 0x00000002;
 
     public static void main(String[] args) {
         int exitCode = 0;
@@ -53,6 +53,15 @@
 
                 String packageName = args[1];
                 extractMetaData(packageName);
+            } else if (commandString.equals("find")) {
+                if (args.length != 2) {
+                    showUsage(1);
+                }
+
+                String packageName = args[1];
+                if (getFilenameFromPackageName(packageName) == null) {
+                    exitCode = 3;
+                }
             } else if (commandString.equals("apply")) {
                 if (args.length < 4) {
                     showUsage(1);
@@ -112,6 +121,7 @@
             "usage: deployagent <command> [<args>]\n\n" +
             "commands:\n" +
             "version                             get the version\n" +
+            "find PKGNAME                        return zero if package found, else non-zero\n" +
             "extract PKGNAME                     extract an installed package's metadata\n" +
             "apply PKGNAME PATCHFILE [-o|-pm]    apply a patch from PATCHFILE (- for stdin) to an installed package\n" +
             " -o <FILE> directs output to FILE, default or - for stdout\n" +
@@ -134,7 +144,7 @@
         return null;
     }
 
-    private static File getFileFromPackageName(String packageName) throws IOException {
+    private static String getFilenameFromPackageName(String packageName) throws IOException {
         StringBuilder commandBuilder = new StringBuilder();
         commandBuilder.append("pm list packages -f " + packageName);
 
@@ -142,16 +152,32 @@
         BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
 
         String packagePrefix = "package:";
+        String packageSuffix = "=" + packageName;
         String line = "";
         while ((line = reader.readLine()) != null) {
-            int packageIndex = line.indexOf(packagePrefix);
-            int equalsIndex = line.indexOf("=" + packageName);
-            return new File(line.substring(packageIndex + packagePrefix.length(), equalsIndex));
+            if (line.endsWith(packageSuffix)) {
+                int packageIndex = line.indexOf(packagePrefix);
+                if (packageIndex == -1) {
+                    throw new IOException("error reading package list");
+                }
+                int equalsIndex = line.lastIndexOf(packageSuffix);
+                String fileName =
+                    line.substring(packageIndex + packagePrefix.length(), equalsIndex);
+                return fileName;
+            }
         }
-
         return null;
     }
 
+    private static File getFileFromPackageName(String packageName) throws IOException {
+        String filename = getFilenameFromPackageName(packageName);
+        if (filename == null) {
+            // Should not happen (function is only called when we know the package exists)
+            throw new IOException("package not found");
+        }
+        return new File(filename);
+    }
+
     private static void extractMetaData(String packageName) throws IOException {
         File apkFile = getFileFromPackageName(packageName);
         APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
diff --git a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
index 5577364..24b2eab 100644
--- a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
+++ b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
@@ -61,22 +61,22 @@
             File hostFile = new File(apkPath);
 
             List<APKEntry> deviceZipEntries = getMetadataFromFile(deviceMetadataPath);
+            System.err.println("Device Entries (" + deviceZipEntries.size() + ")");
             if (verbose) {
                 sb = new StringBuilder();
                 for (APKEntry entry : deviceZipEntries) {
                     APKEntryToString(entry, sb);
                 }
-                System.err.println("Device Entries (" + deviceZipEntries.size() + ")");
                 System.err.println(sb.toString());
             }
 
             List<APKEntry> hostFileEntries = PatchUtils.getAPKMetaData(hostFile).getEntriesList();
+            System.err.println("Host Entries (" + hostFileEntries.size() + ")");
             if (verbose) {
                 sb = new StringBuilder();
                 for (APKEntry entry : hostFileEntries) {
                     APKEntryToString(entry, sb);
                 }
-                System.err.println("Host Entries (" + hostFileEntries.size() + ")");
                 System.err.println(sb.toString());
             }
 
@@ -130,7 +130,8 @@
 
         for (APKEntry deviceZipEntry : deviceZipEntries) {
             for (APKEntry hostZipEntry : hostZipEntries) {
-                if (deviceZipEntry.getCrc32() == hostZipEntry.getCrc32()) {
+                if (deviceZipEntry.getCrc32() == hostZipEntry.getCrc32() &&
+                    deviceZipEntry.getFileName().equals(hostZipEntry.getFileName())) {
                     identicalContents.add(new SimpleEntry(deviceZipEntry, hostZipEntry));
                 }
             }
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8b07f74..1bd57c1 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -348,11 +348,8 @@
 
 asocket* create_local_service_socket(const char* name, atransport* transport) {
 #if !ADB_HOST
-    if (!strcmp(name, "jdwp")) {
-        return create_jdwp_service_socket();
-    }
-    if (!strcmp(name, "track-jdwp")) {
-        return create_jdwp_tracker_service_socket();
+    if (asocket* s = daemon_service_to_socket(name); s) {
+        return s;
     }
 #endif
     int fd = service_to_fd(name, transport);
diff --git a/adb/transport.cpp b/adb/transport.cpp
index c2d4917..f59a135 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -764,6 +764,10 @@
 #if ADB_HOST
         if (t->IsTcpDevice() && !t->kicked()) {
             D("transport: %s unref (attempting reconnection)", t->serial.c_str());
+
+            // We need to clear the transport's keys, so that on the next connection, it tries
+            // again from the beginning.
+            t->ResetKeys();
             reconnect_handler.TrackTransport(t);
         } else {
             D("transport: %s unref (kicking and closing)", t->serial.c_str());
@@ -1009,14 +1013,11 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-        kFeatureShell2, kFeatureCmd, kFeatureStat2, kFeatureFixedPushMkdir,
-#if ADB_HOST
-                kFeatureApex
-#endif
-        // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
-        // to know about. Otherwise, the client can be stuck running an old
-        // version of the server even after upgrading their copy of adb.
-        // (http://b/24370690)
+            kFeatureShell2, kFeatureCmd, kFeatureStat2, kFeatureFixedPushMkdir, kFeatureApex
+            // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
+            // to know about. Otherwise, the client can be stuck running an old
+            // version of the server even after upgrading their copy of adb.
+            // (http://b/24370690)
     };
 
     return *features;
@@ -1304,11 +1305,7 @@
 void unregister_usb_transport(usb_handle* usb) {
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     transport_list.remove_if([usb](atransport* t) {
-        auto connection = t->connection();
-        if (auto usb_connection = dynamic_cast<UsbConnection*>(connection.get())) {
-            return usb_connection->handle_ == usb && t->GetConnectionState() == kCsNoPerm;
-        }
-        return false;
+        return t->GetUsbHandle() == usb && t->GetConnectionState() == kCsNoPerm;
     });
 }
 #endif
@@ -1331,10 +1328,20 @@
 
 #if ADB_HOST
 std::shared_ptr<RSA> atransport::NextKey() {
-    if (keys_.empty()) keys_ = adb_auth_get_private_keys();
+    if (keys_.empty()) {
+        LOG(INFO) << "fetching keys for transport " << this->serial_name();
+        keys_ = adb_auth_get_private_keys();
+
+        // We should have gotten at least one key: the one that's automatically generated.
+        CHECK(!keys_.empty());
+    }
 
     std::shared_ptr<RSA> result = keys_[0];
     keys_.pop_front();
     return result;
 }
+
+void atransport::ResetKeys() {
+    keys_.clear();
+}
 #endif
diff --git a/adb/transport.h b/adb/transport.h
index 9894bdf..790004f 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -37,6 +37,7 @@
 
 #include "adb.h"
 #include "adb_unique_fd.h"
+#include "usb.h"
 
 typedef std::unordered_set<std::string> FeatureSet;
 
@@ -242,6 +243,9 @@
         return connection_;
     }
 
+    void SetUsbHandle(usb_handle* h) { usb_handle_ = h; }
+    usb_handle* GetUsbHandle() { return usb_handle_; }
+
     const TransportId id;
     size_t ref_count = 0;
     bool online = false;
@@ -258,6 +262,7 @@
 
 #if ADB_HOST
     std::shared_ptr<RSA> NextKey();
+    void ResetKeys();
 #endif
 
     char token[TOKEN_SIZE] = {};
@@ -332,6 +337,9 @@
     // The underlying connection object.
     std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
 
+    // USB handle for the connection, if available.
+    usb_handle* usb_handle_ = nullptr;
+
     // A callback that will be invoked when the atransport needs to reconnect.
     ReconnectCallback reconnect_;
 
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index c471bf9..2e5918a 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -180,6 +180,7 @@
     auto connection = std::make_unique<UsbConnection>(h);
     t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(connection)));
     t->type = kTransportUsb;
+    t->SetUsbHandle(h);
 }
 
 int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol) {
diff --git a/base/file.cpp b/base/file.cpp
index d5bb7fe..2f4a517 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -22,6 +22,7 @@
 #include <libgen.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 4e3879b..4e6c879 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <dirent.h>
 #include <fcntl.h>
 
 #if !defined(_WIN32)
@@ -231,3 +232,13 @@
 template <typename T>
 int close(const android::base::unique_fd_impl<T>&)
     __attribute__((__unavailable__("close called on unique_fd")));
+
+template <typename T>
+FILE* fdopen(const android::base::unique_fd_impl<T>&, const char* mode)
+    __attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the "
+                                   "unique_fd, or use android::base::Fdopen to pass ownership")));
+
+template <typename T>
+DIR* fdopendir(const android::base::unique_fd_impl<T>&) __attribute__((
+    __unavailable__("fdopendir takes ownership of the fd passed in; either dup the "
+                    "unique_fd, or use android::base::Fdopendir to pass ownership")));
diff --git a/base/logging.cpp b/base/logging.cpp
index bd09069..f89168c 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -417,6 +417,14 @@
   }
   std::string msg(data_->ToString());
 
+  if (data_->GetSeverity() == FATAL) {
+#ifdef __ANDROID__
+    // Set the bionic abort message early to avoid liblog doing it
+    // with the individual lines, so that we get the whole message.
+    android_set_abort_message(msg.c_str());
+#endif
+  }
+
   {
     // Do the actual logging with the lock held.
     std::lock_guard<std::mutex> lock(LoggingLock());
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 03b3287..5167481 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -9,7 +9,6 @@
         "-Wno-nullability-completeness",
         "-Os",
     ],
-    cpp_std: "gnu++17",
 
     local_include_dirs: ["include"],
 }
@@ -117,14 +116,21 @@
         "libdebuggerd",
         "libbacktrace",
         "libunwindstack",
-        "libdexfile",
+        "libdexfile",  // libunwindstack dependency
+        "libdexfile_external",  // libunwindstack dependency
+        "libdexfile_support",  // libunwindstack dependency
         "liblzma",
         "libcutils",
     ],
     target: {
         recovery: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
-            exclude_static_libs: ["libdexfile"],
+            exclude_static_libs: [
+                "libartbase",
+                "libdexfile",
+                "libdexfile_external",
+                "libdexfile_support",
+            ],
         },
     },
 
@@ -171,12 +177,22 @@
 
     static_libs: [
         "libbacktrace",
+        "libdexfile_external",  // libunwindstack dependency
+        "libdexfile_support",  // libunwindstack dependency
         "libunwindstack",
         "liblzma",
         "libbase",
         "libcutils",
         "liblog",
     ],
+    target: {
+        recovery: {
+            exclude_static_libs: [
+                "libdexfile_external",
+                "libdexfile_support",
+            ],
+        },
+    },
 }
 
 cc_test {
@@ -217,6 +233,8 @@
 
     static_libs: [
         "libdebuggerd",
+        "libdexfile_external",  // libunwindstack dependency
+        "libdexfile_support",  // libunwindstack dependency
         "libunwindstack",
     ],
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 421ce43..d24c887 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdlib.h>
+#include <time.h>
 
 #include <memory>
 #include <string>
@@ -494,3 +495,10 @@
   expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
   ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
 }
+
+TEST_F(TombstoneTest, dump_timestamp) {
+  setenv("TZ", "UTC", 1);
+  tzset();
+  dump_timestamp(&log_, 0);
+  ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 1179263..b20014f 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -78,6 +78,15 @@
   _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
 }
 
+static void dump_timestamp(log_t* log, time_t time) {
+  struct tm tm;
+  localtime_r(&time, &tm);
+
+  char buf[strlen("1970-01-01 00:00:00+0830") + 1];
+  strftime(buf, sizeof(buf), "%F %T%z", &tm);
+  _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
+}
+
 static void dump_probable_cause(log_t* log, const siginfo_t* si, BacktraceMap* map) {
   std::string cause;
   if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
@@ -654,6 +663,7 @@
 
   _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
   dump_header_info(&log);
+  dump_timestamp(&log, time(nullptr));
 
   auto it = threads.find(target_thread);
   if (it == threads.end()) {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 8006c41..ead2105 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -144,8 +144,6 @@
     static_libs: [
         "libhealthhalutils",
     ],
-
-    cpp_std: "c++17",
 }
 
 cc_defaults {
@@ -212,7 +210,6 @@
     name: "libfastboot",
     defaults: ["fastboot_host_defaults"],
 
-    cpp_std: "c++17",
     srcs: [
         "bootimg_utils.cpp",
         "fastboot.cpp",
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 343661d..963916c 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -153,6 +153,7 @@
         if (!FlashPartitionTable(super_name, *new_metadata.get())) {
             return device->WriteFail("Unable to flash new partition table");
         }
+        fs_mgr_overlayfs_teardown();
         return device->WriteOkay("Successfully flashed partition table");
     }
 
@@ -186,5 +187,6 @@
     if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
         return device->WriteFail("Unable to write new partition table");
     }
+    fs_mgr_overlayfs_teardown();
     return device->WriteOkay("Successfully updated partition table");
 }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0978ec1..0b8d9b2 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -46,6 +46,7 @@
 #include <chrono>
 #include <functional>
 #include <regex>
+#include <string>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -78,6 +79,7 @@
 using android::base::Split;
 using android::base::Trim;
 using android::base::unique_fd;
+using namespace std::string_literals;
 
 static const char* serial = nullptr;
 
@@ -1106,6 +1108,14 @@
     return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
 }
 
+static bool is_retrofit_device() {
+    std::string value;
+    if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
+        return false;
+    }
+    return android::base::StartsWith(value, "system_");
+}
+
 static void do_flash(const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
@@ -1148,6 +1158,10 @@
     std::this_thread::sleep_for(std::chrono::milliseconds(1000));
 
     fb->set_transport(open_device());
+
+    if (!is_userspace_fastboot()) {
+        die("Failed to boot into userspace fastboot; one or more components might be unbootable.");
+    }
 }
 
 class ImageSource {
@@ -1304,9 +1318,6 @@
     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.");
-    }
 
     std::string super_name;
     if (fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
@@ -1319,6 +1330,19 @@
         command += ":wipe";
     }
     fb->RawCommand(command, "Updating super partition");
+
+    // Retrofit devices have two super partitions, named super_a and super_b.
+    // On these devices, secondary slots must be flashed as physical
+    // partitions (otherwise they would not mount on first boot). To enforce
+    // this, we delete any logical partitions for the "other" slot.
+    if (is_retrofit_device()) {
+        for (const auto& [image, slot] : os_images_) {
+            std::string partition_name = image->part_name + "_"s + slot;
+            if (image->IsSecondary() && is_logical(partition_name)) {
+                fb->DeletePartition(partition_name);
+            }
+        }
+    }
 }
 
 class ZipImageSource final : public ImageSource {
@@ -1518,7 +1542,7 @@
         return false;
     }
     auto path = find_item_given_name("super_empty.img");
-    if (path.empty()) {
+    if (path.empty() || access(path.c_str(), R_OK)) {
         return false;
     }
     auto metadata = android::fs_mgr::ReadFromImageFile(path);
@@ -1939,8 +1963,7 @@
         fb->RebootTo("recovery");
         fb->WaitForDisconnect();
     } else if (wants_reboot_fastboot) {
-        fb->RebootTo("fastboot");
-        fb->WaitForDisconnect();
+        reboot_to_userspace_fastboot();
     }
 
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 479a06a..ef34771 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -281,7 +281,7 @@
     std::vector<std::string> vars;
     EXPECT_EQ(fb->GetVarAll(&vars), SUCCESS) << "getvar:all failed";
     EXPECT_GT(vars.size(), 0) << "getvar:all did not respond with any INFO responses";
-    for (const auto s : vars) {
+    for (const auto& s : vars) {
         EXPECT_LE(s.size(), FB_RESPONSE_SZ - 4)
                 << "getvar:all included an INFO response: 'INFO" + s << "' which is too long";
     }
@@ -316,7 +316,7 @@
     EXPECT_GT(parts.size(), 0)
             << "getvar:all did not report any partition-size: through INFO responses";
     std::set<std::string> allowed{"ext4", "f2fs", "raw"};
-    for (const auto p : parts) {
+    for (const auto& p : parts) {
         EXPECT_GE(std::get<1>(p), 0);
         std::string part(std::get<0>(p));
         std::set<std::string> allowed{"ext4", "f2fs", "raw"};
@@ -355,7 +355,7 @@
     if (num_slots > 0) {
         EXPECT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
 
-        for (const auto p : parts) {
+        for (const auto& p : parts) {
             std::string part(std::get<0>(p));
             std::regex reg("([[:graph:]]*)_([[:lower:]])");
             std::smatch sm;
@@ -378,7 +378,7 @@
             }
         }
         // Ensure each partition has the correct slot suffix
-        for (const auto iter : part_slots) {
+        for (const auto& iter : part_slots) {
             const std::set<char>& char_set = iter.second;
             std::string chars;
             for (char c : char_set) {
@@ -572,7 +572,7 @@
     std::vector<std::tuple<std::string, uint64_t>> parts;
     EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in locked mode";
     std::string resp;
-    for (const auto tup : parts) {
+    for (const auto& tup : parts) {
         EXPECT_EQ(fb->Flash(std::get<0>(tup), &resp), DEVICE_FAIL)
                 << "Device did not respond with FAIL when trying to flash '" << std::get<0>(tup)
                 << "' in locked mode";
@@ -585,7 +585,7 @@
     std::vector<std::tuple<std::string, uint64_t>> parts;
     EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
     std::string resp;
-    for (const auto tup : parts) {
+    for (const auto& tup : parts) {
         EXPECT_EQ(fb->Erase(std::get<0>(tup), &resp), DEVICE_FAIL)
                 << "Device did not respond with FAIL when trying to erase '" << std::get<0>(tup)
                 << "' in locked mode";
@@ -601,7 +601,7 @@
     EXPECT_EQ(fb->GetVar("slot-count", &resp), SUCCESS) << "getvar:slot-count failed";
     int32_t num_slots = strtol(resp.c_str(), nullptr, 10);
 
-    for (const auto tup : parts) {
+    for (const auto& tup : parts) {
         std::string part(std::get<0>(tup));
         std::regex reg("([[:graph:]]*)_([[:lower:]])");
         std::smatch sm;
@@ -1554,12 +1554,12 @@
 
 void GenerateXmlTests(const extension::Configuration& config) {
     // Build the getvar tests
-    for (const auto it : config.getvars) {
+    for (const auto& it : config.getvars) {
         GETVAR_XML_TESTS.push_back(std::make_pair(it.first, it.second));
     }
 
     // Build the partition tests, to interface with gtest we need to do it this way
-    for (const auto it : config.partitions) {
+    for (const auto& it : config.partitions) {
         const auto tup = std::make_tuple(it.first, it.second);
         PARTITION_XML_TESTS.push_back(tup);  // All partitions
 
@@ -1581,7 +1581,7 @@
 
     // Build the packed tests, only useful if we have a hash
     if (!config.checksum.empty()) {
-        for (const auto it : config.packed) {
+        for (const auto& it : config.packed) {
             for (const auto& test : it.second.tests) {
                 const auto tup = std::make_tuple(it.first, test);
                 if (test.expect == extension::OKAY) {  // only testing the success case
@@ -1608,7 +1608,7 @@
     }
 
     // Build oem tests
-    for (const auto it : config.oem) {
+    for (const auto& it : config.oem) {
         auto oem_cmd = it.second;
         for (const auto& t : oem_cmd.tests) {
             OEM_XML_TESTS.push_back(std::make_tuple(it.first, oem_cmd.restricted, t));
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 6c8a943..ac138a3 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -23,10 +23,6 @@
     cflags: [
         "-Wall",
         "-Werror",
-        "-Wno-unused-variable",
-    ],
-    cppflags: [
-        "-std=gnu++1z",
     ],
 }
 
@@ -42,8 +38,6 @@
         "fs_mgr.cpp",
         "fs_mgr_format.cpp",
         "fs_mgr_verity.cpp",
-        "fs_mgr_avb.cpp",
-        "fs_mgr_avb_ops.cpp",
         "fs_mgr_dm_linear.cpp",
         "fs_mgr_overlayfs.cpp",
         "fs_mgr_vendor_overlay.cpp",
@@ -61,10 +55,12 @@
     ],
     static_libs: [
         "libavb",
+        "libfs_avb",
         "libfstab",
         "libdm",
     ],
     export_static_lib_headers: [
+        "libfs_avb",
         "libfstab",
         "libdm",
     ],
@@ -104,3 +100,28 @@
     export_include_dirs: ["include_fstab"],
     header_libs: ["libbase_headers"],
 }
+
+cc_library_static {
+    name: "libfs_avb",
+    defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
+    export_include_dirs: ["libfs_avb/include"],
+    srcs: [
+        "libfs_avb/avb_ops.cpp",
+        "libfs_avb/fs_avb.cpp",
+    ],
+    static_libs: [
+        "libavb",
+        "libfstab",
+        "libdm",
+    ],
+    export_static_lib_headers: [
+        "libfstab",
+    ],
+    shared_libs: [
+        "libcrypto",
+    ],
+    header_libs: [
+        "libbase_headers",
+    ],
+}
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
new file mode 100644
index 0000000..960410c
--- /dev/null
+++ b/fs_mgr/README.overlayfs.md
@@ -0,0 +1,110 @@
+Android Overlayfs integration with adb remount
+==============================================
+
+Introduction
+------------
+
+Users working with userdebug or eng builds expect to be able to
+remount the system partition as read-write and then add or modify
+any number of files without reflashing the system image, which is
+understandably efficient for a development cycle.
+Limited memory systems that chose to use readonly filesystems like
+*squashfs*, or *Logical Resizable Android Partitions* which land
+system partition images right-sized, and with filesystem that have
+been deduped on the block level to compress the content; means that
+either a remount is not possible directly, or when done offers
+little or no utility because of remaining space limitations or
+support logistics.
+
+*Overlayfs* comes to the rescue for these debug scenarios, and logic
+will _automatically_ setup backing storage for a writable filesystem
+as an upper reference, and mount overtop the lower.  These actions
+will be performed in the **adb disable-verity** and **adb remount**
+requests.
+
+Operations
+----------
+
+### Cookbook
+
+The typical action to utilize the remount facility is:
+
+    $ adb root
+    $ adb disable-verity
+    $ adb reboot
+    $ adb wait-for-device
+    $ adb root
+    $ adb remount
+
+Followed by one of the following:
+
+    $ adb stop
+    $ adb sync
+    $ adb start
+    $ adb reboot
+
+*or*
+
+    $ adb push <source> <destination>
+    $ adb reboot
+
+Note that the sequence above:
+
+    $ adb disable-verity
+    $ adb reboot
+
+can be replaced with:
+
+    $ adb reboot -R
+
+which will not reboot if everything is already prepared and ready
+to go.
+
+None of this changes if *overlayfs* needs to be engaged.
+The decisions whether to use traditional direct filesystem remount,
+or one wrapped by *overlayfs* is automatically determined based on
+a probe of the filesystem types and space remaining.
+
+### Backing Storage
+
+When *overlayfs* logic is feasible, it will use either the
+**/cache/overlay/** directory for non-A/B devices, or the
+**/mnt/scratch/overlay** directory for A/B devices that have
+access to *Logical Resizeable Android Partitions*.
+The backing store is used as soon as possible in the boot
+process and can occur at first stage init, or at the
+mount_all init rc commands.
+
+This early as possible attachment of *overlayfs* means that
+*sepolicy* or *init* itself can also be pushed and used after
+the exec phases that accompany each stage.
+
+Caveats
+-------
+
+- Space used in the backing storage is on a file by file basis
+  and will require more space than if updated in place.  As such
+  it is important to be mindful of any wasted space, for instance
+  **BOARD_<partition>IMAGE_PARTITION_RESERVED_SIZE** being defined
+  will have a negative impact on the overall right-sizing of images
+  and thus free dynamic partition space.
+- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
+  with "*overlayfs: override_creds=off option bypass creator_cred*"
+  if higher than 4.6.
+- *adb enable-verity* will free up overlayfs and as a bonus the
+  device will be reverted pristine to before any content was updated.
+  Update engine does not take advantage of this, will perform a full OTA.
+- Update engine may not run if *fs_mgr_overlayfs_is_setup*() reports
+  true as adb remount overrides are incompatable with an OTA resources.
+- For implementation simplicity on retrofit dynamic partition devices,
+  take the whole alternate super (eg: if "*a*" slot, then the whole of
+  "*system_b*").
+  Since landing a filesystem on the alternate super physical device
+  without differentiating if it is setup to support logical or physical,
+  the alternate slot metadata and previous content will be lost.
+- If dynamic partitions runs out of space, resizing a logical
+  partition larger may fail because of the scratch partition.
+  If this happens, either fastboot flashall or adb enable-verity can
+  be used to clear scratch storage to permit the flash.
+  Then reinstate the overrides and continue.
+- File bugs or submit fixes for review.
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6310238..88f7a2c 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -20,6 +20,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <libgen.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -34,6 +35,7 @@
 #include <unistd.h>
 
 #include <functional>
+#include <map>
 #include <memory>
 #include <string>
 #include <thread>
@@ -53,6 +55,7 @@
 #include <ext4_utils/ext4_sb.h>
 #include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
+#include <fs_avb/fs_avb.h>
 #include <fs_mgr_overlayfs.h>
 #include <libdm/dm.h>
 #include <liblp/metadata_format.h>
@@ -62,7 +65,6 @@
 #include <log/log_properties.h>
 #include <logwrap/logwrap.h>
 
-#include "fs_mgr_avb.h"
 #include "fs_mgr_priv.h"
 
 #define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
@@ -80,8 +82,16 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHashtreeResult;
+using android::fs_mgr::AvbUniquePtr;
+
+using namespace std::literals;
 
 // record fs stat
 enum FsStatFlags {
@@ -95,7 +105,7 @@
     FS_STAT_FULL_MOUNT_FAILED = 0x0100,
     FS_STAT_E2FSCK_FAILED = 0x0200,
     FS_STAT_E2FSCK_FS_FIXED = 0x0400,
-    FS_STAT_EXT4_INVALID_MAGIC = 0x0800,
+    FS_STAT_INVALID_MAGIC = 0x0800,
     FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
     FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
     FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
@@ -123,10 +133,10 @@
     }
 }
 
-static void log_fs_stat(const char* blk_device, int fs_stat)
-{
+static void log_fs_stat(const std::string& blk_device, int fs_stat) {
     if ((fs_stat & FS_STAT_IS_EXT4) == 0) return; // only log ext4
-    std::string msg = android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device, fs_stat);
+    std::string msg =
+            android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
                                                         O_APPEND | O_CREAT, 0664)));
     if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) {
@@ -138,6 +148,18 @@
     return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2";
 }
 
+static bool is_f2fs(const std::string& fs_type) {
+    return fs_type == "f2fs";
+}
+
+static std::string realpath(const std::string& blk_device) {
+    std::string real_path;
+    if (!Realpath(blk_device, &real_path)) {
+        real_path = blk_device;
+    }
+    return real_path;
+}
+
 static bool should_force_check(int fs_stat) {
     return fs_stat &
            (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
@@ -146,20 +168,21 @@
             FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
 }
 
-static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
-{
+static void check_fs(const std::string& blk_device, const std::string& fs_type,
+                     const std::string& target, int* fs_stat) {
     int status;
     int ret;
     long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
-    char tmpmnt_opts[64] = "errors=remount-ro";
-    const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device};
-    const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
+    auto tmpmnt_opts = "errors=remount-ro"s;
+    const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device.c_str()};
+    const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device.c_str()};
+
+    if (*fs_stat & FS_STAT_INVALID_MAGIC) {  // will fail, so do not try
+        return;
+    }
 
     /* Check for the types of filesystems we know how to check */
     if (is_extfs(fs_type)) {
-        if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {  // will fail, so do not try
-            return;
-        }
         /*
          * First try to mount and unmount the filesystem.  We do this because
          * the kernel is more efficient than e2fsck in running the journal and
@@ -175,18 +198,19 @@
          */
         if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) {  // already tried if full mount failed
             errno = 0;
-            if (!strcmp(fs_type, "ext4")) {
+            if (fs_type == "ext4") {
                 // This option is only valid with ext4
-                strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
+                tmpmnt_opts += ",nomblk_io_submit";
             }
-            ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+            ret = mount(blk_device.c_str(), target.c_str(), fs_type.c_str(), tmpmnt_flags,
+                        tmpmnt_opts.c_str());
             PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
                   << ")=" << ret;
             if (!ret) {
                 bool umounted = false;
                 int retry_count = 5;
                 while (retry_count-- > 0) {
-                    umounted = umount(target) == 0;
+                    umounted = umount(target.c_str()) == 0;
                     if (umounted) {
                         LINFO << __FUNCTION__ << "(): unmount(" << target << ") succeeded";
                         break;
@@ -209,10 +233,10 @@
          * (e.g. recent SDK system images). Detect these and skip the check.
          */
         if (access(E2FSCK_BIN, X_OK)) {
-            LINFO << "Not running " << E2FSCK_BIN << " on " << blk_device
+            LINFO << "Not running " << E2FSCK_BIN << " on " << realpath(blk_device)
                   << " (executable not in system image)";
         } else {
-            LINFO << "Running " << E2FSCK_BIN << " on " << blk_device;
+            LINFO << "Running " << E2FSCK_BIN << " on " << realpath(blk_device);
             if (should_force_check(*fs_stat)) {
                 ret = android_fork_execvp_ext(
                     ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
@@ -232,13 +256,9 @@
                 *fs_stat |= FS_STAT_E2FSCK_FS_FIXED;
             }
         }
-    } else if (!strcmp(fs_type, "f2fs")) {
-            const char *f2fs_fsck_argv[] = {
-                    F2FS_FSCK_BIN,
-                    "-a",
-                    blk_device
-            };
-        LINFO << "Running " << F2FS_FSCK_BIN << " -a " << blk_device;
+    } else if (is_f2fs(fs_type)) {
+        const char* f2fs_fsck_argv[] = {F2FS_FSCK_BIN, "-a", blk_device.c_str()};
+        LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
 
         ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv),
                                       const_cast<char **>(f2fs_fsck_argv),
@@ -272,9 +292,10 @@
 }
 
 // Read the primary superblock from an ext4 filesystem.  On failure return
-// false.  If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC.
-static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) {
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
+// false.  If it's not an ext4 filesystem, also set FS_STAT_INVALID_MAGIC.
+static bool read_ext4_superblock(const std::string& blk_device, struct ext4_super_block* sb,
+                                 int* fs_stat) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
 
     if (fd < 0) {
         PERROR << "Failed to open '" << blk_device << "'";
@@ -289,7 +310,7 @@
     if (!is_ext4_superblock_valid(sb)) {
         LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
         // not a valid fs, tune2fs, fsck, and mount  will all fail.
-        *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
+        *fs_stat |= FS_STAT_INVALID_MAGIC;
         return false;
     }
     *fs_stat |= FS_STAT_IS_EXT4;
@@ -315,10 +336,10 @@
 }
 
 // Enable/disable quota support on the filesystem if needed.
-static void tune_quota(const char* blk_device, const struct fstab_rec* rec,
+static void tune_quota(const std::string& blk_device, const FstabEntry& entry,
                        const struct ext4_super_block* sb, int* fs_stat) {
     bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
-    bool want_quota = fs_mgr_is_quota(rec) != 0;
+    bool want_quota = entry.fs_mgr_flags.quota;
 
     if (has_quota == want_quota) {
         return;
@@ -330,7 +351,7 @@
         return;
     }
 
-    const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device};
+    const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device.c_str()};
 
     if (want_quota) {
         LINFO << "Enabling quotas on " << blk_device;
@@ -351,16 +372,16 @@
 }
 
 // Set the number of reserved filesystem blocks if needed.
-static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec,
+static void tune_reserved_size(const std::string& blk_device, const FstabEntry& entry,
                                const struct ext4_super_block* sb, int* fs_stat) {
-    if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+    if (!entry.fs_mgr_flags.reserved_size) {
         return;
     }
 
     // The size to reserve is given in the fstab, but we won't reserve more
     // than 2% of the filesystem.
     const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;
-    uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb);
+    uint64_t reserved_blocks = entry.reserved_size / EXT4_BLOCK_SIZE(sb);
 
     if (reserved_blocks > max_reserved_blocks) {
         LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
@@ -383,7 +404,8 @@
     auto reserved_blocks_str = std::to_string(reserved_blocks);
     auto reserved_gid_str = std::to_string(AID_RESERVED_DISK);
     const char* argv[] = {
-        TUNE2FS_BIN, "-r", reserved_blocks_str.c_str(), "-g", reserved_gid_str.c_str(), blk_device};
+            TUNE2FS_BIN,       "-r", reserved_blocks_str.c_str(), "-g", reserved_gid_str.c_str(),
+            blk_device.c_str()};
     if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
         LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
                << blk_device;
@@ -392,10 +414,10 @@
 }
 
 // Enable file-based encryption if needed.
-static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec,
+static void tune_encrypt(const std::string& blk_device, const FstabEntry& entry,
                          const struct ext4_super_block* sb, int* fs_stat) {
     bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
-    bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0;
+    bool want_encrypt = entry.fs_mgr_flags.file_encryption;
 
     if (has_encrypt || !want_encrypt) {
         return;
@@ -407,7 +429,7 @@
         return;
     }
 
-    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device};
+    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device.c_str()};
 
     LINFO << "Enabling ext4 encryption on " << blk_device;
     if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
@@ -417,6 +439,36 @@
     }
 }
 
+// Read the primary superblock from an f2fs filesystem.  On failure return
+// false.  If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
+#define F2FS_BLKSIZE 4096
+#define F2FS_SUPER_OFFSET 1024
+static bool read_f2fs_superblock(const std::string& blk_device, int* fs_stat) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    __le32 sb1, sb2;
+
+    if (fd < 0) {
+        PERROR << "Failed to open '" << blk_device << "'";
+        return false;
+    }
+
+    if (pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET) != sizeof(sb1)) {
+        PERROR << "Can't read '" << blk_device << "' superblock1";
+        return false;
+    }
+    if (pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET) != sizeof(sb2)) {
+        PERROR << "Can't read '" << blk_device << "' superblock2";
+        return false;
+    }
+
+    if (sb1 != cpu_to_le32(F2FS_SUPER_MAGIC) && sb2 != cpu_to_le32(F2FS_SUPER_MAGIC)) {
+        LINFO << "Invalid f2fs superblock on '" << blk_device << "'";
+        *fs_stat |= FS_STAT_INVALID_MAGIC;
+        return false;
+    }
+    return true;
+}
+
 //
 // Prepare the filesystem on the given block device to be mounted.
 //
@@ -426,10 +478,10 @@
 // If needed, we'll also enable (or disable) filesystem features as specified by
 // the fstab record.
 //
-static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) {
+static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry) {
     int fs_stat = 0;
 
-    if (is_extfs(rec->fs_type)) {
+    if (is_extfs(entry.fs_type)) {
         struct ext4_super_block sb;
 
         if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
@@ -442,60 +494,43 @@
             }
 
             // Note: quotas should be enabled before running fsck.
-            tune_quota(blk_device, rec, &sb, &fs_stat);
+            tune_quota(blk_device, entry, &sb, &fs_stat);
         } else {
             return fs_stat;
         }
+    } else if (is_f2fs(entry.fs_type)) {
+        if (!read_f2fs_superblock(blk_device, &fs_stat)) {
+            return fs_stat;
+        }
     }
 
-    if ((rec->fs_mgr_flags & MF_CHECK) ||
+    if (entry.fs_mgr_flags.check ||
         (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
-        check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+        check_fs(blk_device, entry.fs_type, entry.mount_point, &fs_stat);
     }
 
-    if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+    if (is_extfs(entry.fs_type) &&
+        (entry.fs_mgr_flags.reserved_size || entry.fs_mgr_flags.file_encryption)) {
         struct ext4_super_block sb;
 
         if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
-            tune_reserved_size(blk_device, rec, &sb, &fs_stat);
-            tune_encrypt(blk_device, rec, &sb, &fs_stat);
+            tune_reserved_size(blk_device, entry, &sb, &fs_stat);
+            tune_encrypt(blk_device, entry, &sb, &fs_stat);
         }
     }
 
     return fs_stat;
 }
 
-static void remove_trailing_slashes(char *n)
-{
-    int len;
-
-    len = strlen(n) - 1;
-    while ((*(n + len) == '/') && len) {
-      *(n + len) = '\0';
-      len--;
-    }
-}
-
-/*
- * Mark the given block device as read-only, using the BLKROSET ioctl.
- * Return 0 on success, and -1 on error.
- */
-int fs_mgr_set_blk_ro(const char *blockdev)
-{
-    int fd;
-    int rc = -1;
-    int ON = 1;
-
-    fd = TEMP_FAILURE_RETRY(open(blockdev, O_RDONLY | O_CLOEXEC));
+// Mark the given block device as read-only, using the BLKROSET ioctl.
+bool fs_mgr_set_blk_ro(const std::string& blockdev) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd < 0) {
-        // should never happen
-        return rc;
+        return false;
     }
 
-    rc = ioctl(fd, BLKROSET, &ON);
-    close(fd);
-
-    return rc;
+    int ON = 1;
+    return ioctl(fd, BLKROSET, &ON) == 0;
 }
 
 // Orange state means the device is unlocked, see the following link for details.
@@ -508,40 +543,34 @@
     return false;
 }
 
-/*
- * __mount(): wrapper around the mount() system call which also
- * sets the underlying block device to read-only if the mount is read-only.
- * See "man 2 mount" for return values.
- */
-static int __mount(const char *source, const char *target, const struct fstab_rec *rec)
-{
-    unsigned long mountflags = rec->flags;
-    int ret;
-    int save_errno;
-
-    /* We need this because sometimes we have legacy symlinks
-     * that are lingering around and need cleaning up.
-     */
+// __mount(): wrapper around the mount() system call which also
+// sets the underlying block device to read-only if the mount is read-only.
+// See "man 2 mount" for return values.
+static int __mount(const std::string& source, const std::string& target, const FstabEntry& entry) {
+    // We need this because sometimes we have legacy symlinks that are
+    // lingering around and need cleaning up.
     struct stat info;
-    if (!lstat(target, &info))
-        if ((info.st_mode & S_IFMT) == S_IFLNK)
-            unlink(target);
-    mkdir(target, 0755);
+    if (lstat(target.c_str(), &info) == 0 && (info.st_mode & S_IFMT) == S_IFLNK) {
+        unlink(target.c_str());
+    }
+    mkdir(target.c_str(), 0755);
     errno = 0;
-    ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
-    save_errno = errno;
+    unsigned long mountflags = entry.flags;
+    int ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
+                    entry.fs_options.c_str());
+    int save_errno = errno;
     const char* target_missing = "";
     const char* source_missing = "";
     if (save_errno == ENOENT) {
-        if (access(target, F_OK)) {
+        if (access(target.c_str(), F_OK)) {
             target_missing = "(missing)";
-        } else if (access(source, F_OK)) {
+        } else if (access(source.c_str(), F_OK)) {
             source_missing = "(missing)";
         }
         errno = save_errno;
     }
     PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
-          << target_missing << ",type=" << rec->fs_type << ")=" << ret;
+          << target_missing << ",type=" << entry.fs_type << ")=" << ret;
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
@@ -549,249 +578,208 @@
     return ret;
 }
 
-static int fs_match(const char *in1, const char *in2)
-{
-    char *n1;
-    char *n2;
-    int ret;
-
-    n1 = strdup(in1);
-    n2 = strdup(in2);
-
-    remove_trailing_slashes(n1);
-    remove_trailing_slashes(n2);
-
-    ret = !strcmp(n1, n2);
-
-    free(n1);
-    free(n2);
-
-    return ret;
-}
-
-/*
- * Tries to mount any of the consecutive fstab entries that match
- * the mountpoint of the one given by fstab->recs[start_idx].
- *
- * end_idx: On return, will be the last rec that was looked at.
- * attempted_idx: On return, will indicate which fstab rec
- *     succeeded. In case of failure, it will be the start_idx.
- * Returns
- *   -1 on failure with errno set to match the 1st mount failure.
- *   0 on success.
- */
-static int mount_with_alternatives(fstab* fstab, int start_idx, int* end_idx, int* attempted_idx) {
-    int i;
-    int mount_errno = 0;
-    int mounted = 0;
-
-    if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
-      errno = EINVAL;
-      if (end_idx) *end_idx = start_idx;
-      if (attempted_idx) *attempted_idx = start_idx;
-      return -1;
+static bool fs_match(const std::string& in1, const std::string& in2) {
+    if (in1.empty() || in2.empty()) {
+        return false;
     }
 
-    /* Hunt down an fstab entry for the same mount point that might succeed */
+    auto in1_end = in1.size() - 1;
+    while (in1_end > 0 && in1[in1_end] == '/') {
+        in1_end--;
+    }
+
+    auto in2_end = in2.size() - 1;
+    while (in2_end > 0 && in2[in2_end] == '/') {
+        in2_end--;
+    }
+
+    if (in1_end != in2_end) {
+        return false;
+    }
+
+    for (size_t i = 0; i <= in1_end; ++i) {
+        if (in1[i] != in2[i]) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// Tries to mount any of the consecutive fstab entries that match
+// the mountpoint of the one given by fstab[start_idx].
+//
+// end_idx: On return, will be the last entry that was looked at.
+// attempted_idx: On return, will indicate which fstab entry
+//     succeeded. In case of failure, it will be the start_idx.
+// Sets errno to match the 1st mount failure on failure.
+static bool mount_with_alternatives(const Fstab& fstab, int start_idx, int* end_idx,
+                                    int* attempted_idx) {
+    unsigned long i;
+    int mount_errno = 0;
+    bool mounted = false;
+
+    // Hunt down an fstab entry for the same mount point that might succeed.
     for (i = start_idx;
-         /* We required that fstab entries for the same mountpoint be consecutive */
-         i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
-         i++) {
-            /*
-             * Don't try to mount/encrypt the same mount point again.
-             * Deal with alternate entries for the same point which are required to be all following
-             * each other.
-             */
-            if (mounted) {
-                LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint="
-                       << fstab->recs[i].mount_point << " rec[" << i
-                       << "].fs_type=" << fstab->recs[i].fs_type
-                       << " already mounted as "
-                       << fstab->recs[*attempted_idx].fs_type;
-                continue;
-            }
+         // We required that fstab entries for the same mountpoint be consecutive.
+         i < fstab.size() && fstab[start_idx].mount_point == fstab[i].mount_point; i++) {
+        // Don't try to mount/encrypt the same mount point again.
+        // Deal with alternate entries for the same point which are required to be all following
+        // each other.
+        if (mounted) {
+            LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab[i].mount_point
+                   << " rec[" << i << "].fs_type=" << fstab[i].fs_type << " already mounted as "
+                   << fstab[*attempted_idx].fs_type;
+            continue;
+        }
 
-            int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]);
-            if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {
-                LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint="
-                       << fstab->recs[i].mount_point << " rec[" << i
-                       << "].fs_type=" << fstab->recs[i].fs_type;
-                mount_errno = EINVAL;  // continue bootup for FDE
-                continue;
-            }
+        int fs_stat = prepare_fs_for_mount(fstab[i].blk_device, fstab[i]);
+        if (fs_stat & FS_STAT_INVALID_MAGIC) {
+            LERROR << __FUNCTION__
+                   << "(): skipping mount due to invalid magic, mountpoint=" << fstab[i].mount_point
+                   << " blk_dev=" << realpath(fstab[i].blk_device) << " rec[" << i
+                   << "].fs_type=" << fstab[i].fs_type;
+            mount_errno = EINVAL;  // continue bootup for FDE
+            continue;
+        }
 
-            int retry_count = 2;
-            while (retry_count-- > 0) {
-                if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
-                             &fstab->recs[i])) {
-                    *attempted_idx = i;
-                    mounted = 1;
-                    if (i != start_idx) {
-                        LERROR << __FUNCTION__ << "(): Mounted " << fstab->recs[i].blk_device
-                               << " on " << fstab->recs[i].mount_point
-                               << " with fs_type=" << fstab->recs[i].fs_type << " instead of "
-                               << fstab->recs[start_idx].fs_type;
-                    }
-                    fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
-                    mount_errno = 0;
-                    break;
-                } else {
-                    if (retry_count <= 0) break;  // run check_fs only once
-                    fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
-                    /* back up the first errno for crypto decisions */
-                    if (mount_errno == 0) {
-                        mount_errno = errno;
-                    }
-                    // retry after fsck
-                    check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                             fstab->recs[i].mount_point, &fs_stat);
+        int retry_count = 2;
+        while (retry_count-- > 0) {
+            if (!__mount(fstab[i].blk_device, fstab[i].mount_point, fstab[i])) {
+                *attempted_idx = i;
+                mounted = true;
+                if (i != start_idx) {
+                    LERROR << __FUNCTION__ << "(): Mounted " << fstab[i].blk_device << " on "
+                           << fstab[i].mount_point << " with fs_type=" << fstab[i].fs_type
+                           << " instead of " << fstab[start_idx].fs_type;
                 }
+                fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+                mount_errno = 0;
+                break;
+            } else {
+                if (retry_count <= 0) break;  // run check_fs only once
+                fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+                // back up the first errno for crypto decisions.
+                if (mount_errno == 0) {
+                    mount_errno = errno;
+                }
+                // retry after fsck
+                check_fs(fstab[i].blk_device, fstab[i].fs_type, fstab[i].mount_point, &fs_stat);
             }
-            log_fs_stat(fstab->recs[i].blk_device, fs_stat);
+        }
+        log_fs_stat(fstab[i].blk_device, fs_stat);
     }
 
     /* Adjust i for the case where it was still withing the recs[] */
-    if (i < fstab->num_entries) --i;
+    if (i < fstab.size()) --i;
 
     *end_idx = i;
     if (!mounted) {
         *attempted_idx = start_idx;
         errno = mount_errno;
-        return -1;
+        return false;
     }
-    return 0;
+    return true;
 }
 
-static int translate_ext_labels(struct fstab_rec *rec)
-{
-    DIR *blockdir = NULL;
-    struct dirent *ent;
-    char *label;
-    size_t label_len;
-    int ret = -1;
-
-    if (strncmp(rec->blk_device, "LABEL=", 6))
-        return 0;
-
-    label = rec->blk_device + 6;
-    label_len = strlen(label);
-
-    if (label_len > 16) {
-        LERROR << "FS label is longer than allowed by filesystem";
-        goto out;
+static bool TranslateExtLabels(FstabEntry* entry) {
+    if (!StartsWith(entry->blk_device, "LABEL=")) {
+        return true;
     }
 
+    std::string label = entry->blk_device.substr(6);
+    if (label.size() > 16) {
+        LERROR << "FS label is longer than allowed by filesystem";
+        return false;
+    }
 
-    blockdir = opendir("/dev/block");
+    auto blockdir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
     if (!blockdir) {
         LERROR << "couldn't open /dev/block";
-        goto out;
+        return false;
     }
 
-    while ((ent = readdir(blockdir))) {
-        int fd;
-        char super_buf[1024];
-        struct ext4_super_block *sb;
-
+    struct dirent* ent;
+    while ((ent = readdir(blockdir.get()))) {
         if (ent->d_type != DT_BLK)
             continue;
 
-        fd = openat(dirfd(blockdir), ent->d_name, O_RDONLY);
+        unique_fd fd(TEMP_FAILURE_RETRY(
+                openat(dirfd(blockdir.get()), ent->d_name, O_RDONLY | O_CLOEXEC)));
         if (fd < 0) {
             LERROR << "Cannot open block device /dev/block/" << ent->d_name;
-            goto out;
+            return false;
         }
 
+        ext4_super_block super_block;
         if (TEMP_FAILURE_RETRY(lseek(fd, 1024, SEEK_SET)) < 0 ||
-            TEMP_FAILURE_RETRY(read(fd, super_buf, 1024)) != 1024) {
-            /* Probably a loopback device or something else without a readable
-             * superblock.
-             */
-            close(fd);
+            TEMP_FAILURE_RETRY(read(fd, &super_block, sizeof(super_block))) !=
+                    sizeof(super_block)) {
+            // Probably a loopback device or something else without a readable superblock.
             continue;
         }
 
-        sb = (struct ext4_super_block *)super_buf;
-        if (sb->s_magic != EXT4_SUPER_MAGIC) {
+        if (super_block.s_magic != EXT4_SUPER_MAGIC) {
             LINFO << "/dev/block/" << ent->d_name << " not ext{234}";
             continue;
         }
 
-        if (!strncmp(label, sb->s_volume_name, label_len)) {
-            char *new_blk_device;
+        if (label == super_block.s_volume_name) {
+            std::string new_blk_device = "/dev/block/"s + ent->d_name;
 
-            if (asprintf(&new_blk_device, "/dev/block/%s", ent->d_name) < 0) {
-                LERROR << "Could not allocate block device string";
-                goto out;
-            }
+            LINFO << "resolved label " << entry->blk_device << " to " << new_blk_device;
 
-            LINFO << "resolved label " << rec->blk_device << " to "
-                  << new_blk_device;
-
-            free(rec->blk_device);
-            rec->blk_device = new_blk_device;
-            ret = 0;
-            break;
+            entry->blk_device = new_blk_device;
+            return true;
         }
     }
 
-out:
-    closedir(blockdir);
-    return ret;
+    return false;
 }
 
-static bool needs_block_encryption(const struct fstab_rec* rec)
-{
-    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) &&
-        fs_mgr_is_encryptable(rec))
+static bool needs_block_encryption(const FstabEntry& entry) {
+    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) && entry.is_encryptable())
         return true;
-    if (rec->fs_mgr_flags & MF_FORCECRYPT) return true;
-    if (rec->fs_mgr_flags & MF_CRYPT) {
-        /* Check for existence of convert_fde breadcrumb file */
-        char convert_fde_name[PATH_MAX];
-        snprintf(convert_fde_name, sizeof(convert_fde_name),
-                 "%s/misc/vold/convert_fde", rec->mount_point);
-        if (access(convert_fde_name, F_OK) == 0) return true;
+    if (entry.fs_mgr_flags.force_crypt) return true;
+    if (entry.fs_mgr_flags.crypt) {
+        // Check for existence of convert_fde breadcrumb file.
+        auto convert_fde_name = entry.mount_point + "/misc/vold/convert_fde";
+        if (access(convert_fde_name.c_str(), F_OK) == 0) return true;
     }
-    if (rec->fs_mgr_flags & MF_FORCEFDEORFBE) {
-        /* Check for absence of convert_fbe breadcrumb file */
-        char convert_fbe_name[PATH_MAX];
-        snprintf(convert_fbe_name, sizeof(convert_fbe_name),
-                 "%s/convert_fbe", rec->mount_point);
-        if (access(convert_fbe_name, F_OK) != 0) return true;
+    if (entry.fs_mgr_flags.force_fde_or_fbe) {
+        // Check for absence of convert_fbe breadcrumb file.
+        auto convert_fbe_name = entry.mount_point + "/convert_fbe";
+        if (access(convert_fbe_name.c_str(), F_OK) != 0) return true;
     }
     return false;
 }
 
-static bool should_use_metadata_encryption(const struct fstab_rec* rec) {
-    if (!(rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE))) return false;
-    if (!(rec->fs_mgr_flags & MF_KEYDIRECTORY)) return false;
-    return true;
+static bool should_use_metadata_encryption(const FstabEntry& entry) {
+    return entry.fs_mgr_flags.key_directory &&
+           (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe);
 }
 
 // Check to see if a mountable volume has encryption requirements
-static int handle_encryptable(const struct fstab_rec* rec)
-{
-    /* If this is block encryptable, need to trigger encryption */
-    if (needs_block_encryption(rec)) {
-        if (umount(rec->mount_point) == 0) {
+static int handle_encryptable(const FstabEntry& entry) {
+    // If this is block encryptable, need to trigger encryption.
+    if (needs_block_encryption(entry)) {
+        if (umount(entry.mount_point.c_str()) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
         } else {
-            PWARNING << "Could not umount " << rec->mount_point
-                     << " - allow continue unencrypted";
+            PWARNING << "Could not umount " << entry.mount_point << " - allow continue unencrypted";
             return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
         }
-    } else if (should_use_metadata_encryption(rec)) {
-        if (umount(rec->mount_point) == 0) {
+    } else if (should_use_metadata_encryption(entry)) {
+        if (umount(entry.mount_point.c_str()) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
         } else {
-            PERROR << "Could not umount " << rec->mount_point << " - fail since can't encrypt";
+            PERROR << "Could not umount " << entry.mount_point << " - fail since can't encrypt";
             return FS_MGR_MNTALL_FAIL;
         }
-    } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
-        LINFO << rec->mount_point << " is file encrypted";
+    } else if (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe) {
+        LINFO << entry.mount_point << " is file encrypted";
         return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
-    } else if (fs_mgr_is_encryptable(rec)) {
+    } else if (entry.is_encryptable()) {
         return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
     } else {
         return FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
@@ -832,21 +820,34 @@
     return true;
 }
 
-bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
+bool fs_mgr_update_logical_partition(FstabEntry* entry) {
     // Logical partitions are specified with a named partition rather than a
     // block device, so if the block device is a path, then it has already
     // been updated.
-    if (rec->blk_device[0] == '/') {
+    if (entry->blk_device[0] == '/') {
         return true;
     }
 
     DeviceMapper& dm = DeviceMapper::Instance();
     std::string device_name;
-    if (!dm.GetDmDevicePathByName(rec->blk_device, &device_name)) {
+    if (!dm.GetDmDevicePathByName(entry->blk_device, &device_name)) {
         return false;
     }
+
+    entry->blk_device = device_name;
+    return true;
+}
+
+bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
+    auto entry = FstabRecToFstabEntry(rec);
+
+    if (!fs_mgr_update_logical_partition(&entry)) {
+        return false;
+    }
+
     free(rec->blk_device);
-    rec->blk_device = strdup(device_name.c_str());
+    rec->blk_device = strdup(entry.blk_device.c_str());
+
     return true;
 }
 
@@ -854,13 +855,13 @@
   public:
     CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
 
-    bool Update(struct fstab_rec* rec) {
-        if (!fs_mgr_is_checkpoint(rec)) {
+    bool Update(FstabEntry* entry) {
+        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
             return true;
         }
 
-        if (fs_mgr_is_checkpoint_blk(rec)) {
-            call_vdc({"checkpoint", "restoreCheckpoint", rec->blk_device});
+        if (entry->fs_mgr_flags.checkpoint_blk) {
+            call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device});
         }
 
         if (needs_checkpoint_ == UNKNOWN &&
@@ -873,7 +874,7 @@
             return true;
         }
 
-        if (!UpdateCheckpointPartition(rec)) {
+        if (!UpdateCheckpointPartition(entry)) {
             LERROR << "Could not set up checkpoint partition, skipping!";
             return false;
         }
@@ -881,18 +882,17 @@
         return true;
     }
 
-    bool Revert(struct fstab_rec* rec) {
-        if (!fs_mgr_is_checkpoint(rec)) {
+    bool Revert(FstabEntry* entry) {
+        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
             return true;
         }
 
-        if (device_map_.find(rec->blk_device) == device_map_.end()) {
+        if (device_map_.find(entry->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());
+        std::string bow_device = entry->blk_device;
+        entry->blk_device = device_map_[bow_device];
         device_map_.erase(bow_device);
 
         DeviceMapper& dm = DeviceMapper::Instance();
@@ -904,22 +904,17 @@
     }
 
   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());
+    bool UpdateCheckpointPartition(FstabEntry* entry) {
+        if (entry->fs_mgr_flags.checkpoint_fs) {
+            if (is_f2fs(entry->fs_type)) {
+                entry->fs_options += ",checkpoint=disable";
             } else {
-                LERROR << rec->fs_type << " does not implement checkpoints.";
+                LERROR << entry->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;
+        } else if (entry->fs_mgr_flags.checkpoint_blk) {
+            unique_fd fd(TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+            if (fd < 0) {
+                PERROR << "Cannot open device " << entry->blk_device;
                 return false;
             }
 
@@ -931,7 +926,7 @@
 
             android::dm::DmTable table;
             if (!table.AddTarget(
-                        std::make_unique<android::dm::DmTargetBow>(0, size, rec->blk_device))) {
+                        std::make_unique<android::dm::DmTargetBow>(0, size, entry->blk_device))) {
                 LERROR << "Failed to add bow target";
                 return false;
             }
@@ -948,9 +943,8 @@
                 return false;
             }
 
-            device_map_[name] = rec->blk_device;
-            free(rec->blk_device);
-            rec->blk_device = strdup(name.c_str());
+            device_map_[name] = entry->blk_device;
+            entry->blk_device = name;
         }
         return true;
     }
@@ -960,92 +954,86 @@
     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
- * first successful mount.
- * Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
- */
-int fs_mgr_mount_all(fstab* fstab, int mount_mode) {
-    int i = 0;
+// When multiple fstab records share the same mount_point, it will try to mount each
+// one in turn, and ignore any duplicates after a first successful mount.
+// Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
+int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
     int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
     int error_count = 0;
-    int mret = -1;
-    int mount_errno = 0;
-    int attempted_idx = -1;
     CheckpointManager checkpoint_manager;
-    FsManagerAvbUniquePtr avb_handle(nullptr);
+    AvbUniquePtr avb_handle(nullptr);
 
-    if (!fstab) {
+    if (fstab->empty()) {
         return FS_MGR_MNTALL_FAIL;
     }
 
-    for (i = 0; i < fstab->num_entries; i++) {
-        /* Don't mount entries that are managed by vold or not for the mount mode*/
-        if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
-             ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) ||
-             ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i]))) {
+    for (size_t i = 0; i < fstab->size(); i++) {
+        auto& current_entry = (*fstab)[i];
+
+        // Don't mount entries that are managed by vold or not for the mount mode.
+        if (current_entry.fs_mgr_flags.vold_managed || current_entry.fs_mgr_flags.recovery_only ||
+            current_entry.fs_mgr_flags.first_stage_mount ||
+            ((mount_mode == MOUNT_MODE_LATE) && !current_entry.fs_mgr_flags.late_mount) ||
+            ((mount_mode == MOUNT_MODE_EARLY) && current_entry.fs_mgr_flags.late_mount)) {
             continue;
         }
 
-        /* Skip swap and raw partition entries such as boot, recovery, etc */
-        if (!strcmp(fstab->recs[i].fs_type, "swap") ||
-            !strcmp(fstab->recs[i].fs_type, "emmc") ||
-            !strcmp(fstab->recs[i].fs_type, "mtd")) {
+        // Skip swap and raw partition entries such as boot, recovery, etc.
+        if (current_entry.fs_type == "swap" || current_entry.fs_type == "emmc" ||
+            current_entry.fs_type == "mtd") {
             continue;
         }
 
-        /* Skip mounting the root partition, as it will already have been mounted */
-        if (!strcmp(fstab->recs[i].mount_point, "/") ||
-            !strcmp(fstab->recs[i].mount_point, "/system")) {
-            if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) {
-                fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
+        // Skip mounting the root partition, as it will already have been mounted.
+        if (current_entry.mount_point == "/" || current_entry.mount_point == "/system") {
+            if ((current_entry.flags & MS_RDONLY) != 0) {
+                fs_mgr_set_blk_ro(current_entry.blk_device);
             }
             continue;
         }
 
-        /* Translate LABEL= file system labels into block devices */
-        if (is_extfs(fstab->recs[i].fs_type)) {
-            int tret = translate_ext_labels(&fstab->recs[i]);
-            if (tret < 0) {
+        // Translate LABEL= file system labels into block devices.
+        if (is_extfs(current_entry.fs_type)) {
+            if (!TranslateExtLabels(&current_entry)) {
                 LERROR << "Could not translate label to block device";
                 continue;
             }
         }
 
-        if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
-            if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+        if (current_entry.fs_mgr_flags.logical) {
+            if (!fs_mgr_update_logical_partition(&current_entry)) {
                 LERROR << "Could not set up logical partition, skipping!";
                 continue;
             }
         }
 
-        if (!checkpoint_manager.Update(&fstab->recs[i])) {
+        if (!checkpoint_manager.Update(&current_entry)) {
             continue;
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
-            !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
-            LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
+        if (current_entry.fs_mgr_flags.wait &&
+            !fs_mgr_wait_for_file(current_entry.blk_device, 20s)) {
+            LERROR << "Skipping '" << current_entry.blk_device << "' during mount_all";
             continue;
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+        if (current_entry.fs_mgr_flags.avb) {
             if (!avb_handle) {
-                avb_handle = FsManagerAvbHandle::Open();
+                avb_handle = AvbHandle::Open();
                 if (!avb_handle) {
-                    LERROR << "Failed to open FsManagerAvbHandle";
+                    LERROR << "Failed to open AvbHandle";
                     return FS_MGR_MNTALL_FAIL;
                 }
             }
-            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
-                SetUpAvbHashtreeResult::kFail) {
-                LERROR << "Failed to set up AVB on partition: "
-                       << fstab->recs[i].mount_point << ", skipping!";
-                /* Skips mounting the device. */
+            if (avb_handle->SetUpAvbHashtree(&current_entry, true /* wait_for_verity_dev */) ==
+                AvbHashtreeResult::kFail) {
+                LERROR << "Failed to set up AVB on partition: " << current_entry.mount_point
+                       << ", skipping!";
+                // Skips mounting the device.
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
-            int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
+        } else if ((current_entry.fs_mgr_flags.verify)) {
+            int rc = fs_mgr_setup_verity(&current_entry, true);
             if (__android_log_is_debuggable() &&
                     (rc == FS_MGR_SETUP_VERITY_DISABLED ||
                      rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
@@ -1058,17 +1046,19 @@
 
         int last_idx_inspected;
         int top_idx = i;
+        int attempted_idx = -1;
 
-        mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
+        bool mret = mount_with_alternatives(*fstab, i, &last_idx_inspected, &attempted_idx);
+        auto& attempted_entry = (*fstab)[attempted_idx];
         i = last_idx_inspected;
-        mount_errno = errno;
+        int mount_errno = errno;
 
-        /* Deal with encryptability. */
-        if (!mret) {
-            int status = handle_encryptable(&fstab->recs[attempted_idx]);
+        // Handle success and deal with encryptability.
+        if (mret) {
+            int status = handle_encryptable(attempted_entry);
 
             if (status == FS_MGR_MNTALL_FAIL) {
-                /* Fatal error - no point continuing */
+                // Fatal error - no point continuing.
                 return status;
             }
 
@@ -1079,51 +1069,45 @@
                 }
                 encryptable = status;
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-                    if (!call_vdc(
-                            {"cryptfs", "encryptFstab", fstab->recs[attempted_idx].mount_point})) {
+                    if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.mount_point})) {
                         LERROR << "Encryption failed";
                         return FS_MGR_MNTALL_FAIL;
                     }
                 }
             }
 
-            /* Success!  Go get the next one */
+            // Success!  Go get the next one.
             continue;
         }
 
-        bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
+        // Mounting failed, understand why and retry.
+        bool wiped = partition_wiped(current_entry.blk_device.c_str());
         bool crypt_footer = false;
-        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
-            fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
-            /* top_idx and attempted_idx point at the same partition, but sometimes
-             * at two different lines in the fstab.  Use the top one for formatting
-             * as that is the preferred one.
-             */
-            LERROR << __FUNCTION__ << "(): " << fstab->recs[top_idx].blk_device
-                   << " is wiped and " << fstab->recs[top_idx].mount_point
-                   << " " << fstab->recs[top_idx].fs_type
+        if (mount_errno != EBUSY && mount_errno != EACCES &&
+            current_entry.fs_mgr_flags.formattable && wiped) {
+            // current_entry and attempted_entry point at the same partition, but sometimes
+            // at two different lines in the fstab.  Use current_entry for formatting
+            // as that is the preferred one.
+            LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
+                   << " is wiped and " << current_entry.mount_point << " " << current_entry.fs_type
                    << " is formattable. Format it.";
 
-            checkpoint_manager.Revert(&fstab->recs[top_idx]);
+            checkpoint_manager.Revert(&current_entry);
 
-            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);
+            if (current_entry.is_encryptable() && current_entry.key_loc != KEY_IN_FOOTER) {
+                unique_fd fd(TEMP_FAILURE_RETRY(
+                        open(current_entry.key_loc.c_str(), O_WRONLY | O_CLOEXEC)));
                 if (fd >= 0) {
-                    LINFO << __FUNCTION__ << "(): also wipe "
-                          << fstab->recs[top_idx].key_loc;
+                    LINFO << __FUNCTION__ << "(): also wipe " << current_entry.key_loc;
                     wipe_block_device(fd, get_file_size(fd));
-                    close(fd);
                 } else {
-                    PERROR << __FUNCTION__ << "(): "
-                           << fstab->recs[top_idx].key_loc << " wouldn't open";
+                    PERROR << __FUNCTION__ << "(): " << current_entry.key_loc << " wouldn't open";
                 }
-            } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
-                !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+            } else if (current_entry.is_encryptable() && current_entry.key_loc == KEY_IN_FOOTER) {
                 crypt_footer = true;
             }
-            if (fs_mgr_do_format(&fstab->recs[top_idx], crypt_footer) == 0) {
-                /* Let's replay the mount actions. */
+            if (fs_mgr_do_format(current_entry, crypt_footer) == 0) {
+                // Let's replay the mount actions.
                 i = top_idx - 1;
                 continue;
             } else {
@@ -1134,35 +1118,29 @@
             }
         }
 
-        /* mount(2) returned an error, handle the encryptable/formattable case */
-        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
-            fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
+        // mount(2) returned an error, handle the encryptable/formattable case.
+        if (mount_errno != EBUSY && mount_errno != EACCES && attempted_entry.is_encryptable()) {
             if (wiped) {
-                LERROR << __FUNCTION__ << "(): "
-                       << fstab->recs[attempted_idx].blk_device
-                       << " is wiped and "
-                       << fstab->recs[attempted_idx].mount_point << " "
-                       << fstab->recs[attempted_idx].fs_type
+                LERROR << __FUNCTION__ << "(): " << attempted_entry.blk_device << " is wiped and "
+                       << attempted_entry.mount_point << " " << attempted_entry.fs_type
                        << " is encryptable. Suggest recovery...";
                 encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
                 continue;
             } else {
-                /* Need to mount a tmpfs at this mountpoint for now, and set
-                 * properties that vold will query later for decrypting
-                 */
+                // Need to mount a tmpfs at this mountpoint for now, and set
+                // properties that vold will query later for decrypting
                 LERROR << __FUNCTION__ << "(): possibly an encryptable blkdev "
-                       << fstab->recs[attempted_idx].blk_device
-                       << " for mount " << fstab->recs[attempted_idx].mount_point
-                       << " type " << fstab->recs[attempted_idx].fs_type;
-                if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
+                       << attempted_entry.blk_device << " for mount " << attempted_entry.mount_point
+                       << " type " << attempted_entry.fs_type;
+                if (fs_mgr_do_tmpfs_mount(attempted_entry.mount_point.c_str()) < 0) {
                     ++error_count;
                     continue;
                 }
             }
             encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
-        } else if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
-                   should_use_metadata_encryption(&fstab->recs[attempted_idx])) {
-            if (!call_vdc({"cryptfs", "mountFstab", fstab->recs[attempted_idx].mount_point})) {
+        } else if (mount_errno != EBUSY && mount_errno != EACCES &&
+                   should_use_metadata_encryption(attempted_entry)) {
+            if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.mount_point})) {
                 ++error_count;
             }
             encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@@ -1170,18 +1148,18 @@
         } else {
             // fs_options might be null so we cannot use PERROR << directly.
             // Use StringPrintf to output "(null)" instead.
-            if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) {
+            if (attempted_entry.fs_mgr_flags.no_fail) {
                 PERROR << android::base::StringPrintf(
-                    "Ignoring failure to mount an un-encryptable or wiped "
-                    "partition on %s at %s options: %s",
-                    fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                    fstab->recs[attempted_idx].fs_options);
+                        "Ignoring failure to mount an un-encryptable or wiped "
+                        "partition on %s at %s options: %s",
+                        attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
+                        attempted_entry.fs_options.c_str());
             } else {
                 PERROR << android::base::StringPrintf(
-                    "Failed to mount an un-encryptable or wiped partition "
-                    "on %s at %s options: %s",
-                    fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
-                    fstab->recs[attempted_idx].fs_options);
+                        "Failed to mount an un-encryptable or wiped partition "
+                        "on %s at %s options: %s",
+                        attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
+                        attempted_entry.fs_options.c_str());
                 ++error_count;
             }
             continue;
@@ -1199,20 +1177,13 @@
     }
 }
 
-/* wrapper to __mount() and expects a fully prepared fstab_rec,
- * unlike fs_mgr_do_mount which does more things with avb / verity
- * etc.
- */
-int fs_mgr_do_mount_one(struct fstab_rec *rec)
-{
-    if (!rec) {
-        return FS_MGR_DOMNT_FAILED;
-    }
-
+// wrapper to __mount() and expects a fully prepared fstab_rec,
+// unlike fs_mgr_do_mount which does more things with avb / verity etc.
+int fs_mgr_do_mount_one(const FstabEntry& entry) {
     // Run fsck if needed
-    prepare_fs_for_mount(rec->blk_device, rec);
+    prepare_fs_for_mount(entry.blk_device, entry);
 
-    int ret = __mount(rec->blk_device, rec->mount_point, rec);
+    int ret = __mount(entry.blk_device, entry.mount_point, entry);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
     }
@@ -1220,76 +1191,84 @@
     return ret;
 }
 
-/* If tmp_mount_point is non-null, mount the filesystem there.  This is for the
- * tmp mount we do to check the user password
- * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
- * 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 needs_checkpoint) {
-    int i = 0;
+int fs_mgr_do_mount_one(struct fstab_rec* rec) {
+    if (!rec) {
+        return FS_MGR_DOMNT_FAILED;
+    }
+
+    auto entry = FstabRecToFstabEntry(rec);
+
+    return fs_mgr_do_mount_one(entry);
+}
+
+// If tmp_mount_point is non-null, mount the filesystem there.  This is for the
+// tmp mount we do to check the user password
+// If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
+// in turn, and stop on 1st success, or no more match.
+static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
+                                  const std::string& n_blk_device, const char* tmp_mount_point,
+                                  int needs_checkpoint) {
     int mount_errors = 0;
     int first_mount_errno = 0;
-    char* mount_point;
+    std::string mount_point;
     CheckpointManager checkpoint_manager(needs_checkpoint);
-    FsManagerAvbUniquePtr avb_handle(nullptr);
+    AvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
         return FS_MGR_DOMNT_FAILED;
     }
 
-    for (i = 0; i < fstab->num_entries; i++) {
-        if (!fs_match(fstab->recs[i].mount_point, n_name)) {
+    for (auto& fstab_entry : *fstab) {
+        if (!fs_match(fstab_entry.mount_point, n_name)) {
             continue;
         }
 
-        /* We found our match */
-        /* If this swap or a raw partition, report an error */
-        if (!strcmp(fstab->recs[i].fs_type, "swap") ||
-            !strcmp(fstab->recs[i].fs_type, "emmc") ||
-            !strcmp(fstab->recs[i].fs_type, "mtd")) {
-            LERROR << "Cannot mount filesystem of type "
-                   << fstab->recs[i].fs_type << " on " << n_blk_device;
+        // We found our match.
+        // If this swap or a raw partition, report an error.
+        if (fstab_entry.fs_type == "swap" || fstab_entry.fs_type == "emmc" ||
+            fstab_entry.fs_type == "mtd") {
+            LERROR << "Cannot mount filesystem of type " << fstab_entry.fs_type << " on "
+                   << n_blk_device;
             return FS_MGR_DOMNT_FAILED;
         }
 
-        if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
-            if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+        if (fstab_entry.fs_mgr_flags.logical) {
+            if (!fs_mgr_update_logical_partition(&fstab_entry)) {
                 LERROR << "Could not set up logical partition, skipping!";
                 continue;
             }
         }
 
-        if (!checkpoint_manager.Update(&fstab->recs[i])) {
+        if (!checkpoint_manager.Update(&fstab_entry)) {
             LERROR << "Could not set up checkpoint partition, skipping!";
             continue;
         }
 
-        /* First check the filesystem if requested */
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+        // First check the filesystem if requested.
+        if (fstab_entry.fs_mgr_flags.wait && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
             continue;
         }
 
-        int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
+        int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry);
 
-        if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+        if (fstab_entry.fs_mgr_flags.avb) {
             if (!avb_handle) {
-                avb_handle = FsManagerAvbHandle::Open();
+                avb_handle = AvbHandle::Open();
                 if (!avb_handle) {
-                    LERROR << "Failed to open FsManagerAvbHandle";
+                    LERROR << "Failed to open AvbHandle";
                     return FS_MGR_DOMNT_FAILED;
                 }
             }
-            if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
-                SetUpAvbHashtreeResult::kFail) {
-                LERROR << "Failed to set up AVB on partition: "
-                       << fstab->recs[i].mount_point << ", skipping!";
-                /* Skips mounting the device. */
+            if (avb_handle->SetUpAvbHashtree(&fstab_entry, true /* wait_for_verity_dev */) ==
+                AvbHashtreeResult::kFail) {
+                LERROR << "Failed to set up AVB on partition: " << fstab_entry.mount_point
+                       << ", skipping!";
+                // Skips mounting the device.
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
-            int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
+        } else if (fstab_entry.fs_mgr_flags.verify) {
+            int rc = fs_mgr_setup_verity(&fstab_entry, true);
             if (__android_log_is_debuggable() &&
                     (rc == FS_MGR_SETUP_VERITY_DISABLED ||
                      rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
@@ -1300,15 +1279,15 @@
             }
         }
 
-        /* Now mount it where requested */
+        // Now mount it where requested */
         if (tmp_mount_point) {
             mount_point = tmp_mount_point;
         } else {
-            mount_point = fstab->recs[i].mount_point;
+            mount_point = fstab_entry.mount_point;
         }
         int retry_count = 2;
         while (retry_count-- > 0) {
-            if (!__mount(n_blk_device, mount_point, &fstab->recs[i])) {
+            if (!__mount(n_blk_device, mount_point, fstab_entry)) {
                 fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
                 return FS_MGR_DOMNT_SUCCESS;
             } else {
@@ -1317,10 +1296,10 @@
                 mount_errors++;
                 fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
                 // try again after fsck
-                check_fs(n_blk_device, fstab->recs[i].fs_type, fstab->recs[i].mount_point, &fs_stat);
+                check_fs(n_blk_device, fstab_entry.fs_type, fstab_entry.mount_point, &fs_stat);
             }
         }
-        log_fs_stat(fstab->recs[i].blk_device, fs_stat);
+        log_fs_stat(fstab_entry.blk_device, fs_stat);
     }
 
     // Reach here means the mount attempt fails.
@@ -1328,19 +1307,22 @@
         PERROR << "Cannot mount filesystem on " << n_blk_device << " at " << mount_point;
         if (first_mount_errno == EBUSY) return FS_MGR_DOMNT_BUSY;
     } else {
-        /* We didn't find a match, say so and return an error */
+        // We didn't find a match, say so and return an error.
         LERROR << "Cannot find mount point " << n_name << " in fstab";
     }
     return FS_MGR_DOMNT_FAILED;
 }
 
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+    auto new_fstab = LegacyFstabToFstab(fstab);
+    return fs_mgr_do_mount_helper(&new_fstab, n_name, n_blk_device, tmp_mount_point, -1);
 }
 
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
                     bool needs_checkpoint) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
+    auto new_fstab = LegacyFstabToFstab(fstab);
+    return fs_mgr_do_mount_helper(&new_fstab, n_name, n_blk_device, tmp_mount_point,
+                                  needs_checkpoint);
 }
 
 /*
@@ -1362,89 +1344,74 @@
     return 0;
 }
 
-/* This must be called after mount_all, because the mkswap command needs to be
- * available.
- */
-int fs_mgr_swapon_all(fstab* fstab) {
-    int i = 0;
-    int flags = 0;
-    int err = 0;
-    int ret = 0;
-    int status;
-    const char *mkswap_argv[2] = {
-        MKSWAP_BIN,
-        nullptr
-    };
-
-    if (!fstab) {
-        return -1;
-    }
-
-    for (i = 0; i < fstab->num_entries; i++) {
-        /* Skip non-swap entries */
-        if (strcmp(fstab->recs[i].fs_type, "swap")) {
+bool fs_mgr_swapon_all(const Fstab& fstab) {
+    bool ret = true;
+    for (const auto& entry : fstab) {
+        // Skip non-swap entries.
+        if (entry.fs_type != "swap") {
             continue;
         }
 
-        if (fstab->recs[i].zram_size > 0) {
-            /* A zram_size was specified, so we need to configure the
-             * device.  There is no point in having multiple zram devices
-             * on a system (all the memory comes from the same pool) so
-             * we can assume the device number is 0.
-             */
-            if (fstab->recs[i].max_comp_streams >= 0) {
+        if (entry.zram_size > 0) {
+            // A zram_size was specified, so we need to configure the
+            // device.  There is no point in having multiple zram devices
+            // on a system (all the memory comes from the same pool) so
+            // we can assume the device number is 0.
+            if (entry.max_comp_streams >= 0) {
                 auto zram_mcs_fp = std::unique_ptr<FILE, decltype(&fclose)>{
                         fopen(ZRAM_CONF_MCS, "re"), fclose};
-                if (zram_mcs_fp == NULL) {
+                if (zram_mcs_fp == nullptr) {
                     LERROR << "Unable to open zram conf comp device " << ZRAM_CONF_MCS;
-                    ret = -1;
+                    ret = false;
                     continue;
                 }
-                fprintf(zram_mcs_fp.get(), "%d\n", fstab->recs[i].max_comp_streams);
+                fprintf(zram_mcs_fp.get(), "%d\n", entry.max_comp_streams);
             }
 
             auto zram_fp =
                     std::unique_ptr<FILE, decltype(&fclose)>{fopen(ZRAM_CONF_DEV, "re+"), fclose};
-            if (zram_fp == NULL) {
+            if (zram_fp == nullptr) {
                 LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
-                ret = -1;
+                ret = false;
                 continue;
             }
-            fprintf(zram_fp.get(), "%u\n", fstab->recs[i].zram_size);
+            fprintf(zram_fp.get(), "%" PRId64 "\n", entry.zram_size);
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
-            !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
-            LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'";
-            ret = -1;
+        if (entry.fs_mgr_flags.wait && !fs_mgr_wait_for_file(entry.blk_device, 20s)) {
+            LERROR << "Skipping mkswap for '" << entry.blk_device << "'";
+            ret = false;
             continue;
         }
 
-        /* Initialize the swap area */
-        mkswap_argv[1] = fstab->recs[i].blk_device;
-        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv),
-                                      const_cast<char **>(mkswap_argv),
-                                      &status, true, LOG_KLOG, false, NULL,
-                                      NULL, 0);
+        // Initialize the swap area.
+        const char* mkswap_argv[2] = {
+                MKSWAP_BIN,
+                entry.blk_device.c_str(),
+        };
+        int err = 0;
+        int status;
+        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), const_cast<char**>(mkswap_argv),
+                                      &status, true, LOG_KLOG, false, nullptr, nullptr, 0);
         if (err) {
-            LERROR << "mkswap failed for " << fstab->recs[i].blk_device;
-            ret = -1;
+            LERROR << "mkswap failed for " << entry.blk_device;
+            ret = false;
             continue;
         }
 
         /* If -1, then no priority was specified in fstab, so don't set
          * SWAP_FLAG_PREFER or encode the priority */
-        if (fstab->recs[i].swap_prio >= 0) {
-            flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) &
-                    SWAP_FLAG_PRIO_MASK;
+        int flags = 0;
+        if (entry.swap_prio >= 0) {
+            flags = (entry.swap_prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK;
             flags |= SWAP_FLAG_PREFER;
         } else {
             flags = 0;
         }
-        err = swapon(fstab->recs[i].blk_device, flags);
+        err = swapon(entry.blk_device.c_str(), flags);
         if (err) {
-            LERROR << "swapon failed for " << fstab->recs[i].blk_device;
-            ret = -1;
+            LERROR << "swapon failed for " << entry.blk_device;
+            ret = false;
         }
     }
 
@@ -1498,23 +1465,22 @@
      * logging mode, in which case return that */
     *mode = VERITY_MODE_DEFAULT;
 
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
         LERROR << "Failed to read default fstab";
         return false;
     }
 
-    for (int i = 0; i < fstab->num_entries; i++) {
-        if (fs_mgr_is_avb(&fstab->recs[i])) {
+    for (const auto& entry : fstab) {
+        if (entry.fs_mgr_flags.avb) {
             *mode = VERITY_MODE_RESTART;  // avb only supports restart mode.
             break;
-        } else if (!fs_mgr_is_verified(&fstab->recs[i])) {
+        } else if (!entry.fs_mgr_flags.verify) {
             continue;
         }
 
         int current;
-        if (load_verity_state(&fstab->recs[i], &current) < 0) {
+        if (load_verity_state(entry, &current) < 0) {
             continue;
         }
         if (current != VERITY_MODE_DEFAULT) {
@@ -1526,7 +1492,8 @@
     return true;
 }
 
-bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback) {
+bool fs_mgr_update_verity_state(
+        std::function<void(const std::string& mount_point, int mode)> callback) {
     if (!callback) {
         return false;
     }
@@ -1536,27 +1503,25 @@
         return false;
     }
 
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
         LERROR << "Failed to read default fstab";
         return false;
     }
 
     DeviceMapper& dm = DeviceMapper::Instance();
 
-    for (int i = 0; i < fstab->num_entries; i++) {
-        auto fsrec = &fstab->recs[i];
-        if (!fs_mgr_is_verified(fsrec) && !fs_mgr_is_avb(fsrec)) {
+    for (const auto& entry : fstab) {
+        if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
             continue;
         }
 
         std::string mount_point;
-        if (!strcmp(fsrec->mount_point, "/")) {
+        if (entry.mount_point == "/") {
             // In AVB, the dm device name is vroot instead of system.
-            mount_point = fs_mgr_is_avb(fsrec) ? "vroot" : "system";
+            mount_point = entry.fs_mgr_flags.avb ? "vroot" : "system";
         } else {
-            mount_point = basename(fsrec->mount_point);
+            mount_point = basename(entry.mount_point.c_str());
         }
 
         if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
@@ -1567,7 +1532,7 @@
         const char* status;
         std::vector<DeviceMapper::TargetInfo> table;
         if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
-            if (!fs_mgr_is_verifyatboot(fsrec)) {
+            if (!entry.fs_mgr_flags.verify_at_boot) {
                 PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point;
                 continue;
             }
@@ -1581,7 +1546,7 @@
         // instead of [partition.vroot.verified].
         if (mount_point == "vroot") mount_point = "system";
         if (*status == 'C' || *status == 'V') {
-            callback(fsrec, mount_point.c_str(), mode, *status);
+            callback(mount_point, mode);
         }
     }
 
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index fe0e039..3f075ef 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -51,12 +51,20 @@
 using DmTargetZero = android::dm::DmTargetZero;
 using DmTargetLinear = android::dm::DmTargetLinear;
 
-bool GetPhysicalPartitionDevicePath(const LpMetadataBlockDevice& block_device,
-                                    std::string* result) {
+static bool GetPhysicalPartitionDevicePath(const LpMetadata& metadata,
+                                           const LpMetadataBlockDevice& block_device,
+                                           const std::string& super_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 the super device is the source of this block device's metadata,
+    // make sure we use the correct super device (and not just "super",
+    // which might not exist.)
+    if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
+        path = super_device;
+    }
     if (!android::base::Realpath(path, result)) {
         PERROR << "realpath: " << path;
         return false;
@@ -65,7 +73,7 @@
 }
 
 static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
-                          DmTable* table) {
+                          const std::string& super_device, 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];
@@ -77,7 +85,7 @@
             case LP_TARGET_TYPE_LINEAR: {
                 const auto& block_device = metadata.block_devices[extent.target_source];
                 std::string path;
-                if (!GetPhysicalPartitionDevicePath(block_device, &path)) {
+                if (!GetPhysicalPartitionDevicePath(metadata, block_device, super_device, &path)) {
                     LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
                     return false;
                 }
@@ -102,11 +110,11 @@
 
 static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
                                    bool force_writable, const std::chrono::milliseconds& timeout_ms,
-                                   std::string* path) {
+                                   const std::string& super_device, std::string* path) {
     DeviceMapper& dm = DeviceMapper::Instance();
 
     DmTable table;
-    if (!CreateDmTable(metadata, partition, &table)) {
+    if (!CreateDmTable(metadata, partition, super_device, &table)) {
         return false;
     }
     if (force_writable) {
@@ -137,7 +145,7 @@
         LOG(ERROR) << "Could not read partition table.";
         return true;
     }
-    return CreateLogicalPartitions(*metadata.get());
+    return CreateLogicalPartitions(*metadata.get(), block_device);
 }
 
 std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device) {
@@ -145,14 +153,14 @@
     return ReadMetadata(block_device.c_str(), slot);
 }
 
-bool CreateLogicalPartitions(const LpMetadata& metadata) {
+bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
     for (const auto& partition : metadata.partitions) {
         if (!partition.num_extents) {
             LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
             continue;
         }
         std::string path;
-        if (!CreateLogicalPartition(metadata, partition, false, {}, &path)) {
+        if (!CreateLogicalPartition(metadata, partition, false, {}, super_device, &path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
@@ -171,7 +179,7 @@
     for (const auto& partition : metadata->partitions) {
         if (GetPartitionName(partition) == partition_name) {
             return CreateLogicalPartition(*metadata.get(), partition, force_writable, timeout_ms,
-                                          path);
+                                          block_device, path);
         }
     }
     LERROR << "Could not find any partition with name: " << partition_name;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 0983663..1a0e7ab 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,6 +24,7 @@
 #include <cutils/partition_utils.h>
 #include <sys/mount.h>
 
+#include <android-base/unique_fd.h>
 #include <ext4_utils/ext4.h>
 #include <ext4_utils/ext4_utils.h>
 #include <logwrap/logwrap.h>
@@ -34,30 +35,28 @@
 #include "fs_mgr_priv.h"
 #include "cryptfs.h"
 
-static int get_dev_sz(char *fs_blkdev, uint64_t *dev_sz)
-{
-    int fd;
+using android::base::unique_fd;
 
-    if ((fd = open(fs_blkdev, O_RDONLY)) < 0) {
+static int get_dev_sz(const std::string& fs_blkdev, uint64_t* dev_sz) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(fs_blkdev.c_str(), O_RDONLY | O_CLOEXEC)));
+
+    if (fd < 0) {
         PERROR << "Cannot open block device";
         return -1;
     }
 
     if ((ioctl(fd, BLKGETSIZE64, dev_sz)) == -1) {
         PERROR << "Cannot get block device size";
-        close(fd);
         return -1;
     }
 
-    close(fd);
     return 0;
 }
 
-static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
-{
+static int format_ext4(const std::string& fs_blkdev, const std::string& fs_mnt_point,
+                       bool crypt_footer) {
     uint64_t dev_sz;
     int rc = 0;
-    int status;
 
     rc = get_dev_sz(fs_blkdev, &dev_sz);
     if (rc) {
@@ -71,7 +70,8 @@
 
     std::string size_str = std::to_string(dev_sz / 4096);
     const char* const mke2fs_args[] = {
-        "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr};
+            "/system/bin/mke2fs", "-t",   "ext4", "-b", "4096", fs_blkdev.c_str(),
+            size_str.c_str(),     nullptr};
 
     rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), NULL,
                                  true, LOG_KLOG, true, nullptr, nullptr, 0);
@@ -81,12 +81,7 @@
     }
 
     const char* const e2fsdroid_args[] = {
-        "/system/bin/e2fsdroid",
-        "-e",
-        "-a",
-        fs_mnt_point,
-        fs_blkdev,
-        nullptr};
+            "/system/bin/e2fsdroid", "-e", "-a", fs_mnt_point.c_str(), fs_blkdev.c_str(), nullptr};
 
     rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
                                  NULL, true, LOG_KLOG, true, nullptr, nullptr, 0);
@@ -97,10 +92,7 @@
     return rc;
 }
 
-static int format_f2fs(char *fs_blkdev, uint64_t dev_sz, bool crypt_footer)
-{
-    int status;
-
+static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer) {
     if (!dev_sz) {
         int rc = get_dev_sz(fs_blkdev, &dev_sz);
         if (rc) {
@@ -118,7 +110,7 @@
     const char* const args[] = {
         "/system/bin/make_f2fs",
         "-g", "android",
-        fs_blkdev,
+        fs_blkdev.c_str(),
         size_str.c_str(),
         nullptr
     };
@@ -128,20 +120,21 @@
                                    LOG_KLOG, true, nullptr, nullptr, 0);
 }
 
-int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
-{
-    int rc = -EINVAL;
+int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
+    LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
 
-    LERROR << __FUNCTION__ << ": Format " << fstab->blk_device
-           << " as '" << fstab->fs_type << "'";
-
-    if (!strncmp(fstab->fs_type, "f2fs", 4)) {
-        rc = format_f2fs(fstab->blk_device, fstab->length, crypt_footer);
-    } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
-        rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
+    if (entry.fs_type == "f2fs") {
+        return format_f2fs(entry.blk_device, entry.length, crypt_footer);
+    } else if (entry.fs_type == "ext4") {
+        return format_ext4(entry.blk_device, entry.mount_point, crypt_footer);
     } else {
-        LERROR << "File system type '" << fstab->fs_type << "' is not supported";
+        LERROR << "File system type '" << entry.fs_type << "' is not supported";
+        return -EINVAL;
     }
+}
 
-    return rc;
+int fs_mgr_do_format(struct fstab_rec* rec, bool crypt_footer) {
+    auto entry = FstabRecToFstabEntry(rec);
+
+    return fs_mgr_do_format(entry, crypt_footer);
 }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index e89c91c..e0891eb 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -33,31 +33,33 @@
 
 #include "fs_mgr_priv.h"
 
+using android::base::Split;
 using android::base::StartsWith;
 
 const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
 
 struct fs_mgr_flag_values {
-    char *key_loc;
-    char* key_dir;
-    char *verity_loc;
-    char *sysfs_path;
-    long long part_length;
-    char *label;
-    int partnum;
-    int swap_prio;
-    int max_comp_streams;
-    unsigned int zram_size;
-    uint64_t reserved_size;
-    unsigned int file_contents_mode;
-    unsigned int file_names_mode;
-    unsigned int erase_blk_size;
-    unsigned int logical_blk_size;
+    std::string key_loc;
+    std::string key_dir;
+    std::string verity_loc;
+    std::string sysfs_path;
+    off64_t part_length = 0;
+    std::string label;
+    int partnum = -1;
+    int swap_prio = -1;
+    int max_comp_streams = 0;
+    off64_t zram_size = 0;
+    off64_t reserved_size = 0;
+    int file_contents_mode = 0;
+    int file_names_mode = 0;
+    off64_t erase_blk_size = 0;
+    off64_t logical_blk_size = 0;
+    std::string vbmeta_partition;
 };
 
 struct flag_list {
     const char *name;
-    unsigned int flag;
+    uint64_t flag;
 };
 
 static struct flag_list mount_flags[] = {
@@ -97,11 +99,13 @@
         {"verifyatboot", MF_VERIFYATBOOT},
         {"verify", MF_VERIFY},
         {"avb", MF_AVB},
+        {"avb=", MF_AVB},
         {"noemulatedsd", MF_NOEMULATEDSD},
         {"notrim", MF_NOTRIM},
         {"formattable", MF_FORMATTABLE},
         {"slotselect", MF_SLOTSELECT},
         {"nofail", MF_NOFAIL},
+        {"first_stage_mount", MF_FIRST_STAGE_MOUNT},
         {"latemount", MF_LATEMOUNT},
         {"reservedsize=", MF_RESERVEDSIZE},
         {"quota", MF_QUOTA},
@@ -112,6 +116,7 @@
         {"logical", MF_LOGICAL},
         {"checkpoint=block", MF_CHECKPOINT_BLK},
         {"checkpoint=fs", MF_CHECKPOINT_FS},
+        {"slotselect_other", MF_SLOTSELECT_OTHER},
         {0, 0},
 };
 
@@ -119,9 +124,11 @@
 #define EM_ICE          2
 #define EM_AES_256_CTS  3
 #define EM_AES_256_HEH  4
+#define EM_ADIANTUM     5
 
 static const struct flag_list file_contents_encryption_modes[] = {
     {"aes-256-xts", EM_AES_256_XTS},
+    {"adiantum", EM_ADIANTUM},
     {"software", EM_AES_256_XTS}, /* alias for backwards compatibility */
     {"ice", EM_ICE}, /* hardware-specific inline cryptographic engine */
     {0, 0},
@@ -130,12 +137,12 @@
 static const struct flag_list file_names_encryption_modes[] = {
     {"aes-256-cts", EM_AES_256_CTS},
     {"aes-256-heh", EM_AES_256_HEH},
+    {"adiantum", EM_ADIANTUM},
     {0, 0},
 };
 
-static unsigned int encryption_mode_to_flag(const struct flag_list *list,
-                                            const char *mode, const char *type)
-{
+static int encryption_mode_to_flag(const struct flag_list* list, const char* mode,
+                                   const char* type) {
     const struct flag_list *j;
 
     for (j = list; j->name; ++j) {
@@ -147,9 +154,7 @@
     return 0;
 }
 
-static const char *flag_to_encryption_mode(const struct flag_list *list,
-                                           unsigned int flag)
-{
+static const char* flag_to_encryption_mode(const struct flag_list* list, uint64_t flag) {
     const struct flag_list *j;
 
     for (j = list; j->name; ++j) {
@@ -160,9 +165,8 @@
     return nullptr;
 }
 
-static uint64_t calculate_zram_size(unsigned int percentage)
-{
-    uint64_t total;
+static off64_t calculate_zram_size(unsigned int percentage) {
+    off64_t total;
 
     total  = sysconf(_SC_PHYS_PAGES);
     total *= percentage;
@@ -173,10 +177,9 @@
     return total;
 }
 
-static uint64_t parse_size(const char *arg)
-{
+static off64_t parse_size(const char* arg) {
     char *endptr;
-    uint64_t size = strtoull(arg, &endptr, 10);
+    off64_t size = strtoll(arg, &endptr, 10);
     if (*endptr == 'k' || *endptr == 'K')
         size *= 1024LL;
     else if (*endptr == 'm' || *endptr == 'M')
@@ -205,23 +208,13 @@
     return false;
 }
 
-static int parse_flags(char *flags, struct flag_list *fl,
-                       struct fs_mgr_flag_values *flag_vals,
-                       char *fs_options, int fs_options_len)
-{
-    int f = 0;
+static uint64_t parse_flags(char* flags, struct flag_list* fl, struct fs_mgr_flag_values* flag_vals,
+                            char* fs_options, int fs_options_len) {
+    uint64_t f = 0;
     int i;
     char *p;
     char *savep;
 
-    /* initialize flag values.  If we find a relevant flag, we'll
-     * update the value */
-    if (flag_vals) {
-        memset(flag_vals, 0, sizeof(*flag_vals));
-        flag_vals->partnum = -1;
-        flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
-    }
-
     /* initialize fs_options to the null string */
     if (fs_options && (fs_options_len > 0)) {
         fs_options[0] = '\0';
@@ -247,22 +240,22 @@
                     /* The encryptable flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(arg);
+                    flag_vals->key_loc = arg;
                 } else if (flag == MF_VERIFY) {
                     /* If the verify flag is followed by an = and the
                      * location for the verity state,  get it and return it.
                      */
-                    flag_vals->verity_loc = strdup(arg);
+                    flag_vals->verity_loc = arg;
                 } else if (flag == MF_FORCECRYPT) {
                     /* The forceencrypt flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(arg);
+                    flag_vals->key_loc = arg;
                 } else if (flag == MF_FORCEFDEORFBE) {
                     /* The forcefdeorfbe flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(arg);
+                    flag_vals->key_loc = arg;
                     flag_vals->file_contents_mode = EM_AES_256_XTS;
                     flag_vals->file_names_mode = EM_AES_256_CTS;
                 } else if (flag == MF_FILEENCRYPTION) {
@@ -283,6 +276,8 @@
                         flag_vals->file_names_mode =
                             encryption_mode_to_flag(file_names_encryption_modes,
                                                     colon + 1, "file names");
+                    } else if (flag_vals->file_contents_mode == EM_ADIANTUM) {
+                        flag_vals->file_names_mode = EM_ADIANTUM;
                     } else {
                         flag_vals->file_names_mode = EM_AES_256_CTS;
                     }
@@ -290,7 +285,7 @@
                     /* The metadata flag is followed by an = and the
                      * directory for the keys.  Get it and return it.
                      */
-                    flag_vals->key_dir = strdup(arg);
+                    flag_vals->key_dir = arg;
                 } else if (flag == MF_LENGTH) {
                     /* The length flag is followed by an = and the
                      * size of the partition.  Get it and return it.
@@ -307,8 +302,7 @@
                     auto label_end = strchr(label_start, ':');
 
                     if (label_end) {
-                        flag_vals->label = strndup(label_start,
-                                                   (int) (label_end - label_start));
+                        flag_vals->label = std::string(label_start, (int)(label_end - label_start));
                         auto part_start = label_end + 1;
                         if (!strcmp(part_start, "auto")) {
                             flag_vals->partnum = -1;
@@ -322,6 +316,8 @@
                     flag_vals->swap_prio = strtoll(arg, NULL, 0);
                 } else if (flag == MF_MAX_COMP_STREAMS) {
                     flag_vals->max_comp_streams = strtoll(arg, NULL, 0);
+                } else if (flag == MF_AVB) {
+                    flag_vals->vbmeta_partition = arg;
                 } else if (flag == MF_ZRAMSIZE) {
                     auto is_percent = !!strrchr(arg, '%');
                     auto val = strtoll(arg, NULL, 0);
@@ -339,7 +335,7 @@
                      * erase block size. Get it, check that it is a power of 2 and
                      * at least 4096, and return it.
                      */
-                    auto val = strtoul(arg, NULL, 0);
+                    auto val = strtoll(arg, nullptr, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->erase_blk_size = val;
                 } else if (flag == MF_LOGICALBLKSIZE) {
@@ -347,12 +343,12 @@
                      * logical block size. Get it, check that it is a power of 2 and
                      * at least 4096, and return it.
                      */
-                    auto val = strtoul(arg, NULL, 0);
+                    auto val = strtoll(arg, nullptr, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->logical_blk_size = val;
                 } else if (flag == MF_SYSFS) {
                     /* The path to trigger device gc by idle-maint of vold. */
-                    flag_vals->sysfs_path = strdup(arg);
+                    flag_vals->sysfs_path = arg;
                 }
                 break;
             }
@@ -509,49 +505,17 @@
     return false;
 }
 
-static struct fstab* fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts) {
-    int cnt, entries;
+static bool fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
     ssize_t len;
     size_t alloc_len = 0;
     char *line = NULL;
     const char *delim = " \t";
     char *save_ptr, *p;
-    struct fstab *fstab = NULL;
+    Fstab fstab;
     struct fs_mgr_flag_values flag_vals;
 #define FS_OPTIONS_LEN 1024
     char tmp_fs_options[FS_OPTIONS_LEN];
 
-    entries = 0;
-    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
-        /* if the last character is a newline, shorten the string by 1 byte */
-        if (line[len - 1] == '\n') {
-            line[len - 1] = '\0';
-        }
-        /* Skip any leading whitespace */
-        p = line;
-        while (isspace(*p)) {
-            p++;
-        }
-        /* ignore comments or empty lines */
-        if (*p == '#' || *p == '\0')
-            continue;
-        entries++;
-    }
-
-    if (!entries) {
-        LERROR << "No entries found in fstab";
-        goto err;
-    }
-
-    /* Allocate and init the fstab structure */
-    fstab = static_cast<struct fstab *>(calloc(1, sizeof(struct fstab)));
-    fstab->num_entries = entries;
-    fstab->recs = static_cast<struct fstab_rec *>(
-        calloc(fstab->num_entries, sizeof(struct fstab_rec)));
-
-    fseek(fstab_file, 0, SEEK_SET);
-
-    cnt = 0;
     while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
         /* if the last character is a newline, shorten the string by 1 byte */
         if (line[len - 1] == '\n') {
@@ -567,46 +531,36 @@
         if (*p == '#' || *p == '\0')
             continue;
 
-        /* If a non-comment entry is greater than the size we allocated, give an
-         * error and quit.  This can happen in the unlikely case the file changes
-         * between the two reads.
-         */
-        if (cnt >= entries) {
-            LERROR << "Tried to process more entries than counted";
-            break;
-        }
+        FstabEntry entry;
 
         if (!(p = strtok_r(line, delim, &save_ptr))) {
             LERROR << "Error parsing mount source";
             goto err;
         }
-        fstab->recs[cnt].blk_device = strdup(p);
+        entry.blk_device = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing mount_point";
             goto err;
         }
-        fstab->recs[cnt].mount_point = strdup(p);
+        entry.mount_point = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing fs_type";
             goto err;
         }
-        fstab->recs[cnt].fs_type = strdup(p);
+        entry.fs_type = p;
 
         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing mount_flags";
             goto err;
         }
         tmp_fs_options[0] = '\0';
-        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
-                                       tmp_fs_options, FS_OPTIONS_LEN);
+        entry.flags = parse_flags(p, mount_flags, NULL, tmp_fs_options, FS_OPTIONS_LEN);
 
         /* fs_options are optional */
         if (tmp_fs_options[0]) {
-            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
-        } else {
-            fstab->recs[cnt].fs_options = NULL;
+            entry.fs_options = tmp_fs_options;
         }
 
         // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
@@ -616,78 +570,48 @@
             LERROR << "Error parsing fs_mgr_options";
             goto err;
         }
-        fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
-                                                    &flag_vals, NULL, 0);
-        fstab->recs[cnt].key_loc = flag_vals.key_loc;
-        fstab->recs[cnt].key_dir = flag_vals.key_dir;
-        fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
-        fstab->recs[cnt].length = flag_vals.part_length;
-        fstab->recs[cnt].label = flag_vals.label;
-        fstab->recs[cnt].partnum = flag_vals.partnum;
-        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
-        fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
-        fstab->recs[cnt].zram_size = flag_vals.zram_size;
-        fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
-        fstab->recs[cnt].file_contents_mode = flag_vals.file_contents_mode;
-        fstab->recs[cnt].file_names_mode = flag_vals.file_names_mode;
-        fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
-        fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
-        fstab->recs[cnt].sysfs_path = flag_vals.sysfs_path;
-        if (fstab->recs[cnt].fs_mgr_flags & MF_LOGICAL) {
-            fstab->recs[cnt].logical_partition_name = strdup(fstab->recs[cnt].blk_device);
+        entry.fs_mgr_flags.val = parse_flags(p, fs_mgr_flags, &flag_vals, NULL, 0);
+
+        entry.key_loc = std::move(flag_vals.key_loc);
+        entry.key_dir = std::move(flag_vals.key_dir);
+        entry.verity_loc = std::move(flag_vals.verity_loc);
+        entry.length = flag_vals.part_length;
+        entry.label = std::move(flag_vals.label);
+        entry.partnum = flag_vals.partnum;
+        entry.swap_prio = flag_vals.swap_prio;
+        entry.max_comp_streams = flag_vals.max_comp_streams;
+        entry.zram_size = flag_vals.zram_size;
+        entry.reserved_size = flag_vals.reserved_size;
+        entry.file_contents_mode = flag_vals.file_contents_mode;
+        entry.file_names_mode = flag_vals.file_names_mode;
+        entry.erase_blk_size = flag_vals.erase_blk_size;
+        entry.logical_blk_size = flag_vals.logical_blk_size;
+        entry.sysfs_path = std::move(flag_vals.sysfs_path);
+        entry.vbmeta_partition = std::move(flag_vals.vbmeta_partition);
+        if (entry.fs_mgr_flags.logical) {
+            entry.logical_partition_name = entry.blk_device;
         }
 
-        cnt++;
+        fstab.emplace_back(std::move(entry));
     }
+
+    if (fstab.empty()) {
+        LERROR << "No entries found in fstab";
+        goto err;
+    }
+
     /* If an A/B partition, modify block device to be the real block device */
-    if (!fs_mgr_update_for_slotselect(fstab)) {
+    if (!fs_mgr_update_for_slotselect(&fstab)) {
         LERROR << "Error updating for slotselect";
         goto err;
     }
     free(line);
-    return fstab;
+    *fstab_out = std::move(fstab);
+    return true;
 
 err:
     free(line);
-    if (fstab)
-        fs_mgr_free_fstab(fstab);
-    return NULL;
-}
-
-/* merges fstab entries from both a and b, then returns the merged result.
- * note that the caller should only manage the return pointer without
- * doing further memory management for the two inputs, i.e. only need to
- * frees up memory of the return value without touching a and b. */
-static struct fstab *in_place_merge(struct fstab *a, struct fstab *b)
-{
-    if (!a && !b) return nullptr;
-    if (!a) return b;
-    if (!b) return a;
-
-    int total_entries = a->num_entries + b->num_entries;
-    a->recs = static_cast<struct fstab_rec *>(realloc(
-        a->recs, total_entries * (sizeof(struct fstab_rec))));
-    if (!a->recs) {
-        LERROR << __FUNCTION__ << "(): failed to allocate fstab recs";
-        // If realloc() fails the original block is left untouched;
-        // it is not freed or moved. So we have to free both a and b here.
-        fs_mgr_free_fstab(a);
-        fs_mgr_free_fstab(b);
-        return nullptr;
-    }
-
-    for (int i = a->num_entries, j = 0; i < total_entries; i++, j++) {
-        // Copy the structs by assignment.
-        a->recs[i] = b->recs[j];
-    }
-
-    // We can't call fs_mgr_free_fstab because a->recs still references the
-    // memory allocated by strdup.
-    free(b->recs);
-    free(b);
-
-    a->num_entries = total_entries;
-    return a;
+    return false;
 }
 
 /* Extracts <device>s from the by-name symlinks specified in a fstab:
@@ -700,11 +624,11 @@
  *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
  * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
  */
-static std::set<std::string> extract_boot_devices(const fstab& fstab) {
+static std::set<std::string> extract_boot_devices(const Fstab& fstab) {
     std::set<std::string> boot_devices;
 
-    for (int i = 0; i < fstab.num_entries; i++) {
-        std::string blk_device(fstab.recs[i].blk_device);
+    for (const auto& entry : fstab) {
+        std::string blk_device = entry.blk_device;
         // Skips blk_device that doesn't conform to the format.
         if (!android::base::StartsWith(blk_device, "/dev/block") ||
             android::base::StartsWith(blk_device, "/dev/block/by-name") ||
@@ -733,33 +657,36 @@
     return boot_devices;
 }
 
-struct fstab *fs_mgr_read_fstab(const char *fstab_path)
-{
-    struct fstab *fstab;
-
-    auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fstab_path, "re"), fclose};
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
+    auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (!fstab_file) {
-        PERROR << __FUNCTION__<< "(): cannot open file: '" << fstab_path << "'";
+        PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+        return false;
+    }
+
+    if (!fs_mgr_read_fstab_file(fstab_file.get(), path == "/proc/mounts", fstab)) {
+        LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
+        return false;
+    }
+
+    return true;
+}
+
+struct fstab* fs_mgr_read_fstab(const char* fstab_path) {
+    Fstab fstab;
+    if (!ReadFstabFromFile(fstab_path, &fstab)) {
         return nullptr;
     }
 
-    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 << "'";
-    }
-
-    return fstab;
+    return FstabToLegacyFstab(fstab);
 }
 
-/* Returns fstab entries parsed from the device tree if they
- * exist
- */
-struct fstab *fs_mgr_read_fstab_dt()
-{
+// Returns fstab entries parsed from the device tree if they exist
+bool ReadFstabFromDt(Fstab* fstab) {
     std::string fstab_buf = read_fstab_from_dt();
     if (fstab_buf.empty()) {
         LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
-        return nullptr;
+        return false;
     }
 
     std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
@@ -767,16 +694,25 @@
                  fstab_buf.length(), "r"), fclose);
     if (!fstab_file) {
         PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
+        return false;
+    }
+
+    if (!fs_mgr_read_fstab_file(fstab_file.get(), false, fstab)) {
+        LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
+               << std::endl << fstab_buf;
+        return false;
+    }
+
+    return true;
+}
+
+struct fstab* fs_mgr_read_fstab_dt() {
+    Fstab fstab;
+    if (!ReadFstabFromDt(&fstab)) {
         return nullptr;
     }
 
-    struct fstab* fstab = fs_mgr_read_fstab_file(fstab_file.get(), false);
-    if (!fstab) {
-        LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
-               << std::endl << fstab_buf;
-    }
-
-    return fstab;
+    return FstabToLegacyFstab(fstab);
 }
 
 /*
@@ -802,32 +738,42 @@
     return std::string();
 }
 
-/*
- * loads the fstab file and combines with fstab entries passed in from device tree.
- */
-struct fstab *fs_mgr_read_fstab_default()
-{
-    std::string default_fstab;
+// Loads the fstab file and combines with fstab entries passed in from device tree.
+bool ReadDefaultFstab(Fstab* fstab) {
+    Fstab dt_fstab;
+    ReadFstabFromDt(&dt_fstab);
 
+    *fstab = std::move(dt_fstab);
+
+    std::string default_fstab_path;
     // Use different fstab paths for normal boot and recovery boot, respectively
     if (access("/system/bin/recovery", F_OK) == 0) {
-        default_fstab = "/etc/recovery.fstab";
+        default_fstab_path = "/etc/recovery.fstab";
     } else {  // normal boot
-        default_fstab = get_fstab_path();
+        default_fstab_path = get_fstab_path();
     }
 
-    struct fstab* fstab = nullptr;
-    if (!default_fstab.empty()) {
-        fstab = fs_mgr_read_fstab(default_fstab.c_str());
+    Fstab default_fstab;
+    if (!default_fstab_path.empty()) {
+        ReadFstabFromFile(default_fstab_path, &default_fstab);
     } else {
         LINFO << __FUNCTION__ << "(): failed to find device default fstab";
     }
 
-    struct fstab* fstab_dt = fs_mgr_read_fstab_dt();
+    for (auto&& entry : default_fstab) {
+        fstab->emplace_back(std::move(entry));
+    }
 
-    // combines fstab entries passed in from device tree with
-    // the ones found from default_fstab file
-    return in_place_merge(fstab_dt, fstab);
+    return !fstab->empty();
+}
+
+struct fstab* fs_mgr_read_fstab_default() {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return nullptr;
+    }
+
+    return FstabToLegacyFstab(fstab);
 }
 
 void fs_mgr_free_fstab(struct fstab *fstab)
@@ -904,20 +850,93 @@
 }
 
 std::set<std::string> fs_mgr_get_boot_devices() {
-    // boot_devices can be specified in device tree.
-    std::string dt_value;
-    std::string file_name = get_android_dt_dir() + "/boot_devices";
-    if (read_dt_file(file_name, &dt_value)) {
-        auto boot_devices = android::base::Split(dt_value, ",");
+    // First check the kernel commandline, then try the device tree otherwise
+    std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
+    std::string value;
+    if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
+        read_dt_file(dt_file_name, &value)) {
+        auto boot_devices = Split(value, ",");
         return std::set<std::string>(boot_devices.begin(), boot_devices.end());
     }
 
     // Fallback to extract boot devices from fstab.
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (fstab) return extract_boot_devices(*fstab);
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return {};
+    }
 
-    return {};
+    return extract_boot_devices(fstab);
+}
+
+FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec) {
+    FstabEntry entry;
+    entry.blk_device = fstab_rec->blk_device;
+    entry.logical_partition_name = fstab_rec->logical_partition_name;
+    entry.mount_point = fstab_rec->mount_point;
+    entry.fs_type = fstab_rec->fs_type;
+    entry.flags = fstab_rec->flags;
+    entry.fs_options = fstab_rec->fs_options;
+    entry.fs_mgr_flags.val = fstab_rec->fs_mgr_flags;
+    entry.key_loc = fstab_rec->key_loc;
+    entry.key_dir = fstab_rec->key_dir;
+    entry.verity_loc = fstab_rec->verity_loc;
+    entry.length = fstab_rec->length;
+    entry.label = fstab_rec->label;
+    entry.partnum = fstab_rec->partnum;
+    entry.swap_prio = fstab_rec->swap_prio;
+    entry.max_comp_streams = fstab_rec->max_comp_streams;
+    entry.zram_size = fstab_rec->zram_size;
+    entry.reserved_size = fstab_rec->reserved_size;
+    entry.file_contents_mode = fstab_rec->file_contents_mode;
+    entry.file_names_mode = fstab_rec->file_names_mode;
+    entry.erase_blk_size = fstab_rec->erase_blk_size;
+    entry.logical_blk_size = fstab_rec->logical_blk_size;
+    entry.sysfs_path = fstab_rec->sysfs_path;
+
+    return entry;
+}
+
+Fstab LegacyFstabToFstab(const struct fstab* legacy_fstab) {
+    Fstab fstab;
+    for (int i = 0; i < legacy_fstab->num_entries; i++) {
+        fstab.emplace_back(FstabRecToFstabEntry(&legacy_fstab->recs[i]));
+    }
+
+    return fstab;
+}
+
+fstab* FstabToLegacyFstab(const Fstab& fstab) {
+    struct fstab* legacy_fstab = static_cast<struct fstab*>(calloc(1, sizeof(struct fstab)));
+    legacy_fstab->num_entries = fstab.size();
+    legacy_fstab->recs =
+            static_cast<fstab_rec*>(calloc(legacy_fstab->num_entries, sizeof(fstab_rec)));
+
+    for (int i = 0; i < legacy_fstab->num_entries; i++) {
+        legacy_fstab->recs[i].blk_device = strdup(fstab[i].blk_device.c_str());
+        legacy_fstab->recs[i].logical_partition_name =
+                strdup(fstab[i].logical_partition_name.c_str());
+        legacy_fstab->recs[i].mount_point = strdup(fstab[i].mount_point.c_str());
+        legacy_fstab->recs[i].fs_type = strdup(fstab[i].fs_type.c_str());
+        legacy_fstab->recs[i].flags = fstab[i].flags;
+        legacy_fstab->recs[i].fs_options = strdup(fstab[i].fs_options.c_str());
+        legacy_fstab->recs[i].fs_mgr_flags = fstab[i].fs_mgr_flags.val;
+        legacy_fstab->recs[i].key_loc = strdup(fstab[i].key_loc.c_str());
+        legacy_fstab->recs[i].key_dir = strdup(fstab[i].key_dir.c_str());
+        legacy_fstab->recs[i].verity_loc = strdup(fstab[i].verity_loc.c_str());
+        legacy_fstab->recs[i].length = fstab[i].length;
+        legacy_fstab->recs[i].label = strdup(fstab[i].label.c_str());
+        legacy_fstab->recs[i].partnum = fstab[i].partnum;
+        legacy_fstab->recs[i].swap_prio = fstab[i].swap_prio;
+        legacy_fstab->recs[i].max_comp_streams = fstab[i].max_comp_streams;
+        legacy_fstab->recs[i].zram_size = fstab[i].zram_size;
+        legacy_fstab->recs[i].reserved_size = fstab[i].reserved_size;
+        legacy_fstab->recs[i].file_contents_mode = fstab[i].file_contents_mode;
+        legacy_fstab->recs[i].file_names_mode = fstab[i].file_names_mode;
+        legacy_fstab->recs[i].erase_blk_size = fstab[i].erase_blk_size;
+        legacy_fstab->recs[i].logical_blk_size = fstab[i].logical_blk_size;
+        legacy_fstab->recs[i].sysfs_path = strdup(fstab[i].sysfs_path.c_str());
+    }
+    return legacy_fstab;
 }
 
 int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
@@ -991,6 +1010,10 @@
     return fstab->fs_mgr_flags & MF_NOFAIL;
 }
 
+int fs_mgr_is_first_stage_mount(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_FIRST_STAGE_MOUNT;
+}
+
 int fs_mgr_is_latemount(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_LATEMOUNT;
 }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index bef46e1..8bfcd81 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -70,19 +70,11 @@
 
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
-bool fs_mgr_overlayfs_mount_all(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*>&) {
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab*) {
     return {};
 }
 
@@ -96,6 +88,10 @@
     return false;
 }
 
+bool fs_mgr_overlayfs_is_setup() {
+    return false;
+}
+
 #else  // ALLOW_ADBD_DISABLE_VERITY == 0
 
 namespace {
@@ -127,28 +123,31 @@
 
 // At less than 1% free space return value of false,
 // means we will try to wrap with overlayfs.
-bool fs_mgr_filesystem_has_space(const char* mount_point) {
+bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
     // If we have access issues to find out space remaining, return true
     // to prevent us trying to override with overlayfs.
     struct statvfs vst;
-    if (statvfs(mount_point, &vst)) return true;
+    if (statvfs(mount_point.c_str(), &vst)) return true;
 
     static constexpr int kPercentThreshold = 1;  // 1%
 
     return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
 }
 
-bool fs_mgr_overlayfs_enabled(struct fstab_rec* fsrec) {
+bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
     // readonly filesystem, can not be mount -o remount,rw
     // if squashfs or if free space is (near) zero making such a remount
     // virtually useless, or if there are shared blocks that prevent remount,rw
-    if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+    if ("squashfs" == entry->fs_type || !fs_mgr_filesystem_has_space(entry->mount_point)) {
         return true;
     }
-    if (fs_mgr_is_logical(fsrec)) {
-        fs_mgr_update_logical_partition(fsrec);
+    if (entry->fs_mgr_flags.logical) {
+        fs_mgr_update_logical_partition(entry);
     }
-    return fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device);
+    auto save_errno = errno;
+    auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
+    errno = save_errno;
+    return has_shared_blocks;
 }
 
 bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
@@ -246,22 +245,16 @@
 }
 
 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;
+    Fstab fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+        return false;
+    }
     const auto lowerdir = kLowerdirOption + mount_point;
-    for (auto i = 0; i < fstab->num_entries; ++i) {
-        const auto fsrec = &fstab->recs[i];
-        const auto fs_type = fsrec->fs_type;
-        if (!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;
+    for (const auto& entry : fstab) {
+        if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
+        if (mount_point != entry.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, ",");
+        const auto options = android::base::Split(entry.fs_options, ",");
         for (const auto& opt : options) {
             if (opt == lowerdir) {
                 return true;
@@ -273,34 +266,27 @@
 
 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);
-    });
+    auto save_errno = errno;
+    fs_mgr_update_verity_state(
+            [&ret](const std::string& mount_point, int) { ret.emplace_back(mount_point); });
+    if ((errno == ENOENT) || (errno == ENXIO)) errno = save_errno;
     return ret;
 }
 
-bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
-    if (!fsrec) return false;
-
-    auto fsrec_mount_point = fsrec->mount_point;
-    if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
-    if (!fsrec->blk_device) return false;
-
-    if (!fsrec->fs_type) return false;
-
+bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
     // Don't check entries that are managed by vold.
-    if (fsrec->fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) return false;
+    if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
 
     // Only concerned with readonly partitions.
-    if (!(fsrec->flags & MS_RDONLY)) return false;
+    if (!(entry->flags & MS_RDONLY)) return false;
 
     // If unbindable, do not allow overlayfs as this could expose us to
     // security issues.  On Android, this could also be used to turn off
     // the ability to overlay an otherwise acceptable filesystem since
     // /system and /vendor are never bound(sic) to.
-    if (fsrec->flags & MS_UNBINDABLE) return false;
+    if (entry->flags & MS_UNBINDABLE) return false;
 
-    if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
+    if (!fs_mgr_overlayfs_enabled(entry)) return false;
 
     return true;
 }
@@ -382,19 +368,33 @@
     return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
 }
 
+const auto kPhysicalDevice = "/dev/block/by-name/"s;
+
 std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
-    return "/dev/block/by-name/" + fs_mgr_get_super_partition_name(slot_number);
+    return kPhysicalDevice + 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;
+bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
+    for (const auto& entry : fstab) {
+        if (entry.fs_mgr_flags.logical) {
+            return true;
+        }
     }
     return false;
 }
 
+void fs_mgr_overlayfs_umount_scratch() {
+    // 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);
+    LINFO << "umount(" << kScratchMountPoint << ")";
+    rmdir(kScratchMountPoint.c_str());
+}
+
 // reduce 'DM_DEV_STATUS failed for scratch: No such device or address' noise
 std::string scratch_device_cache;
 
@@ -408,13 +408,7 @@
 
     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);
+        fs_mgr_overlayfs_umount_scratch();
     }
     auto builder = MetadataBuilder::New(super_device, slot_number);
     if (!builder) {
@@ -510,7 +504,7 @@
     // hijack __mount() report format to help triage
     auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
     const auto opt_list = android::base::Split(options, ",");
-    for (const auto opt : opt_list) {
+    for (const auto& opt : opt_list) {
         if (android::base::StartsWith(opt, kUpperdirOption)) {
             report = report + "," + opt;
             break;
@@ -529,15 +523,12 @@
     }
 }
 
-std::vector<std::string> fs_mgr_candidate_list(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(fsrec->mount_point));
+    for (auto& entry : *fstab) {
+        if (!fs_mgr_wants_overlayfs(&entry)) continue;
+        std::string new_mount_point(fs_mgr_mount_point(entry.mount_point.c_str()));
         if (mount_point && (new_mount_point != mount_point)) continue;
         if (std::find(verity.begin(), verity.end(), android::base::Basename(new_mount_point)) !=
             verity.end()) {
@@ -569,10 +560,9 @@
     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")) {
+    if (std::find_if(fstab->begin(), fstab->end(), [](const auto& entry) {
+            return entry.mount_point == "/" || entry.mount_point == "/system ";
+        }) != fstab->end()) {
         return mounts;
     }
 
@@ -594,26 +584,20 @@
         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("");
+    FstabEntry entry;
+    entry.blk_device = device_path;
+    entry.mount_point = kScratchMountPoint;
+    entry.fs_type = mnt_type;
+    entry.flags = MS_RELATIME;
     auto save_errno = errno;
-    auto mounted = fs_mgr_do_mount_one(fsrec) == 0;
+    auto mounted = fs_mgr_do_mount_one(entry) == 0;
     if (!mounted) {
-        free(fsrec->fs_type);
         if (mnt_type == "f2fs") {
-            fsrec->fs_type = strdup("ext4");
+            entry.fs_type = "ext4";
         } else {
-            fsrec->fs_type = strdup("f2fs");
+            entry.fs_type = "f2fs";
         }
-        mounted = fs_mgr_do_mount_one(fsrec) == 0;
+        mounted = fs_mgr_do_mount_one(entry) == 0;
         if (!mounted) save_errno = errno;
     }
     setfscreatecon(nullptr);
@@ -635,88 +619,20 @@
 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 "";
+    // Is this a multiple super device (retrofit)?
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    auto path = fs_mgr_overlayfs_super_device(slot_number == 0);
+    if (super_device == path) {
+        // Create from within single super device;
+        auto& dm = DeviceMapper::Instance();
+        const auto partition_name = android::base::Basename(kScratchMountPoint);
+        if (!dm.GetDmDevicePathByName(partition_name, &path)) return "";
+    }
     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_create = !fs_mgr_rw_access(scratch_device);
-    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) {
-        LERROR << "open " << super_device << " metadata";
-        return false;
-    }
-    const auto partition_name = android::base::Basename(kScratchMountPoint);
-    auto partition = builder->FindPartition(partition_name);
-    auto partition_exists = partition != nullptr;
-    auto changed = false;
-    if (!partition_exists) {
-        partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
-        if (!partition) {
-            LERROR << "create " << partition_name;
-            return false;
-        }
-        changed = true;
-    }
-    // Take half of free space, minimum 512MB or free space - 256KB margin.
-    static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
-    static constexpr auto kMarginSize = uint64_t(256 * 1024);
-    if (partition->size() < kMinimumSize) {
-        auto partition_size =
-                builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
-        if ((partition_size > kMinimumSize) || !partition->size()) {
-            partition_size = std::max(std::min(kMinimumSize, partition_size - kMarginSize),
-                                      partition_size / 2);
-            if (partition_size > partition->size()) {
-                if (!builder->ResizePartition(partition, partition_size)) {
-                    LERROR << "resize " << partition_name;
-                    return false;
-                }
-                if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
-                changed = true;
-                partition_exists = false;
-            }
-        }
-    }
-    // land the update back on to the partition
-    if (changed) {
-        auto metadata = builder->Export();
-        if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
-            LERROR << "add partition " << partition_name;
-            return false;
-        }
-
-        if (change) *change = true;
-    }
-
-    if (changed || partition_create) {
-        if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
-                                    &scratch_device))
-            return false;
-
-        if (change) *change = true;
-    }
-
-    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;
-    }
-
+bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
     // Force mkfs by design for overlay support of adb remount, simplify and
     // thus do not rely on fsck to correct problems that could creep in.
     auto command = ""s;
@@ -735,6 +651,114 @@
         LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
         return false;
     }
+    return true;
+}
+
+bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
+                                     bool* partition_exists, bool* change) {
+    *scratch_device = fs_mgr_overlayfs_scratch_device();
+    *partition_exists = fs_mgr_rw_access(*scratch_device);
+    auto partition_create = !*partition_exists;
+    // Do we need to create a logical "scratch" partition?
+    if (!partition_create && android::base::StartsWith(*scratch_device, kPhysicalDevice)) {
+        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;
+    if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        LERROR << "open " << super_device << " metadata";
+        return false;
+    }
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+    auto partition = builder->FindPartition(partition_name);
+    *partition_exists = partition != nullptr;
+    auto changed = false;
+    if (!*partition_exists) {
+        partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+        if (!partition) {
+            LERROR << "create " << partition_name;
+            return false;
+        }
+        changed = true;
+    }
+    // Take half of free space, minimum 512MB or maximum free - margin.
+    static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
+    if (partition->size() < kMinimumSize) {
+        auto partition_size =
+                builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+        if ((partition_size > kMinimumSize) || !partition->size()) {
+            // Leave some space for free space jitter of a few erase
+            // blocks, in case they are needed for any individual updates
+            // to any other partition that needs to be flashed while
+            // overlayfs is in force.  Of course if margin_size is not
+            // enough could normally get a flash failure, so
+            // ResizePartition() will delete the scratch partition in
+            // order to fulfill.  Deleting scratch will destroy all of
+            // the adb remount overrides :-( .
+            auto margin_size = uint64_t(3 * 256 * 1024);
+            BlockDeviceInfo info;
+            if (builder->GetBlockDeviceInfo(partition_name, &info)) {
+                margin_size = 3 * info.logical_block_size;
+            }
+            partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
+                                      partition_size / 2);
+            if (partition_size > partition->size()) {
+                if (!builder->ResizePartition(partition, partition_size)) {
+                    LERROR << "resize " << partition_name;
+                    return false;
+                }
+                if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
+                changed = true;
+                *partition_exists = false;
+            }
+        }
+    }
+    // land the update back on to the partition
+    if (changed) {
+        auto metadata = builder->Export();
+        if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+            LERROR << "add partition " << partition_name;
+            return false;
+        }
+
+        if (change) *change = true;
+    }
+
+    if (changed || partition_create) {
+        if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+                                    scratch_device))
+            return false;
+
+        if (change) *change = true;
+    }
+    return true;
+}
+
+// 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;
+
+    std::string scratch_device;
+    bool partition_exists;
+    if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists, change)) {
+        return false;
+    }
+
+    // If the partition exists, assume first that it can be mounted.
+    auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
+    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; fall through to make it.
+        errno = 0;
+    }
+
+    if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) return false;
 
     if (change) *change = true;
 
@@ -744,6 +768,7 @@
 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 (android::base::StartsWith(scratch_device, kPhysicalDevice)) return true;
     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);
@@ -753,14 +778,20 @@
     return builder->FindPartition(android::base::Basename(kScratchMountPoint)) != nullptr;
 }
 
+bool fs_mgr_overlayfs_invalid() {
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
+
+    // in recovery or fastbootd mode, not allowed!
+    if (fs_mgr_access("/system/bin/recovery")) return true;
+
+    return false;
+}
+
 }  // namespace
 
-bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
+bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
     auto ret = false;
-
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
-
-    if (!fstab) return ret;
+    if (fs_mgr_overlayfs_invalid()) return ret;
 
     auto scratch_can_be_mounted = true;
     for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
@@ -773,8 +804,7 @@
                 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());
+                fs_mgr_overlayfs_umount_scratch();
             }
         }
         if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
@@ -782,17 +812,12 @@
     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_overlayfs_invalid()) return {};
 
-std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return {};
-
-    if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
+    if (std::find_if(fstab->begin(), fstab->end(), [](const auto& entry) {
+            return entry.mount_point == kScratchMountPoint;
+        }) != fstab->end()) {
         return {};
     }
 
@@ -805,14 +830,6 @@
     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) {
@@ -825,10 +842,13 @@
         return ret;
     }
 
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    if (!fstab) return ret;
-    auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(mount_point));
+    auto save_errno = errno;
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return false;
+    }
+    errno = save_errno;
+    auto mounts = fs_mgr_candidate_list(&fstab, fs_mgr_mount_point(mount_point));
     if (mounts.empty()) return ret;
 
     std::string dir;
@@ -836,18 +856,23 @@
         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())) {
+                !fs_mgr_overlayfs_has_logical(fstab)) {
                 continue;
             }
-            if (!fs_mgr_overlayfs_setup_scratch(fstab.get(), change)) continue;
+            if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
         } else {
-            if (!fs_mgr_get_entry_for_mount_point(fstab.get(), overlay_mount_point)) continue;
+            if (std::find_if(fstab.begin(), fstab.end(), [&overlay_mount_point](const auto& entry) {
+                    return entry.mount_point == overlay_mount_point;
+                }) == fstab.end()) {
+                continue;
+            }
         }
         dir = overlay_mount_point;
         break;
     }
     if (dir.empty()) {
-        errno = ESRCH;
+        if (change && *change) errno = ESRCH;
+        if (errno == EPERM) errno = save_errno;
         return ret;
     }
 
@@ -867,6 +892,7 @@
     auto ret = true;
     // If scratch exists, but is not mounted, lets gain access to clean
     // specific override entries.
+    auto mount_scratch = false;
     if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         auto scratch_device = fs_mgr_overlayfs_scratch_device();
         if (scratch_device.empty()) {
@@ -876,7 +902,8 @@
             CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
                                    &scratch_device);
         }
-        fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type());
+        mount_scratch = 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);
@@ -894,9 +921,24 @@
         PERROR << "teardown";
         ret = false;
     }
+    if (mount_scratch) fs_mgr_overlayfs_umount_scratch();
+
     return ret;
 }
 
+bool fs_mgr_overlayfs_is_setup() {
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return false;
+    }
+    if (fs_mgr_overlayfs_invalid()) return false;
+    for (const auto& mount_point : fs_mgr_candidate_list(&fstab)) {
+        if (fs_mgr_overlayfs_already_mounted(mount_point)) return true;
+    }
+    return false;
+}
+
 #endif  // ALLOW_ADBD_DISABLE_VERITY != 0
 
 bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 23a92d3..7d1159b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -21,8 +21,9 @@
 #include <string>
 
 #include <android-base/logging.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
 
-#include "fs_mgr.h"
 #include "fs_mgr_priv_boot_config.h"
 
 /* The CHECK() in logging.h will use program invocation name as the tag.
@@ -117,6 +118,10 @@
 #define MF_LOGICAL        0x10000000
 #define MF_CHECKPOINT_BLK 0x20000000
 #define MF_CHECKPOINT_FS  0x40000000
+#define MF_FIRST_STAGE_MOUNT \
+                          0x80000000
+#define MF_SLOTSELECT_OTHER  \
+                         0x100000000
 // clang-format on
 
 #define DM_BUF_SIZE 4096
@@ -129,11 +134,11 @@
                           const std::chrono::milliseconds relative_timeout,
                           FileWaitMode wait_mode = FileWaitMode::Exists);
 
-int fs_mgr_set_blk_ro(const char* blockdev);
-bool fs_mgr_update_for_slotselect(fstab* fstab);
+bool fs_mgr_set_blk_ro(const std::string& blockdev);
+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(fstab_rec* fstab, int* mode);
+int load_verity_state(const FstabEntry& entry, int* mode);
 
 #endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 3b01d0e..3cb718c 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -21,6 +21,19 @@
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 
+// https://source.android.com/devices/tech/ota/ab/ab_implement#partitions
+// All partitions that are A/B-ed should be named as follows (slots are always
+// named a, b, etc.): boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.
+static std::string other_suffix(const std::string& slot_suffix) {
+    if (slot_suffix == "_a") {
+        return "_b";
+    }
+    if (slot_suffix == "_b") {
+        return "_a";
+    }
+    return "";
+}
+
 // Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
 // if that parameter does not exist.
 std::string fs_mgr_get_slot_suffix() {
@@ -31,36 +44,24 @@
 }
 
 // Updates |fstab| for slot_suffix. Returns true on success, false on error.
-bool fs_mgr_update_for_slotselect(struct fstab *fstab) {
-    int n;
+bool fs_mgr_update_for_slotselect(Fstab* fstab) {
     std::string ab_suffix;
 
-    for (n = 0; n < fstab->num_entries; n++) {
-        fstab_rec& record = fstab->recs[n];
-        if (record.fs_mgr_flags & MF_SLOTSELECT) {
-            if (ab_suffix.empty()) {
-                ab_suffix = fs_mgr_get_slot_suffix();
-                // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
-                if (ab_suffix.empty()) return false;
-            }
-
-            char* new_blk_device;
-            if (asprintf(&new_blk_device, "%s%s", record.blk_device, ab_suffix.c_str()) <= 0) {
-                return false;
-            }
-            free(record.blk_device);
-            record.blk_device = new_blk_device;
-
-            char* new_partition_name;
-            if (record.logical_partition_name) {
-                if (asprintf(&new_partition_name, "%s%s", record.logical_partition_name,
-                             ab_suffix.c_str()) <= 0) {
-                    return false;
-                }
-                free(record.logical_partition_name);
-                record.logical_partition_name = new_partition_name;
-            }
+    for (auto& entry : *fstab) {
+        if (!entry.fs_mgr_flags.slot_select && !entry.fs_mgr_flags.slot_select_other) {
+            continue;
         }
+
+        if (ab_suffix.empty()) {
+            ab_suffix = fs_mgr_get_slot_suffix();
+            // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
+            if (ab_suffix.empty()) return false;
+        }
+
+        const auto& update_suffix =
+                entry.fs_mgr_flags.slot_select ? ab_suffix : other_suffix(ab_suffix);
+        entry.blk_device = entry.blk_device + update_suffix;
+        entry.logical_partition_name = entry.logical_partition_name + update_suffix;
     }
     return true;
 }
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 2727a6d..9adf8cc 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -536,8 +536,7 @@
     return 0;
 }
 
-static int compare_last_signature(struct fstab_rec *fstab, int *match)
-{
+static int compare_last_signature(const FstabEntry& entry, int* match) {
     char tag[METADATA_TAG_MAX_LENGTH + 1];
     int fd = -1;
     int rc = -1;
@@ -549,42 +548,40 @@
 
     *match = 1;
 
-    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
-            FEC_DEFAULT_ROOTS) == -1) {
-        PERROR << "Failed to open '" << fstab->blk_device << "'";
+    if (fec_open(&f, entry.blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) ==
+        -1) {
+        PERROR << "Failed to open '" << entry.blk_device << "'";
         return rc;
     }
 
     // read verity metadata
     if (fec_verity_get_metadata(f, &verity) == -1) {
-        PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+        PERROR << "Failed to get verity metadata '" << entry.blk_device << "'";
         goto out;
     }
 
     SHA256(verity.signature, sizeof(verity.signature), curr);
 
-    if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
-            basename(fstab->mount_point)) >= (int)sizeof(tag)) {
-        LERROR << "Metadata tag name too long for " << fstab->mount_point;
+    if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s", basename(entry.mount_point.c_str())) >=
+        (int)sizeof(tag)) {
+        LERROR << "Metadata tag name too long for " << entry.mount_point;
         goto out;
     }
 
-    if (metadata_find(fstab->verity_loc, tag, SHA256_DIGEST_LENGTH,
-            &offset) < 0) {
+    if (metadata_find(entry.verity_loc.c_str(), tag, SHA256_DIGEST_LENGTH, &offset) < 0) {
         goto out;
     }
 
-    fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
+    fd = TEMP_FAILURE_RETRY(open(entry.verity_loc.c_str(), O_RDWR | O_SYNC | O_CLOEXEC));
 
     if (fd == -1) {
-        PERROR << "Failed to open " << fstab->verity_loc;
+        PERROR << "Failed to open " << entry.verity_loc;
         goto out;
     }
 
-    if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
-            offset)) != sizeof(prev)) {
-        PERROR << "Failed to read " << sizeof(prev) << " bytes from "
-               << fstab->verity_loc << " offset " << offset;
+    if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev), offset)) != sizeof(prev)) {
+        PERROR << "Failed to read " << sizeof(prev) << " bytes from " << entry.verity_loc
+               << " offset " << offset;
         goto out;
     }
 
@@ -594,8 +591,8 @@
         /* update current signature hash */
         if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
                 offset)) != sizeof(curr)) {
-            PERROR << "Failed to write " << sizeof(curr) << " bytes to "
-                   << fstab->verity_loc << " offset " << offset;
+            PERROR << "Failed to write " << sizeof(curr) << " bytes to " << entry.verity_loc
+                   << " offset " << offset;
             goto out;
         }
     }
@@ -607,28 +604,23 @@
     return rc;
 }
 
-static int get_verity_state_offset(struct fstab_rec *fstab, off64_t *offset)
-{
+static int get_verity_state_offset(const FstabEntry& entry, off64_t* offset) {
     char tag[METADATA_TAG_MAX_LENGTH + 1];
 
-    if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
-            basename(fstab->mount_point)) >= (int)sizeof(tag)) {
-        LERROR << "Metadata tag name too long for " << fstab->mount_point;
+    if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s", basename(entry.mount_point.c_str())) >=
+        (int)sizeof(tag)) {
+        LERROR << "Metadata tag name too long for " << entry.mount_point;
         return -1;
     }
 
-    return metadata_find(fstab->verity_loc, tag, sizeof(struct verity_state),
-                offset);
+    return metadata_find(entry.verity_loc.c_str(), tag, sizeof(struct verity_state), offset);
 }
 
-int load_verity_state(struct fstab_rec* fstab, int* mode) {
-    int match = 0;
-    off64_t offset = 0;
-
-    /* unless otherwise specified, use EIO mode */
+int load_verity_state(const FstabEntry& entry, int* mode) {
+    // unless otherwise specified, use EIO mode.
     *mode = VERITY_MODE_EIO;
 
-    /* use the kernel parameter if set */
+    // use the kernel parameter if set.
     std::string veritymode;
     if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
         if (veritymode == "enforcing") {
@@ -637,7 +629,8 @@
         return 0;
     }
 
-    if (get_verity_state_offset(fstab, &offset) < 0) {
+    off64_t offset = 0;
+    if (get_verity_state_offset(entry, &offset) < 0) {
         /* fall back to stateless behavior */
         return 0;
     }
@@ -645,16 +638,17 @@
     if (was_verity_restart()) {
         /* device was restarted after dm-verity detected a corrupted
          * block, so use EIO mode */
-        return write_verity_state(fstab->verity_loc, offset, *mode);
+        return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
     }
 
-    if (!compare_last_signature(fstab, &match) && !match) {
+    int match = 0;
+    if (!compare_last_signature(entry, &match) && !match) {
         /* partition has been reflashed, reset dm-verity state */
         *mode = VERITY_MODE_DEFAULT;
-        return write_verity_state(fstab->verity_loc, offset, *mode);
+        return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
     }
 
-    return read_verity_state(fstab->verity_loc, offset, mode);
+    return read_verity_state(entry.verity_loc.c_str(), offset, mode);
 }
 
 // Update the verity table using the actual block device path.
@@ -716,8 +710,7 @@
 // prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
 // mount. The 'wait_for_verity_dev' parameter makes this function wait for the
 // verity device to get created before return
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev)
-{
+int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) {
     int retval = FS_MGR_SETUP_VERITY_FAIL;
     int fd = -1;
     std::string verity_blk_name;
@@ -725,20 +718,20 @@
     struct fec_verity_metadata verity;
     struct verity_table_params params = { .table = NULL };
 
-    const std::string mount_point(basename(fstab->mount_point));
+    const std::string mount_point(basename(entry->mount_point.c_str()));
     bool verified_at_boot = false;
 
     android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
 
-    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
-            FEC_DEFAULT_ROOTS) < 0) {
-        PERROR << "Failed to open '" << fstab->blk_device << "'";
+    if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) <
+        0) {
+        PERROR << "Failed to open '" << entry->blk_device << "'";
         return retval;
     }
 
     // read verity metadata
     if (fec_verity_get_metadata(f, &verity) < 0) {
-        PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+        PERROR << "Failed to get verity metadata '" << entry->blk_device << "'";
         // Allow verity disabled when the device is unlocked without metadata
         if (fs_mgr_is_device_unlocked()) {
             retval = FS_MGR_SETUP_VERITY_SKIPPED;
@@ -760,9 +753,9 @@
         params.ecc.valid = false;
     }
 
-    params.ecc_dev = fstab->blk_device;
+    params.ecc_dev = entry->blk_device.c_str();
 
-    if (load_verity_state(fstab, &params.mode) < 0) {
+    if (load_verity_state(*entry, &params.mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
          * restart loop, and no corrupted data will be exposed to userspace
@@ -803,8 +796,8 @@
           << " (mode " << params.mode << ")";
 
     // Update the verity params using the actual block device path
-    update_verity_table_blk_device(fstab->blk_device, &params.table,
-                                   fstab->fs_mgr_flags & MF_SLOTSELECT);
+    update_verity_table_blk_device(entry->blk_device, &params.table,
+                                   entry->fs_mgr_flags.slot_select);
 
     // load the verity mapping table
     if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
@@ -848,31 +841,29 @@
     }
 
     // mark the underlying block device as read-only
-    fs_mgr_set_blk_ro(fstab->blk_device);
+    fs_mgr_set_blk_ro(entry->blk_device);
 
     // Verify the entire partition in one go
     // If there is an error, allow it to mount as a normal verity partition.
-    if (fstab->fs_mgr_flags & MF_VERIFYATBOOT) {
-        LINFO << "Verifying partition " << fstab->blk_device << " at boot";
+    if (entry->fs_mgr_flags.verify_at_boot) {
+        LINFO << "Verifying partition " << entry->blk_device << " at boot";
         int err = read_partition(verity_blk_name.c_str(), verity.data_size);
         if (!err) {
-            LINFO << "Verified verity partition "
-                  << fstab->blk_device << " at boot";
+            LINFO << "Verified verity partition " << entry->blk_device << " at boot";
             verified_at_boot = true;
         }
     }
 
     // assign the new verity block device as the block device
     if (!verified_at_boot) {
-        free(fstab->blk_device);
-        fstab->blk_device = strdup(verity_blk_name.c_str());
+        entry->blk_device = verity_blk_name;
     } else if (!dm.DeleteDevice(mount_point)) {
         LERROR << "Failed to remove verity device " << mount_point.c_str();
         goto out;
     }
 
     // make sure we've set everything up properly
-    if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(entry->blk_device, 1s)) {
         goto out;
     }
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 5dabe76..814ba46 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -50,10 +50,6 @@
     MOUNT_MODE_LATE = 2
 };
 
-// Callback function for verity 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
 #define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
@@ -63,7 +59,8 @@
 #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(fstab* fstab, int mount_mode);
+// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
+int fs_mgr_mount_all(Fstab* fstab, int mount_mode);
 
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
@@ -72,22 +69,26 @@
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
 int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
                     bool need_cp);
+int fs_mgr_do_mount_one(const FstabEntry& entry);
 int fs_mgr_do_mount_one(fstab_rec* rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
 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_verity_state(
+        std::function<void(const std::string& mount_point, int mode)> callback);
+bool fs_mgr_swapon_all(const Fstab& fstab);
+bool fs_mgr_update_logical_partition(FstabEntry* entry);
 bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
 
-int fs_mgr_do_format(fstab_rec* fstab, bool reserve_footer);
+int fs_mgr_do_format(const FstabEntry& entry, bool reserve_footer);
+int fs_mgr_do_format(fstab_rec* rec, 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(fstab_rec* fstab, bool wait_for_verity_dev);
+int fs_mgr_setup_verity(FstabEntry* 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
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 66abfca..f065071 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -43,7 +43,7 @@
 
 // Create block devices for all logical partitions in the given metadata. The
 // metadata must have been read from the current slot.
-bool CreateLogicalPartitions(const LpMetadata& metadata);
+bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& block_device);
 
 // Create block devices for all logical partitions. This is a convenience
 // method for ReadMetadata and CreateLogicalPartitions.
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index deaf4cb..4bf2238 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,14 +21,12 @@
 #include <string>
 #include <vector>
 
-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_mount_all(Fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab* 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);
+bool fs_mgr_overlayfs_is_setup();
 bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
 std::string fs_mgr_get_context(const std::string& mount_point);
 
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index bb40511..0997254 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef __CORE_FS_TAB_H
-#define __CORE_FS_TAB_H
+#pragma once
 
 #include <linux/dm-ioctl.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <sys/types.h>
 
 #include <set>
 #include <string>
+#include <vector>
 
 /*
  * The entries must be kept in the same order as they were seen in the fstab.
@@ -42,21 +43,21 @@
     char* fs_type;
     unsigned long flags;
     char* fs_options;
-    int fs_mgr_flags;
+    uint64_t fs_mgr_flags;
     char* key_loc;
     char* key_dir;
     char* verity_loc;
-    long long length;
+    off64_t length;
     char* label;
     int partnum;
     int swap_prio;
     int max_comp_streams;
-    unsigned int zram_size;
-    uint64_t reserved_size;
-    unsigned int file_contents_mode;
-    unsigned int file_names_mode;
-    unsigned int erase_blk_size;
-    unsigned int logical_blk_size;
+    off64_t zram_size;
+    off64_t reserved_size;
+    int file_contents_mode;
+    int file_names_mode;
+    off64_t erase_blk_size;
+    off64_t logical_blk_size;
     char* sysfs_path;
 };
 
@@ -83,6 +84,7 @@
 int fs_mgr_is_formattable(const struct fstab_rec* fstab);
 int fs_mgr_is_slotselect(const struct fstab_rec* fstab);
 int fs_mgr_is_nofail(const struct fstab_rec* fstab);
+int fs_mgr_is_first_stage_mount(const struct fstab_rec* fstab);
 int fs_mgr_is_latemount(const struct fstab_rec* fstab);
 int fs_mgr_is_quota(const struct fstab_rec* fstab);
 int fs_mgr_is_logical(const struct fstab_rec* fstab);
@@ -94,4 +96,94 @@
 std::string fs_mgr_get_slot_suffix();
 std::set<std::string> fs_mgr_get_boot_devices();
 
-#endif /* __CORE_FS_TAB_H */
+struct FstabEntry {
+    std::string blk_device;
+    std::string logical_partition_name;
+    std::string mount_point;
+    std::string fs_type;
+    unsigned long flags = 0;
+    std::string fs_options;
+    std::string key_loc;
+    std::string key_dir;
+    std::string verity_loc;
+    off64_t length = 0;
+    std::string label;
+    int partnum = -1;
+    int swap_prio = -1;
+    int max_comp_streams = 0;
+    off64_t zram_size = 0;
+    off64_t reserved_size = 0;
+    int file_contents_mode = 0;
+    int file_names_mode = 0;
+    off64_t erase_blk_size = 0;
+    off64_t logical_blk_size = 0;
+    std::string sysfs_path;
+    std::string vbmeta_partition;
+
+    // TODO: Remove this union once fstab_rec is deprecated. It only serves as a
+    // convenient way to convert between fstab_rec::fs_mgr_flags and these bools.
+    union {
+        uint64_t val;
+        struct {
+            // bit 0
+            bool wait : 1;
+            bool check : 1;
+            bool crypt : 1;
+            bool nonremovable : 1;
+            bool vold_managed : 1;
+            bool length : 1;
+            bool recovery_only : 1;
+            bool swap_prio : 1;
+
+            // bit 8
+            bool zram_size : 1;
+            bool verify : 1;
+            bool force_crypt : 1;
+            bool no_emulated_sd : 1;  // No emulated sdcard daemon; sd card is the only external
+                                      // storage.
+            bool no_trim : 1;
+            bool file_encryption : 1;
+            bool formattable : 1;
+            bool slot_select : 1;
+
+            // bit 16
+            bool force_fde_or_fbe : 1;
+            bool late_mount : 1;
+            bool no_fail : 1;
+            bool verify_at_boot : 1;
+            bool max_comp_streams : 1;
+            bool reserved_size : 1;
+            bool quota : 1;
+            bool erase_blk_size : 1;
+
+            // bit 24
+            bool logical_blk_size : 1;
+            bool avb : 1;
+            bool key_directory : 1;
+            bool sysfs : 1;
+            bool logical : 1;
+            bool checkpoint_blk : 1;
+            bool checkpoint_fs : 1;
+            bool first_stage_mount : 1;
+
+            // bit 32
+            bool slot_select_other : 1;
+        };
+    } fs_mgr_flags;
+
+    bool is_encryptable() const {
+        return fs_mgr_flags.crypt || fs_mgr_flags.force_crypt || fs_mgr_flags.force_fde_or_fbe;
+    }
+};
+
+// An Fstab is a collection of FstabEntry structs.
+using Fstab = std::vector<FstabEntry>;
+
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
+bool ReadFstabFromDt(Fstab* fstab);
+bool ReadDefaultFstab(Fstab* fstab);
+
+// Temporary conversion functions.
+FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec);
+Fstab LegacyFstabToFstab(const struct fstab* legacy_fstab);
+fstab* FstabToLegacyFstab(const Fstab& fstab);
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
similarity index 93%
rename from fs_mgr/fs_mgr_avb_ops.cpp
rename to fs_mgr/libfs_avb/avb_ops.cpp
index 18efa22..f56a517 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -22,7 +22,7 @@
  * SOFTWARE.
  */
 
-#include "fs_mgr_priv_avb_ops.h"
+#include "avb_ops.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -37,15 +37,17 @@
 #include <libavb/libavb.h>
 #include <utils/Compat.h>
 
-#include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 
 using namespace std::literals;
 
+namespace android {
+namespace fs_mgr {
+
 static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,
                                        size_t num_bytes, void* buffer, size_t* out_num_read) {
     return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition(
-        partition, offset, num_bytes, buffer, out_num_read);
+            partition, offset, num_bytes, buffer, out_num_read);
 }
 
 static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
@@ -58,9 +60,10 @@
 }
 
 static AvbIOResult dummy_validate_vbmeta_public_key(
-    AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
-    size_t public_key_length ATTRIBUTE_UNUSED, const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
-    size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {
+        AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
+        size_t public_key_length ATTRIBUTE_UNUSED,
+        const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
+        size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {
     // vbmeta public key has been checked in bootloader phase.
     // In user-space, returns true to pass the check.
     //
@@ -178,3 +181,6 @@
     return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
 }
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_priv_avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
similarity index 89%
rename from fs_mgr/fs_mgr_priv_avb_ops.h
rename to fs_mgr/libfs_avb/avb_ops.h
index 44eb1da..e6b33c2 100644
--- a/fs_mgr/fs_mgr_priv_avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -22,15 +22,14 @@
  * SOFTWARE.
  */
 
-#ifndef __CORE_FS_MGR_PRIV_AVB_OPS_H
-#define __CORE_FS_MGR_PRIV_AVB_OPS_H
+#pragma once
 
-#include <map>
 #include <string>
 
 #include <libavb/libavb.h>
 
-#include "fs_mgr.h"
+namespace android {
+namespace fs_mgr {
 
 // This class provides C++ bindings to interact with libavb, a small
 // self-contained piece of code that's intended to be used in bootloaders.
@@ -42,7 +41,7 @@
 //     read and verify the metadata and store it into the out_data parameter.
 //     The caller MUST check the integrity of metadata against the
 //     androidboot.vbmeta.{hash_alg, size, digest} values from /proc/cmdline.
-//     e.g., see class FsManagerAvbVerifier for more details.
+//     e.g., see class AvbVerifier for more details.
 //
 class FsManagerAvbOps {
   public:
@@ -60,6 +59,7 @@
 
   private:
     AvbOps avb_ops_;
-    std::map<std::string, std::string> by_name_symlink_map_;
 };
-#endif /* __CORE_FS_MGR_PRIV_AVB_OPS_H */
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
similarity index 84%
rename from fs_mgr/fs_mgr_avb.cpp
rename to fs_mgr/libfs_avb/fs_avb.cpp
index 6f94d45..89c755e 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "fs_mgr_avb.h"
+#include "fs_avb/fs_avb.h"
 
 #include <fcntl.h>
 #include <libgen.h>
@@ -35,10 +35,12 @@
 #include <libavb/libavb.h>
 #include <libdm/dm.h>
 
-#include "fs_mgr.h"
+#include "avb_ops.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_avb_ops.h"
-#include "fs_mgr_priv_sha.h"
+#include "sha.h"
+
+namespace android {
+namespace fs_mgr {
 
 static inline bool nibble_value(const char& c, uint8_t* value) {
     FS_MGR_CHECK(value != nullptr);
@@ -117,14 +119,14 @@
 //   - androidboot.vbmeta.hash_alg
 //   - androidboot.vbmeta.size
 //   - androidboot.vbmeta.digest
-class FsManagerAvbVerifier {
+class AvbVerifier {
   public:
-    // The factory method to return a unique_ptr<FsManagerAvbVerifier>
-    static std::unique_ptr<FsManagerAvbVerifier> Create();
+    // The factory method to return a unique_ptr<AvbVerifier>
+    static std::unique_ptr<AvbVerifier> Create();
     bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
 
   protected:
-    FsManagerAvbVerifier() = default;
+    AvbVerifier() = default;
 
   private:
     enum HashAlgorithm {
@@ -138,10 +140,10 @@
     size_t vbmeta_size_;
 };
 
-std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
-    std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
+std::unique_ptr<AvbVerifier> AvbVerifier::Create() {
+    std::unique_ptr<AvbVerifier> avb_verifier(new AvbVerifier());
     if (!avb_verifier) {
-        LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
+        LERROR << "Failed to create unique_ptr<AvbVerifier>";
         return nullptr;
     }
 
@@ -184,7 +186,7 @@
     return avb_verifier;
 }
 
-bool FsManagerAvbVerifier::VerifyVbmetaImages(const AvbSlotVerifyData& verify_data) {
+bool AvbVerifier::VerifyVbmetaImages(const AvbSlotVerifyData& verify_data) {
     if (verify_data.num_vbmeta_images == 0) {
         LERROR << "No vbmeta images";
         return false;
@@ -195,10 +197,10 @@
 
     if (hash_alg_ == kSHA256) {
         std::tie(total_size, digest_matched) =
-            verify_vbmeta_digest<SHA256Hasher>(verify_data, digest_);
+                verify_vbmeta_digest<SHA256Hasher>(verify_data, digest_);
     } else if (hash_alg_ == kSHA512) {
         std::tie(total_size, digest_matched) =
-            verify_vbmeta_digest<SHA512Hasher>(verify_data, digest_);
+                verify_vbmeta_digest<SHA512Hasher>(verify_data, digest_);
     }
 
     if (total_size != vbmeta_size_) {
@@ -263,19 +265,20 @@
     return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
 }
 
-static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
+static bool hashtree_dm_verity_setup(FstabEntry* fstab_entry,
                                      const AvbHashtreeDescriptor& hashtree_desc,
                                      const std::string& salt, const std::string& root_digest,
                                      bool wait_for_verity_dev) {
     android::dm::DmTable table;
-    if (!construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
+    if (!construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device,
+                                &table) ||
         !table.valid()) {
         LERROR << "Failed to construct verity table.";
         return false;
     }
     table.set_readonly(true);
 
-    const std::string mount_point(basename(fstab_entry->mount_point));
+    const std::string mount_point(basename(fstab_entry->mount_point.c_str()));
     android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
     if (!dm.CreateDevice(mount_point, table)) {
         LERROR << "Couldn't create verity device!";
@@ -292,8 +295,7 @@
     fs_mgr_set_blk_ro(fstab_entry->blk_device);
 
     // Updates fstab_rec->blk_device to verity device name.
-    free(fstab_entry->blk_device);
-    fstab_entry->blk_device = strdup(dev_path.c_str());
+    fstab_entry->blk_device = dev_path;
 
     // Makes sure we've set everything up properly.
     if (wait_for_verity_dev && !fs_mgr_wait_for_file(dev_path, 1s)) {
@@ -314,9 +316,9 @@
         // Get descriptors from vbmeta_images[i].
         size_t num_descriptors;
         std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
-            avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
-                                   verify_data.vbmeta_images[i].vbmeta_size, &num_descriptors),
-            avb_free);
+                avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
+                                       verify_data.vbmeta_images[i].vbmeta_size, &num_descriptors),
+                avb_free);
 
         if (!descriptors || num_descriptors < 1) {
             continue;
@@ -329,9 +331,10 @@
                 continue;
             }
             if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
-                desc_partition_name = (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
+                desc_partition_name =
+                        (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
                 if (!avb_hashtree_descriptor_validate_and_byteswap(
-                        (AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
+                            (AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
                     continue;
                 }
                 if (out_hashtree_desc->partition_name_len != partition_name.length()) {
@@ -361,12 +364,12 @@
     return true;
 }
 
-FsManagerAvbUniquePtr FsManagerAvbHandle::Open() {
+AvbUniquePtr AvbHandle::Open() {
     bool is_device_unlocked = fs_mgr_is_device_unlocked();
 
-    FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
+    AvbUniquePtr avb_handle(new AvbHandle());
     if (!avb_handle) {
-        LERROR << "Failed to allocate FsManagerAvbHandle";
+        LERROR << "Failed to allocate AvbHandle";
         return nullptr;
     }
 
@@ -406,7 +409,7 @@
 
     // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
     avb_handle->avb_version_ =
-        android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+            android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
 
     // Checks whether FLAGS_VERIFICATION_DISABLED is set:
     //   - Only the top-level vbmeta struct is read.
@@ -414,18 +417,18 @@
     //     and AVB HASHTREE descriptor(s).
     AvbVBMetaImageHeader vbmeta_header;
     avb_vbmeta_image_header_to_host_byte_order(
-        (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
-        &vbmeta_header);
-    bool verification_disabled =
-        ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+            (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
+            &vbmeta_header);
+    bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+                                  AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
 
     if (verification_disabled) {
         avb_handle->status_ = kAvbHandleVerificationDisabled;
     } else {
         // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
-        std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
+        std::unique_ptr<AvbVerifier> avb_verifier = AvbVerifier::Create();
         if (!avb_verifier) {
-            LERROR << "Failed to create FsManagerAvbVerifier";
+            LERROR << "Failed to create AvbVerifier";
             return nullptr;
         }
         if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
@@ -434,8 +437,8 @@
         }
 
         // Checks whether FLAGS_HASHTREE_DISABLED is set.
-        bool hashtree_disabled =
-            ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+        bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+                                  AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
         if (hashtree_disabled) {
             avb_handle->status_ = kAvbHandleHashtreeDisabled;
         }
@@ -445,28 +448,27 @@
     return avb_handle;
 }
 
-SetUpAvbHashtreeResult FsManagerAvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
-                                                            bool wait_for_verity_dev) {
+AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
     if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
         avb_slot_data_->num_vbmeta_images < 1) {
-        return SetUpAvbHashtreeResult::kFail;
+        return AvbHashtreeResult::kFail;
     }
 
     if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
         LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
-        return SetUpAvbHashtreeResult::kDisabled;
+        return AvbHashtreeResult::kDisabled;
     }
 
     // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
     // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
     std::string partition_name;
-    if (fstab_entry->fs_mgr_flags & MF_LOGICAL) {
+    if (fstab_entry->fs_mgr_flags.logical) {
         partition_name = fstab_entry->logical_partition_name;
     } else {
-        partition_name = basename(fstab_entry->blk_device);
+        partition_name = basename(fstab_entry->blk_device.c_str());
     }
 
-    if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
+    if (fstab_entry->fs_mgr_flags.slot_select) {
         auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
         if (ab_suffix != std::string::npos) {
             partition_name.erase(ab_suffix);
@@ -478,14 +480,17 @@
     std::string root_digest;
     if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
                                  &root_digest)) {
-        return SetUpAvbHashtreeResult::kFail;
+        return AvbHashtreeResult::kFail;
     }
 
     // Converts HASHTREE descriptor to verity_table_params.
     if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
                                   wait_for_verity_dev)) {
-        return SetUpAvbHashtreeResult::kFail;
+        return AvbHashtreeResult::kFail;
     }
 
-    return SetUpAvbHashtreeResult::kSuccess;
+    return AvbHashtreeResult::kSuccess;
 }
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
similarity index 75%
rename from fs_mgr/include/fs_mgr_avb.h
rename to fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index bb55a14..08bdbdc 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef __CORE_FS_MGR_AVB_H
-#define __CORE_FS_MGR_AVB_H
+#pragma once
 
-#include <map>
 #include <memory>
 #include <string>
 
+#include <fstab/fstab.h>
 #include <libavb/libavb.h>
 
-#include "fs_mgr.h"
+namespace android {
+namespace fs_mgr {
 
-enum class SetUpAvbHashtreeResult {
+enum class AvbHashtreeResult {
     kSuccess = 0,
     kFail,
     kDisabled,
@@ -33,17 +33,15 @@
 
 class FsManagerAvbOps;
 
-class FsManagerAvbHandle;
-using FsManagerAvbUniquePtr = std::unique_ptr<FsManagerAvbHandle>;
-
-using ByNameSymlinkMap = std::map<std::string, std::string>;
+class AvbHandle;
+using AvbUniquePtr = std::unique_ptr<AvbHandle>;
 
 // Provides a factory method to return a unique_ptr pointing to itself and the
 // SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
 // descriptors to load verity table into kernel through ioctl.
-class FsManagerAvbHandle {
+class AvbHandle {
   public:
-    // The factory method to return a FsManagerAvbUniquePtr that holds
+    // The factory method to return a AvbUniquePtr that holds
     // the verified AVB (external/avb) metadata of all verified partitions
     // in avb_slot_data_.vbmeta_images[].
     //
@@ -51,7 +49,7 @@
     //   - androidboot.vbmeta.{hash_alg, size, digest}.
     //
     // A typical usage will be:
-    //   - FsManagerAvbUniquePtr handle = FsManagerAvbHandle::Open();
+    //   - AvbUniquePtr handle = AvbHandle::Open();
     //
     // Possible return values:
     //   - nullptr: any error when reading and verifying the metadata,
@@ -75,7 +73,7 @@
     //   - a valid unique_ptr with status kAvbHandleSuccess: the metadata
     //     is verified and can be trusted.
     //
-    static FsManagerAvbUniquePtr Open();
+    static AvbUniquePtr Open();
 
     // Sets up dm-verity on the given fstab entry.
     // The 'wait_for_verity_dev' parameter makes this function wait for the
@@ -87,17 +85,17 @@
     //     failed to get the HASHTREE descriptor, runtime error when set up
     //     device-mapper, etc.
     //   - kDisabled: hashtree is disabled.
-    SetUpAvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
+    AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev);
 
     const std::string& avb_version() const { return avb_version_; }
 
-    FsManagerAvbHandle(const FsManagerAvbHandle&) = delete;             // no copy
-    FsManagerAvbHandle& operator=(const FsManagerAvbHandle&) = delete;  // no assignment
+    AvbHandle(const AvbHandle&) = delete;             // no copy
+    AvbHandle& operator=(const AvbHandle&) = delete;  // no assignment
 
-    FsManagerAvbHandle(FsManagerAvbHandle&&) noexcept = delete;             // no move
-    FsManagerAvbHandle& operator=(FsManagerAvbHandle&&) noexcept = delete;  // no move assignment
+    AvbHandle(AvbHandle&&) noexcept = delete;             // no move
+    AvbHandle& operator=(AvbHandle&&) noexcept = delete;  // no move assignment
 
-    ~FsManagerAvbHandle() {
+    ~AvbHandle() {
         if (avb_slot_data_) {
             avb_slot_verify_data_free(avb_slot_data_);
         }
@@ -112,11 +110,12 @@
         kAvbHandleVerificationError,
     };
 
-    FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
+    AvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
 
     AvbSlotVerifyData* avb_slot_data_;
     AvbHandleStatus status_;
     std::string avb_version_;
 };
 
-#endif /* __CORE_FS_MGR_AVB_H */
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_priv_sha.h b/fs_mgr/libfs_avb/sha.h
similarity index 92%
rename from fs_mgr/fs_mgr_priv_sha.h
rename to fs_mgr/libfs_avb/sha.h
index 5b53eea..2d3ca6d 100644
--- a/fs_mgr/fs_mgr_priv_sha.h
+++ b/fs_mgr/libfs_avb/sha.h
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef __CORE_FS_MGR_PRIV_SHA_H
-#define __CORE_FS_MGR_PRIV_SHA_H
+#pragma once
 
 #include <openssl/sha.h>
 
+namespace android {
+namespace fs_mgr {
+
 class SHA256Hasher {
   private:
     SHA256_CTX sha256_ctx;
@@ -59,4 +61,5 @@
     }
 };
 
-#endif /* __CORE_FS_MGR_PRIV_SHA_H */
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index da86d75..07f9d66 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -578,6 +578,12 @@
     CHECK_NE(sectors_per_block, 0);
     CHECK(sectors_needed % sectors_per_block == 0);
 
+    if (IsABDevice() && !IsRetrofitDevice() && GetPartitionSlotSuffix(partition->name()) == "_b") {
+        // Allocate "a" partitions top-down and "b" partitions bottom-up, to
+        // minimize fragmentation during OTA.
+        free_regions = PrioritizeSecondHalfOfSuper(free_regions);
+    }
+
     // Find gaps that we can use for new extents. Note we store new extents in a
     // temporary vector, and only commit them if we are guaranteed enough free
     // space.
@@ -600,14 +606,23 @@
         }
 
         uint64_t sectors = std::min(sectors_needed, region.length());
+        if (sectors < region.length()) {
+            const auto& block_device = block_devices_[region.device_index];
+            if (block_device.alignment) {
+                const uint64_t alignment = block_device.alignment / LP_SECTOR_SIZE;
+                sectors = AlignTo(sectors, alignment);
+                sectors = std::min(sectors, region.length());
+            }
+        }
         CHECK(sectors % sectors_per_block == 0);
 
         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) {
+        if (sectors >= sectors_needed) {
+            sectors_needed = 0;
             break;
         }
+        sectors_needed -= sectors;
     }
     if (sectors_needed) {
         LERROR << "Not enough free space to expand partition: " << partition->name();
@@ -621,6 +636,40 @@
     return true;
 }
 
+std::vector<MetadataBuilder::Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
+        const std::vector<Interval>& free_list) {
+    const auto& super = block_devices_[0];
+    uint64_t first_sector = super.first_logical_sector;
+    uint64_t last_sector = super.size / LP_SECTOR_SIZE;
+    uint64_t midpoint = first_sector + (last_sector - first_sector) / 2;
+
+    // Choose an aligned sector for the midpoint. This could lead to one half
+    // being slightly larger than the other, but this will not restrict the
+    // size of partitions (it might lead to one extra extent if "B" overflows).
+    midpoint = AlignSector(super, midpoint);
+
+    std::vector<Interval> first_half;
+    std::vector<Interval> second_half;
+    for (const auto& region : free_list) {
+        // Note: deprioritze if not the main super partition. Even though we
+        // don't call this for retrofit devices, we will allow adding additional
+        // block devices on non-retrofit devices.
+        if (region.device_index != 0 || region.end <= midpoint) {
+            first_half.emplace_back(region);
+            continue;
+        }
+        if (region.start < midpoint && region.end > midpoint) {
+            // Split this into two regions.
+            first_half.emplace_back(region.device_index, region.start, midpoint);
+            second_half.emplace_back(region.device_index, midpoint, region.end);
+        } else {
+            second_half.emplace_back(region);
+        }
+    }
+    second_half.insert(second_half.end(), first_half.begin(), first_half.end());
+    return second_half;
+}
+
 void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
     partition->ShrinkTo(aligned_size);
 }
@@ -930,5 +979,9 @@
     return android::base::GetBoolProperty("ro.build.ab_update", false);
 }
 
+bool MetadataBuilder::IsRetrofitDevice() const {
+    return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 926fe12..3793964 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -209,8 +209,8 @@
         ASSERT_TRUE(builder->ResizePartition(a, a->size() + 4096));
         ASSERT_TRUE(builder->ResizePartition(b, b->size() + 4096));
     }
-    EXPECT_EQ(a->size(), 40960);
-    EXPECT_EQ(b->size(), 40960);
+    EXPECT_EQ(a->size(), 7864320);
+    EXPECT_EQ(b->size(), 7864320);
 
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
@@ -218,7 +218,7 @@
     // Check that each starting sector is aligned.
     for (const auto& extent : exported->extents) {
         ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
-        EXPECT_EQ(extent.num_sectors, 8);
+        EXPECT_EQ(extent.num_sectors, 1536);
 
         uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
         uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
@@ -645,7 +645,7 @@
     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].num_sectors, 129600);
     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);
@@ -720,3 +720,65 @@
     ASSERT_EQ(builder->AddPartition("system", 0), nullptr);
     ASSERT_NE(builder->AddPartition("system_a", 0), nullptr);
 }
+
+TEST_F(BuilderTest, ABExtents) {
+    BlockDeviceInfo device_info("super", 10_GiB, 768 * 1024, 0, 4096);
+
+    // A and B slots should be allocated from separate halves of the partition,
+    // to mitigate allocating too many extents. (b/120433288)
+    MetadataBuilder::OverrideABForTesting(true);
+    auto builder = MetadataBuilder::New(device_info, 65536, 2);
+    ASSERT_NE(builder, nullptr);
+    Partition* system_a = builder->AddPartition("system_a", 0);
+    ASSERT_NE(system_a, nullptr);
+    Partition* system_b = builder->AddPartition("system_b", 0);
+    ASSERT_NE(system_b, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(system_a, 2_GiB));
+    ASSERT_TRUE(builder->ResizePartition(system_b, 2_GiB));
+
+    builder->RemovePartition("system_a");
+    system_a = builder->AddPartition("system_a", 0);
+    ASSERT_NE(system_a, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(system_a, 3_GiB));
+
+    EXPECT_EQ(system_a->extents().size(), static_cast<size_t>(1));
+    EXPECT_EQ(system_b->extents().size(), static_cast<size_t>(1));
+    ASSERT_TRUE(builder->ResizePartition(system_b, 6_GiB));
+    EXPECT_EQ(system_b->extents().size(), static_cast<size_t>(2));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_EQ(exported->extents.size(), static_cast<size_t>(3));
+    EXPECT_EQ(exported->extents[0].target_data, 10487808);
+    EXPECT_EQ(exported->extents[0].num_sectors, 10483712);
+    EXPECT_EQ(exported->extents[1].target_data, 6292992);
+    EXPECT_EQ(exported->extents[1].num_sectors, 2099712);
+    EXPECT_EQ(exported->extents[2].target_data, 1536);
+    EXPECT_EQ(exported->extents[2].num_sectors, 6291456);
+}
+
+TEST_F(BuilderTest, PartialExtents) {
+    // super has a minimum extent size of 768KiB.
+    BlockDeviceInfo device_info("super", 1_GiB, 768 * 1024, 0, 4096);
+    auto builder = MetadataBuilder::New(device_info, 65536, 1);
+    ASSERT_NE(builder, nullptr);
+    Partition* system = builder->AddPartition("system", 0);
+    ASSERT_NE(system, nullptr);
+    Partition* vendor = builder->AddPartition("vendor", 0);
+    ASSERT_NE(vendor, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment + 4096));
+    ASSERT_TRUE(builder->ResizePartition(vendor, device_info.alignment));
+    ASSERT_EQ(system->size(), device_info.alignment * 2);
+    ASSERT_EQ(vendor->size(), device_info.alignment);
+
+    ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment * 2));
+    ASSERT_EQ(system->extents().size(), static_cast<size_t>(1));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_EQ(exported->extents.size(), static_cast<size_t>(2));
+    EXPECT_EQ(exported->extents[0].target_data, 1536);
+    EXPECT_EQ(exported->extents[0].num_sectors, 3072);
+    EXPECT_EQ(exported->extents[1].target_data, 4608);
+    EXPECT_EQ(exported->extents[1].num_sectors, 1536);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 4bb38d6..f477b4b 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -275,6 +275,7 @@
                        const LpMetadataPartition& source);
     bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
     bool IsABDevice() const;
+    bool IsRetrofitDevice() const;
 
     struct Interval {
         uint32_t device_index;
@@ -294,6 +295,7 @@
     std::vector<Interval> GetFreeRegions() const;
     void ExtentsToFreeList(const std::vector<Interval>& extents,
                            std::vector<Interval>* free_regions) const;
+    std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list);
 
     static bool sABOverrideValue;
     static bool sABOverrideSet;
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index d8195ca..454258b 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -84,7 +84,6 @@
 // with potentially invalid metadata, or random partition data with metadata.
 static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
                                          const std::string& slot_suffix, std::string* blob) {
-    const LpMetadataHeader& header = metadata.header;
     const LpMetadataGeometry& geometry = metadata.geometry;
 
     *blob = SerializeMetadata(metadata);
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index d9e017c..9e211e3 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -27,6 +27,7 @@
 GREEN="${ESCAPE}[38;5;40m"
 RED="${ESCAPE}[38;5;196m"
 ORANGE="${ESCAPE}[38;5;255:165:0m"
+BLUE="${ESCAPE}[35m"
 NORMAL="${ESCAPE}[0m"
 
 # Helper functions
@@ -35,17 +36,28 @@
 
 Returns: true if device is in fastboot mode" ]
 inFastboot() {
-  fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+  fastboot devices |
+    if [ -n "${ANDROID_SERIAL}" ]; then
+      grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+    else
+      wc -l | grep '^1$' >/dev/null
+    fi
 }
 
 [ "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
+  adb devices |
+    grep -v 'List of devices attached' |
+    if [ -n "${ANDROID_SERIAL}" ]; then
+      grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+    else
+      wc -l | grep '^1$' >/dev/null
+    fi
 }
 
-[ "USAGE: adb_sh <commands>
+[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
 
 Returns: true if the command succeeded" ]
 adb_sh() {
@@ -80,12 +92,12 @@
 
 Returns: true if device is (likely) a debug build" ]
 isDebuggable() {
-  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
+  if inAdb && [ 1 -ne "`get_property ro.debuggable`" ]; then
     false
   fi
 }
 
-[ "USAGE: adb_su <commands>
+[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
 
 Returns: true if the command running as root succeeded" ]
 adb_su() {
@@ -138,6 +150,8 @@
 
 [ "USAGE: adb_root
 
+NB: This can be flakey on devices due to USB state
+
 Returns: true if device in root state" ]
 adb_root() {
   adb root >/dev/null </dev/null 2>/dev/null
@@ -159,6 +173,9 @@
     false
     return
   fi
+  if [ "${O}" != "${O#*FAILED}" ]; then
+    O="${1}: <empty>"
+  fi
   if [ -n "${2}" -a "${1}: ${2}" != "${O}" ]; then
     echo "${2} != ${O}" >&2
     false
@@ -167,14 +184,21 @@
   echo ${O} >&2
 }
 
-[ "USAGE: die [-t <epoch>] [message] >/dev/stderr
+[ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
 
-If -t <epoch> argument is supplied, dump logcat.
+If -d, or -t <epoch> argument is supplied, dump logcat.
 
 Returns: exit failure, report status" ]
 die() {
-  if [ X"-t" = X"${1}" -a -n "${2}" ]; then
-    adb_logcat -b all -v nsec -t ${2} >&2
+  if [ X"-d" = X"${1}" ]; then
+    adb_logcat -b all -v nsec -d >&2
+    shift
+  elif [ X"-t" = X"${1}" ]; then
+    if [ -n "${2}" ]; then
+      adb_logcat -b all -v nsec -t ${2} >&2
+    else
+      adb_logcat -b all -v nsec -d >&2
+    fi
     shift 2
   fi
   echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
@@ -238,15 +262,20 @@
     die "${@}"
 }
 
-[ "USAGE: skip_administrative_mounts < /proc/mounts
+[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
 
 Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
 skip_administrative_mounts() {
+  if [ "data" = "${1}" ]; then
+    grep -v " /data "
+  else
+    cat -
+  fi |
   grep -v \
     -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\) " \
     -e "^\(bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
     -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-    -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\|data\) "
+    -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\) "
 }
 
 if [ X"-s" = X"${1}" -a -n "${2}" ]; then
@@ -259,24 +288,59 @@
   echo "${ORANGE}[  WARNING ]${NORMAL} device not in adb mode ... waiting 2 minutes"
   adb_wait 2m
 fi
-inAdb || die "device not in adb mode"
+inAdb || die "specified device not in adb mode"
 isDebuggable || die "device not a debug build"
+enforcing=true
+if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} device does not have sepolicy in enforcing mode"
+  enforcing=false
+fi
 
 # Do something
+
+echo "${GREEN}[ RUN      ]${NORMAL} Testing kernel support for overlayfs" >&2
+
+overlayfs_supported=true;
 adb_wait || die "wait for device failed"
 adb_sh ls -d /sys/module/overlay </dev/null >/dev/null &&
   echo "${GREEN}[       OK ]${NORMAL} overlay module present" >&2 ||
-  die "overlay module not present"
-adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
-  echo "${GREEN}[       OK ]${NORMAL} overlay module supports override_creds" >&2 ||
-  die "overlay module can not be used on ANDROID"
+  (
+    echo "${ORANGE}[  WARNING ]${NORMAL} overlay module not present" >&2 &&
+      false
+  ) ||
+  overlayfs_supported=false
+if ${overlayfs_supported}; then
+  case `adb_sh uname -r </dev/null` in
+    4.[6789].* | 4.[1-9][0-9]* | [56789].*)
+      adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
+        echo "${GREEN}[       OK ]${NORMAL} overlay module supports override_creds" >&2 ||
+        (
+          echo "${ORANGE}[  WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
+          false
+        ) ||
+        overlayfs_supported=false;
+      ;;
+    *)
+      echo "${GREEN}[       OK ]${NORMAL} overlay module uses callers creds" >&2
+      ;;
+  esac
+fi
+
 adb_root ||
   die "initial setup"
+
+echo "${GREEN}[ RUN      ]${NORMAL} Checking current overlayfs status" >&2
+
+# We can not universally use adb enable-verity to ensure device is
+# in a overlayfs disabled state since it can prevent reboot on
+# devices that remount the physical content rather than overlayfs.
+# So lets do our best to surgically wipe the overlayfs state without
+# having to go through enable-verity transition.
 reboot=false
 OVERLAYFS_BACKING="cache mnt/scratch"
 for d in ${OVERLAYFS_BACKING}; do
   if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
-    echo "${ORANGE}[  WARNING ]${NORMAL} /${d}/overlay is setup, wiping" >&2
+    echo "${ORANGE}[  WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
     adb_sh rm -rf /${d}/overlay </dev/null ||
       die "/${d}/overlay wipe"
     reboot=true
@@ -285,9 +349,10 @@
 if ${reboot}; then
   echo "${ORANGE}[  WARNING ]${NORMAL} rebooting before test" >&2
   adb_reboot &&
-    adb_wait 2m &&
-    adb_root ||
-    die "reboot after wipe"
+    adb_wait 2m ||
+    die "lost device after reboot after wipe"
+  adb_root ||
+    die "lost device after elevation to root after wipe"
 fi
 D=`adb_sh df -k </dev/null` &&
   H=`echo "${D}" | head -1` &&
@@ -296,126 +361,271 @@
   echo "${D}" &&
   echo "${ORANGE}[  WARNING ]${NORMAL} overlays present before setup" >&2 ||
   echo "${GREEN}[       OK ]${NORMAL} no overlay present before setup" >&2
-adb_sh df -k `adb_sh cat /proc/mounts |
-                skip_administrative_mounts |
-                cut -s -d' ' -f1`
+overlayfs_needed=true
+D=`adb_sh cat /proc/mounts </dev/null |
+   skip_administrative_mounts data |
+   cut -s -d' ' -f1`
+D=`adb_sh df -k ${D} </dev/null`
+echo "${D}"
+if [ X"${D}" = X"${D##* 100[%] }" ]; then
+  overlayfs_needed=false
+elif ! ${overlayfs_supported}; then
+  die "need overlayfs, but do not have it"
+fi
+
+echo "${GREEN}[ RUN      ]${NORMAL} disable verity" >&2
 
 T=`adb_date`
-D=`adb disable-verity 2>&1`
+H=`adb disable-verity 2>&1`
 err=${?}
-echo "${D}"
-if [ ${err} != 0 -o X"${D}" != X"${D##*setup failed}" ]; then
-  die -t ${T} "setup for overlay"
-fi
+L=
+D="${H%?Now reboot your device for settings to take effect}"
 if [ X"${D}" != X"${D##*using overlayfs}" ]; then
   echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
 fi
-adb_reboot &&
-  adb_wait &&
-  D=`adb_sh df -k </dev/null` &&
-  H=`echo "${D}" | head -1` &&
-  D=`echo "${D}" | grep "^overlay "` &&
-  echo "${H}" &&
-  echo "${D}" ||
-  die "overlay takeover failed"
-echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
-  echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
-
-T=`adb_date`
-adb_root &&
-  adb remount &&
-  D=`adb_sh df -k </dev/null` ||
-  die -t ${T} "can not collect filesystem data"
-if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
-  echo "${ORANGE}[     INFO ]${NORMAL} using scratch dynamic partition for overrides" >&2
-  H=`adb_sh cat /proc/mounts | sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
-  [ -n "${H}" ] &&
-    echo "${ORANGE}[     INFO ]${NORMAL} scratch filesystem ${H}"
+if [ ${err} != 0 ]; then
+  echo "${H}"
+  ( [ -n "${L}" ] && echo "${L}" && false ) ||
+  die -t "${T}" "disable-verity"
 fi
-for d in ${OVERLAYFS_BACKING}; do
-  if adb_sh ls -d /${d}/overlay/system/upper </dev/null >/dev/null 2>/dev/null; then
-    echo "${ORANGE}[     INFO ]${NORMAL} /${d}/overlay is setup" >&2
+rebooted=false
+if [ X"${D}" != X"${H}" ]; then
+  echo "${H}"
+  if [ X"${D}" != X"${D##*setup failed}" ]; then
+    echo "${ORANGE}[  WARNING ]${NORMAL} overlayfs setup whined" >&2
   fi
-done
+  D=`adb_sh df -k </dev/null` &&
+    H=`echo "${D}" | head -1` &&
+    D=`echo "${D}" | grep "^overlay " || true` &&
+    [ -z "${D}" ] ||
+    ( echo "${H}" && echo "${D}" && false ) ||
+    die -t ${T} "overlay takeover unexpected at this phase"
+  echo "${GREEN}[     INFO ]${NORMAL} rebooting as requested" >&2
+  L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
+  adb_reboot &&
+    adb_wait 2m ||
+    die "lost device after reboot requested"
+  adb_root ||
+    die "lost device after elevation to root"
+  rebooted=true
+  # re-disable verity to see the setup remarks expected
+  T=`adb_date`
+  H=`adb disable-verity 2>&1`
+  err=${?}
+  D="${H%?Now reboot your device for settings to take effect}"
+  if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+    echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
+  fi
+  if [ ${err} != 0 ]; then
+    T=
+  fi
+fi
+if ${overlayfs_supported} && ${overlayfs_needed} && [ X"${D}" != X"${D##*setup failed}" ]; then
+  echo "${D}"
+  ( [ -n "${L}" ] && echo "${L}" && false ) ||
+  die -t "${T}" "setup for overlay"
+fi
+if [ X"${D}" != X"${D##*Successfully disabled verity}" ]; then
+  echo "${H}"
+  D=`adb_sh df -k </dev/null` &&
+    H=`echo "${D}" | head -1` &&
+    D=`echo "${D}" | grep "^overlay " || true` &&
+    [ -z "${D}" ] ||
+    ( echo "${H}" && echo "${D}" && false ) ||
+    ( [ -n "${L}" ] && echo "${L}" && false ) ||
+    die -t "${T}" "overlay takeover unexpected"
+  [ -n "${L}" ] && echo "${L}"
+  die -t "${T}" "unexpected report of verity being disabled a second time"
+elif ${rebooted}; then
+  echo "${GREEN}[       OK ]${NORMAL} verity already disabled" >&2
+else
+  echo "${ORANGE}[  WARNING ]${NORMAL} verity already disabled" >&2
+fi
 
-H=`echo "${D}" | head -1` &&
-  D=`echo "${D}" | grep "^overlay "` &&
-  echo "${H}" &&
-  echo "${D}" &&
+echo "${GREEN}[ RUN      ]${NORMAL} remount" >&2
+
+adb remount ||
+  ( [ -n "${L}" ] && echo "${L}" && false ) ||
+  die -t "${T}" "adb remount failed"
+D=`adb_sh df -k </dev/null` &&
+  H=`echo "${D}" | head -1` &&
+  D=`echo "${D}" | grep "^overlay "` ||
+  ( [ -n "${L}" ] && echo "${L}" && false )
+ret=${?}
+uses_dynamic_scratch=false
+scratch_partition=
+if ${overlayfs_needed}; then
+  if [ ${ret} != 0 ]; then
+    die -t ${T} "overlay takeover failed"
+  fi
   echo "${D}" | 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 (ro)"
+   echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover not complete" >&2
+  scratch_partition=scratch
+  if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
+    echo "${BLUE}[     INFO ]${NORMAL} using ${scratch_partition} dynamic partition for overrides" >&2
+  fi
+  M=`adb_sh cat /proc/mounts </dev/null |
+     sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
+  [ -n "${M}" ] &&
+    echo "${BLUE}[     INFO ]${NORMAL} scratch filesystem ${M}"
+  uses_dynamic_scratch=true
+  if [ "${M}" != "${M##*/dev/block/by-name/}" ]; then
+    uses_dynamic_scratch=false
+    scratch_partition="${M##*/dev/block/by-name/}"
+  fi
+  scratch_size=`adb_sh df -k /mnt/scratch </dev/null 2>/dev/null |
+                while read device kblocks used available use mounted on; do
+                  if [ "/mnt/scratch" = "\${mounted}" ]; then
+                    echo \${kblocks}
+                  fi
+                done` &&
+    [ -n "${scratch_size}" ] ||
+    die "scratch size"
+  echo "${BLUE}[     INFO ]${NORMAL} scratch size ${scratch_size}KB" >&2
+  for d in ${OVERLAYFS_BACKING}; do
+    if adb_sh ls -d /${d}/overlay/system/upper </dev/null >/dev/null 2>/dev/null; then
+      echo "${BLUE}[     INFO ]${NORMAL} /${d}/overlay is setup" >&2
+    fi
+  done
 
-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
+  echo "${H}" &&
+    echo "${D}" &&
+    echo "${D}" | 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 data) ||
+    die "remount overlayfs missed a spot (ro)"
+else
+  if [ ${ret} = 0 ]; then
+    die -t ${T} "unexpected overlay takeover"
+  fi
+fi
 
 # Check something
+
+echo "${GREEN}[ RUN      ]${NORMAL} push content to /system and /vendor" >&2
+
 A="Hello World! $(date)"
 echo "${A}" | adb_sh "cat - > /system/hello"
 echo "${A}" | adb_sh "cat - > /vendor/hello"
 B="`adb_cat /system/hello`" ||
   die "sytem hello"
-check_eq "${A}" "${B}" system before reboot
+check_eq "${A}" "${B}" /system before reboot
 B="`adb_cat /vendor/hello`" ||
   die "vendor hello"
-check_eq "${A}" "${B}" vendor before reboot
+check_eq "${A}" "${B}" /vendor before reboot
+
+echo "${GREEN}[ RUN      ]${NORMAL} reboot to confirm content persistent" >&2
+
 adb_reboot &&
-  adb_wait &&
-  B="`adb_cat /system/hello`" ||
-  die "re-read system hello after reboot"
-check_eq "${A}" "${B}" system after reboot
+  adb_wait 2m ||
+  die "reboot after override content added failed"
+
+if ${overlayfs_needed}; then
+  D=`adb_su df -k </dev/null` &&
+    H=`echo "${D}" | head -1` &&
+    D=`echo "${D}" | grep "^overlay "` ||
+    ( echo "${L}" && false ) ||
+    die -d "overlay takeover failed after reboot"
+
+  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
+fi
+
+B="`adb_cat /system/hello`" ||
+  die "re-read /system/hello after reboot"
+check_eq "${A}" "${B}" /system after reboot
+echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
 # 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
+if ${enforcing}; then
+  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
+  echo "${GREEN}[       OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
+fi
 adb_root &&
   B="`adb_cat /vendor/hello`" ||
-  die "re-read vendor hello after reboot"
+  die "re-read /vendor/hello after reboot"
 check_eq "${A}" "${B}" vendor after reboot
+echo "${GREEN}[       OK ]${NORMAL} /vendor content remains after reboot" >&2
 
-adb reboot-fastboot &&
-  fastboot_wait 2m &&
-  fastboot flash vendor ||
-  die "fastbootd flash vendor"
-# check scratch via fastboot
-fastboot_getvar partition-type:scratch raw &&
-  fastboot_getvar has-slot:scratch no &&
-  fastboot_getvar is-logical:scratch yes ||
-  die "fastboot can not see scratch parameters"
-echo "${ORANGE}[     INFO ]${NORMAL} expect fastboot erase scratch to fail" >&2
-fastboot erase scratch &&
-  die "fastbootd can erase scratch"
-echo "${ORANGE}[     INFO ]${NORMAL} expect fastboot format scratch to fail" >&2
-fastboot format scratch &&
-  die "fastbootd can format scratch"
-fastboot reboot ||
-  die "can not reboot out of fastbootd"
-echo "${ORANGE}[  WARNING ]${NORMAL} adb after fastboot ... waiting 2 minutes"
-adb_wait 2m ||
-  die "did not reboot after flash"
-adb_root &&
-  D=`adb_sh df -k </dev/null` &&
-  H=`echo "${D}" | head -1` &&
-  D=`echo "${D}" | grep "^overlay "` &&
-  echo "${H}" &&
-  echo "${D}" &&
-  echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
-  die  "overlay system takeover after flash vendor"
-echo "${D}" | 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 ||
-  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
+echo "${GREEN}[ RUN      ]${NORMAL} flash vendor, confirm its content disappears" >&2
+
+H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
+if [ -z "${ANDROID_PRODUCT_OUT}" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} build tree not setup, skipping"
+elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} vendor image missing, skipping"
+elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} wrong vendor image, skipping"
+else
+  adb reboot-fastboot &&
+    fastboot_wait 2m &&
+    fastboot flash vendor ||
+    ( fastboot reboot && false) ||
+    die "fastboot flash vendor"
+  if [ -n "${scratch_paritition}" ]; then
+    fastboot_getvar partition-type:${scratch_partition} raw ||
+      ( fastboot reboot && false) ||
+      die "fastboot can not see ${scratch_partition} parameters"
+    if ${uses_dynamic_scratch}; then
+      # check ${scratch_partition} via fastboot
+      fastboot_getvar has-slot:${scratch_partition} no &&
+        fastboot_getvar is-logical:${scratch_partition} yes ||
+        ( fastboot reboot && false) ||
+        die "fastboot can not see ${scratch_partition} parameters"
+    else
+      fastboot_getvar is-logical:${scratch_partition} no ||
+        ( fastboot reboot && false) ||
+        die "fastboot can not see ${scratch_partition} parameters"
+    fi
+    if ! ${uses_dynamic_scratch}; then
+      fastboot reboot-bootloader ||
+        die "Reboot into fastboot"
+    fi
+    if ${uses_dynamic_scratch}; then
+      echo "${BLUE}[     INFO ]${NORMAL} expect fastboot erase ${scratch_partition} to fail" >&2
+      fastboot erase ${scratch_partition} &&
+        ( fastboot reboot || true) &&
+        die "fastboot can erase ${scratch_partition}"
+    fi
+    echo "${BLUE}[     INFO ]${NORMAL} expect fastboot format ${scratch_partition} to fail" >&2
+    fastboot format ${scratch_partition} &&
+      ( fastboot reboot || true) &&
+      die "fastboot can format ${scratch_partition}"
+  fi
+  fastboot reboot ||
+    die "can not reboot out of fastboot"
+  echo "${ORANGE}[  WARNING ]${NORMAL} adb after fastboot ... waiting 2 minutes"
+  adb_wait 2m ||
+    die "did not reboot after flash"
+  if ${overlayfs_needed}; then
+    adb_root &&
+      D=`adb_sh df -k </dev/null` &&
+      H=`echo "${D}" | head -1` &&
+      D=`echo "${D}" | grep "^overlay "` &&
+      echo "${H}" &&
+      echo "${D}" &&
+      echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+      die  "overlay /system takeover after flash vendor"
+    echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
+      die  "overlay supposed to be minus /vendor takeover after flash vendor"
+  fi
+  B="`adb_cat /system/hello`" ||
+    die "re-read /system/hello after flash vendor"
+  check_eq "${A}" "${B}" system after flash vendor
+  adb_root ||
+    die "adb root"
+  B="`adb_cat /vendor/hello`" &&
+    die "re-read /vendor/hello after flash vendor"
+  check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+fi
+
+echo "${GREEN}[ RUN      ]${NORMAL} remove test content (cleanup)" >&2
 
 T=`adb_date`
 adb remount &&
@@ -423,33 +633,41 @@
   adb_sh rm /system/hello </dev/null ||
   die -t ${T} "cleanup hello"
 B="`adb_cat /system/hello`" &&
-  die "re-read system hello after rm"
+  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"
+  die "re-read /vendor/hello after rm"
 check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
 
-adb reboot-fastboot &&
-  dd if=/dev/zero of=adb-remount-test.img bs=4096 count=16 &&
-  fastboot_wait 2m ||
-  die "reboot into fastbootd"
-fastboot flash scratch adb-remount-test.img
-err=${?}
-rm adb-remount-test.img
-[ 0 -eq ${err} ] ||
-  die "fastbootd flash scratch"
-fastboot reboot ||
-  die "can not reboot out of fastbootd"
-adb_wait 2m &&
-  adb_root ||
-  die "did not reboot after flash"
-T=`adb_date`
-D=`adb disable-verity 2>&1`
-err=${?}
-echo "${D}"
-[ ${err} = 0 ] &&
-  [ X"${D}" = X"${D##*setup failed}" ] &&
-  [ X"${D}" != X"${D##*using overlayfs}" ] ||
-  die -t ${T} "setup for overlayfs"
+if [ -n "${scratch_partition}" ]; then
+
+  echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition}" >&2
+
+  adb reboot-fastboot ||
+    die "Reboot into fastbootd"
+  dd if=/dev/zero of=/tmp/adb-remount-test.img bs=4096 count=16 2>/dev/null &&
+    fastboot_wait 2m ||
+    ( rm /tmp/adb-remount-test.img && false) ||
+    die "reboot into fastboot"
+  fastboot flash --force ${scratch_partition} /tmp/adb-remount-test.img
+  err=${?}
+  rm /tmp/adb-remount-test.img
+  fastboot reboot ||
+    die "can not reboot out of fastboot"
+  [ 0 -eq ${err} ] ||
+    die "fastboot flash ${scratch_partition}"
+  adb_wait 2m &&
+    adb_root ||
+    die "did not reboot after flash"
+  T=`adb_date`
+  D=`adb disable-verity 2>&1`
+  err=${?}
+  echo "${D}"
+  [ ${err} = 0 ] &&
+    [ X"${D}" = X"${D##*setup failed}" ] &&
+    [ X"${D}" != X"${D##*using overlayfs}" ] &&
+    echo "${GREEN}[       OK ]${NORMAL} ${scratch_partition} recreated" >&2 ||
+    die -t ${T} "setup for overlayfs"
+fi
 
 echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
diff --git a/init/Android.bp b/init/Android.bp
index ea66ac6..e7b8516 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -19,6 +19,7 @@
     cpp_std: "experimental",
     sanitize: {
         misc_undefined: ["signed-integer-overflow"],
+        address: false,  // TODO(b/120561310): Fix ASAN to work without /proc mounted and re-enable.
     },
     cflags: [
         "-DLOG_UEVENTS=0",
diff --git a/init/Android.mk b/init/Android.mk
index 700c81e..bdd0301 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -35,7 +35,6 @@
     -Wall -Wextra \
     -Wno-unused-parameter \
     -Werror \
-    -std=gnu++1z \
 
 # --
 
@@ -70,6 +69,7 @@
     $(TARGET_RAMDISK_OUT)/sys \
 
 LOCAL_STATIC_LIBRARIES := \
+    libfs_avb \
     libfs_mgr \
     libfec \
     libfec_rs \
@@ -104,9 +104,6 @@
 LOCAL_REQUIRED_MODULES := \
    init_second_stage \
 
-ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
-endif
 include $(BUILD_PHONY_PACKAGE)
 
 include $(CLEAR_VARS)
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 6eb65b2..7fd4e27 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -473,9 +473,10 @@
         // Only needed if someone explicitly changes the default log level in their init.rc.
         android::base::ScopedLogSeverity info(android::base::INFO);
 
-        struct fstab* fstab = fs_mgr_read_fstab(fstabfile);
-        int child_ret = fs_mgr_mount_all(fstab, mount_mode);
-        fs_mgr_free_fstab(fstab);
+        Fstab fstab;
+        ReadFstabFromFile(fstabfile, &fstab);
+
+        int child_ret = fs_mgr_mount_all(&fstab, mount_mode);
         if (child_ret == -1) {
             PLOG(ERROR) << "fs_mgr_mount_all returned an error";
         }
@@ -612,14 +613,15 @@
 }
 
 static Result<Success> do_swapon_all(const BuiltinArguments& args) {
-    struct fstab *fstab;
-    int ret;
+    Fstab fstab;
+    if (!ReadFstabFromFile(args[1], &fstab)) {
+        return Error() << "Could not read fstab '" << args[1] << "'";
+    }
 
-    fstab = fs_mgr_read_fstab(args[1].c_str());
-    ret = fs_mgr_swapon_all(fstab);
-    fs_mgr_free_fstab(fstab);
+    if (!fs_mgr_swapon_all(fstab)) {
+        return Error() << "fs_mgr_swapon_all() failed";
+    }
 
-    if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
     return Success();
 }
 
@@ -733,13 +735,10 @@
     return Success();
 }
 
-static void verity_update_property(fstab_rec *fstab, const char *mount_point,
-                                   int mode, int status) {
-    property_set("partition."s + mount_point + ".verified", std::to_string(mode));
-}
-
 static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
-    if (!fs_mgr_update_verity_state(verity_update_property)) {
+    if (!fs_mgr_update_verity_state([](const std::string& mount_point, int mode) {
+            property_set("partition." + mount_point + ".verified", std::to_string(mode));
+        })) {
         return Error() << "fs_mgr_update_verity_state() failed";
     }
     return Success();
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index a3257f5..acefdf0 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -30,8 +30,8 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
+#include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
-#include <fs_mgr_avb.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
 #include <liblp/liblp.h>
@@ -42,7 +42,12 @@
 #include "uevent_listener.h"
 #include "util.h"
 
+using android::base::ReadFileToString;
+using android::base::Split;
 using android::base::Timer;
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHashtreeResult;
+using android::fs_mgr::AvbUniquePtr;
 
 using namespace std::literals;
 
@@ -53,7 +58,7 @@
 // ------------------
 class FirstStageMount {
   public:
-    FirstStageMount();
+    FirstStageMount(Fstab fstab);
     virtual ~FirstStageMount() = default;
 
     // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
@@ -67,8 +72,10 @@
     bool InitRequiredDevices();
     bool InitMappedDevice(const std::string& verity_device);
     bool CreateLogicalPartitions();
-    bool MountPartition(fstab_rec* fstab_rec);
+    bool MountPartition(FstabEntry* fstab_entry);
     bool MountPartitions();
+    bool TrySwitchSystemAsRoot();
+    bool TrySkipMountingPartitions();
     bool IsDmLinearEnabled();
     bool GetDmLinearMetadataDevice();
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
@@ -77,13 +84,12 @@
 
     // Pure virtual functions.
     virtual bool GetDmVerityDevices() = 0;
-    virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+    virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
 
     bool need_dm_verity_;
 
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+    Fstab fstab_;
     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_;
@@ -92,33 +98,37 @@
 
 class FirstStageMountVBootV1 : public FirstStageMount {
   public:
-    FirstStageMountVBootV1() = default;
+    FirstStageMountVBootV1(Fstab fstab) : FirstStageMount(std::move(fstab)) {}
     ~FirstStageMountVBootV1() override = default;
 
   protected:
     bool GetDmVerityDevices() override;
-    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+    bool SetUpDmVerity(FstabEntry* fstab_entry) override;
 };
 
 class FirstStageMountVBootV2 : public FirstStageMount {
   public:
     friend void SetInitAvbVersionInRecovery();
 
-    FirstStageMountVBootV2();
+    FirstStageMountVBootV2(Fstab fstab);
     ~FirstStageMountVBootV2() override = default;
 
   protected:
     bool GetDmVerityDevices() override;
-    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+    bool SetUpDmVerity(FstabEntry* fstab_entry) override;
     bool InitAvbHandle();
 
-    std::string device_tree_vbmeta_parts_;
-    FsManagerAvbUniquePtr avb_handle_;
+    std::vector<std::string> vbmeta_partitions_;
+    AvbUniquePtr avb_handle_;
 };
 
 // Static Functions
 // ----------------
-static inline bool IsDtVbmetaCompatible() {
+static inline bool IsDtVbmetaCompatible(const Fstab& fstab) {
+    if (std::any_of(fstab.begin(), fstab.end(),
+                    [](const auto& entry) { return entry.fs_mgr_flags.avb; })) {
+        return true;
+    }
     return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
 }
 
@@ -126,20 +136,26 @@
     return access("/system/bin/recovery", F_OK) == 0;
 }
 
+static Fstab ReadFirstStageFstab() {
+    Fstab fstab;
+    if (!ReadFstabFromDt(&fstab)) {
+        if (ReadDefaultFstab(&fstab)) {
+            fstab.erase(std::remove_if(fstab.begin(), fstab.end(),
+                                       [](const auto& entry) {
+                                           return !entry.fs_mgr_flags.first_stage_mount;
+                                       }),
+                        fstab.end());
+        } else {
+            LOG(INFO) << "Failed to fstab for first stage mount";
+        }
+    }
+    return fstab;
+}
+
 // Class Definitions
 // -----------------
-FirstStageMount::FirstStageMount()
-    : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
-    if (device_tree_fstab_) {
-        // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
-        // for easier manipulation later, e.g., range-base for loop.
-        for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
-            mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
-        }
-    } else {
-        LOG(INFO) << "Failed to read fstab from device tree";
-    }
-
+FirstStageMount::FirstStageMount(Fstab fstab)
+    : need_dm_verity_(false), fstab_(std::move(fstab)), uevent_listener_(16 * 1024 * 1024) {
     auto boot_devices = fs_mgr_get_boot_devices();
     device_handler_ = std::make_unique<DeviceHandler>(
             std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
@@ -149,15 +165,16 @@
 }
 
 std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
-    if (IsDtVbmetaCompatible()) {
-        return std::make_unique<FirstStageMountVBootV2>();
+    auto fstab = ReadFirstStageFstab();
+    if (IsDtVbmetaCompatible(fstab)) {
+        return std::make_unique<FirstStageMountVBootV2>(std::move(fstab));
     } else {
-        return std::make_unique<FirstStageMountVBootV1>();
+        return std::make_unique<FirstStageMountVBootV1>(std::move(fstab));
     }
 }
 
 bool FirstStageMount::DoFirstStageMount() {
-    if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
+    if (!IsDmLinearEnabled() && fstab_.empty()) {
         // Nothing to mount.
         LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
         return true;
@@ -177,8 +194,8 @@
 }
 
 bool FirstStageMount::IsDmLinearEnabled() {
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (fs_mgr_is_logical(fstab_rec)) return true;
+    for (const auto& entry : fstab_) {
+        if (entry.fs_mgr_flags.logical) return true;
     }
     return false;
 }
@@ -249,9 +266,10 @@
 }
 
 bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
-    auto partition_names = GetBlockDevicePartitionNames(metadata);
+    auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata);
     for (const auto& partition_name : partition_names) {
-        if (partition_name == lp_metadata_partition_) {
+        const auto super_device = android::fs_mgr::GetMetadataSuperBlockDevice(metadata);
+        if (partition_name == android::fs_mgr::GetBlockDevicePartitionName(*super_device)) {
             continue;
         }
         required_devices_partition_names_.emplace(partition_name);
@@ -289,7 +307,7 @@
     if (!InitDmLinearBackingDevices(*metadata.get())) {
         return false;
     }
-    return android::fs_mgr::CreateLogicalPartitions(*metadata.get());
+    return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), lp_metadata_partition_);
 }
 
 ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
@@ -363,57 +381,102 @@
     return true;
 }
 
-bool FirstStageMount::MountPartition(fstab_rec* fstab_rec) {
-    if (fs_mgr_is_logical(fstab_rec)) {
-        if (!fs_mgr_update_logical_partition(fstab_rec)) {
+bool FirstStageMount::MountPartition(FstabEntry* fstab_entry) {
+    if (fstab_entry->fs_mgr_flags.logical) {
+        if (!fs_mgr_update_logical_partition(fstab_entry)) {
             return false;
         }
-        if (!InitMappedDevice(fstab_rec->blk_device)) {
+        if (!InitMappedDevice(fstab_entry->blk_device)) {
             return false;
         }
     }
-    if (!SetUpDmVerity(fstab_rec)) {
-        PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+    if (!SetUpDmVerity(fstab_entry)) {
+        PLOG(ERROR) << "Failed to setup verity for '" << fstab_entry->mount_point << "'";
         return false;
     }
-    if (fs_mgr_do_mount_one(fstab_rec)) {
-        PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+    if (fs_mgr_do_mount_one(*fstab_entry)) {
+        PLOG(ERROR) << "Failed to mount '" << fstab_entry->mount_point << "'";
         return false;
     }
     return true;
 }
 
-bool FirstStageMount::MountPartitions() {
-    // If system is in the fstab then we're not a system-as-root device, and in
-    // this case, we mount system first then pivot to it.  From that point on,
-    // we are effectively identical to a system-as-root device.
-    auto system_partition =
-            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(),
-                         [](const auto& rec) { return rec->mount_point == "/system"s; });
+// If system is in the fstab then we're not a system-as-root device, and in
+// this case, we mount system first then pivot to it.  From that point on,
+// we are effectively identical to a system-as-root device.
+bool FirstStageMount::TrySwitchSystemAsRoot() {
+    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/system";
+    });
 
-    if (system_partition != mount_fstab_recs_.end()) {
-        if (!MountPartition(*system_partition)) {
+    if (system_partition != fstab_.end()) {
+        if (!MountPartition(&(*system_partition))) {
             return false;
         }
 
-        SwitchRoot((*system_partition)->mount_point);
+        SwitchRoot((*system_partition).mount_point);
 
-        mount_fstab_recs_.erase(system_partition);
+        fstab_.erase(system_partition);
     }
 
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (!MountPartition(fstab_rec) && !fs_mgr_is_nofail(fstab_rec)) {
+    return true;
+}
+
+// For GSI to skip mounting /product and /product_services, until there are
+// well-defined interfaces between them and /system. Otherwise, the GSI flashed
+// on /system might not be able to work with /product and /product_services.
+// When they're skipped here, /system/product and /system/product_services in
+// GSI will be used.
+bool FirstStageMount::TrySkipMountingPartitions() {
+    constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
+
+    std::string skip_config;
+    if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
+        return true;
+    }
+
+    for (const auto& skip_mount_point : Split(skip_config, "\n")) {
+        if (skip_mount_point.empty()) {
+            continue;
+        }
+        auto removing_entry =
+                std::find_if(fstab_.begin(), fstab_.end(), [&skip_mount_point](const auto& entry) {
+                    return entry.mount_point == skip_mount_point;
+                });
+        if (removing_entry != fstab_.end()) {
+            fstab_.erase(removing_entry);
+            LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
+        }
+    }
+
+    return true;
+}
+
+bool FirstStageMount::MountPartitions() {
+    if (!TrySwitchSystemAsRoot()) return false;
+
+    if (!TrySkipMountingPartitions()) return false;
+
+    for (auto& fstab_entry : fstab_) {
+        if (!MountPartition(&fstab_entry) && !fstab_entry.fs_mgr_flags.no_fail) {
             return false;
         }
     }
 
     // heads up for instantiating required device(s) for overlayfs logic
-    const auto devices = fs_mgr_overlayfs_required_devices(device_tree_fstab_.get());
+    const auto devices = fs_mgr_overlayfs_required_devices(&fstab_);
     for (auto const& device : devices) {
-        InitMappedDevice(device);
+        if (android::base::StartsWith(device, "/dev/block/by-name/")) {
+            required_devices_partition_names_.emplace(basename(device.c_str()));
+            auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+            uevent_listener_.RegenerateUevents(uevent_callback);
+            uevent_listener_.Poll(uevent_callback, 10s);
+        } else {
+            InitMappedDevice(device);
+        }
     }
 
-    fs_mgr_overlayfs_mount_all(device_tree_fstab_.get());
+    fs_mgr_overlayfs_mount_all(&fstab_);
 
     return true;
 }
@@ -422,25 +485,25 @@
     std::string verity_loc_device;
     need_dm_verity_ = false;
 
-    for (auto fstab_rec : mount_fstab_recs_) {
+    for (const auto& fstab_entry : fstab_) {
         // Don't allow verifyatboot in the first stage.
-        if (fs_mgr_is_verifyatboot(fstab_rec)) {
+        if (fstab_entry.fs_mgr_flags.verify_at_boot) {
             LOG(ERROR) << "Partitions can't be verified at boot";
             return false;
         }
         // Checks for verified partitions.
-        if (fs_mgr_is_verified(fstab_rec)) {
+        if (fstab_entry.fs_mgr_flags.verify) {
             need_dm_verity_ = true;
         }
         // Checks if verity metadata is on a separate partition. Note that it is
         // not partition specific, so there must be only one additional partition
         // that carries verity state.
-        if (fstab_rec->verity_loc) {
+        if (!fstab_entry.verity_loc.empty()) {
             if (verity_loc_device.empty()) {
-                verity_loc_device = fstab_rec->verity_loc;
-            } else if (verity_loc_device != fstab_rec->verity_loc) {
+                verity_loc_device = fstab_entry.verity_loc;
+            } else if (verity_loc_device != fstab_entry.verity_loc) {
                 LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
-                           << fstab_rec->verity_loc;
+                           << fstab_entry.verity_loc;
                 return false;
             }
         }
@@ -448,9 +511,9 @@
 
     // Includes the partition names of fstab records and verity_loc_device (if any).
     // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (!fs_mgr_is_logical(fstab_rec)) {
-            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+    for (const auto& fstab_entry : fstab_) {
+        if (!fstab_entry.fs_mgr_flags.logical) {
+            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
         }
     }
 
@@ -461,19 +524,19 @@
     return true;
 }
 
-bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
-    if (fs_mgr_is_verified(fstab_rec)) {
-        int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
+bool FirstStageMountVBootV1::SetUpDmVerity(FstabEntry* fstab_entry) {
+    if (fstab_entry->fs_mgr_flags.verify) {
+        int ret = fs_mgr_setup_verity(fstab_entry, false /* wait_for_verity_dev */);
         switch (ret) {
             case FS_MGR_SETUP_VERITY_SKIPPED:
             case FS_MGR_SETUP_VERITY_DISABLED:
-                LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
+                LOG(INFO) << "Verity disabled/skipped for '" << fstab_entry->mount_point << "'";
                 return true;
             case FS_MGR_SETUP_VERITY_SUCCESS:
                 // The exact block device name (fstab_rec->blk_device) is changed to
                 // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
                 // first stage.
-                return InitMappedDevice(fstab_rec->blk_device);
+                return InitMappedDevice(fstab_entry->blk_device);
             default:
                 return false;
         }
@@ -481,22 +544,27 @@
     return true;  // Returns true to mount the partition.
 }
 
-// FirstStageMountVBootV2 constructor.
-// Gets the vbmeta partitions from device tree.
-// /{
-//     firmware {
-//         android {
-//             vbmeta {
-//                 compatible = "android,vbmeta";
-//                 parts = "vbmeta,boot,system,vendor"
-//             };
-//         };
-//     };
-//  }
-FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
-    if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
-        PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
-        return;
+// First retrieve any vbmeta partitions from device tree (legacy) then read through the fstab
+// for any further vbmeta partitions.
+FirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab)
+    : FirstStageMount(std::move(fstab)), avb_handle_(nullptr) {
+    std::string device_tree_vbmeta_parts;
+    read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts);
+
+    for (auto&& partition : Split(device_tree_vbmeta_parts, ",")) {
+        if (!partition.empty()) {
+            vbmeta_partitions_.emplace_back(std::move(partition));
+        }
+    }
+
+    for (const auto& entry : fstab_) {
+        if (!entry.vbmeta_partition.empty()) {
+            vbmeta_partitions_.emplace_back(entry.vbmeta_partition);
+        }
+    }
+
+    if (vbmeta_partitions_.empty()) {
+        LOG(ERROR) << "Failed to read vbmeta partitions.";
     }
 }
 
@@ -506,30 +574,27 @@
     std::set<std::string> logical_partitions;
 
     // fstab_rec->blk_device has A/B suffix.
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (fs_mgr_is_avb(fstab_rec)) {
+    for (const auto& fstab_entry : fstab_) {
+        if (fstab_entry.fs_mgr_flags.avb) {
             need_dm_verity_ = true;
         }
-        if (fs_mgr_is_logical(fstab_rec)) {
+        if (fstab_entry.fs_mgr_flags.logical) {
             // Don't try to find logical partitions via uevent regeneration.
-            logical_partitions.emplace(basename(fstab_rec->blk_device));
+            logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));
         } else {
-            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
         }
     }
 
-    // libavb verifies AVB metadata on all verified partitions at once.
-    // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
-    // for libavb to verify metadata, even if there is only /vendor in the
-    // above mount_fstab_recs_.
+    // Any partitions needed for verifying the partitions used in first stage mount, e.g. vbmeta
+    // must be provided as vbmeta_partitions.
     if (need_dm_verity_) {
-        if (device_tree_vbmeta_parts_.empty()) {
-            LOG(ERROR) << "Missing vbmeta parts in device tree";
+        if (vbmeta_partitions_.empty()) {
+            LOG(ERROR) << "Missing vbmeta partitions";
             return false;
         }
-        std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
         std::string ab_suffix = fs_mgr_get_slot_suffix();
-        for (const auto& partition : partitions) {
+        for (const auto& partition : vbmeta_partitions_) {
             std::string partition_name = partition + ab_suffix;
             if (logical_partitions.count(partition_name)) {
                 continue;
@@ -544,19 +609,19 @@
     return true;
 }
 
-bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
-    if (fs_mgr_is_avb(fstab_rec)) {
+bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
+    if (fstab_entry->fs_mgr_flags.avb) {
         if (!InitAvbHandle()) return false;
-        SetUpAvbHashtreeResult hashtree_result =
-                avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
+        AvbHashtreeResult hashtree_result =
+                avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
         switch (hashtree_result) {
-            case SetUpAvbHashtreeResult::kDisabled:
+            case AvbHashtreeResult::kDisabled:
                 return true;  // Returns true to mount the partition.
-            case SetUpAvbHashtreeResult::kSuccess:
+            case AvbHashtreeResult::kSuccess:
                 // The exact block device name (fstab_rec->blk_device) is changed to
                 // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
                 // first stage.
-                return InitMappedDevice(fstab_rec->blk_device);
+                return InitMappedDevice(fstab_entry->blk_device);
             default:
                 return false;
         }
@@ -567,10 +632,10 @@
 bool FirstStageMountVBootV2::InitAvbHandle() {
     if (avb_handle_) return true;  // Returns true if the handle is already initialized.
 
-    avb_handle_ = FsManagerAvbHandle::Open();
+    avb_handle_ = AvbHandle::Open();
 
     if (!avb_handle_) {
-        PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
+        PLOG(ERROR) << "Failed to open AvbHandle";
         return false;
     }
     // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
@@ -602,25 +667,27 @@
         return;
     }
 
-    if (!IsDtVbmetaCompatible()) {
+    auto fstab = ReadFirstStageFstab();
+
+    if (!IsDtVbmetaCompatible(fstab)) {
         LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
         return;
     }
 
-    // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
+    // Initializes required devices for the subsequent AvbHandle::Open()
     // to verify AVB metadata on all partitions in the verified chain.
     // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
     // Open() function returns a valid handle.
     // We don't need to mount partitions here in recovery mode.
-    FirstStageMountVBootV2 avb_first_mount;
+    FirstStageMountVBootV2 avb_first_mount(std::move(fstab));
     if (!avb_first_mount.InitDevices()) {
         LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
         return;
     }
 
-    FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open();
+    AvbUniquePtr avb_handle = AvbHandle::Open();
     if (!avb_handle) {
-        PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
+        PLOG(ERROR) << "Failed to open AvbHandle for INIT_AVB_VERSION";
         return;
     }
     setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 8cf2128..62cd2be 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -86,9 +86,8 @@
     }
 }
 
-UeventListener::UeventListener() {
-    // is 2MB enough? udev uses 128MB!
-    device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
+UeventListener::UeventListener(size_t uevent_socket_rcvbuf_size) {
+    device_fd_.reset(uevent_open_socket(uevent_socket_rcvbuf_size, true));
     if (device_fd_ == -1) {
         LOG(FATAL) << "Could not open uevent socket";
     }
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index 5b453fe..aea094e 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -41,7 +41,7 @@
 
 class UeventListener {
   public:
-    UeventListener();
+    UeventListener(size_t uevent_socket_rcvbuf_size);
 
     void RegenerateUevents(const ListenerCallback& callback) const;
     ListenerAction RegenerateUeventsForPath(const std::string& path,
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 66491dd..7545d53 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -233,29 +233,26 @@
     SelabelInitialize();
 
     std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
-    UeventListener uevent_listener;
 
-    {
-        // Keep the current product name base configuration so we remain backwards compatible and
-        // allow it to override everything.
-        // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
-        auto hardware = android::base::GetProperty("ro.hardware", "");
+    // Keep the current product name base configuration so we remain backwards compatible and
+    // allow it to override everything.
+    // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
+    auto hardware = android::base::GetProperty("ro.hardware", "");
 
-        auto ueventd_configuration =
-                ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
-                             "/ueventd." + hardware + ".rc"});
+    auto ueventd_configuration = ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc",
+                                              "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});
 
-        uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
-                std::move(ueventd_configuration.dev_permissions),
-                std::move(ueventd_configuration.sysfs_permissions),
-                std::move(ueventd_configuration.subsystems), fs_mgr_get_boot_devices(), true));
-        uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
-                std::move(ueventd_configuration.firmware_directories)));
+    uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
+            std::move(ueventd_configuration.dev_permissions),
+            std::move(ueventd_configuration.sysfs_permissions),
+            std::move(ueventd_configuration.subsystems), fs_mgr_get_boot_devices(), true));
+    uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
+            std::move(ueventd_configuration.firmware_directories)));
 
-        if (ueventd_configuration.enable_modalias_handling) {
-            uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>());
-        }
+    if (ueventd_configuration.enable_modalias_handling) {
+        uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>());
     }
+    UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
 
     if (access(COLDBOOT_DONE, F_OK) != 0) {
         ColdBoot cold_boot(uevent_listener, uevent_handlers);
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 677938e..aac3fe5 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -19,9 +19,13 @@
 #include <grp.h>
 #include <pwd.h>
 
+#include <android-base/parseint.h>
+
 #include "keyword_map.h"
 #include "parser.h"
 
+using android::base::ParseByteCount;
+
 namespace android {
 namespace init {
 
@@ -101,6 +105,22 @@
     return Success();
 }
 
+Result<Success> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
+                                                size_t* uevent_socket_rcvbuf_size) {
+    if (args.size() != 2) {
+        return Error() << "uevent_socket_rcvbuf_size lines take exactly one parameter";
+    }
+
+    size_t parsed_size;
+    if (!ParseByteCount(args[1], &parsed_size)) {
+        return Error() << "could not parse size '" << args[1] << "' for uevent_socket_rcvbuf_line";
+    }
+
+    *uevent_socket_rcvbuf_size = parsed_size;
+
+    return Success();
+}
+
 class SubsystemParser : public SectionParser {
   public:
     SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
@@ -202,6 +222,9 @@
     parser.AddSingleLineParser("modalias_handling",
                                std::bind(ParseModaliasHandlingLine, _1,
                                          &ueventd_configuration.enable_modalias_handling));
+    parser.AddSingleLineParser("uevent_socket_rcvbuf_size",
+                               std::bind(ParseUeventSocketRcvbufSizeLine, _1,
+                                         &ueventd_configuration.uevent_socket_rcvbuf_size));
 
     for (const auto& config : configs) {
         parser.ParseConfig(config);
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 7d30edf..d476dec 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -31,6 +31,7 @@
     std::vector<Permissions> dev_permissions;
     std::vector<std::string> firmware_directories;
     bool enable_modalias_handling = false;
+    size_t uevent_socket_rcvbuf_size = 0;
 };
 
 UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index c3af341..9c1cedf 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -138,6 +138,15 @@
     TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories});
 }
 
+TEST(ueventd_parser, UeventSocketRcvbufSize) {
+    auto ueventd_file = R"(
+uevent_socket_rcvbuf_size 8k
+uevent_socket_rcvbuf_size 8M
+)";
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, false, 8 * 1024 * 1024});
+}
+
 TEST(ueventd_parser, AllTogether) {
     auto ueventd_file = R"(
 
@@ -169,6 +178,8 @@
 /sys/devices/virtual/*/input   poll_delay  0660  root   input
 firmware_directories /more
 
+uevent_socket_rcvbuf_size 6M
+
 #ending comment
 )";
 
@@ -197,8 +208,10 @@
             "/more",
     };
 
-    TestUeventdFile(ueventd_file,
-                    {subsystems, sysfs_permissions, permissions, firmware_directories});
+    size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
+
+    TestUeventdFile(ueventd_file, {subsystems, sysfs_permissions, permissions, firmware_directories,
+                                   false, uevent_socket_rcvbuf_size});
 }
 
 // All of these lines are ill-formed, so test that there is 0 output.
@@ -213,6 +226,8 @@
 /sys/devices/platform/trusty.*      trusty_version        0440  baduidbad   log
 /sys/devices/platform/trusty.*      trusty_version        0440  root   baduidbad
 
+uevent_socket_rcvbuf_size blah
+
 subsystem #no name
 
 )";
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 8b0c53e..ac94e69 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -311,7 +311,7 @@
 };
 
 FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
-    base::unique_fd epoll_fd(epoll_create1(/* no flag */ 0));
+    base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
     if (epoll_fd.get() == -1) {
         PLOG(ERROR) << "Failed to open FD for epoll";
         opened_ = false;
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 39cb995..038b59e 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -76,7 +76,7 @@
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
 
-  if (Tid() != android::base::GetThreadId()) {
+  if (Tid() != static_cast<pid_t>(android::base::GetThreadId())) {
     return UnwindThread(num_ignore_frames);
   }
 
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index ee52f5e..1490fbc 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -84,7 +84,7 @@
     { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "system/bin" },
+    { 00755, AID_ROOT,         AID_SHELL,        0, "system/bin" },
     { 00755, AID_ROOT,         AID_ROOT,         0, "system/etc/ppp" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/vendor" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
@@ -108,7 +108,9 @@
 // although the developer is advised to restrict the scope to the /vendor or
 // oem/ file-system since the intent is to provide support for customized
 // portions of a separate vendor.img or oem.img.  Has to remain open so that
-// customization can also land on /system/vendor, /system/oem or /system/odm.
+// customization can also land on /system/vendor, /system/oem, /system/odm,
+// /system/product or /system/product_services.
+//
 // We expect build-time checking or filtering when constructing the associated
 // fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
 static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
@@ -117,11 +119,17 @@
 static const char oem_conf_file[] = "/oem/etc/fs_config_files";
 static const char odm_conf_dir[] = "/odm/etc/fs_config_dirs";
 static const char odm_conf_file[] = "/odm/etc/fs_config_files";
+static const char product_conf_dir[] = "/product/etc/fs_config_dirs";
+static const char product_conf_file[] = "/product/etc/fs_config_files";
+static const char product_services_conf_dir[] = "/product_services/etc/fs_config_dirs";
+static const char product_services_conf_file[] = "/product_services/etc/fs_config_files";
 static const char* conf[][2] = {
-    {sys_conf_file, sys_conf_dir},
-    {ven_conf_file, ven_conf_dir},
-    {oem_conf_file, oem_conf_dir},
-    {odm_conf_file, odm_conf_dir},
+        {sys_conf_file, sys_conf_dir},
+        {ven_conf_file, ven_conf_dir},
+        {oem_conf_file, oem_conf_dir},
+        {odm_conf_file, odm_conf_dir},
+        {product_conf_file, product_conf_dir},
+        {product_services_conf_file, product_services_conf_dir},
 };
 
 // Do not use android_files to grant Linux capabilities.  Use ambient capabilities in their
@@ -150,7 +158,11 @@
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_file + 1 },
     { 00600, AID_ROOT,      AID_ROOT,      0, "product/build.prop" },
+    { 00444, AID_ROOT,      AID_ROOT,      0, product_conf_dir + 1 },
+    { 00444, AID_ROOT,      AID_ROOT,      0, product_conf_file + 1 },
     { 00600, AID_ROOT,      AID_ROOT,      0, "product_services/build.prop" },
+    { 00444, AID_ROOT,      AID_ROOT,      0, product_services_conf_dir + 1 },
+    { 00444, AID_ROOT,      AID_ROOT,      0, product_services_conf_file + 1 },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
@@ -236,10 +248,10 @@
     return fd;
 }
 
-// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>" or
-// "vendor/<stuff>"
+// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>",
+// "product_services/<stuff>" or "vendor/<stuff>"
 static bool is_partition(const char* path, size_t len) {
-    static const char* partitions[] = {"odm/", "oem/", "product/", "vendor/"};
+    static const char* partitions[] = {"odm/", "oem/", "product/", "product_services/", "vendor/"};
     for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
         size_t plen = strlen(partitions[i]);
         if (len <= plen) continue;
diff --git a/libcutils/include/cutils/partition_utils.h b/libcutils/include/cutils/partition_utils.h
index 7518559..8bc9b48 100644
--- a/libcutils/include/cutils/partition_utils.h
+++ b/libcutils/include/cutils/partition_utils.h
@@ -21,7 +21,7 @@
 
 __BEGIN_DECLS
 
-int partition_wiped(char *source);
+int partition_wiped(const char* source);
 
 __END_DECLS
 
diff --git a/libcutils/partition_utils.cpp b/libcutils/partition_utils.cpp
index 2211ff6..b840559 100644
--- a/libcutils/partition_utils.cpp
+++ b/libcutils/partition_utils.cpp
@@ -39,8 +39,7 @@
     return ret;
 }
 
-int partition_wiped(char *source)
-{
+int partition_wiped(const char* source) {
     uint8_t buf[4096];
     int fd, ret;
 
@@ -67,4 +66,3 @@
 
     return 0;
 }
-
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index 2dfceed..721de7c 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -95,6 +95,8 @@
 int uevent_open_socket(int buf_sz, bool passcred) {
     struct sockaddr_nl addr;
     int on = passcred;
+    int buf_sz_readback = 0;
+    socklen_t optlen = sizeof(buf_sz_readback);
     int s;
 
     memset(&addr, 0, sizeof(addr));
@@ -105,11 +107,21 @@
     s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
     if (s < 0) return -1;
 
-    /* buf_sz should be less than net.core.rmem_max for this to succeed */
-    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||
+          getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {
         close(s);
         return -1;
     }
+    /* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we
+     * want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in
+     * case we don't have CAP_NET_ADMIN. This is the case, for example, for
+     * healthd. */
+    if (buf_sz_readback < 2 * buf_sz) {
+        if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {
+            close(s);
+            return -1;
+        }
+    }
 
     setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
 
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
index aab3743..3e191ad 100644
--- a/libmeminfo/Android.bp
+++ b/libmeminfo/Android.bp
@@ -54,6 +54,11 @@
     srcs: [
         "libmeminfo_test.cpp"
     ],
+
+    data: [
+        "testdata1/*",
+        "testdata2/*"
+    ],
 }
 
 cc_benchmark {
@@ -67,4 +72,8 @@
         "libmeminfo",
         "libprocinfo",
     ],
+
+    data: [
+        "testdata1/*",
+    ],
 }
diff --git a/libmeminfo/OWNERS b/libmeminfo/OWNERS
new file mode 100644
index 0000000..26e71fe
--- /dev/null
+++ b/libmeminfo/OWNERS
@@ -0,0 +1 @@
+sspatil@google.com
diff --git a/libmeminfo/include/meminfo/meminfo.h b/libmeminfo/include/meminfo/meminfo.h
index c328648..809054b 100644
--- a/libmeminfo/include/meminfo/meminfo.h
+++ b/libmeminfo/include/meminfo/meminfo.h
@@ -31,6 +31,8 @@
     uint64_t pss;
     uint64_t uss;
 
+    uint64_t swap;
+
     uint64_t private_clean;
     uint64_t private_dirty;
     uint64_t shared_clean;
@@ -41,6 +43,7 @@
           rss(0),
           pss(0),
           uss(0),
+          swap(0),
           private_clean(0),
           private_dirty(0),
           shared_clean(0),
@@ -49,7 +52,7 @@
     ~MemUsage() = default;
 
     void clear() {
-        vss = rss = pss = uss = 0;
+        vss = rss = pss = uss = swap = 0;
         private_clean = private_dirty = shared_clean = shared_dirty = 0;
     }
 };
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index b37c56b..92375d3 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -29,13 +29,16 @@
 class ProcMemInfo final {
     // Per-process memory accounting
   public:
-    ProcMemInfo(pid_t pid, bool get_wss = false);
+    // Reset the working set accounting of the process via /proc/<pid>/clear_refs
+    static bool ResetWorkingSet(pid_t pid);
+
+    ProcMemInfo(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0);
 
     const std::vector<Vma>& Maps();
     const MemUsage& Usage();
     const MemUsage& Wss();
+    const std::vector<uint16_t>& SwapOffsets();
 
-    bool WssReset();
     ~ProcMemInfo() = default;
 
   private:
@@ -44,11 +47,14 @@
 
     pid_t pid_;
     bool get_wss_;
+    uint64_t pgflags_;
+    uint64_t pgflags_mask_;
 
     std::vector<Vma> maps_;
 
     MemUsage usage_;
     MemUsage wss_;
+    std::vector<uint16_t> swap_offsets_;
 };
 
 }  // namespace meminfo
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
index f5e05bd..f18ae08 100644
--- a/libmeminfo/include/meminfo/sysmeminfo.h
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -18,6 +18,7 @@
 
 #include <sys/types.h>
 
+#include <functional>
 #include <map>
 #include <string>
 #include <vector>
@@ -28,6 +29,21 @@
 class SysMemInfo final {
     // System or Global memory accounting
   public:
+    static constexpr const char* kMemTotal = "MemTotal:";
+    static constexpr const char* kMemFree = "MemFree:";
+    static constexpr const char* kMemBuffers = "Buffers:";
+    static constexpr const char* kMemCached = "Cached:";
+    static constexpr const char* kMemShmem = "Shmem:";
+    static constexpr const char* kMemSlab = "Slab:";
+    static constexpr const char* kMemSReclaim = "SReclaimable:";
+    static constexpr const char* kMemSUnreclaim = "SUnreclaim:";
+    static constexpr const char* kMemSwapTotal = "SwapTotal:";
+    static constexpr const char* kMemSwapFree = "SwapFree:";
+    static constexpr const char* kMemMapped = "Mapped:";
+    static constexpr const char* kMemVmallocUsed = "VmallocUsed:";
+    static constexpr const char* kMemPageTables = "PageTables:";
+    static constexpr const char* kMemKernelStack = "KernelStack:";
+
     static const std::vector<std::string> kDefaultSysMemInfoTags;
 
     SysMemInfo() = default;
@@ -36,26 +52,32 @@
     bool ReadMemInfo(const std::string& path = "/proc/meminfo");
     bool ReadMemInfo(const std::vector<std::string>& tags,
                      const std::string& path = "/proc/meminfo");
+    bool ReadMemInfo(const std::vector<std::string>& tags, std::vector<uint64_t>* out,
+                     const std::string& path = "/proc/meminfo");
+    bool ReadMemInfo(std::vector<uint64_t>* out, const std::string& path = "/proc/meminfo");
 
     // getters
-    uint64_t mem_total_kb() { return mem_in_kb_["MemTotal:"]; }
-    uint64_t mem_free_kb() { return mem_in_kb_["MemFree:"]; }
-    uint64_t mem_buffers_kb() { return mem_in_kb_["Buffers:"]; }
-    uint64_t mem_cached_kb() { return mem_in_kb_["Cached:"]; }
-    uint64_t mem_shmem_kb() { return mem_in_kb_["Shmem:"]; }
-    uint64_t mem_slab_kb() { return mem_in_kb_["Slab:"]; }
-    uint64_t mem_slab_reclailmable_kb() { return mem_in_kb_["SReclaimable:"]; }
-    uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_["SUnreclaim:"]; }
-    uint64_t mem_swap_kb() { return mem_in_kb_["SwapTotal:"]; }
-    uint64_t mem_free_swap_kb() { return mem_in_kb_["SwapFree:"]; }
-    uint64_t mem_zram_kb() { return mem_in_kb_["Zram:"]; }
-    uint64_t mem_mapped_kb() { return mem_in_kb_["Mapped:"]; }
-    uint64_t mem_vmalloc_used_kb() { return mem_in_kb_["VmallocUsed:"]; }
-    uint64_t mem_page_tables_kb() { return mem_in_kb_["PageTables:"]; }
-    uint64_t mem_kernel_stack_kb() { return mem_in_kb_["KernelStack:"]; }
+    uint64_t mem_total_kb() { return mem_in_kb_[kMemTotal]; }
+    uint64_t mem_free_kb() { return mem_in_kb_[kMemFree]; }
+    uint64_t mem_buffers_kb() { return mem_in_kb_[kMemBuffers]; }
+    uint64_t mem_cached_kb() { return mem_in_kb_[kMemCached]; }
+    uint64_t mem_shmem_kb() { return mem_in_kb_[kMemShmem]; }
+    uint64_t mem_slab_kb() { return mem_in_kb_[kMemSlab]; }
+    uint64_t mem_slab_reclaimable_kb() { return mem_in_kb_[kMemSReclaim]; }
+    uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_[kMemSUnreclaim]; }
+    uint64_t mem_swap_kb() { return mem_in_kb_[kMemSwapTotal]; }
+    uint64_t mem_swap_free_kb() { return mem_in_kb_[kMemSwapFree]; }
+    uint64_t mem_mapped_kb() { return mem_in_kb_[kMemMapped]; }
+    uint64_t mem_vmalloc_used_kb() { return mem_in_kb_[kMemVmallocUsed]; }
+    uint64_t mem_page_tables_kb() { return mem_in_kb_[kMemPageTables]; }
+    uint64_t mem_kernel_stack_kb() { return mem_in_kb_[kMemKernelStack]; }
+    uint64_t mem_zram_kb(const std::string& zram_dev = "");
 
   private:
     std::map<std::string, uint64_t> mem_in_kb_;
+    bool MemZramDevice(const std::string& zram_dev, uint64_t* mem_zram_dev);
+    bool ReadMemInfo(const std::vector<std::string>& tags, const std::string& path,
+                     std::function<void(const std::string&, uint64_t)> store_val);
 };
 
 }  // namespace meminfo
diff --git a/libmeminfo/libmeminfo_benchmark.cpp b/libmeminfo/libmeminfo_benchmark.cpp
index e2239f0..2660a4d 100644
--- a/libmeminfo/libmeminfo_benchmark.cpp
+++ b/libmeminfo/libmeminfo_benchmark.cpp
@@ -17,6 +17,8 @@
 #include <meminfo/sysmeminfo.h>
 
 #include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
@@ -27,6 +29,8 @@
 
 #include <benchmark/benchmark.h>
 
+using ::android::meminfo::SysMemInfo;
+
 enum {
     MEMINFO_TOTAL,
     MEMINFO_FREE,
@@ -46,7 +50,7 @@
     MEMINFO_COUNT
 };
 
-void get_mem_info(uint64_t mem[], const char* file) {
+static void get_mem_info(uint64_t mem[], const char* file) {
     char buffer[4096];
     unsigned int numFound = 0;
 
@@ -67,8 +71,8 @@
     buffer[len] = 0;
 
     static const char* const tags[] = {
-            "MemTotal:",     "MemFree:",    "Buffers:",     "Cached:",       "Shmem:", "Slab:",
-            "SReclaimable:", "SUnreclaim:", "SwapTotal:",   "SwapFree:",     "ZRam:",  "Mapped:",
+            "MemTotal:",     "MemFree:",    "Buffers:",     "Cached:",   "Shmem:", "Slab:",
+            "SReclaimable:", "SUnreclaim:", "SwapTotal:",   "SwapFree:", "ZRam:",  "Mapped:",
             "VmallocUsed:",  "PageTables:", "KernelStack:", NULL};
 
     static const int tagsLen[] = {9, 8, 8, 7, 6, 5, 13, 11, 10, 9, 5, 7, 12, 11, 12, 0};
@@ -78,7 +82,8 @@
     while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
         int i = 0;
         while (tags[i]) {
-            //std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) << std::endl;
+            // std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) <<
+            // std::endl;
             if (strncmp(p, tags[i], tagsLen[i]) == 0) {
                 p += tagsLen[i];
                 while (*p == ' ') p++;
@@ -101,7 +106,7 @@
     }
 }
 
-static void BM_ParseSysMemInfo(benchmark::State& state) {
+static void BM_ReadMemInfo_old(benchmark::State& state) {
     std::string meminfo = R"meminfo(MemTotal:        3019740 kB
 MemFree:         1809728 kB
 MemAvailable:    2546560 kB
@@ -155,9 +160,9 @@
         get_mem_info(mem, tf.path);
     }
 }
-BENCHMARK(BM_ParseSysMemInfo);
+BENCHMARK(BM_ReadMemInfo_old);
 
-static void BM_ReadMemInfo(benchmark::State& state) {
+static void BM_ReadMemInfo_new(benchmark::State& state) {
     std::string meminfo = R"meminfo(MemTotal:        3019740 kB
 MemFree:         1809728 kB
 MemAvailable:    2546560 kB
@@ -207,11 +212,189 @@
     android::base::WriteStringToFd(meminfo, tf.fd);
 
     std::string file = std::string(tf.path);
-    ::android::meminfo::SysMemInfo mi;
+    std::vector<uint64_t> mem(MEMINFO_COUNT);
+    const std::vector<std::string> tags = {
+            SysMemInfo::kMemTotal,      SysMemInfo::kMemFree,        SysMemInfo::kMemBuffers,
+            SysMemInfo::kMemCached,     SysMemInfo::kMemShmem,       SysMemInfo::kMemSlab,
+            SysMemInfo::kMemSReclaim,   SysMemInfo::kMemSUnreclaim,  SysMemInfo::kMemSwapTotal,
+            SysMemInfo::kMemSwapFree,   SysMemInfo::kMemMapped,      SysMemInfo::kMemVmallocUsed,
+            SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,
+    };
+
+    SysMemInfo smi;
     for (auto _ : state) {
-        mi.ReadMemInfo(file);
+        smi.ReadMemInfo(tags, &mem, file);
     }
 }
-BENCHMARK(BM_ReadMemInfo);
+BENCHMARK(BM_ReadMemInfo_new);
+
+static uint64_t get_zram_mem_used(const std::string& zram_dir) {
+    FILE* f = fopen((zram_dir + "mm_stat").c_str(), "r");
+    if (f) {
+        uint64_t mem_used_total = 0;
+
+        int matched = fscanf(f, "%*d %*d %" SCNu64 " %*d %*d %*d %*d", &mem_used_total);
+        if (matched != 1)
+            fprintf(stderr, "warning: failed to parse %s\n", (zram_dir + "mm_stat").c_str());
+
+        fclose(f);
+        return mem_used_total;
+    }
+
+    f = fopen((zram_dir + "mem_used_total").c_str(), "r");
+    if (f) {
+        uint64_t mem_used_total = 0;
+
+        int matched = fscanf(f, "%" SCNu64, &mem_used_total);
+        if (matched != 1)
+            fprintf(stderr, "warning: failed to parse %s\n", (zram_dir + "mem_used_total").c_str());
+
+        fclose(f);
+        return mem_used_total;
+    }
+
+    return 0;
+}
+
+static void BM_ZramTotal_old(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+    for (auto _ : state) {
+        uint64_t zram_total __attribute__((unused)) = get_zram_mem_used(zram_mmstat_dir) / 1024;
+    }
+}
+BENCHMARK(BM_ZramTotal_old);
+
+static void BM_ZramTotal_new(benchmark::State& state) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+    SysMemInfo smi;
+    for (auto _ : state) {
+        uint64_t zram_total __attribute__((unused)) = smi.mem_zram_kb(zram_mmstat_dir);
+    }
+}
+BENCHMARK(BM_ZramTotal_new);
+
+static void BM_MemInfoWithZram_old(benchmark::State& state) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    ::android::base::WriteStringToFd(meminfo, tf.fd);
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+    std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+    uint64_t mem[MEMINFO_COUNT];
+    for (auto _ : state) {
+        get_mem_info(mem, tf.path);
+        mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used("/sys/block/zram0/") / 1024;
+        CHECK_EQ(mem[MEMINFO_KERNEL_STACK], 4880u);
+    }
+}
+BENCHMARK(BM_MemInfoWithZram_old);
+
+static void BM_MemInfoWithZram_new(benchmark::State& state) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    android::base::WriteStringToFd(meminfo, tf.fd);
+
+    std::string file = std::string(tf.path);
+    std::vector<uint64_t> mem(MEMINFO_COUNT);
+    std::vector<std::string> tags(SysMemInfo::kDefaultSysMemInfoTags);
+    auto it = tags.begin();
+    tags.insert(it + MEMINFO_ZRAM_TOTAL, "Zram:");
+    SysMemInfo smi;
+
+    for (auto _ : state) {
+        smi.ReadMemInfo(tags, &mem, file);
+        CHECK_EQ(mem[MEMINFO_KERNEL_STACK], 4880u);
+    }
+}
+BENCHMARK(BM_MemInfoWithZram_new);
 
 BENCHMARK_MAIN();
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 7a2be41..2856c2d 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -73,7 +73,7 @@
     }
 }
 
-TEST_F(ValidateProcMemInfo, TestMapsUsage) {
+TEST_F(ValidateProcMemInfo, TestMaps) {
     const std::vector<Vma>& maps = proc_mem->Maps();
     ASSERT_FALSE(maps.empty());
     ASSERT_EQ(proc->num_maps, maps.size());
@@ -96,6 +96,30 @@
     EXPECT_EQ(proc_usage.uss, proc_mem->Usage().uss);
 }
 
+TEST_F(ValidateProcMemInfo, TestSwapUsage) {
+    const std::vector<Vma>& maps = proc_mem->Maps();
+    ASSERT_FALSE(maps.empty());
+    ASSERT_EQ(proc->num_maps, maps.size());
+
+    pm_memusage_t map_usage, proc_usage;
+    pm_memusage_zero(&map_usage);
+    pm_memusage_zero(&proc_usage);
+    for (size_t i = 0; i < maps.size(); i++) {
+        ASSERT_EQ(0, pm_map_usage(proc->maps[i], &map_usage));
+        EXPECT_EQ(map_usage.swap, maps[i].usage.swap) << "SWAP mismatch for map: " << maps[i].name;
+        pm_memusage_add(&proc_usage, &map_usage);
+    }
+
+    EXPECT_EQ(proc_usage.swap, proc_mem->Usage().swap);
+}
+
+TEST_F(ValidateProcMemInfo, TestSwapOffsets) {
+    const MemUsage& proc_usage = proc_mem->Usage();
+    const std::vector<uint16_t>& swap_offsets = proc_mem->SwapOffsets();
+
+    EXPECT_EQ(proc_usage.swap / getpagesize(), swap_offsets.size());
+}
+
 class ValidateProcMemInfoWss : public ::testing::Test {
   protected:
     void SetUp() override {
@@ -118,7 +142,7 @@
 
 TEST_F(ValidateProcMemInfoWss, TestWorkingTestReset) {
     // Expect reset to succeed
-    EXPECT_TRUE(proc_mem->WssReset());
+    EXPECT_TRUE(ProcMemInfo::ResetWorkingSet(pid));
 }
 
 TEST_F(ValidateProcMemInfoWss, TestWssEquality) {
@@ -221,6 +245,93 @@
     }
 }
 
+TEST(TestProcMemInfo, TestMapsEmpty) {
+    ProcMemInfo proc_mem(pid);
+    const std::vector<Vma>& maps = proc_mem.Maps();
+    EXPECT_GT(maps.size(), 0);
+}
+
+TEST(TestProcMemInfo, TestUsageEmpty) {
+    // If we created the object for getting working set,
+    // the usage must be empty
+    ProcMemInfo proc_mem(pid, true);
+    const MemUsage& usage = proc_mem.Usage();
+    EXPECT_EQ(usage.rss, 0);
+    EXPECT_EQ(usage.vss, 0);
+    EXPECT_EQ(usage.pss, 0);
+    EXPECT_EQ(usage.uss, 0);
+    EXPECT_EQ(usage.swap, 0);
+}
+
+TEST(TestProcMemInfoWssReset, TestWssEmpty) {
+    // If we created the object for getting usage,
+    // the working set must be empty
+    ProcMemInfo proc_mem(pid, false);
+    const MemUsage& wss = proc_mem.Wss();
+    EXPECT_EQ(wss.rss, 0);
+    EXPECT_EQ(wss.vss, 0);
+    EXPECT_EQ(wss.pss, 0);
+    EXPECT_EQ(wss.uss, 0);
+    EXPECT_EQ(wss.swap, 0);
+}
+
+TEST(TestProcMemInfoWssReset, TestSwapOffsetsEmpty) {
+    // If we created the object for getting working set,
+    // the swap offsets must be empty
+    ProcMemInfo proc_mem(pid, true);
+    const std::vector<uint16_t>& swap_offsets = proc_mem.SwapOffsets();
+    EXPECT_EQ(swap_offsets.size(), 0);
+}
+
+TEST(ValidateProcMemInfoFlags, TestPageFlags1) {
+    // Create proc object using libpagemap
+    pm_kernel_t* ker;
+    ASSERT_EQ(0, pm_kernel_create(&ker));
+    pm_process_t* proc;
+    ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+
+    // count swapbacked pages using libpagemap
+    pm_memusage_t proc_usage;
+    pm_memusage_zero(&proc_usage);
+    ASSERT_EQ(0, pm_process_usage_flags(proc, &proc_usage, (1 << KPF_SWAPBACKED),
+                                        (1 << KPF_SWAPBACKED)));
+
+    // Create ProcMemInfo that counts swapbacked pages
+    ProcMemInfo proc_mem(pid, false, (1 << KPF_SWAPBACKED), (1 << KPF_SWAPBACKED));
+
+    EXPECT_EQ(proc_usage.vss, proc_mem.Usage().vss);
+    EXPECT_EQ(proc_usage.rss, proc_mem.Usage().rss);
+    EXPECT_EQ(proc_usage.pss, proc_mem.Usage().pss);
+    EXPECT_EQ(proc_usage.uss, proc_mem.Usage().uss);
+
+    pm_process_destroy(proc);
+    pm_kernel_destroy(ker);
+}
+
+TEST(ValidateProcMemInfoFlags, TestPageFlags2) {
+    // Create proc object using libpagemap
+    pm_kernel_t* ker;
+    ASSERT_EQ(0, pm_kernel_create(&ker));
+    pm_process_t* proc;
+    ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+
+    // count non-swapbacked pages using libpagemap
+    pm_memusage_t proc_usage;
+    pm_memusage_zero(&proc_usage);
+    ASSERT_EQ(0, pm_process_usage_flags(proc, &proc_usage, (1 << KPF_SWAPBACKED), 0));
+
+    // Create ProcMemInfo that counts non-swapbacked pages
+    ProcMemInfo proc_mem(pid, false, 0, (1 << KPF_SWAPBACKED));
+
+    EXPECT_EQ(proc_usage.vss, proc_mem.Usage().vss);
+    EXPECT_EQ(proc_usage.rss, proc_mem.Usage().rss);
+    EXPECT_EQ(proc_usage.pss, proc_mem.Usage().pss);
+    EXPECT_EQ(proc_usage.uss, proc_mem.Usage().uss);
+
+    pm_process_destroy(proc);
+    pm_kernel_destroy(ker);
+}
+
 TEST(SysMemInfoParser, TestSysMemInfoFile) {
     std::string meminfo = R"meminfo(MemTotal:        3019740 kB
 MemFree:         1809728 kB
@@ -236,8 +347,8 @@
 Inactive(file):   456852 kB
 Unevictable:        3096 kB
 Mlocked:            3096 kB
-SwapTotal:             0 kB
-SwapFree:              0 kB
+SwapTotal:         32768 kB
+SwapFree:           4096 kB
 Dirty:                32 kB
 Writeback:             0 kB
 AnonPages:         74988 kB
@@ -254,7 +365,7 @@
 CommitLimit:     1509868 kB
 Committed_AS:      80296 kB
 VmallocTotal:   263061440 kB
-VmallocUsed:           0 kB
+VmallocUsed:       65536 kB
 VmallocChunk:          0 kB
 AnonHugePages:      6144 kB
 ShmemHugePages:        0 kB
@@ -274,7 +385,19 @@
     SysMemInfo mi;
     ASSERT_TRUE(mi.ReadMemInfo(tf.path));
     EXPECT_EQ(mi.mem_total_kb(), 3019740);
+    EXPECT_EQ(mi.mem_free_kb(), 1809728);
+    EXPECT_EQ(mi.mem_buffers_kb(), 54736);
+    EXPECT_EQ(mi.mem_cached_kb(), 776052);
+    EXPECT_EQ(mi.mem_shmem_kb(), 4020);
+    EXPECT_EQ(mi.mem_slab_kb(), 86464);
+    EXPECT_EQ(mi.mem_slab_reclaimable_kb(), 44432);
+    EXPECT_EQ(mi.mem_slab_unreclaimable_kb(), 42032);
+    EXPECT_EQ(mi.mem_swap_kb(), 32768);
+    EXPECT_EQ(mi.mem_swap_free_kb(), 4096);
+    EXPECT_EQ(mi.mem_mapped_kb(), 62624);
+    EXPECT_EQ(mi.mem_vmalloc_used_kb(), 65536);
     EXPECT_EQ(mi.mem_page_tables_kb(), 2900);
+    EXPECT_EQ(mi.mem_kernel_stack_kb(), 4880);
 }
 
 TEST(SysMemInfoParser, TestEmptyFile) {
@@ -288,6 +411,111 @@
     EXPECT_EQ(mi.mem_total_kb(), 0);
 }
 
+TEST(SysMemInfoParser, TestZramTotal) {
+    std::string exec_dir = ::android::base::GetExecutableDirectory();
+
+    SysMemInfo mi;
+    std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+    EXPECT_EQ(mi.mem_zram_kb(zram_mmstat_dir), 30504);
+
+    std::string zram_memused_dir = exec_dir + "/testdata2/";
+    EXPECT_EQ(mi.mem_zram_kb(zram_memused_dir), 30504);
+}
+
+enum {
+    MEMINFO_TOTAL,
+    MEMINFO_FREE,
+    MEMINFO_BUFFERS,
+    MEMINFO_CACHED,
+    MEMINFO_SHMEM,
+    MEMINFO_SLAB,
+    MEMINFO_SLAB_RECLAIMABLE,
+    MEMINFO_SLAB_UNRECLAIMABLE,
+    MEMINFO_SWAP_TOTAL,
+    MEMINFO_SWAP_FREE,
+    MEMINFO_ZRAM_TOTAL,
+    MEMINFO_MAPPED,
+    MEMINFO_VMALLOC_USED,
+    MEMINFO_PAGE_TABLES,
+    MEMINFO_KERNEL_STACK,
+    MEMINFO_COUNT
+};
+
+TEST(SysMemInfoParser, TestZramWithTags) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:         32768 kB
+SwapFree:           4096 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:       65536 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
+    std::string file = std::string(tf.path);
+    std::vector<uint64_t> mem(MEMINFO_COUNT);
+    std::vector<std::string> tags(SysMemInfo::kDefaultSysMemInfoTags);
+    auto it = tags.begin();
+    tags.insert(it + MEMINFO_ZRAM_TOTAL, "Zram:");
+    SysMemInfo mi;
+
+    // Read system memory info
+    EXPECT_TRUE(mi.ReadMemInfo(tags, &mem, file));
+
+    EXPECT_EQ(mem[MEMINFO_TOTAL], 3019740);
+    EXPECT_EQ(mem[MEMINFO_FREE], 1809728);
+    EXPECT_EQ(mem[MEMINFO_BUFFERS], 54736);
+    EXPECT_EQ(mem[MEMINFO_CACHED], 776052);
+    EXPECT_EQ(mem[MEMINFO_SHMEM], 4020);
+    EXPECT_EQ(mem[MEMINFO_SLAB], 86464);
+    EXPECT_EQ(mem[MEMINFO_SLAB_RECLAIMABLE], 44432);
+    EXPECT_EQ(mem[MEMINFO_SLAB_UNRECLAIMABLE], 42032);
+    EXPECT_EQ(mem[MEMINFO_SWAP_TOTAL], 32768);
+    EXPECT_EQ(mem[MEMINFO_SWAP_FREE], 4096);
+    EXPECT_EQ(mem[MEMINFO_MAPPED], 62624);
+    EXPECT_EQ(mem[MEMINFO_VMALLOC_USED], 65536);
+    EXPECT_EQ(mem[MEMINFO_PAGE_TABLES], 2900);
+    EXPECT_EQ(mem[MEMINFO_KERNEL_STACK], 4880);
+}
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     if (argc <= 1) {
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index fe91d25..8867e14 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -44,6 +44,8 @@
     to->pss += from.pss;
     to->uss += from.uss;
 
+    to->swap += from.swap;
+
     to->private_clean += from.private_clean;
     to->private_dirty += from.private_dirty;
 
@@ -51,48 +53,78 @@
     to->shared_dirty += from.shared_dirty;
 }
 
-ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss) : pid_(pid), get_wss_(get_wss) {
-    if (!ReadMaps(get_wss_)) {
-        LOG(ERROR) << "Failed to read maps for Process " << pid_;
-    }
-}
-
-const std::vector<Vma>& ProcMemInfo::Maps() {
-    return maps_;
-}
-
-const MemUsage& ProcMemInfo::Usage() {
-    if (get_wss_) {
-        LOG(WARNING) << "Trying to read memory usage from working set object";
-    }
-    return usage_;
-}
-
-const MemUsage& ProcMemInfo::Wss() {
-    if (!get_wss_) {
-        LOG(WARNING) << "Trying to read working set when there is none";
-    }
-
-    return wss_;
-}
-
-bool ProcMemInfo::WssReset() {
-    if (!get_wss_) {
-        LOG(ERROR) << "Trying to reset working set from a memory usage counting object";
-        return false;
-    }
-
-    std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid_);
+bool ProcMemInfo::ResetWorkingSet(pid_t pid) {
+    std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid);
     if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
         PLOG(ERROR) << "Failed to write to " << clear_refs_path;
         return false;
     }
 
-    wss_.clear();
     return true;
 }
 
+ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss, uint64_t pgflags, uint64_t pgflags_mask)
+    : pid_(pid), get_wss_(get_wss), pgflags_(pgflags), pgflags_mask_(pgflags_mask) {}
+
+const std::vector<Vma>& ProcMemInfo::Maps() {
+    if (maps_.empty() && !ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to read maps for Process " << pid_;
+    }
+
+    return maps_;
+}
+
+const MemUsage& ProcMemInfo::Usage() {
+    if (get_wss_) {
+        LOG(WARNING) << "Trying to read process memory usage for " << pid_
+                     << " using invalid object";
+        return usage_;
+    }
+
+    if (maps_.empty() && !ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to get memory usage for Process " << pid_;
+    }
+
+    return usage_;
+}
+
+const MemUsage& ProcMemInfo::Wss() {
+    if (!get_wss_) {
+        LOG(WARNING) << "Trying to read process working set for " << pid_
+                     << " using invalid object";
+        return wss_;
+    }
+
+    if (maps_.empty() && !ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to get working set for Process " << pid_;
+    }
+
+    return wss_;
+}
+
+const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() {
+    if (get_wss_) {
+        LOG(WARNING) << "Trying to read process swap offsets for " << pid_
+                     << " using invalid object";
+        return swap_offsets_;
+    }
+
+    if (maps_.empty() && !ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to get swap offsets for Process " << pid_;
+    }
+
+    return swap_offsets_;
+}
+
 bool ProcMemInfo::ReadMaps(bool get_wss) {
+    // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
+    // running for the lifetime of the system can recycle the objects and don't have to
+    // unnecessarily retain and update this object in memory (which can get significantly large).
+    // E.g. A program that only needs to reset the working set will never all ->Maps(), ->Usage().
+    // E.g. A program that is monitoring smaps_rollup, may never call ->maps(), Usage(), so it
+    // doesn't make sense for us to parse and retain unnecessary memory accounting stats by default.
+    if (!maps_.empty()) return true;
+
     // parse and read /proc/<pid>/maps
     std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
     if (!::android::procinfo::ReadMapFile(
@@ -115,8 +147,8 @@
 
     for (auto& vma : maps_) {
         if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss)) {
-            LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start
-                       << "-" << vma.end << "]";
+            LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
+                       << vma.end << "]";
             maps_.clear();
             return false;
         }
@@ -153,18 +185,24 @@
         if (!PAGE_PRESENT(p) && !PAGE_SWAPPED(p)) continue;
 
         if (PAGE_SWAPPED(p)) {
-            // TODO: do what's needed for swapped pages
+            vma.usage.swap += pagesz;
+            swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(p));
             continue;
         }
 
         uint64_t page_frame = PAGE_PFN(p);
         if (!pinfo.PageFlags(page_frame, &pg_flags[i])) {
             LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
+            swap_offsets_.clear();
             return false;
         }
 
+        // skip unwanted pages from the count
+        if ((pg_flags[i] & pgflags_mask_) != pgflags_) continue;
+
         if (!pinfo.PageMapCount(page_frame, &pg_counts[i])) {
             LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
+            swap_offsets_.clear();
             return false;
         }
 
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
index 50fa213..4ec1c99 100644
--- a/libmeminfo/sysmeminfo.cpp
+++ b/libmeminfo/sysmeminfo.cpp
@@ -17,11 +17,16 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <cctype>
+#include <cstdio>
 #include <fstream>
+#include <iterator>
 #include <string>
 #include <utility>
 #include <vector>
@@ -29,7 +34,9 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 
 #include "meminfo_private.h"
 
@@ -37,13 +44,37 @@
 namespace meminfo {
 
 const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
-    "MemTotal:", "MemFree:",      "Buffers:",     "Cached:",     "Shmem:",
-    "Slab:",     "SReclaimable:", "SUnreclaim:",  "SwapTotal:",  "SwapFree:",
-    "ZRam:",     "Mapped:",       "VmallocUsed:", "PageTables:", "KernelStack:",
+        SysMemInfo::kMemTotal,      SysMemInfo::kMemFree,        SysMemInfo::kMemBuffers,
+        SysMemInfo::kMemCached,     SysMemInfo::kMemShmem,       SysMemInfo::kMemSlab,
+        SysMemInfo::kMemSReclaim,   SysMemInfo::kMemSUnreclaim,  SysMemInfo::kMemSwapTotal,
+        SysMemInfo::kMemSwapFree,   SysMemInfo::kMemMapped,      SysMemInfo::kMemVmallocUsed,
+        SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,
 };
 
 bool SysMemInfo::ReadMemInfo(const std::string& path) {
-    return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, path);
+    return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, path,
+                       [&](const std::string& tag, uint64_t val) { mem_in_kb_[tag] = val; });
+}
+
+bool SysMemInfo::ReadMemInfo(std::vector<uint64_t>* out, const std::string& path) {
+    return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, out, path);
+}
+
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, std::vector<uint64_t>* out,
+                             const std::string& path) {
+    out->clear();
+    out->resize(tags.size());
+
+    return ReadMemInfo(tags, path, [&]([[maybe_unused]] const std::string& tag, uint64_t val) {
+        auto it = std::find(tags.begin(), tags.end(), tag);
+        if (it == tags.end()) {
+            LOG(ERROR) << "Tried to store invalid tag: " << tag;
+            return;
+        }
+        auto index = std::distance(tags.begin(), it);
+        // store the values in the same order as the tags
+        out->at(index) = val;
+    });
 }
 
 // TODO: Delete this function if it can't match up with the c-like implementation below.
@@ -82,7 +113,8 @@
 }
 
 #else
-bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path,
+                             std::function<void(const std::string&, uint64_t)> store_val) {
     char buffer[4096];
     int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
     if (fd < 0) {
@@ -99,31 +131,103 @@
     buffer[len] = '\0';
     char* p = buffer;
     uint32_t found = 0;
+    uint32_t lineno = 0;
+    bool zram_tag_found = false;
     while (*p && found < tags.size()) {
         for (auto& tag : tags) {
+            // Special case for "Zram:" tag that android_os_Debug and friends look
+            // up along with the rest of the numbers from /proc/meminfo
+            if (!zram_tag_found && tag == "Zram:") {
+                store_val(tag, mem_zram_kb());
+                zram_tag_found = true;
+                found++;
+                continue;
+            }
+
             if (strncmp(p, tag.c_str(), tag.size()) == 0) {
                 p += tag.size();
                 while (*p == ' ') p++;
                 char* endptr = nullptr;
-                mem_in_kb_[tag] = strtoull(p, &endptr, 10);
+                uint64_t val = strtoull(p, &endptr, 10);
                 if (p == endptr) {
-                    PLOG(ERROR) << "Failed to parse line in file: " << path;
+                    PLOG(ERROR) << "Failed to parse line:" << lineno + 1 << " in file: " << path;
                     return false;
                 }
+                store_val(tag, val);
                 p = endptr;
                 found++;
                 break;
             }
         }
+
         while (*p && *p != '\n') {
             p++;
         }
         if (*p) p++;
+        lineno++;
     }
 
     return true;
 }
 #endif
 
+uint64_t SysMemInfo::mem_zram_kb(const std::string& zram_dev) {
+    uint64_t mem_zram_total = 0;
+    if (!zram_dev.empty()) {
+        if (!MemZramDevice(zram_dev, &mem_zram_total)) {
+            return 0;
+        }
+        return mem_zram_total / 1024;
+    }
+
+    constexpr uint32_t kMaxZramDevices = 256;
+    for (uint32_t i = 0; i < kMaxZramDevices; i++) {
+        std::string zram_dev = ::android::base::StringPrintf("/sys/block/zram%u/", i);
+        if (access(zram_dev.c_str(), F_OK)) {
+            // We assume zram devices appear in range 0-255 and appear always in sequence
+            // under /sys/block. So, stop looking for them once we find one is missing.
+            break;
+        }
+
+        uint64_t mem_zram_dev;
+        if (!MemZramDevice(zram_dev, &mem_zram_dev)) {
+            return 0;
+        }
+
+        mem_zram_total += mem_zram_dev;
+    }
+
+    return mem_zram_total / 1024;
+}
+
+bool SysMemInfo::MemZramDevice(const std::string& zram_dev, uint64_t* mem_zram_dev) {
+    std::string mmstat = ::android::base::StringPrintf("%s/%s", zram_dev.c_str(), "mm_stat");
+    auto mmstat_fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mmstat.c_str(), "re"), fclose};
+    if (mmstat_fp != nullptr) {
+        // only if we do have mmstat, use it. Otherwise, fall through to trying out the old
+        // 'mem_used_total'
+        if (fscanf(mmstat_fp.get(), "%*" SCNu64 " %*" SCNu64 " %" SCNu64, mem_zram_dev) != 1) {
+            PLOG(ERROR) << "Malformed mm_stat file in: " << zram_dev;
+            return false;
+        }
+        return true;
+    }
+
+    std::string content;
+    if (::android::base::ReadFileToString(zram_dev + "mem_used_total", &content)) {
+        *mem_zram_dev = strtoull(content.c_str(), NULL, 10);
+        if (*mem_zram_dev == ULLONG_MAX) {
+            PLOG(ERROR) << "Malformed mem_used_total file for zram dev: " << zram_dev
+                        << " content: " << content;
+            return false;
+        }
+
+        return true;
+    }
+
+    LOG(ERROR) << "Can't find memory status under: " << zram_dev;
+    return false;
+}
+
 }  // namespace meminfo
 }  // namespace android
diff --git a/libmeminfo/testdata1/mm_stat b/libmeminfo/testdata1/mm_stat
new file mode 100644
index 0000000..32b325f
--- /dev/null
+++ b/libmeminfo/testdata1/mm_stat
@@ -0,0 +1 @@
+ 145674240 26801454 31236096        0 45772800     3042     1887      517
diff --git a/libmeminfo/testdata2/mem_used_total b/libmeminfo/testdata2/mem_used_total
new file mode 100644
index 0000000..97fcf41
--- /dev/null
+++ b/libmeminfo/testdata2/mem_used_total
@@ -0,0 +1 @@
+31236096
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index 0870130..715d63d 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -22,6 +22,22 @@
 
     srcs: ["procmem.cpp"],
     shared_libs: [
+        "libbase",
+        "libmeminfo",
+    ],
+}
+
+cc_binary {
+    name: "procrank2",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+
+    srcs: ["procrank.cpp"],
+    shared_libs: [
+        "libbase",
         "libmeminfo",
     ],
 }
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
index 3571e41..56de12d 100644
--- a/libmeminfo/tools/procmem.cpp
+++ b/libmeminfo/tools/procmem.cpp
@@ -15,20 +15,33 @@
  */
 
 #include <errno.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <unistd.h>
 
-#include <iomanip>
 #include <iostream>
 #include <sstream>
 #include <string>
 #include <vector>
 
+#include <android-base/stringprintf.h>
 #include <meminfo/procmeminfo.h>
 
+using Vma = ::android::meminfo::Vma;
 using ProcMemInfo = ::android::meminfo::ProcMemInfo;
 using MemUsage = ::android::meminfo::MemUsage;
 
+// Global flags to control procmem output
+
+// Set to use page idle bits for working set detection
+bool use_pageidle = false;
+// hides map entries with zero rss
+bool hide_zeroes = false;
+// Reset working set and exit
+bool reset_wss = false;
+// Show working set, mutually exclusive with reset_wss;
+bool show_wss = false;
+
 static void usage(const char* cmd) {
     fprintf(stderr,
             "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
@@ -42,68 +55,56 @@
             cmd);
 }
 
-static void show_footer(uint32_t nelem, const std::string& padding) {
-    std::string elem(7, '-');
-
-    for (uint32_t i = 0; i < nelem; ++i) {
-        std::cout << std::setw(7) << elem << padding;
+static void print_separator(std::stringstream& ss) {
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "-------",
+                                            "-------", "-------", "-------", "-------", "-------",
+                                            "-------", "");
+        return;
     }
-    std::cout << std::endl;
+    ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "-------",
+                                        "-------", "-------", "-------", "-------", "-------",
+                                        "-------", "-------", "");
 }
 
-static void show_header(const std::vector<std::string>& header, const std::string& padding) {
-    if (header.empty()) return;
-
-    for (size_t i = 0; i < header.size() - 1; ++i) {
-        std::cout << std::setw(7) << header[i] << padding;
+static void print_header(std::stringstream& ss) {
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "WRss",
+                                            "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi",
+                                            "Name");
+    } else {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "Vss",
+                                            "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl", "PrDi",
+                                            "Name");
     }
-    std::cout << header.back() << std::endl;
-    show_footer(header.size() - 1, padding);
+    print_separator(ss);
 }
 
-static void scan_usage(std::stringstream& ss, const MemUsage& usage, const std::string& padding,
-                       bool show_wss) {
-    // clear string stream first.
-    ss.str("");
-    // TODO: use ::android::base::StringPrintf instead of <iomanip> here.
-    if (!show_wss)
-        ss << std::setw(6) << usage.vss/1024 << padding;
-    ss << std::setw(6) << usage.rss/1024 << padding << std::setw(6)
-       << usage.pss/1024 << padding << std::setw(6) << usage.uss/1024 << padding
-       << std::setw(6) << usage.shared_clean/1024 << padding << std::setw(6)
-       << usage.shared_dirty/1024 << padding << std::setw(6)
-       << usage.private_clean/1024 << padding << std::setw(6)
-       << usage.private_dirty/1024 << padding;
+static void print_stats(std::stringstream& ss, const MemUsage& stats) {
+    if (!show_wss) {
+        ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", stats.vss / 1024);
+    }
+
+    ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64
+                                        "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ",
+                                        stats.rss / 1024, stats.pss / 1024, stats.uss / 1024,
+                                        stats.shared_clean / 1024, stats.shared_dirty / 1024,
+                                        stats.private_clean / 1024, stats.private_dirty / 1024);
 }
 
-static int show(ProcMemInfo& proc, bool hide_zeroes, bool show_wss) {
-    const std::vector<std::string> main_header = {"Vss",  "Rss",  "Pss",  "Uss", "ShCl",
-                                                  "ShDi", "PrCl", "PrDi", "Name"};
-    const std::vector<std::string> wss_header = {"WRss",  "WPss",  "WUss",  "WShCl",
-                                                 "WShDi", "WPrCl", "WPrDi", "Name"};
-    const std::vector<std::string>& header = show_wss ? wss_header : main_header;
-
-    // Read process memory stats
-    const MemUsage& stats = show_wss ? proc.Wss() : proc.Usage();
-    const std::vector<::android::meminfo::Vma>& maps = proc.Maps();
-
-    // following retains 'procmem' output so as to not break any scripts
-    // that rely on it.
-    std::string spaces = "  ";
-    show_header(header, spaces);
-    const std::string padding = "K  ";
+static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
     std::stringstream ss;
+    print_header(ss);
     for (auto& vma : maps) {
         const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
         if (hide_zeroes && vma_stats.rss == 0) {
             continue;
         }
-        scan_usage(ss, vma_stats, padding, show_wss);
+        print_stats(ss, vma_stats);
         ss << vma.name << std::endl;
-        std::cout << ss.str();
     }
-    show_footer(header.size() - 1, spaces);
-    scan_usage(ss, stats, padding, show_wss);
+    print_separator(ss);
+    print_stats(ss, proc_stats);
     ss << "TOTAL" << std::endl;
     std::cout << ss.str();
 
@@ -111,28 +112,43 @@
 }
 
 int main(int argc, char* argv[]) {
-    bool use_pageidle = false;
-    bool hide_zeroes = false;
-    bool wss_reset = false;
-    bool show_wss = false;
     int opt;
+    auto pss_sort = [](const Vma& a, const Vma& b) {
+        uint64_t pss_a = show_wss ? a.wss.pss : a.usage.pss;
+        uint64_t pss_b = show_wss ? b.wss.pss : b.usage.pss;
+        return pss_a > pss_b;
+    };
 
+    auto uss_sort = [](const Vma& a, const Vma& b) {
+        uint64_t uss_a = show_wss ? a.wss.uss : a.usage.uss;
+        uint64_t uss_b = show_wss ? b.wss.uss : b.usage.uss;
+        return uss_a > uss_b;
+    };
+
+    std::function<bool(const Vma& a, const Vma& b)> sort_func = nullptr;
     while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
         switch (opt) {
             case 'h':
                 hide_zeroes = true;
                 break;
             case 'i':
+                // TODO: libmeminfo doesn't support the flag to chose
+                // between idle page tracking vs clear_refs. So for now,
+                // this flag is unused and the library defaults to using
+                // /proc/<pid>/clear_refs for finding the working set.
                 use_pageidle = true;
                 break;
             case 'm':
+                // this is the default
                 break;
             case 'p':
+                sort_func = pss_sort;
                 break;
             case 'u':
+                sort_func = uss_sort;
                 break;
             case 'W':
-                wss_reset = true;
+                reset_wss = true;
                 break;
             case 'w':
                 show_wss = true;
@@ -157,15 +173,20 @@
         exit(EXIT_FAILURE);
     }
 
-    bool need_wss = wss_reset || show_wss;
-    ProcMemInfo proc(pid, need_wss);
-    if (wss_reset) {
-        if (!proc.WssReset()) {
+    if (reset_wss) {
+        if (!ProcMemInfo::ResetWorkingSet(pid)) {
             std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
             exit(EXIT_FAILURE);
         }
         return 0;
     }
 
-    return show(proc, hide_zeroes, show_wss);
+    ProcMemInfo proc(pid, show_wss);
+    const MemUsage& proc_stats = show_wss ? proc.Wss() : proc.Usage();
+    std::vector<Vma> maps(proc.Maps());
+    if (sort_func != nullptr) {
+        std::sort(maps.begin(), maps.end(), sort_func);
+    }
+
+    return show(proc_stats, maps);
 }
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
new file mode 100644
index 0000000..e39e7fa
--- /dev/null
+++ b/libmeminfo/tools/procrank.cpp
@@ -0,0 +1,530 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <linux/oom.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+
+struct ProcessRecord {
+  public:
+    ProcessRecord(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0)
+        : pid_(-1),
+          procmem_(nullptr),
+          oomadj_(OOM_SCORE_ADJ_MAX + 1),
+          cmdline_(""),
+          proportional_swap_(0),
+          unique_swap_(0),
+          zswap_(0) {
+        std::unique_ptr<ProcMemInfo> procmem =
+                std::make_unique<ProcMemInfo>(pid, get_wss, pgflags, pgflags_mask);
+        if (procmem == nullptr) {
+            std::cerr << "Failed to create ProcMemInfo for: " << pid << std::endl;
+            return;
+        }
+
+        std::string fname = ::android::base::StringPrintf("/proc/%d/oom_score_adj", pid);
+        auto oomscore_fp =
+                std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname.c_str(), "re"), fclose};
+        if (oomscore_fp == nullptr) {
+            std::cerr << "Failed to open oom_score_adj file: " << fname << std::endl;
+            return;
+        }
+
+        if (fscanf(oomscore_fp.get(), "%d\n", &oomadj_) != 1) {
+            std::cerr << "Failed to read oomadj from: " << fname << std::endl;
+            return;
+        }
+
+        fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+        if (!::android::base::ReadFileToString(fname, &cmdline_)) {
+            std::cerr << "Failed to read cmdline from: " << fname << std::endl;
+            cmdline_ = "<unknown>";
+        }
+        // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
+        // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
+        // e.g. xtra-daemon, lowi-server
+        // The .c_str() assignment below then takes care of trimming the cmdline at the first
+        // 0x00. This is how original procrank worked (luckily)
+        cmdline_.resize(strlen(cmdline_.c_str()));
+        procmem_ = std::move(procmem);
+        pid_ = pid;
+    }
+
+    bool valid() const { return pid_ != -1; }
+
+    void CalculateSwap(const uint16_t* swap_offset_array, float zram_compression_ratio) {
+        const std::vector<uint16_t>& swp_offs = procmem_->SwapOffsets();
+        for (auto& off : swp_offs) {
+            proportional_swap_ += getpagesize() / swap_offset_array[off];
+            unique_swap_ += swap_offset_array[off] == 1 ? getpagesize() : 0;
+            zswap_ = proportional_swap_ * zram_compression_ratio;
+        }
+    }
+
+    // Getters
+    pid_t pid() const { return pid_; }
+    const std::string& cmdline() const { return cmdline_; }
+    int32_t oomadj() const { return oomadj_; }
+    uint64_t proportional_swap() const { return proportional_swap_; }
+    uint64_t unique_swap() const { return unique_swap_; }
+    uint64_t zswap() const { return zswap_; }
+
+    // Wrappers to ProcMemInfo
+    const std::vector<uint16_t>& SwapOffsets() const { return procmem_->SwapOffsets(); }
+    const MemUsage& Usage() const { return procmem_->Usage(); }
+    const MemUsage& Wss() const { return procmem_->Wss(); }
+
+  private:
+    pid_t pid_;
+    std::unique_ptr<ProcMemInfo> procmem_;
+    int32_t oomadj_;
+    std::string cmdline_;
+    uint64_t proportional_swap_;
+    uint64_t unique_swap_;
+    uint64_t zswap_;
+};
+
+// Show working set instead of memory consumption
+bool show_wss = false;
+// Reset working set of each process
+bool reset_wss = false;
+// Show per-process oom_score_adj column
+bool show_oomadj = false;
+// True if the device has swap enabled
+bool has_swap = false;
+// True, if device has zram enabled
+bool has_zram = false;
+// If zram is enabled, the compression ratio is zram used / swap used.
+float zram_compression_ratio = 0.0;
+// Sort process in reverse, default is descending
+bool reverse_sort = false;
+
+// Calculated total memory usage across all processes in the system
+uint64_t total_pss = 0;
+uint64_t total_uss = 0;
+uint64_t total_swap = 0;
+uint64_t total_pswap = 0;
+uint64_t total_uswap = 0;
+uint64_t total_zswap = 0;
+
+static void usage(const char* myname) {
+    std::cerr << "Usage: " << myname << " [ -W ] [ -v | -r | -p | -u | -s | -h ]" << std::endl
+              << "    -v  Sort by VSS." << std::endl
+              << "    -r  Sort by RSS." << std::endl
+              << "    -p  Sort by PSS." << std::endl
+              << "    -u  Sort by USS." << std::endl
+              << "    -s  Sort by swap." << std::endl
+              << "        (Default sort order is PSS.)" << std::endl
+              << "    -R  Reverse sort order (default is descending)." << std::endl
+              << "    -c  Only show cached (storage backed) pages" << std::endl
+              << "    -C  Only show non-cached (ram/swap backed) pages" << std::endl
+              << "    -k  Only show pages collapsed by KSM" << std::endl
+              << "    -w  Display statistics for working set only." << std::endl
+              << "    -W  Reset working set of all processes." << std::endl
+              << "    -o  Show and sort by oom score against lowmemorykiller thresholds."
+              << std::endl
+              << "    -h  Display this help screen." << std::endl;
+}
+
+static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) {
+    pids->clear();
+    std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+    if (!procdir) return false;
+
+    struct dirent* dir;
+    pid_t pid;
+    while ((dir = readdir(procdir.get()))) {
+        if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+        if (!for_each_pid(pid)) return false;
+        pids->push_back(pid);
+    }
+
+    return true;
+}
+
+static bool count_swap_offsets(const ProcessRecord& proc, uint16_t* swap_offset_array,
+                               uint32_t size) {
+    const std::vector<uint16_t>& swp_offs = proc.SwapOffsets();
+    for (auto& off : swp_offs) {
+        if (off >= size) {
+            std::cerr << "swap offset " << off << " is out of bounds for process: " << proc.pid()
+                      << std::endl;
+            return false;
+        }
+
+        if (swap_offset_array[off] == USHRT_MAX) {
+            std::cerr << "swap offset " << off << " ref count overflow in process: " << proc.pid()
+                      << std::endl;
+            return false;
+        }
+
+        swap_offset_array[off]++;
+    }
+
+    return true;
+}
+
+static void print_header(std::stringstream& ss) {
+    ss.str("");
+    ss << ::android::base::StringPrintf("%5s  ", "PID");
+    if (show_oomadj) {
+        ss << ::android::base::StringPrintf("%5s  ", "oom");
+    }
+
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss");
+        // now swap statistics here, working set pages by definition shouldn't end up in swap.
+    } else {
+        ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
+        if (has_swap) {
+            ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap");
+            if (has_zram) {
+                ss << ::android::base::StringPrintf("%7s  ", "ZSwap");
+            }
+        }
+    }
+
+    ss << "cmdline";
+}
+
+static void print_process_record(std::stringstream& ss, ProcessRecord& proc) {
+    ss << ::android::base::StringPrintf("%5d  ", proc.pid());
+    if (show_oomadj) {
+        ss << ::android::base::StringPrintf("%5d  ", proc.oomadj());
+    }
+
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ",
+                                            proc.Wss().rss / 1024, proc.Wss().pss / 1024,
+                                            proc.Wss().uss / 1024);
+    } else {
+        ss << ::android::base::StringPrintf("%7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64
+                                            "K  ",
+                                            proc.Usage().vss / 1024, proc.Usage().rss / 1024,
+                                            proc.Usage().pss / 1024, proc.Usage().uss / 1024);
+        if (has_swap) {
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.Usage().swap / 1024);
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.proportional_swap() / 1024);
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", proc.unique_swap() / 1024);
+            if (has_zram) {
+                ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", (proc.zswap() / 1024));
+            }
+        }
+    }
+}
+
+static void print_processes(std::stringstream& ss, std::vector<ProcessRecord>& procs,
+                            uint16_t* swap_offset_array) {
+    for (auto& proc : procs) {
+        total_pss += show_wss ? proc.Wss().pss : proc.Usage().pss;
+        total_uss += show_wss ? proc.Wss().uss : proc.Usage().uss;
+        if (!show_wss && has_swap) {
+            proc.CalculateSwap(swap_offset_array, zram_compression_ratio);
+            total_swap += proc.Usage().swap;
+            total_pswap += proc.proportional_swap();
+            total_uswap += proc.unique_swap();
+            if (has_zram) {
+                total_zswap += proc.zswap();
+            }
+        }
+
+        print_process_record(ss, proc);
+        ss << proc.cmdline() << std::endl;
+    }
+}
+
+static void print_separator(std::stringstream& ss) {
+    ss << ::android::base::StringPrintf("%5s  ", "");
+    if (show_oomadj) {
+        ss << ::android::base::StringPrintf("%5s  ", "");
+    }
+
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "", "------", "------");
+    } else {
+        ss << ::android::base::StringPrintf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
+        if (has_swap) {
+            ss << ::android::base::StringPrintf("%7s  %7s  %7s  ", "------", "------", "------");
+            if (has_zram) {
+                ss << ::android::base::StringPrintf("%7s  ", "------");
+            }
+        }
+    }
+
+    ss << ::android::base::StringPrintf("%s", "------");
+}
+
+static void print_totals(std::stringstream& ss) {
+    ss << ::android::base::StringPrintf("%5s  ", "");
+    if (show_oomadj) {
+        ss << ::android::base::StringPrintf("%5s  ", "");
+    }
+
+    if (show_wss) {
+        ss << ::android::base::StringPrintf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
+                                            total_pss / 1024, total_uss / 1024);
+    } else {
+        ss << ::android::base::StringPrintf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ", "", "",
+                                            total_pss / 1024, total_uss / 1024);
+        if (has_swap) {
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_swap / 1024);
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_pswap / 1024);
+            ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_uswap / 1024);
+            if (has_zram) {
+                ss << ::android::base::StringPrintf("%6" PRIu64 "K  ", total_zswap / 1024);
+            }
+        }
+    }
+    ss << "TOTAL";
+}
+
+static void print_sysmeminfo(std::stringstream& ss, ::android::meminfo::SysMemInfo& smi) {
+    if (has_swap) {
+        ss << ::android::base::StringPrintf("ZRAM: %" PRIu64 "K physical used for %" PRIu64
+                                            "K in swap "
+                                            "(%" PRIu64 "K total swap)",
+                                            smi.mem_zram_kb(),
+                                            (smi.mem_swap_kb() - smi.mem_swap_free_kb()),
+                                            smi.mem_swap_kb())
+           << std::endl;
+    }
+
+    ss << ::android::base::StringPrintf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64
+                                        "K buffers, "
+                                        "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64
+                                        "K slab",
+                                        smi.mem_total_kb(), smi.mem_free_kb(), smi.mem_buffers_kb(),
+                                        smi.mem_cached_kb(), smi.mem_shmem_kb(), smi.mem_slab_kb());
+}
+
+int main(int argc, char* argv[]) {
+    auto pss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.pss < stats_b.pss : stats_a.pss > stats_b.pss;
+    };
+
+    auto uss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.uss < stats_b.uss : stats_a.uss > stats_b.uss;
+    };
+
+    auto rss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.rss < stats_b.pss : stats_a.pss > stats_b.pss;
+    };
+
+    auto vss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.vss < stats_b.vss : stats_a.vss > stats_b.vss;
+    };
+
+    auto swap_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+        MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+        return reverse_sort ? stats_a.swap < stats_b.swap : stats_a.swap > stats_b.swap;
+    };
+
+    auto oomadj_sort = [](ProcessRecord& a, ProcessRecord& b) {
+        return reverse_sort ? a.oomadj() < b.oomadj() : a.oomadj() > b.oomadj();
+    };
+
+    // default PSS sort
+    std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort = pss_sort;
+
+    // count all pages by default
+    uint64_t pgflags = 0;
+    uint64_t pgflags_mask = 0;
+
+    int opt;
+    while ((opt = getopt(argc, argv, "cChkoprRsuvwW")) != -1) {
+        switch (opt) {
+            case 'c':
+                pgflags = 0;
+                pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'C':
+                pgflags = (1 << KPF_SWAPBACKED);
+                pgflags_mask = (1 << KPF_SWAPBACKED);
+                break;
+            case 'h':
+                usage(argv[0]);
+                return 0;
+                break;
+            case 'k':
+                pgflags = (1 << KPF_KSM);
+                pgflags_mask = (1 << KPF_KSM);
+                break;
+            case 'o':
+                proc_sort = oomadj_sort;
+                show_oomadj = true;
+                break;
+            case 'p':
+                proc_sort = pss_sort;
+                break;
+            case 'r':
+                proc_sort = rss_sort;
+                break;
+            case 'R':
+                reverse_sort = true;
+                break;
+            case 's':
+                proc_sort = swap_sort;
+                break;
+            case 'u':
+                proc_sort = uss_sort;
+                break;
+            case 'v':
+                proc_sort = vss_sort;
+                break;
+            case 'w':
+                show_wss = true;
+                break;
+            case 'W':
+                reset_wss = true;
+                break;
+            default:
+                abort();
+        }
+    }
+
+    std::vector<pid_t> pids;
+    std::vector<ProcessRecord> procs;
+    if (reset_wss) {
+        if (!read_all_pids(&pids,
+                           [&](pid_t pid) -> bool { return ProcMemInfo::ResetWorkingSet(pid); })) {
+            std::cerr << "Failed to reset working set of all processes" << std::endl;
+            exit(EXIT_FAILURE);
+        }
+        // we are done, all other options passed to procrank are ignored in the presence of '-W'
+        return 0;
+    }
+
+    ::android::meminfo::SysMemInfo smi;
+    if (!smi.ReadMemInfo()) {
+        std::cerr << "Failed to get system memory info" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    // Figure out swap and zram
+    uint64_t swap_total = smi.mem_swap_kb() * 1024;
+    has_swap = swap_total > 0;
+    // Allocate the swap array
+    auto swap_offset_array = std::make_unique<uint16_t[]>(swap_total / getpagesize());
+    if (has_swap) {
+        has_zram = smi.mem_zram_kb() > 0;
+        if (has_zram) {
+            zram_compression_ratio = static_cast<float>(smi.mem_zram_kb()) /
+                                     (smi.mem_swap_kb() - smi.mem_swap_free_kb());
+        }
+    }
+
+    auto mark_swap_usage = [&](pid_t pid) -> bool {
+        ProcessRecord proc(pid, show_wss, pgflags, pgflags_mask);
+        if (!proc.valid()) {
+            std::cerr << "Failed to create process record for: " << pid << std::endl;
+            return false;
+        }
+
+        // Skip processes with no memory mappings
+        uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss;
+        if (vss == 0) return true;
+
+        // collect swap_offset counts from all processes in 1st pass
+        if (!show_wss && has_swap &&
+            !count_swap_offsets(proc, swap_offset_array.get(), swap_total / getpagesize())) {
+            std::cerr << "Failed to count swap offsets for process: " << pid << std::endl;
+            return false;
+        }
+
+        procs.push_back(std::move(proc));
+        return true;
+    };
+
+    // Get a list of all pids currently running in the system in
+    // 1st pass through all processes. Mark each swap offset used by the process as we find them
+    // for calculating proportional swap usage later.
+    if (!read_all_pids(&pids, mark_swap_usage)) {
+        std::cerr << "Failed to read all pids from the system" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    std::stringstream ss;
+    if (procs.empty()) {
+        // This would happen in corner cases where procrank is being run to find KSM usage on a
+        // system with no KSM and combined with working set determination as follows
+        //   procrank -w -u -k
+        //   procrank -w -s -k
+        //   procrank -w -o -k
+        ss << "<empty>" << std::endl << std::endl;
+        print_sysmeminfo(ss, smi);
+        ss << std::endl;
+        std::cout << ss.str();
+        return 0;
+    }
+
+    // Sort all process records, default is PSS descending
+    std::sort(procs.begin(), procs.end(), proc_sort);
+
+    // start dumping output in string stream
+    print_header(ss);
+    ss << std::endl;
+
+    // 2nd pass to calculate and get per process stats to add them up
+    print_processes(ss, procs, swap_offset_array.get());
+
+    // Add separator to output
+    print_separator(ss);
+    ss << std::endl;
+
+    // Add totals to output
+    print_totals(ss);
+    ss << std::endl << std::endl;
+
+    // Add system information at the end
+    print_sysmeminfo(ss, smi);
+    ss << std::endl;
+
+    // dump on the screen
+    std::cout << ss.str();
+
+    return 0;
+}
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index a046dad..89837f7 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -35,6 +35,13 @@
     end = begin + 1;
   }
   Range range{begin, end};
+  if (valid_mappings_range_.end != 0 &&
+      (begin < valid_mappings_range_.begin || end > valid_mappings_range_.end)) {
+    MEM_LOG_ALWAYS_FATAL("allocation %p-%p is outside mapping range %p-%p",
+                         reinterpret_cast<void*>(begin), reinterpret_cast<void*>(end),
+                         reinterpret_cast<void*>(valid_mappings_range_.begin),
+                         reinterpret_cast<void*>(valid_mappings_range_.end));
+  }
   auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
   if (inserted.second) {
     valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
@@ -87,6 +94,11 @@
   }
 }
 
+void HeapWalker::Mapping(uintptr_t begin, uintptr_t end) {
+  valid_mappings_range_.begin = std::min(valid_mappings_range_.begin, begin);
+  valid_mappings_range_.end = std::max(valid_mappings_range_.end, end);
+}
+
 void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
   roots_.push_back(Range{begin, end});
 }
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index b37cc62..9e3db08 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -59,6 +59,8 @@
         segv_page_count_(0) {
     valid_allocations_range_.end = 0;
     valid_allocations_range_.begin = ~valid_allocations_range_.end;
+    valid_mappings_range_.end = 0;
+    valid_mappings_range_.begin = ~valid_allocations_range_.end;
 
     segv_handler_.install(
         SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
@@ -68,6 +70,7 @@
 
   ~HeapWalker() {}
   bool Allocation(uintptr_t begin, uintptr_t end);
+  void Mapping(uintptr_t begin, uintptr_t end);
   void Root(uintptr_t begin, uintptr_t end);
   void Root(const allocator::vector<uintptr_t>& vals);
 
@@ -98,6 +101,7 @@
   AllocationMap allocations_;
   size_t allocation_bytes_;
   Range valid_allocations_range_;
+  Range valid_mappings_range_;
 
   allocator::vector<Range> roots_;
   allocator::vector<uintptr_t> root_vals_;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index b160de9..3d7b8a8 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -87,6 +87,11 @@
                                         const allocator::vector<Mapping>& mappings,
                                         const allocator::vector<uintptr_t>& refs) {
   MEM_ALOGI("searching process %d for allocations", pid_);
+
+  for (auto it = mappings.begin(); it != mappings.end(); it++) {
+    heap_walker_.Mapping(it->begin, it->end);
+  }
+
   allocator::vector<Mapping> heap_mappings{mappings};
   allocator::vector<Mapping> anon_mappings{mappings};
   allocator::vector<Mapping> globals_mappings{mappings};
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 84a0ec6..9610cd6 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -73,6 +73,24 @@
   ASSERT_FALSE(heap_walker.Allocation(2, 3));
 }
 
+TEST_F(HeapWalkerTest, mapping) {
+  HeapWalker heap_walker(heap_);
+  heap_walker.Mapping(2, 3);
+  heap_walker.Mapping(4, 5);
+  ASSERT_TRUE(heap_walker.Allocation(2, 3));
+  ASSERT_TRUE(heap_walker.Allocation(4, 5));
+  // space between mappings is not checked, but could be in the future
+  ASSERT_TRUE(heap_walker.Allocation(3, 4));
+
+  // re-enable malloc, ASSERT_DEATH may allocate
+  disable_malloc_.Enable();
+  ASSERT_DEATH({ heap_walker.Allocation(1, 2); }, "0x1-0x2.*outside.*0x2-0x5");
+  ASSERT_DEATH({ heap_walker.Allocation(1, 3); }, "0x1-0x3.*outside.*0x2-0x5");
+  ASSERT_DEATH({ heap_walker.Allocation(4, 6); }, "0x4-0x6.*outside.*0x2-0x5");
+  ASSERT_DEATH({ heap_walker.Allocation(5, 6); }, "0x5-0x6.*outside.*0x2-0x5");
+  ASSERT_DEATH({ heap_walker.Allocation(1, 6); }, "0x1-0x6.*outside.*0x2-0x5");
+}
+
 #define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
 #define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))
 
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 56bd6c4..71c04a6 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -116,7 +116,7 @@
     FIELD_BATTERY_RESISTANCE_UOHMS = 1448,
     FIELD_BATTERY_CURRENT_UA = 1449,
     FIELD_HARDWARE_LOCATION = 1450,
-    ACTION_BATTERY_CAUSED_SHUTDOWN = 1441,
+    ACTION_BATTERY_CAUSED_SHUTDOWN = 1451,
 };
 
 enum {
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index 8516640..32691a3 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -61,7 +61,11 @@
 #define USAGE_ERROR_ACTION(m,p) \
     heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
+#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
 #include "../../../../external/dlmalloc/malloc.c"
+#pragma GCC diagnostic pop
 
 static void heap_error(const char* msg, const char* function, void* p) {
     ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: CODE FLINGER: %s IN %s addr=%p",
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index 0fc4201..981241e 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <stdlib.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index fe314b3..5b8179f 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -508,7 +508,7 @@
 
   out->len = len;
   out->block_size = block_size;
-  out->cur_out_ptr = 0ll;
+  out->cur_out_ptr = 0LL;
   out->chunk_cnt = 0;
   out->crc32 = 0;
   out->use_crc = crc;
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index a9832db..41ca79b 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -26,7 +26,7 @@
 int write_to_logger(android_log_context context, log_id_t id);
 void note_log_drop();
 void stats_log_close();
-
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
 #ifdef __cplusplus
 }
 #endif
@@ -244,6 +244,14 @@
         return ret >= 0;
     }
 
+    bool AppendCharArray(const char* value, size_t len) {
+        int retval = android_log_write_char_array(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
     android_log_list_element read() { return android_log_read_next(ctx); }
     android_log_list_element peek() { return android_log_peek_next(ctx); }
 };
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 72770d4..f4a7e94 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -193,3 +193,47 @@
     errno = save_errno;
     return ret;
 }
+
+static inline void copy4LE(uint8_t* buf, uint32_t val) {
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+}
+
+// Note: this function differs from android_log_write_string8_len in that the length passed in
+// should be treated as actual length and not max length.
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t actual_len) {
+    size_t needed;
+    ssize_t len = actual_len;
+    android_log_context_internal* context;
+
+    context = (android_log_context_internal*)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    if (!value) {
+        value = "";
+        len = 0;
+    }
+    needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        /* Truncate string for delivery */
+        len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+        if (len <= 0) {
+            context->overflow = true;
+            return -EIO;
+        }
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+    copy4LE(&context->storage[context->pos + 1], len);
+    if (len) {
+        memcpy(&context->storage[context->pos + 5], value, len);
+    }
+    context->pos += needed;
+    return len;
+}
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 29d23c8..da5d86c 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
     name: "libsysutils",
     vendor_available: true,
     vndk: {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a5e0dc9..89d4fc0 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -94,7 +94,10 @@
                 "DexFile.cpp",
                 "DexFiles.cpp",
             ],
-            exclude_shared_libs: ["libdexfile"],
+            exclude_shared_libs: [
+                "libdexfile_external",
+                "libdexfile_support",
+            ],
         },
         recovery: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
@@ -102,7 +105,10 @@
                 "DexFile.cpp",
                 "DexFiles.cpp",
             ],
-            exclude_shared_libs: ["libdexfile"],
+            exclude_shared_libs: [
+                "libdexfile_external",
+                "libdexfile_support",
+            ],
         },
     },
 
@@ -127,7 +133,8 @@
 
     shared_libs: [
         "libbase",
-        "libdexfile",
+        "libdexfile_external",
+        "libdexfile_support",
         "liblog",
         "liblzma",
     ],
@@ -215,6 +222,7 @@
         "liblzma",
         "libunwindstack",
         "libdexfile",
+        "libdexfile_support",
     ],
 
     static_libs: [
@@ -235,6 +243,8 @@
         "tests/files/offline/jit_map_arm/*",
         "tests/files/offline/gnu_debugdata_arm/*",
         "tests/files/offline/offset_arm/*",
+        "tests/files/offline/shared_lib_in_apk_arm64/*",
+        "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index 8ec560c..9b0b232 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -23,13 +23,7 @@
 #include <memory>
 
 #include <android-base/unique_fd.h>
-
-#include <dex/class_accessor-inl.h>
-#include <dex/code_item_accessors-inl.h>
-#include <dex/compact_dex_file.h>
-#include <dex/dex_file-inl.h>
-#include <dex/dex_file_loader.h>
-#include <dex/standard_dex_file.h>
+#include <art_api/ext_dex_file.h>
 
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
@@ -38,169 +32,71 @@
 
 namespace unwindstack {
 
-DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info) {
+std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+                                         MapInfo* info) {
   if (!info->name.empty()) {
-    std::unique_ptr<DexFileFromFile> dex_file(new DexFileFromFile);
-    if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) {
-      return dex_file.release();
+    std::unique_ptr<DexFile> dex_file =
+        DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
+    if (dex_file) {
+      return dex_file;
     }
   }
-
-  std::unique_ptr<DexFileFromMemory> dex_file(new DexFileFromMemory);
-  if (dex_file->Open(dex_file_offset_in_memory, memory)) {
-    return dex_file.release();
-  }
-  return nullptr;
-}
-
-DexFileFromFile::~DexFileFromFile() {
-  if (size_ != 0) {
-    munmap(mapped_memory_, size_);
-  }
+  return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
 }
 
 bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
                                    uint64_t* method_offset) {
-  if (dex_file_ == nullptr) {
+  art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset);
+  if (method_info.offset == 0) {
     return false;
   }
-
-  if (!dex_file_->IsInDataSection(dex_file_->Begin() + dex_offset)) {
-    return false;  // The DEX offset is not within the bytecode of this dex file.
-  }
-
-  if (dex_file_->IsCompactDexFile()) {
-    // The data section of compact dex files might be shared.
-    // Check the subrange unique to this compact dex.
-    const auto& cdex_header = dex_file_->AsCompactDexFile()->GetHeader();
-    uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin();
-    uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd();
-    if (dex_offset < begin || dex_offset >= end) {
-      return false;  // The DEX offset is not within the bytecode of this dex file.
-    }
-  }
-
-  // The method data is cached in a std::map indexed by method end offset and
-  // contains the start offset and the method member index.
-  // Only cache the method data as it is searched. Do not read the entire
-  // set of method data into the cache at once.
-  // This is done because many unwinds only find a single frame with dex file
-  // info, so reading the entire method data is wasteful. However, still cache
-  // the data so that anything doing multiple unwinds will have this data
-  // cached for future use.
-
-  // First look in the method cache.
-  auto entry = method_cache_.upper_bound(dex_offset);
-  if (entry != method_cache_.end() && dex_offset >= entry->second.first) {
-    *method_name = dex_file_->PrettyMethod(entry->second.second, false);
-    *method_offset = dex_offset - entry->second.first;
-    return true;
-  }
-
-  // Check the methods we haven't cached.
-  for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) {
-    art::ClassAccessor accessor(*dex_file_, dex_file_->GetClassDef(class_def_index_));
-
-    for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
-      art::CodeItemInstructionAccessor code = method.GetInstructions();
-      if (!code.HasCodeItem()) {
-        continue;
-      }
-      uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
-      uint32_t offset_end = offset + code.InsnsSizeInBytes();
-      uint32_t member_index = method.GetIndex();
-      method_cache_[offset_end] = std::make_pair(offset, member_index);
-      if (offset <= dex_offset && dex_offset < offset_end) {
-        *method_name = dex_file_->PrettyMethod(member_index, false);
-        *method_offset = dex_offset - offset;
-        return true;
-      }
-    }
-  }
-  return false;
+  *method_name = method_info.name;
+  *method_offset = dex_offset - method_info.offset;
+  return true;
 }
 
-bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
+std::unique_ptr<DexFileFromFile> DexFileFromFile::Create(uint64_t dex_file_offset_in_file,
+                                                         const std::string& file) {
   android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
   if (fd == -1) {
-    return false;
-  }
-  struct stat buf;
-  if (fstat(fd, &buf) == -1) {
-    return false;
-  }
-  uint64_t length;
-  if (buf.st_size < 0 ||
-      __builtin_add_overflow(dex_file_offset_in_file, sizeof(art::DexFile::Header), &length) ||
-      static_cast<uint64_t>(buf.st_size) < length) {
-    return false;
+    return nullptr;
   }
 
-  mapped_memory_ = mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-  if (mapped_memory_ == MAP_FAILED) {
-    return false;
-  }
-  size_ = buf.st_size;
-
-  uint8_t* memory = reinterpret_cast<uint8_t*>(mapped_memory_);
-
-  art::DexFile::Header* header =
-      reinterpret_cast<art::DexFile::Header*>(&memory[dex_file_offset_in_file]);
-  if (!art::StandardDexFile::IsMagicValid(header->magic_) &&
-      !art::CompactDexFile::IsMagicValid(header->magic_)) {
-    return false;
-  }
-
-  if (__builtin_add_overflow(dex_file_offset_in_file, header->file_size_, &length) ||
-      static_cast<uint64_t>(buf.st_size) < length) {
-    return false;
-  }
-
-  art::DexFileLoader loader;
   std::string error_msg;
-  auto dex = loader.Open(&memory[dex_file_offset_in_file], header->file_size_, "", 0, nullptr,
-                         false, false, &error_msg);
-  dex_file_.reset(dex.release());
-  return dex_file_ != nullptr;
+  std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+      OpenFromFd(fd, dex_file_offset_in_file, file, &error_msg);
+  if (art_dex_file == nullptr) {
+    return nullptr;
+  }
+
+  return std::unique_ptr<DexFileFromFile>(new DexFileFromFile(std::move(*art_dex_file.release())));
 }
 
-bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
-  memory_.resize(sizeof(art::DexFile::Header));
-  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
-    return false;
-  }
+std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
+                                                             Memory* memory,
+                                                             const std::string& name) {
+  std::vector<uint8_t> backing_memory;
 
-  art::DexFile::Header* header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
-  uint32_t file_size = header->file_size_;
-  if (art::CompactDexFile::IsMagicValid(header->magic_)) {
-    // Compact dex file store data section separately so that it can be shared.
-    // Therefore we need to extend the read memory range to include it.
-    // TODO: This might be wasteful as we might read data in between as well.
-    //       In practice, this should be fine, as such sharing only happens on disk.
-    uint32_t computed_file_size;
-    if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
-      return false;
+  for (size_t size = 0;;) {
+    std::string error_msg;
+    std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+        OpenFromMemory(backing_memory.data(), &size, name, &error_msg);
+
+    if (art_dex_file != nullptr) {
+      return std::unique_ptr<DexFileFromMemory>(
+          new DexFileFromMemory(std::move(*art_dex_file.release()), std::move(backing_memory)));
     }
-    if (computed_file_size > file_size) {
-      file_size = computed_file_size;
+
+    if (!error_msg.empty()) {
+      return nullptr;
     }
-  } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
-    return false;
+
+    backing_memory.resize(size);
+    if (!memory->ReadFully(dex_file_offset_in_memory, backing_memory.data(),
+                           backing_memory.size())) {
+      return nullptr;
+    }
   }
-
-  memory_.resize(file_size);
-  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
-    return false;
-  }
-
-  header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
-
-  art::DexFileLoader loader;
-  std::string error_msg;
-  auto dex =
-      loader.Open(memory_.data(), header->file_size_, "", 0, nullptr, false, false, &error_msg);
-  dex_file_.reset(dex.release());
-  return dex_file_ != nullptr;
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index c123158..5797dee 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -25,48 +25,41 @@
 #include <utility>
 #include <vector>
 
-#include <dex/dex_file-inl.h>
+#include <art_api/ext_dex_file.h>
 
 namespace unwindstack {
 
-class DexFile {
+class DexFile : protected art_api::dex::DexFile {
  public:
-  DexFile() = default;
   virtual ~DexFile() = default;
 
   bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
 
-  static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
+  static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+                                         MapInfo* info);
 
  protected:
-  void Init();
-
-  std::unique_ptr<const art::DexFile> dex_file_;
-  std::map<uint32_t, std::pair<uint64_t, uint32_t>> method_cache_;  // dex offset to method index.
-
-  uint32_t class_def_index_ = 0;
+  DexFile(art_api::dex::DexFile&& art_dex_file) : art_api::dex::DexFile(std::move(art_dex_file)) {}
 };
 
 class DexFileFromFile : public DexFile {
  public:
-  DexFileFromFile() = default;
-  virtual ~DexFileFromFile();
-
-  bool Open(uint64_t dex_file_offset_in_file, const std::string& name);
+  static std::unique_ptr<DexFileFromFile> Create(uint64_t dex_file_offset_in_file,
+                                                 const std::string& file);
 
  private:
-  void* mapped_memory_ = nullptr;
-  size_t size_ = 0;
+  DexFileFromFile(art_api::dex::DexFile&& art_dex_file) : DexFile(std::move(art_dex_file)) {}
 };
 
 class DexFileFromMemory : public DexFile {
  public:
-  DexFileFromMemory() = default;
-  virtual ~DexFileFromMemory() = default;
-
-  bool Open(uint64_t dex_file_offset_in_memory, Memory* memory);
+  static std::unique_ptr<DexFileFromMemory> Create(uint64_t dex_file_offset_in_memory,
+                                                   Memory* memory, const std::string& name);
 
  private:
+  DexFileFromMemory(art_api::dex::DexFile&& art_dex_file, std::vector<uint8_t>&& memory)
+      : DexFile(std::move(art_dex_file)), memory_(std::move(memory)) {}
+
   std::vector<uint8_t> memory_;
 };
 
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index 451a0b9..63a77e5 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -48,11 +48,7 @@
 DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
     : Global(memory, search_libs) {}
 
-DexFiles::~DexFiles() {
-  for (auto& entry : files_) {
-    delete entry.second;
-  }
-}
+DexFiles::~DexFiles() {}
 
 void DexFiles::ProcessArch() {
   switch (arch()) {
@@ -137,10 +133,11 @@
   DexFile* dex_file;
   auto entry = files_.find(dex_file_offset);
   if (entry == files_.end()) {
-    dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
-    files_[dex_file_offset] = dex_file;
+    std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
+    dex_file = new_dex_file.get();
+    files_[dex_file_offset] = std::move(new_dex_file);
   } else {
-    dex_file = entry->second;
+    dex_file = entry->second.get();
   }
   return dex_file;
 }
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 0fa1638..4ba8a4b 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -260,7 +260,7 @@
   }
 
   // Log any of the expression data.
-  for (const auto line : expression_lines) {
+  for (const auto& line : expression_lines) {
     log(indent + 1, "%s", line.c_str());
   }
   return true;
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4d72ead..5b586a2 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -140,6 +140,10 @@
   return true;
 }
 
+bool Elf::GetBuildID(std::string* build_id) {
+  return valid_ && interface_->GetBuildID(build_id);
+}
+
 void Elf::GetLastError(ErrorData* data) {
   if (valid_) {
     *data = interface_->last_error();
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index f59a472..d0af94a 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -237,6 +237,56 @@
   }
 }
 
+template <typename NhdrType>
+bool ElfInterface::ReadBuildID(std::string* build_id) {
+  // Ensure there is no overflow in any of the calulations below.
+  uint64_t tmp;
+  if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
+    return false;
+  }
+
+  uint64_t offset = 0;
+  while (offset < gnu_build_id_size_) {
+    if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
+      return false;
+    }
+    NhdrType hdr;
+    if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
+      return false;
+    }
+    offset += sizeof(hdr);
+
+    if (gnu_build_id_size_ - offset < hdr.n_namesz) {
+      return false;
+    }
+    if (hdr.n_namesz > 0) {
+      std::string name(hdr.n_namesz, '\0');
+      if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
+        return false;
+      }
+
+      // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+      if (name.back() == '\0')
+        name.resize(name.size() - 1);
+
+      // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+      offset += (hdr.n_namesz + 3) & ~3;
+
+      if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+        if (gnu_build_id_size_ - offset < hdr.n_descsz) {
+          return false;
+        }
+        build_id->resize(hdr.n_descsz);
+        return memory_->ReadFully(gnu_build_id_offset_ + offset, &(*build_id)[0],
+                                  hdr.n_descsz);
+      }
+    }
+    // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+    offset += (hdr.n_descsz + 3) & ~3;
+  }
+  return false;
+}
+
 template <typename EhdrType, typename ShdrType>
 void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
   uint64_t offset = ehdr.e_shoff;
@@ -308,6 +358,15 @@
       // In order to read soname, keep track of address to offset mapping.
       strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr),
                                                             static_cast<uint64_t>(shdr.sh_offset)));
+    } else if (shdr.sh_type == SHT_NOTE) {
+      if (shdr.sh_name < sec_size) {
+        std::string name;
+        if (memory_->ReadString(sec_offset + shdr.sh_name, &name) &&
+            name == ".note.gnu.build-id") {
+          gnu_build_id_offset_ = shdr.sh_offset;
+          gnu_build_id_size_ = shdr.sh_size;
+        }
+      }
     }
   }
 }
@@ -492,6 +551,9 @@
 template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
 template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
 
+template bool ElfInterface::ReadBuildID<Elf32_Nhdr>(std::string*);
+template bool ElfInterface::ReadBuildID<Elf64_Nhdr>(std::string*);
+
 template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
 template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
 
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index 7a3de01..fdfd705 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 #include <sys/mman.h>
 
 #include <string>
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index e3b48ca..44ec5c1 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -29,6 +29,38 @@
 
 namespace unwindstack {
 
+bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
+  // One last attempt, see if the previous map is read-only with the
+  // same name and stretches across this map.
+  for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
+    if (*iter == this) {
+      if (iter == maps_->begin()) {
+        return false;
+      }
+      --iter;
+      MapInfo* prev_map = *iter;
+      // Make sure this is a read-only map.
+      if (prev_map->flags != PROT_READ) {
+        return false;
+      }
+      uint64_t map_size = end - prev_map->end;
+      if (!memory->Init(name, prev_map->offset, map_size)) {
+        return false;
+      }
+      uint64_t max_size;
+      if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
+        return false;
+      }
+      if (!memory->Init(name, prev_map->offset, max_size)) {
+        return false;
+      }
+      elf_offset = offset - prev_map->offset;
+      return true;
+    }
+  }
+  return false;
+}
+
 Memory* MapInfo::GetFileMemory() {
   std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
   if (offset == 0) {
@@ -38,8 +70,12 @@
     return nullptr;
   }
 
-  // There are two possibilities when the offset is non-zero.
-  // - There is an elf file embedded in a file.
+  // These are the possibilities when the offset is non-zero.
+  // - There is an elf file embedded in a file, and the offset is the
+  //   the start of the elf in the file.
+  // - There is an elf file embedded in a file, and the offset is the
+  //   the start of the executable part of the file. The actual start
+  //   of the elf is in the read-only segment preceeding this map.
   // - The whole file is an elf file, and the offset needs to be saved.
   //
   // Map in just the part of the file for the map. If this is not
@@ -53,27 +89,41 @@
     return nullptr;
   }
 
-  uint64_t max_size;
-  if (!Elf::GetInfo(memory.get(), &max_size)) {
-    // Init as if the whole file is an elf.
-    if (memory->Init(name, 0)) {
-      elf_offset = offset;
-      return memory.release();
+  // Check if the start of this map is an embedded elf.
+  uint64_t max_size = 0;
+  uint64_t file_offset = offset;
+  if (Elf::GetInfo(memory.get(), &max_size)) {
+    if (max_size > map_size) {
+      if (memory->Init(name, file_offset, max_size)) {
+        return memory.release();
+      }
+      // Try to reinit using the default map_size.
+      if (memory->Init(name, file_offset, map_size)) {
+        return memory.release();
+      }
+      return nullptr;
     }
-    return nullptr;
+    return memory.release();
   }
 
-  if (max_size > map_size) {
-    if (memory->Init(name, offset, max_size)) {
-      return memory.release();
-    }
-    // Try to reinit using the default map_size.
-    if (memory->Init(name, offset, map_size)) {
-      return memory.release();
-    }
-    return nullptr;
+  // No elf at offset, try to init as if the whole file is an elf.
+  if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
+    elf_offset = offset;
+    return memory.release();
   }
-  return memory.release();
+
+  // See if the map previous to this one contains a read-only map
+  // that represents the real start of the elf data.
+  if (InitFileMemoryFromPreviousReadOnlyMap(memory.get())) {
+    return memory.release();
+  }
+
+  // Failed to find elf at start of file or at read-only map, return
+  // file object from the current map.
+  if (memory->Init(name, offset, map_size)) {
+    return memory.release();
+  }
+  return nullptr;
 }
 
 Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
@@ -110,29 +160,27 @@
     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;
+  // Find the read-only map by looking at the previous map. The linker
+  // doesn't guarantee that this invariant will always be true. However,
+  // if that changes, there is likely something else that will change and
+  // break something.
   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;
+  for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
+    if (*iter == this) {
+      if (iter != maps_->begin()) {
+        --iter;
+        ro_map_info = *iter;
+      }
+      break;
     }
   }
 
-  if (ro_map_info == nullptr) {
+  if (ro_map_info == nullptr || ro_map_info->name != name || ro_map_info->offset >= offset) {
     return nullptr;
   }
 
   // Make sure that relative pc values are corrected properly.
-  elf_offset = offset - closest_offset;
+  elf_offset = offset - ro_map_info->offset;
 
   MemoryRanges* ranges = new MemoryRanges;
   ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 8729871..a9fb859 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index a30d65e..9904fef 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -16,6 +16,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/stat.h>
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index de22bde..885dc94 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index a68f6e0..e9787aa 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 2e6908c..14a4e31 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 0b835a1..3f67d92 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index ebad3f4..74cd1cb 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <functional>
 
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 14ebdbb..e3c15a2 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -17,6 +17,7 @@
 #include <elf.h>
 #include <stdint.h>
 
+#include <algorithm>
 #include <string>
 
 #include <unwindstack/Memory.h>
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index b3c5549..792cd0b 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -239,8 +239,14 @@
 
     if (!stepped) {
       if (return_address_attempt) {
-        // Remove the speculative frame.
-        frames_.pop_back();
+        // Only remove the speculative frame if there are more than two frames
+        // or the pc in the first frame is in a valid map.
+        // This allows for a case where the code jumps into the middle of
+        // nowhere, but there is no other unwind information after that.
+        if (frames_.size() != 2 || maps_->Find(frames_[0].pc) != nullptr) {
+          // Remove the speculative frame.
+          frames_.pop_back();
+        }
         break;
       } else if (in_device_map) {
         // Do not attempt any other unwinding, pc or sp is in a device
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index c202a33..0336173 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -66,7 +66,7 @@
 
   std::mutex lock_;
   bool initialized_ = false;
-  std::unordered_map<uint64_t, DexFile*> files_;
+  std::unordered_map<uint64_t, std::unique_ptr<DexFile>> files_;
 
   uint64_t entry_addr_ = 0;
   uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index e5b0a89..27f7201 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -65,6 +65,8 @@
 
   bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
 
+  bool GetBuildID(std::string* build_id);
+
   uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
 
   bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index a45eba8..52992d9 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -62,6 +62,8 @@
 
   virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
 
+  virtual bool GetBuildID(std::string* build_id) = 0;
+
   virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
   virtual bool IsValidPc(uint64_t pc);
@@ -85,6 +87,8 @@
   uint64_t debug_frame_size() { return debug_frame_size_; }
   uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
   uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
+  uint64_t gnu_build_id_offset() { return gnu_build_id_offset_; }
+  uint64_t gnu_build_id_size() { return gnu_build_id_size_; }
 
   DwarfSection* eh_frame() { return eh_frame_.get(); }
   DwarfSection* debug_frame() { return debug_frame_.get(); }
@@ -123,6 +127,9 @@
   template <typename EhdrType>
   static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
 
+  template <typename NhdrType>
+  bool ReadBuildID(std::string* build_id);
+
   Memory* memory_;
   std::unordered_map<uint64_t, LoadInfo> pt_loads_;
 
@@ -143,6 +150,9 @@
   uint64_t gnu_debugdata_offset_ = 0;
   uint64_t gnu_debugdata_size_ = 0;
 
+  uint64_t gnu_build_id_offset_ = 0;
+  uint64_t gnu_build_id_size_ = 0;
+
   uint8_t soname_type_ = SONAME_UNKNOWN;
   std::string soname_;
 
@@ -182,6 +192,10 @@
     return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
   }
 
+  bool GetBuildID(std::string* build_id) {
+    return ElfInterface::ReadBuildID<Elf32_Nhdr>(build_id);
+  }
+
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
   }
@@ -212,6 +226,10 @@
     return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
   }
 
+  bool GetBuildID(std::string* build_id) {
+    return ElfInterface::ReadBuildID<Elf64_Nhdr>(build_id);
+  }
+
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
   }
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 9c6b552..cfdefd0 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -83,6 +83,7 @@
   void operator=(const MapInfo&) = delete;
 
   Memory* GetFileMemory();
+  bool InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory);
 
   // Protect the creation of the elf object.
   std::mutex mutex_;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 95d2176..21ca47b 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -21,44 +21,37 @@
 #include <unordered_map>
 
 #include <android-base/file.h>
-
+#include <dex/dex_file.h>
+#include <gtest/gtest.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Memory.h>
 
-#include <dex/code_item_accessors-inl.h>
-#include <dex/standard_dex_file.h>
-
-#include <gtest/gtest.h>
-
 #include "DexFile.h"
-
 #include "DexFileData.h"
 #include "MemoryFake.h"
 
 namespace unwindstack {
 
 TEST(DexFileTest, from_file_open_non_exist) {
-  DexFileFromFile dex_file;
-  ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
+  EXPECT_TRUE(DexFileFromFile::Create(0, "/file/does/not/exist") == nullptr);
 }
 
 TEST(DexFileTest, from_file_open_too_small) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
 
-  ASSERT_EQ(sizeof(art::DexFile::Header) - 2,
+  ASSERT_EQ(sizeof(art::DexFile::Header) - 1,
             static_cast<size_t>(
-                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)) - 2)));
+                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header) - 1))));
 
   // Header too small.
-  DexFileFromFile dex_file;
-  ASSERT_FALSE(dex_file.Open(0, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 
   // Header correct, file too small.
   ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
   ASSERT_EQ(sizeof(art::DexFile::Header), static_cast<size_t>(TEMP_FAILURE_RETRY(write(
                                               tf.fd, kDexData, sizeof(art::DexFile::Header)))));
-  ASSERT_FALSE(dex_file.Open(0, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
 }
 
 TEST(DexFileTest, from_file_open) {
@@ -68,8 +61,7 @@
   ASSERT_EQ(sizeof(kDexData),
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
-  DexFileFromFile dex_file;
-  ASSERT_TRUE(dex_file.Open(0, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) != nullptr);
 }
 
 TEST(DexFileTest, from_file_open_non_zero_offset) {
@@ -80,35 +72,31 @@
   ASSERT_EQ(sizeof(kDexData),
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
-  DexFileFromFile dex_file;
-  ASSERT_TRUE(dex_file.Open(0x100, tf.path));
+  EXPECT_TRUE(DexFileFromFile::Create(0x100, tf.path) != nullptr);
 }
 
 TEST(DexFileTest, from_memory_fail_too_small_for_header) {
   MemoryFake memory;
 
   memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
-  DexFileFromMemory dex_file;
 
-  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
 }
 
 TEST(DexFileTest, from_memory_fail_too_small_for_data) {
   MemoryFake memory;
 
   memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
-  DexFileFromMemory dex_file;
 
-  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
 }
 
 TEST(DexFileTest, from_memory_open) {
   MemoryFake memory;
 
   memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
-  DexFileFromMemory dex_file;
 
-  ASSERT_TRUE(dex_file.Open(0x1000, &memory));
+  EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
 }
 
 TEST(DexFileTest, create_using_file) {
@@ -121,8 +109,7 @@
 
   MemoryFake memory;
   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);
+  EXPECT_TRUE(DexFile::Create(0x500, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_file_non_zero_start) {
@@ -135,8 +122,7 @@
 
   MemoryFake memory;
   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);
+  EXPECT_TRUE(DexFile::Create(0x600, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_file_non_zero_offset) {
@@ -149,24 +135,21 @@
 
   MemoryFake memory;
   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);
+  EXPECT_TRUE(DexFile::Create(0x400, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_empty_file) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
   MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
-  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
-  ASSERT_TRUE(dex_file != nullptr);
+  EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
   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);
+  EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_is_malformed) {
@@ -179,22 +162,13 @@
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
   MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
-  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+  std::unique_ptr<DexFile> dex_file = DexFile::Create(0x4000, &memory, &info);
   ASSERT_TRUE(dex_file != nullptr);
 
   // Check it came from memory by clearing memory and verifying it fails.
   memory.Clear();
-  dex_file.reset(DexFile::Create(0x4000, &memory, &info));
-  ASSERT_TRUE(dex_file == nullptr);
-}
-
-TEST(DexFileTest, get_method_not_opened) {
-  std::string method("something");
-  uint64_t method_offset = 100;
-  DexFile dex_file;
-  dex_file.GetMethodInformation(0x100, &method, &method_offset);
-  EXPECT_EQ("something", method);
-  EXPECT_EQ(100U, method_offset);
+  dex_file = DexFile::Create(0x4000, &memory, &info);
+  EXPECT_TRUE(dex_file == nullptr);
 }
 
 TEST(DexFileTest, get_method) {
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index a3bf5ce..c2bd0f6 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -72,6 +72,9 @@
 
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
   bool GetGlobalVariable(const std::string&, uint64_t*) override;
+  bool GetBuildID(std::string*) override {
+    return false;
+  }
 
   bool Step(uint64_t, Regs*, Memory*, bool*) override;
 
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 9326bff..6023dc4 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -116,6 +116,21 @@
   void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
                uint64_t sym_offset, const char* name);
 
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildID();
+
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildIDTwoNotes();
+
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildIDSectionTooSmallForName();
+
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildIDSectionTooSmallForDesc();
+
+  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+  void BuildIDSectionTooSmallForHeader();
+
   MemoryFake memory_;
 };
 
@@ -898,7 +913,7 @@
 
   Ehdr ehdr = {};
   ehdr.e_shoff = offset;
-  ehdr.e_shnum = 6;
+  ehdr.e_shnum = 7;
   ehdr.e_shentsize = sizeof(Shdr);
   ehdr.e_shstrndx = 2;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
@@ -958,10 +973,19 @@
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
   offset += ehdr.e_shentsize;
 
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = 0xf00;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
   memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
   memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
   memory_.SetMemory(0xf300, ".eh_frame", sizeof(".eh_frame"));
   memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
@@ -974,6 +998,8 @@
   EXPECT_EQ(0x800U, elf->eh_frame_size());
   EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
   EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size());
+  EXPECT_EQ(0xb000U, elf->gnu_build_id_offset());
+  EXPECT_EQ(0xf00U, elf->gnu_build_id_size());
 }
 
 TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
@@ -1153,4 +1179,321 @@
   EXPECT_FALSE(elf->IsValidPc(0x2a00));
 }
 
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildID() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
+  note_offset += sizeof("BUILDID");
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_section);
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  std::string build_id;
+  ASSERT_TRUE(elf->GetBuildID(&build_id));
+  EXPECT_STREQ(build_id.c_str(), "BUILDID");
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDTwoNotes() {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 8;  // "WRONG" aligned to 4
+  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "WRONG", sizeof("WRONG"));
+  note_offset += 8;
+  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
+  note_offset += sizeof("BUILDID");
+
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section[note_offset], &note_header, sizeof(note_header));
+  note_offset += sizeof(note_header);
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
+  note_offset += sizeof("BUILDID");
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_section);
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  std::string build_id;
+  ASSERT_TRUE(elf->GetBuildID(&build_id));
+  EXPECT_STREQ(build_id.c_str(), "BUILDID");
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForName () {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
+  note_offset += sizeof("BUILDID");
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_header) + 1;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  std::string build_id;
+  ASSERT_FALSE(elf->GetBuildID(&build_id));
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForDesc () {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
+  note_offset += sizeof("BUILDID");
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_header) + sizeof("GNU") + 1;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  std::string build_id;
+  ASSERT_FALSE(elf->GetBuildID(&build_id));
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForHeader () {
+  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t offset = 0x2000;
+
+  Ehdr ehdr = {};
+  ehdr.e_shoff = offset;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Shdr);
+  ehdr.e_shstrndx = 2;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  offset += ehdr.e_shentsize;
+
+  char note_section[128];
+  Nhdr note_header = {};
+  note_header.n_namesz = 4;  // "GNU"
+  note_header.n_descsz = 8; // "BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  memcpy(&note_section[note_offset], "BUILDID", sizeof("BUILDID"));
+  note_offset += sizeof("BUILDID");
+
+  Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_header) - 1;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+  memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  std::string build_id;
+  ASSERT_FALSE(elf->GetBuildID(&build_id));
+}
+
+TEST_F(ElfInterfaceTest, build_id32) {
+  BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id64) {
+  BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes32) {
+  BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes64) {
+  BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name32) {
+  BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name64) {
+  BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc32) {
+  BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc64) {
+  BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header32) {
+  BuildIDSectionTooSmallForHeader<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header64) {
+  BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index ccf8927..7766218 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -311,6 +311,7 @@
   void InitHeaders(uint64_t) override {}
   bool GetSoname(std::string*) override { return false; }
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
+  bool GetBuildID(std::string*) override { return false; }
 
   MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
   MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 0987bc1..943b3c9 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -59,16 +59,16 @@
   }
 
   static void SetUpTestCase() {
-    std::vector<uint8_t> buffer(1024);
-    memset(buffer.data(), 0, buffer.size());
+    std::vector<uint8_t> buffer(12288, 0);
     memcpy(buffer.data(), ELFMAG, SELFMAG);
     buffer[EI_CLASS] = ELFCLASS32;
-    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), 1024));
 
     memset(buffer.data(), 0, buffer.size());
-    memcpy(&buffer[0x100], ELFMAG, SELFMAG);
-    buffer[0x100 + EI_CLASS] = ELFCLASS64;
-    ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+    memcpy(&buffer[0x1000], ELFMAG, SELFMAG);
+    buffer[0x1000 + EI_CLASS] = ELFCLASS64;
+    buffer[0x2000] = 0xff;
+    ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, buffer.data(), buffer.size()));
 
     InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
     InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
@@ -84,13 +84,13 @@
 
   static TemporaryFile elf_;
 
-  static TemporaryFile elf_at_100_;
+  static TemporaryFile elf_at_1000_;
 
   static TemporaryFile elf32_at_map_;
   static TemporaryFile elf64_at_map_;
 };
 TemporaryFile MapInfoCreateMemoryTest::elf_;
-TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_1000_;
 TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
 TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
@@ -134,7 +134,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(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path);
+  MapInfo info(nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -312,4 +312,43 @@
   }
 }
 
+TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
+  Maps maps;
+  maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+  maps.Add(0x1000, 0x2000, 0x1000, PROT_READ, elf_at_1000_.path, 0);
+  maps.Add(0x2000, 0x3000, 0x2000, PROT_READ | PROT_EXEC, elf_at_1000_.path, 0);
+
+  MapInfo* map_info = maps.Find(0x2000);
+  ASSERT_TRUE(map_info != nullptr);
+
+  // Set up the size
+  Elf64_Ehdr ehdr;
+  ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+  // Will not give the elf memory, because the read-only entry does not
+  // extend over the executable segment.
+  std::unique_ptr<Memory> memory(map_info->CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  std::vector<uint8_t> buffer(0x100);
+  EXPECT_EQ(0x2000U, map_info->offset);
+  EXPECT_EQ(0U, map_info->elf_offset);
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
+  EXPECT_EQ(0xffU, buffer[0]);
+
+  // Now init the elf data enough so that the file memory object will be used.
+  ehdr.e_shoff = 0x4000;
+  ehdr.e_shnum = 1;
+  ehdr.e_shentsize = 0x100;
+  ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+  ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+  memory.reset(map_info->CreateMemory(process_memory_));
+  EXPECT_EQ(0x2000U, map_info->offset);
+  EXPECT_EQ(0x1000U, map_info->elf_offset);
+  Elf64_Ehdr ehdr_mem;
+  ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem)));
+  EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index a65c077..dc015b4 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1239,4 +1239,79 @@
   EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
 }
 
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_arm64/", ARCH_ARM64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 000000000014ccbc (offset 0x39000)  linker64 (__dl_syscall+28)\n"
+      "  #01 pc 000000000005426c (offset 0x39000)  linker64 "
+      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+      "  #02 pc 00000000000008bc  vdso.so\n"
+      "  #03 pc 00000000000846f4 (offset 0x40000)  libc.so (abort+172)\n"
+      "  #04 pc 0000000000084ad4 (offset 0x40000)  libc.so (__assert2+36)\n"
+      "  #05 pc 000000000003d5b4 (offset 0x40000)  ANGLEPrebuilt.apk (ANGLEGetUtilityAPI+56)\n"
+      "  #06 pc 000000000007fe68 (offset 0x40000)  libc.so (__libc_init)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7e82b018bcULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+  // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64));
+  // Add the memory that represents the shared library.
+  MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory_.get());
+  AddMemory(dir_ + "lib_mem.data", memory);
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 000000000014ccbc (offset 0x39000)  linker64 (__dl_syscall+28)\n"
+      "  #01 pc 000000000005426c (offset 0x39000)  linker64 "
+      "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+      "  #02 pc 00000000000008bc  vdso.so\n"
+      "  #03 pc 00000000000846f4 (offset 0x40000)  libc.so (abort+172)\n"
+      "  #04 pc 0000000000084ad4 (offset 0x40000)  libc.so (__assert2+36)\n"
+      "  #05 pc 000000000003d5b4 (offset 0x2211000)  ANGLEPrebuilt.apk\n"
+      "  #06 pc 000000000007fe68 (offset 0x40000)  libc.so (__libc_init)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7e82b018bcULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+  // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 831d3b5..c4b8763 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -660,6 +660,54 @@
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
 
   // Fake as if code called a nullptr function.
+  regs_.set_pc(0x20000);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0x10010, false));
+  regs_.FakeSetReturnAddress(0x12);
+  regs_.FakeSetReturnAddressValid(true);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x20000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x20000U, frame->map_start);
+  EXPECT_EQ(0x22000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that a speculative frame is added and left if there are only
+// two frames and the pc is in the middle nowhere.
+TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  // Fake as if code called a nullptr function.
   regs_.set_pc(0);
   regs_.set_sp(0x10000);
   regs_.FakeSetReturnAddress(0x1202);
@@ -669,7 +717,7 @@
   unwinder.Unwind();
   EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
 
-  ASSERT_EQ(1U, unwinder.NumFrames());
+  ASSERT_EQ(2U, unwinder.NumFrames());
 
   auto* frame = &unwinder.frames()[0];
   EXPECT_EQ(0U, frame->num);
@@ -684,6 +732,20 @@
   EXPECT_EQ(0U, frame->map_end);
   EXPECT_EQ(0U, frame->map_load_bias);
   EXPECT_EQ(0, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
 }
 
 // Verify that an unwind stops when a frame is in given suffix.
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
new file mode 100644
index 0000000..0277359
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
new file mode 100644
index 0000000..20008fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
new file mode 100644
index 0000000..b90933b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
new file mode 100644
index 0000000..c4fc067
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 4000 00:00 0   ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 40000 00:00 0   ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0   libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0   libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0   vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0   linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0   linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
new file mode 100644
index 0000000..1e2ea32
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
new file mode 100644
index 0000000..ec07e15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
new file mode 100644
index 0000000..825bb1a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
new file mode 100644
index 0000000..205ebd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
new file mode 100644
index 0000000..f39d127
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
new file mode 100644
index 0000000..20008fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
new file mode 100644
index 0000000..b90933b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
new file mode 100644
index 0000000..386d57a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 21d5000 00:00 0   ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 2211000 00:00 0   ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0   libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0   libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0   vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0   linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0   linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
new file mode 100644
index 0000000..1e2ea32
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
new file mode 100644
index 0000000..ec07e15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
new file mode 100644
index 0000000..825bb1a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
new file mode 100644
index 0000000..205ebd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 22ca7bf..e729453 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -35,7 +35,12 @@
 #include <unwindstack/Unwinder.h>
 
 static bool Attach(pid_t pid) {
-  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+  if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+    ptrace(PTRACE_DETACH, pid, 0, 0);
     return false;
   }
 
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 640992f..652dbd1 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -50,7 +50,12 @@
 };
 
 static bool Attach(pid_t pid) {
-  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+  if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+    ptrace(PTRACE_DETACH, pid, 0, 0);
     return false;
   }
 
@@ -224,7 +229,7 @@
     sp_map_start = map_info->start;
   }
 
-  for (auto frame : unwinder.frames()) {
+  for (const auto& frame : unwinder.frames()) {
     map_info = maps.Find(frame.sp);
     if (map_info != nullptr && sp_map_start != map_info->start) {
       stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index aebeb95..3f2dfb0 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -123,6 +123,15 @@
     printf("Soname: %s\n", soname.c_str());
   }
 
+  std::string build_id;
+  if (elf.GetBuildID(&build_id)) {
+    printf("Build ID: ");
+    for (size_t i = 0; i < build_id.size(); ++i) {
+      printf("%02hhx", build_id[i]);
+    }
+    printf("\n");
+  }
+
   ElfInterface* interface = elf.interface();
   if (elf.machine_type() == EM_ARM) {
     DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 102fdf0..b3f943d 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -51,9 +51,6 @@
 
 // --- Looper ---
 
-// Hint for number of file descriptors to be associated with the epoll instance.
-static const int EPOLL_SIZE_HINT = 8;
-
 // Maximum number of file descriptors for which to retrieve poll events each iteration.
 static const int EPOLL_MAX_EVENTS = 16;
 
@@ -139,7 +136,7 @@
     }
 
     // Allocate the new epoll instance and register the wake pipe.
-    mEpollFd.reset(epoll_create(EPOLL_SIZE_HINT));
+    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     struct epoll_event eventItem;
diff --git a/llkd/README.md b/llkd/README.md
index 3da7a2f..224e184 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -160,7 +160,7 @@
 NB: false is a very very very unlikely process to want to blacklist.
 
 #### ro.llk.blacklist.parent
-default 0,2 (kernel and [kthreadd]).
+default 0,2,adbd (kernel, [kthreadd] and adbd).
 The string "*false*" is the equivalent to an *empty* list.
 Do not watch processes that have this parent.
 A parent process can be comm, cmdline or pid reference.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index b16b1d8..1efa32b 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -55,7 +55,11 @@
 #define LLK_BLACKLIST_PROCESS_DEFAULT  \
     "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
 #define LLK_BLACKLIST_PARENT_PROPERTY  "ro.llk.blacklist.parent"
+#ifdef __PTRACE_ENABLED__  // defined if userdebug build
+#define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd],adbd"
+#else
 #define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd]"
+#endif
 #define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
 #define LLK_BLACKLIST_UID_DEFAULT      ""
 #define LLK_BLACKLIST_STACK_PROPERTY   "ro.llk.blacklist.process.stack"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 427dace..267da4a 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -510,9 +510,7 @@
     return android::base::Trim(content) == string;
 }
 
-void llkPanicKernel(bool dump, pid_t tid, const char* state,
-                    const std::string& message = "") __noreturn;
-void llkPanicKernel(bool dump, pid_t tid, const char* state, const std::string& message) {
+void llkPanicKernel(bool dump, pid_t tid, const char* state, const std::string& message = "") {
     if (!message.empty()) LOG(ERROR) << message;
     auto sysrqTriggerFd = llkFileToWriteFd("/proc/sysrq-trigger");
     if (sysrqTriggerFd < 0) {
@@ -521,6 +519,7 @@
         // The answer to life, the universe and everything
         ::exit(42);
         // NOTREACHED
+        return;
     }
     ::sync();
     if (dump) {
@@ -544,6 +543,13 @@
     llkWriteStringToFile(message + (message.empty() ? "" : "\n") +
                                  "SysRq : Trigger a crash : 'livelock,"s + state + "'\n",
                          "/dev/kmsg");
+    // Because panic is such a serious thing to do, let us
+    // make sure that the tid being inspected still exists!
+    auto piddir = procdir + std::to_string(tid) + "/stat";
+    if (access(piddir.c_str(), F_OK) != 0) {
+        PLOG(WARNING) << piddir;
+        return;
+    }
     android::base::WriteStringToFd("c", sysrqTriggerFd);
     // NOTREACHED
     // DYB
@@ -610,7 +616,7 @@
 
 std::string llkFormat(const std::unordered_set<std::string>& blacklist) {
     std::string ret;
-    for (auto entry : blacklist) {
+    for (const auto& entry : blacklist) {
         if (ret.size()) {
             ret += ",";
         }
@@ -909,6 +915,7 @@
     ms -= llkCycle;
     auto myPid = ::getpid();
     auto myTid = ::gettid();
+    auto dump = true;
     for (auto dp = llkTopDirectory.read(); dp != nullptr; dp = llkTopDirectory.read()) {
         std::string piddir;
 
@@ -1109,9 +1116,10 @@
             const auto message = state + " "s + llkFormat(procp->count) + " " +
                                  std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
                                  std::to_string(tid) + " " + procp->getComm() + " [panic]";
-            llkPanicKernel(true, tid,
+            llkPanicKernel(dump, tid,
                            (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
                            message);
+            dump = false;
         }
         LOG(VERBOSE) << "+closedir()";
     }
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 70f6faa..6637deb 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -12,5 +12,6 @@
         "-Werror",
     ],
     stl: "none",
+    system_shared_libs: [],
     export_include_dirs: ["include"],
 }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index aad00ad..f88f6b9 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -9,6 +9,10 @@
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 
+# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
+# Since init.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
+LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
+
 include $(BUILD_PREBUILT)
 
 #######################################
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index ca6aafe..461184a 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -6,35 +6,40 @@
 
 # All binaries gets the same configuration 'legacy'
 dir.legacy = /system
+dir.legacy = /product
 dir.legacy = /vendor
 dir.legacy = /odm
 dir.legacy = /sbin
 
-# Except for /postinstall, where only /system is searched
+# Except for /postinstall, where only /system and /product are searched
 dir.postinstall = /postinstall
 
 [legacy]
 namespace.default.isolated = false
 
 namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
 namespace.default.search.paths += /vendor/${LIB}
 namespace.default.search.paths += /odm/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}
-namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths +=           /product/${LIB}
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
 
 ###############################################################################
 # Namespace config for binaries under /postinstall.
 # Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# /system/lib and /product/lib in the search paths. This is because linker
+# calls realpath on the search paths and this causes selinux denial if the
+# paths (/vendor, /odm) are not allowed to the poinstall binaries.
+# There is no reason to allow the binaries to access the paths.
 ###############################################################################
 [postinstall]
 namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 4576776..d10f7c1 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -5,6 +5,7 @@
     export ANDROID_ASSETS /system/app
     export ANDROID_DATA /data
     export ANDROID_STORAGE /storage
+    export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
     export EXTERNAL_STORAGE /sdcard
     export ASEC_MOUNTPOINT /mnt/asec
     export BOOTCLASSPATH %BOOTCLASSPATH%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 349168e..6fb1a8b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -583,8 +583,8 @@
     # Set SELinux security contexts on upgrade or policy update.
     restorecon --recursive --skip-ce /data
 
-    # Check any timezone data in /data is newer than the copy in /system, delete if not.
-    exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
+    # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
+    exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
 
     # If there is no post-fs-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index d47506c..35f469a 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,7 +1,5 @@
 firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
-
-subsystem adf
-    devname uevent_devname
+uevent_socket_rcvbuf_size 16M
 
 subsystem graphics
     devname uevent_devpath
@@ -11,26 +9,10 @@
     devname uevent_devpath
     dirname /dev/dri
 
-subsystem oncrpc
-    devname uevent_devpath
-    dirname /dev/oncrpc
-
-subsystem adsp
-    devname uevent_devpath
-    dirname /dev/adsp
-
-subsystem msm_camera
-    devname uevent_devpath
-    dirname /dev/msm_camera
-
 subsystem input
     devname uevent_devpath
     dirname /dev/input
 
-subsystem mtd
-    devname uevent_devpath
-    dirname /dev/mtd
-
 subsystem sound
     devname uevent_devpath
     dirname /dev/snd
@@ -58,73 +40,27 @@
 
 /dev/pmsg0                0222   root       log
 
-# the msm hw3d client device node is world writable/readable.
-/dev/msm_hw3dc            0666   root       root
-
-# gpu driver for adreno200 is globally accessible
-/dev/kgsl                 0666   root       root
-
 # kms driver for drm based gpu
 /dev/dri/*                0666   root       graphics
 
 # these should not be world writable
 /dev/diag                 0660   radio      radio
-/dev/diag_arm9            0660   radio      radio
 /dev/ttyMSM0              0600   bluetooth  bluetooth
 /dev/uhid                 0660   uhid       uhid
 /dev/uinput               0660   uhid       uhid
-/dev/alarm                0664   system     radio
 /dev/rtc0                 0640   system     system
 /dev/tty0                 0660   root       system
 /dev/graphics/*           0660   root       graphics
-/dev/msm_hw3dm            0660   system     graphics
 /dev/input/*              0660   root       input
 /dev/v4l-touch*           0660   root       input
-/dev/eac                  0660   root       audio
-/dev/cam                  0660   root       camera
-/dev/pmem                 0660   system     graphics
-/dev/pmem_adsp*           0660   system     audio
-/dev/pmem_camera*         0660   system     camera
-/dev/oncrpc/*             0660   root       system
-/dev/adsp/*               0660   system     audio
 /dev/snd/*                0660   system     audio
-/dev/mt9t013              0660   system     system
-/dev/msm_camera/*         0660   system     system
-/dev/akm8976_daemon       0640   compass    system
-/dev/akm8976_aot          0640   compass    system
-/dev/akm8973_daemon       0640   compass    system
-/dev/akm8973_aot          0640   compass    system
-/dev/bma150               0640   compass    system
-/dev/cm3602               0640   compass    system
-/dev/akm8976_pffd         0640   compass    system
-/dev/lightsensor          0640   system     system
-/dev/msm_pcm_out*         0660   system     audio
-/dev/msm_pcm_in*          0660   system     audio
-/dev/msm_pcm_ctl*         0660   system     audio
-/dev/msm_snd*             0660   system     audio
 /dev/msm_mp3*             0660   system     audio
-/dev/audience_a1026*      0660   system     audio
-/dev/tpa2018d1*           0660   system     audio
-/dev/msm_audpre           0660   system     audio
-/dev/msm_audio_ctl        0660   system     audio
-/dev/htc-acoustic         0660   system     audio
-/dev/vdec                 0660   system     audio
-/dev/q6venc               0660   system     audio
-/dev/snd/dsp              0660   system     audio
-/dev/snd/dsp1             0660   system     audio
-/dev/snd/mixer            0660   system     audio
-/dev/smd0                 0640   radio      radio
-/dev/qmi                  0640   radio      radio
-/dev/qmi0                 0640   radio      radio
-/dev/qmi1                 0640   radio      radio
-/dev/qmi2                 0640   radio      radio
 /dev/bus/usb/*            0660   root       usb
 /dev/mtp_usb              0660   root       mtp
 /dev/usb_accessory        0660   root       usb
 /dev/tun                  0660   system     vpn
 
 # CDMA radio interface MUX
-/dev/ts0710mux*           0640   radio      radio
 /dev/ppp                  0660   radio      vpn
 
 # sysfs properties
@@ -134,6 +70,3 @@
 /sys/devices/virtual/usb_composite/*   enable      0664  root   system
 /sys/devices/system/cpu/cpu*   cpufreq/scaling_max_freq   0664  system system
 /sys/devices/system/cpu/cpu*   cpufreq/scaling_min_freq   0664  system system
-
-# DVB API device nodes
-/dev/dvb*                 0660   root       system
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index d8cf867..ffda3a5 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -1,5 +1,4 @@
-Android's shell and utilities
-=============================
+# Android's shell and utilities
 
 Since IceCreamSandwich Android has used
 [mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
@@ -34,8 +33,7 @@
 full list for a release by running `toybox` directly.
 
 
-Android 2.3 (Gingerbread)
--------------------------
+## Android 2.3 (Gingerbread)
 
 BSD: cat dd newfs\_msdos
 
@@ -46,8 +44,7 @@
 umount uptime vmstat watchprops wipe
 
 
-Android 4.0 (IceCreamSandwich)
-------------------------------
+## Android 4.0 (IceCreamSandwich)
 
 BSD: cat dd newfs\_msdos
 
@@ -58,8 +55,7 @@
 touch umount uptime vmstat watchprops wipe
 
 
-Android 4.1-4.3 (JellyBean)
----------------------------
+## Android 4.1-4.3 (JellyBean)
 
 BSD: cat cp dd du grep newfs\_msdos
 
@@ -71,8 +67,7 @@
 sync top touch umount uptime vmstat watchprops wipe
 
 
-Android 4.4 (KitKat)
---------------------
+## Android 4.4 (KitKat)
 
 BSD: cat cp dd du grep newfs\_msdos
 
@@ -84,8 +79,7 @@
 stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
 
 
-Android 5.0 (Lollipop)
-----------------------
+## Android 5.0 (Lollipop)
 
 BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
 
@@ -97,8 +91,7 @@
 top touch umount uptime vmstat watchprops wipe
 
 
-Android 6.0 (Marshmallow)
--------------------------
+## Android 6.0 (Marshmallow)
 
 BSD: dd du grep
 
@@ -118,8 +111,7 @@
 vmstat wc which whoami xargs yes
 
 
-Android 7.0 (Nougat)
---------------------
+## Android 7.0 (Nougat)
 
 BSD: dd grep
 
@@ -140,8 +132,7 @@
 uptime usleep vmstat wc which whoami xargs xxd yes
 
 
-Android 8.0 (Oreo)
-------------------
+## Android 8.0 (Oreo)
 
 BSD: dd grep
 
@@ -164,8 +155,8 @@
 tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
 vmstat wc which whoami xargs xxd yes zcat
 
-Android P
----------
+
+## Android 9.0 (Pie)
 
 BSD: dd grep
 
@@ -190,8 +181,8 @@
 umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
 which whoami xargs xxd yes zcat
 
-Android Q
----------
+
+## Android Q
 
 BSD: grep fsck\_msdos newfs\_msdos
 
@@ -201,17 +192,22 @@
 
 toolbox: getevent getprop
 
-toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date dd df diff dirname
-dmesg dos2unix du echo env expand expr fallocate false file find flock
-fmt free getenforce groups gunzip gzip head hostname hwclock id ifconfig
-inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
-losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
-mkswap mktemp modinfo modprobe more mount mountpoint mv nc netcat netstat
-nice nl nohup nsenter od paste patch pgrep pidof pkill pmap printenv
-printf ps pwd readlink realpath renice restorecon rm rmdir rmmod runcon
-sed sendevent seq setenforce setprop setsid sha1sum sha224sum sha256sum
-sha384sum sha512sum sleep sort split start stat stop strings stty swapoff
-swapon sync sysctl tac tail tar taskset tee time timeout top touch tr
-true truncate tty ulimit umount uname uniq unix2dos unshare uptime usleep
-uudecode uuencode vmstat wc which whoami xargs xxd yes zcat
+toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze getconf
+getenforce getfattr grep groups gunzip gzip head help hostname hwclock
+i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
+install ionice iorenice iotop kill killall ln load\_policy log logname
+losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
+paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
+printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
+rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee time timeout top touch tr traceroute traceroute6
+true truncate tty tunctl ulimit umount uname uniq unix2dos unlink
+unshare uptime usleep uudecode uuencode uuidgen vconfig vmstat watch
+wc which whoami xargs xxd yes zcat