Merge "fastboot: Flashall does proper snapshot cancel"
diff --git a/adb/SOCKET-ACTIVATION.txt b/adb/SOCKET-ACTIVATION.txt
new file mode 100644
index 0000000..4ef62ac
--- /dev/null
+++ b/adb/SOCKET-ACTIVATION.txt
@@ -0,0 +1,42 @@
+adb can be configured to work with systemd-style socket activation,
+allowing the daemon to start automatically when the adb control port
+is forwarded across a network. You need two files, placed in the usual
+systemd service directories (e.g., ~/.config/systemd/user for a user
+service).
+
+adb.service:
+
+--- START adb.service CUT HERE ---
+[Unit]
+Description=adb
+After=adb.socket
+Requires=adb.socket
+[Service]
+Type=simple
+# FD 3 is part of the systemd interface
+ExecStart=/path/to/adb server nodaemon -L acceptfd:3
+--- END adb.service CUT HERE ---
+
+--- START adb.socket CUT HERE ---
+[Unit]
+Description=adb
+PartOf=adb.service
+[Socket]
+ListenStream=127.0.0.1:5037
+Accept=no
+[Install]
+WantedBy=sockets.target
+--- END adb.socket CUT HERE ---
+
+After installing the adb service, the adb server will be started
+automatically on any connection to 127.0.0.1:5037 (the default adb
+control port), even after adb kill-server kills the server.
+
+Other "superserver" launcher systems (like macOS launchd) can be
+configured analogously. The important part is that adb be started with
+"server" and "nodaemon" command line arguments and that the listen
+address (passed to -L) name a file descriptor that's ready to
+accept(2) connections and that's already bound to the desired address
+and listening. inetd-style pre-accepted sockets do _not_ work in this
+configuration: the file descriptor passed to acceptfd must be the
+serve socket, not the accepted connection socket.
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index d91ae35..f724cb5 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -222,7 +222,7 @@
     int port;
     std::string error;
     if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) {
-        LOG(FATAL) << "failed to parse server socket spec: " << error;
+        return {};
     }
 
     return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port);
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 0ffdbc2..813a8a9 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -107,6 +107,7 @@
         "       localfilesystem:<unix domain socket name>\n"
         "       dev:<character device name>\n"
         "       jdwp:<process pid> (remote only)\n"
+        "       acceptfd:<fd> (listen only)\n"
         " forward --remove LOCAL   remove specific forward socket connection\n"
         " forward --remove-all     remove all forward socket connections\n"
         " ppp TTY [PARAMETER...]   run PPP over USB\n"
@@ -136,8 +137,8 @@
         "     run remote shell command (interactive shell if no command given)\n"
         "     -e: choose escape character, or \"none\"; default '~'\n"
         "     -n: don't read from stdin\n"
-        "     -T: disable PTY allocation\n"
-        "     -t: force PTY allocation\n"
+        "     -T: disable pty allocation\n"
+        "     -t: allocate a pty if on a tty (-tt: force pty allocation)\n"
         "     -x: disable remote exit codes and stdout/stderr separation\n"
         " emu COMMAND              run emulator console command\n"
         "\n"
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 703eb2f..fbfeb53 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -52,6 +52,8 @@
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 
+typedef void(sync_ls_cb)(unsigned mode, uint64_t size, uint64_t time, const char* name);
+
 struct syncsendbuf {
     unsigned id;
     unsigned size;
@@ -210,6 +212,7 @@
             Error("failed to get feature set: %s", error.c_str());
         } else {
             have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
+            have_ls_v2_ = CanUseFeature(features_, kFeatureLs2);
             fd.reset(adb_connect("sync:", &error));
             if (fd < 0) {
                 Error("connect failed: %s", error.c_str());
@@ -357,7 +360,7 @@
                             << msg.stat_v1.id;
             }
 
-            if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) {
+            if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.mtime == 0) {
                 // There's no way for us to know what the error was.
                 errno = ENOPROTOOPT;
                 return false;
@@ -365,13 +368,52 @@
 
             st->st_mode = msg.stat_v1.mode;
             st->st_size = msg.stat_v1.size;
-            st->st_ctime = msg.stat_v1.time;
-            st->st_mtime = msg.stat_v1.time;
+            st->st_ctime = msg.stat_v1.mtime;
+            st->st_mtime = msg.stat_v1.mtime;
         }
 
         return true;
     }
 
+    bool SendLs(const char* path) {
+        return SendRequest(have_ls_v2_ ? ID_LIST_V2 : ID_LIST_V1, path);
+    }
+
+  private:
+    template <bool v2>
+    static bool FinishLsImpl(borrowed_fd fd, const std::function<sync_ls_cb>& callback) {
+        using dent_type =
+                std::conditional_t<v2, decltype(syncmsg::dent_v2), decltype(syncmsg::dent_v1)>;
+
+        while (true) {
+            dent_type dent;
+            if (!ReadFdExactly(fd, &dent, sizeof(dent))) return false;
+
+            uint32_t expected_id = v2 ? ID_DENT_V2 : ID_DENT_V1;
+            if (dent.id == ID_DONE) return true;
+            if (dent.id != expected_id) return false;
+
+            // Maximum length of a file name excluding null terminator (NAME_MAX) on Linux is 255.
+            char buf[256];
+            size_t len = dent.namelen;
+            if (len > 255) return false;
+
+            if (!ReadFdExactly(fd, buf, len)) return false;
+            buf[len] = 0;
+
+            callback(dent.mode, dent.size, dent.mtime, buf);
+        }
+    }
+
+  public:
+    bool FinishLs(const std::function<sync_ls_cb>& callback) {
+        if (have_ls_v2_) {
+            return FinishLsImpl<true>(this->fd, callback);
+        } else {
+            return FinishLsImpl<false>(this->fd, callback);
+        }
+    }
+
     // Sending header, payload, and footer in a single write makes a huge
     // difference to "adb sync" performance.
     bool SendSmallFile(const char* path_and_mode,
@@ -578,6 +620,7 @@
     bool expect_done_;
     FeatureSet features_;
     bool have_stat_v2_;
+    bool have_ls_v2_;
 
     TransferLedger global_ledger_;
     TransferLedger current_ledger_;
@@ -609,28 +652,9 @@
     }
 };
 
-typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
-
 static bool sync_ls(SyncConnection& sc, const char* path,
                     const std::function<sync_ls_cb>& func) {
-    if (!sc.SendRequest(ID_LIST, path)) return false;
-
-    while (true) {
-        syncmsg msg;
-        if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
-
-        if (msg.dent.id == ID_DONE) return true;
-        if (msg.dent.id != ID_DENT) return false;
-
-        size_t len = msg.dent.namelen;
-        if (len > 256) return false; // TODO: resize buffer? continue?
-
-        char buf[257];
-        if (!ReadFdExactly(sc.fd, buf, len)) return false;
-        buf[len] = 0;
-
-        func(msg.dent.mode, msg.dent.size, msg.dent.time, buf);
-    }
+    return sc.SendLs(path) && sc.FinishLs(func);
 }
 
 static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
@@ -787,9 +811,8 @@
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
-    return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
-                                const char* name) {
-        printf("%08x %08x %08x %s\n", mode, size, time, name);
+    return sync_ls(sc, path, [](unsigned mode, uint64_t size, uint64_t time, const char* name) {
+        printf("%08x %08" PRIx64 " %08" PRIx64 " %s\n", mode, size, time, name);
     });
 }
 
@@ -1062,7 +1085,7 @@
     file_list->push_back(ci);
 
     // Put the files/dirs in rpath on the lists.
-    auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
+    auto callback = [&](unsigned mode, uint64_t size, uint64_t time, const char* name) {
         if (IsDotOrDotDot(name)) {
             return;
         }
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 0e70d47..d6af708 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -139,7 +139,7 @@
     lstat(path, &st);
     msg.stat_v1.mode = st.st_mode;
     msg.stat_v1.size = st.st_size;
-    msg.stat_v1.time = st.st_mtime;
+    msg.stat_v1.mtime = st.st_mtime;
     return WriteFdExactly(s, &msg.stat_v1, sizeof(msg.stat_v1));
 }
 
@@ -174,40 +174,73 @@
     return WriteFdExactly(s, &msg.stat_v2, sizeof(msg.stat_v2));
 }
 
+template <bool v2>
 static bool do_list(int s, const char* path) {
     dirent* de;
 
-    syncmsg msg;
-    msg.dent.id = ID_DENT;
+    using MessageType =
+            std::conditional_t<v2, decltype(syncmsg::dent_v2), decltype(syncmsg::dent_v1)>;
+    MessageType msg;
+    uint32_t msg_id;
+    if constexpr (v2) {
+        msg_id = ID_DENT_V2;
+    } else {
+        msg_id = ID_DENT_V1;
+    }
 
     std::unique_ptr<DIR, int(*)(DIR*)> d(opendir(path), closedir);
     if (!d) goto done;
 
     while ((de = readdir(d.get()))) {
+        memset(&msg, 0, sizeof(msg));
+        msg.id = msg_id;
+
         std::string filename(StringPrintf("%s/%s", path, de->d_name));
 
         struct stat st;
         if (lstat(filename.c_str(), &st) == 0) {
-            size_t d_name_length = strlen(de->d_name);
-            msg.dent.mode = st.st_mode;
-            msg.dent.size = st.st_size;
-            msg.dent.time = st.st_mtime;
-            msg.dent.namelen = d_name_length;
+            msg.mode = st.st_mode;
+            msg.size = st.st_size;
+            msg.mtime = st.st_mtime;
 
-            if (!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
-                    !WriteFdExactly(s, de->d_name, d_name_length)) {
-                return false;
+            if constexpr (v2) {
+                msg.dev = st.st_dev;
+                msg.ino = st.st_ino;
+                msg.nlink = st.st_nlink;
+                msg.uid = st.st_uid;
+                msg.gid = st.st_gid;
+                msg.atime = st.st_atime;
+                msg.ctime = st.st_ctime;
             }
+        } else {
+            if constexpr (v2) {
+                msg.error = errno;
+            } else {
+                continue;
+            }
+        }
+
+        size_t d_name_length = strlen(de->d_name);
+        msg.namelen = d_name_length;
+
+        if (!WriteFdExactly(s, &msg, sizeof(msg)) ||
+            !WriteFdExactly(s, de->d_name, d_name_length)) {
+            return false;
         }
     }
 
 done:
-    msg.dent.id = ID_DONE;
-    msg.dent.mode = 0;
-    msg.dent.size = 0;
-    msg.dent.time = 0;
-    msg.dent.namelen = 0;
-    return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
+    memset(&msg, 0, sizeof(msg));
+    msg.id = ID_DONE;
+    return WriteFdExactly(s, &msg, sizeof(msg));
+}
+
+static bool do_list_v1(int s, const char* path) {
+    return do_list<false>(s, path);
+}
+
+static bool do_list_v2(int s, const char* path) {
+    return do_list<true>(s, path);
 }
 
 // Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
@@ -499,8 +532,10 @@
       return "lstat_v2";
     case ID_STAT_V2:
       return "stat_v2";
-    case ID_LIST:
-      return "list";
+    case ID_LIST_V1:
+      return "list_v1";
+    case ID_LIST_V2:
+      return "list_v2";
     case ID_SEND:
       return "send";
     case ID_RECV:
@@ -546,8 +581,11 @@
         case ID_STAT_V2:
             if (!do_stat_v2(fd, request.id, name)) return false;
             break;
-        case ID_LIST:
-            if (!do_list(fd, name)) return false;
+        case ID_LIST_V1:
+            if (!do_list_v1(fd, name)) return false;
+            break;
+        case ID_LIST_V2:
+            if (!do_list_v2(fd, name)) return false;
             break;
         case ID_SEND:
             if (!do_send(fd, name, buffer)) return false;
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index 338d776..b19fa5d 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -84,7 +84,7 @@
 using usb_os_desc_guid_t = usb_os_desc_ext_prop<20, 39>;
 usb_os_desc_guid_t os_desc_guid = {
     .bPropertyName = "DeviceInterfaceGUID",
-    .bProperty = "{64379D6C-D531-4BED-BBEC-5A16FC07D6BC}",
+    .bProperty = "{F72FE0D4-CBCB-407D-8814-9ED673D0DD6B}",
 };
 
 struct usb_ext_prop_values {
diff --git a/adb/file_sync_protocol.h b/adb/file_sync_protocol.h
index 108639a..508c138 100644
--- a/adb/file_sync_protocol.h
+++ b/adb/file_sync_protocol.h
@@ -21,10 +21,14 @@
 #define ID_LSTAT_V1 MKID('S', 'T', 'A', 'T')
 #define ID_STAT_V2 MKID('S', 'T', 'A', '2')
 #define ID_LSTAT_V2 MKID('L', 'S', 'T', '2')
-#define ID_LIST MKID('L', 'I', 'S', 'T')
+
+#define ID_LIST_V1 MKID('L', 'I', 'S', 'T')
+#define ID_LIST_V2 MKID('L', 'I', 'S', '2')
+#define ID_DENT_V1 MKID('D', 'E', 'N', 'T')
+#define ID_DENT_V2 MKID('D', 'N', 'T', '2')
+
 #define ID_SEND MKID('S', 'E', 'N', 'D')
 #define ID_RECV MKID('R', 'E', 'C', 'V')
-#define ID_DENT MKID('D', 'E', 'N', 'T')
 #define ID_DONE MKID('D', 'O', 'N', 'E')
 #define ID_DATA MKID('D', 'A', 'T', 'A')
 #define ID_OKAY MKID('O', 'K', 'A', 'Y')
@@ -42,7 +46,7 @@
         uint32_t id;
         uint32_t mode;
         uint32_t size;
-        uint32_t time;
+        uint32_t mtime;
     } stat_v1;
     struct __attribute__((packed)) {
         uint32_t id;
@@ -62,17 +66,32 @@
         uint32_t id;
         uint32_t mode;
         uint32_t size;
-        uint32_t time;
+        uint32_t mtime;
         uint32_t namelen;
-    } dent;
+    } dent_v1; // followed by `namelen` bytes of the name.
+    struct __attribute__((packed)) {
+        uint32_t id;
+        uint32_t error;
+        uint64_t dev;
+        uint64_t ino;
+        uint32_t mode;
+        uint32_t nlink;
+        uint32_t uid;
+        uint32_t gid;
+        uint64_t size;
+        int64_t atime;
+        int64_t mtime;
+        int64_t ctime;
+        uint32_t namelen;
+    } dent_v2; // followed by `namelen` bytes of the name.
     struct __attribute__((packed)) {
         uint32_t id;
         uint32_t size;
-    } data;
+    } data; // followed by `size` bytes of data.
     struct __attribute__((packed)) {
         uint32_t id;
         uint32_t msglen;
-    } status;
+    } status; // followed by `msglen` bytes of error message, if id == ID_FAIL.
 };
 
 #define SYNC_DATA_MAX (64 * 1024)
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 27e8c46..9ce443e 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -16,6 +16,7 @@
 
 #include "socket_spec.h"
 
+#include <limits>
 #include <string>
 #include <string_view>
 #include <unordered_map>
@@ -28,10 +29,12 @@
 #include <cutils/sockets.h>
 
 #include "adb.h"
+#include "adb_utils.h"
 #include "sysdeps.h"
 
 using namespace std::string_literals;
 
+using android::base::ConsumePrefix;
 using android::base::StringPrintf;
 
 #if defined(__linux__)
@@ -131,7 +134,7 @@
             return true;
         }
     }
-    return spec.starts_with("tcp:");
+    return spec.starts_with("tcp:") || spec.starts_with("acceptfd:");
 }
 
 bool is_local_socket_spec(std::string_view spec) {
@@ -235,6 +238,9 @@
         *error = "vsock is only supported on linux";
         return false;
 #endif  // ADB_LINUX
+    } else if (address.starts_with("acceptfd:")) {
+        *error = "cannot connect to acceptfd";
+        return false;
     }
 
     for (const auto& it : kLocalSocketTypes) {
@@ -334,6 +340,46 @@
         *error = "vsock is only supported on linux";
         return -1;
 #endif  // ADB_LINUX
+    } else if (ConsumePrefix(&spec, "acceptfd:")) {
+#if ADB_WINDOWS
+        *error = "socket activation not supported under Windows";
+        return -1;
+#else
+        // We inherited the socket from some kind of launcher. It's already bound and
+        // listening. Return a copy of the FD instead of the FD itself so we implement the
+        // normal "listen" contract and can succeed more than once.
+        unsigned int fd_u;
+        if (!ParseUint(&fd_u, spec) || fd_u > std::numeric_limits<int>::max()) {
+            *error = "invalid fd";
+            return -1;
+        }
+        int fd = static_cast<int>(fd_u);
+        int flags = get_fd_flags(fd);
+        if (flags < 0) {
+            *error = android::base::StringPrintf("could not get flags of inherited fd %d: '%s'", fd,
+                                                 strerror(errno));
+            return -1;
+        }
+        if (flags & FD_CLOEXEC) {
+            *error = android::base::StringPrintf("fd %d was not inherited from parent", fd);
+            return -1;
+        }
+
+        int dummy_sock_type;
+        socklen_t dummy_sock_type_size = sizeof(dummy_sock_type);
+        if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &dummy_sock_type, &dummy_sock_type_size)) {
+            *error = android::base::StringPrintf("fd %d does not refer to a socket", fd);
+            return -1;
+        }
+
+        int new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+        if (new_fd < 0) {
+            *error = android::base::StringPrintf("could not dup inherited fd %d: '%s'", fd,
+                                                 strerror(errno));
+            return -1;
+        }
+        return new_fd;
+#endif
     }
 
     for (const auto& it : kLocalSocketTypes) {
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 466c2ce..0c5a6b4 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -349,8 +349,15 @@
     return c == '/';
 }
 
+static __inline__ int get_fd_flags(borrowed_fd fd) {
+    return fcntl(fd.get(), F_GETFD);
+}
+
 static __inline__ void close_on_exec(borrowed_fd fd) {
-    fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
+    int flags = get_fd_flags(fd);
+    if (flags >= 0 && (flags & FD_CLOEXEC) == 0) {
+        fcntl(fd.get(), F_SETFD, flags | FD_CLOEXEC);
+    }
 }
 
 // Open a file and return a file descriptor that may be used with unix_read(),
diff --git a/adb/transport.cpp b/adb/transport.cpp
index d9749ac..9dd6ec6 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -55,7 +55,7 @@
 using android::base::ScopedLockAssertion;
 
 static void remove_transport(atransport* transport);
-static void transport_unref(atransport* transport);
+static void transport_destroy(atransport* transport);
 
 // TODO: unordered_map<TransportId, atransport*>
 static auto& transport_list = *new std::list<atransport*>();
@@ -66,6 +66,7 @@
 const char* const kFeatureShell2 = "shell_v2";
 const char* const kFeatureCmd = "cmd";
 const char* const kFeatureStat2 = "stat_v2";
+const char* const kFeatureLs2 = "ls_v2";
 const char* const kFeatureLibusb = "libusb";
 const char* const kFeaturePushSync = "push_sync";
 const char* const kFeatureApex = "apex";
@@ -676,7 +677,6 @@
     if (t->GetConnectionState() != kCsNoPerm) {
         // The connection gets a reference to the atransport. It will release it
         // upon a read/write error.
-        t->ref_count++;
         t->connection()->SetTransportName(t->serial_name());
         t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
             if (!check_header(p.get(), t)) {
@@ -695,7 +695,7 @@
             LOG(INFO) << t->serial_name() << ": connection terminated: " << error;
             fdevent_run_on_main_thread([t]() {
                 handle_offline(t);
-                transport_unref(t);
+                transport_destroy(t);
             });
         });
 
@@ -771,36 +771,27 @@
     }
 }
 
-static void transport_unref(atransport* t) {
+static void transport_destroy(atransport* t) {
     check_main_thread();
     CHECK(t != nullptr);
 
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
-    CHECK_GT(t->ref_count, 0u);
-    t->ref_count--;
-    if (t->ref_count == 0) {
-        LOG(INFO) << "destroying transport " << t->serial_name();
-        t->connection()->Stop();
+    LOG(INFO) << "destroying transport " << t->serial_name();
+    t->connection()->Stop();
 #if ADB_HOST
-        if (t->IsTcpDevice() && !t->kicked()) {
-            D("transport: %s unref (attempting reconnection)", t->serial.c_str());
+    if (t->IsTcpDevice() && !t->kicked()) {
+        D("transport: %s destroy (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());
-            remove_transport(t);
-        }
-#else
-        D("transport: %s unref (kicking and closing)", t->serial.c_str());
-        remove_transport(t);
+        // 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);
+        return;
+    }
 #endif
 
-    } else {
-        D("transport: %s unref (count=%zu)", t->serial.c_str(), t->ref_count);
-    }
+    D("transport: %s destroy (kicking and closing)", t->serial.c_str());
+    remove_transport(t);
 }
 
 static int qual_match(const std::string& to_test, const char* prefix, const std::string& qual,
@@ -1045,6 +1036,7 @@
             kFeatureShell2,
             kFeatureCmd,
             kFeatureStat2,
+            kFeatureLs2,
             kFeatureFixedPushMkdir,
             kFeatureApex,
             kFeatureAbb,
diff --git a/adb/transport.h b/adb/transport.h
index 89d76b8..ea77117 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -57,6 +57,7 @@
 // The 'cmd' command is available
 extern const char* const kFeatureCmd;
 extern const char* const kFeatureStat2;
+extern const char* const kFeatureLs2;
 // The server is running with libusb enabled.
 extern const char* const kFeatureLibusb;
 // adbd supports `push --sync`.
@@ -266,7 +267,7 @@
     usb_handle* GetUsbHandle() { return usb_handle_; }
 
     const TransportId id;
-    size_t ref_count = 0;
+
     bool online = false;
     TransportType type = kTransportAny;
 
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index b20f278..f3d7cb0 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -53,30 +53,34 @@
   CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
 };
 
-#define ASSERT_MATCH(str, pattern)                                             \
-  do {                                                                         \
-    if (!std::regex_search((str), std::regex((pattern)))) {                    \
-      FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << (str); \
-    }                                                                          \
+#define ASSERT_MATCH(str, pattern)                                           \
+  do {                                                                       \
+    auto __s = (str);                                                        \
+    if (!std::regex_search(__s, std::regex((pattern)))) {                    \
+      FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
+    }                                                                        \
   } while (0)
 
-#define ASSERT_NOT_MATCH(str, pattern)                                                     \
-  do {                                                                                     \
-    if (std::regex_search((str), std::regex((pattern)))) {                                 \
-      FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
-    }                                                                                      \
+#define ASSERT_NOT_MATCH(str, pattern)                                                   \
+  do {                                                                                   \
+    auto __s = (str);                                                                    \
+    if (std::regex_search(__s, std::regex((pattern)))) {                                 \
+      FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
+    }                                                                                    \
   } while (0)
 
-#define EXPECT_MATCH(str, pattern)                                                    \
-  do {                                                                                \
-    if (!std::regex_search((str), std::regex((pattern)))) {                           \
-      ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << (str); \
-    }                                                                                 \
+#define EXPECT_MATCH(str, pattern)                                                  \
+  do {                                                                              \
+    auto __s = (str);                                                               \
+    if (!std::regex_search(__s, std::regex((pattern)))) {                           \
+      ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
+    }                                                                               \
   } while (0)
 
-#define EXPECT_NOT_MATCH(str, pattern)                                                            \
-  do {                                                                                            \
-    if (std::regex_search((str), std::regex((pattern)))) {                                        \
-      ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
-    }                                                                                             \
+#define EXPECT_NOT_MATCH(str, pattern)                                                          \
+  do {                                                                                          \
+    auto __s = (str);                                                                           \
+    if (std::regex_search(__s, std::regex((pattern)))) {                                        \
+      ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
+    }                                                                                           \
   } while (0)
diff --git a/cli-test/.clang-format b/cli-test/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/cli-test/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/cli-test/Android.bp b/cli-test/Android.bp
new file mode 100644
index 0000000..37a1d1b
--- /dev/null
+++ b/cli-test/Android.bp
@@ -0,0 +1,7 @@
+cc_binary {
+    name: "cli-test",
+    host_supported: true,
+    srcs: ["cli-test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+    shared_libs: ["libbase"],
+}
diff --git a/cli-test/README.md b/cli-test/README.md
new file mode 100644
index 0000000..643eb74
--- /dev/null
+++ b/cli-test/README.md
@@ -0,0 +1,90 @@
+# cli-test
+
+## What?
+
+`cli-test` makes integration testing of command-line tools easier.
+
+## Goals
+
+* Readable syntax. Common cases should be concise, and pretty much anyone
+  should be able to read tests even if they've never seen this tool before.
+
+* Minimal issues with quoting. The toybox tests -- being shell scripts --
+  quickly become a nightmare of quoting. Using a non ad hoc format (such as
+  JSON) would have introduced similar but different quoting issues. A custom
+  format, while annoying, side-steps this.
+
+* Sensible defaults. We expect your exit status to be 0 unless you say
+  otherwise. We expect nothing on stderr unless you say otherwise. And so on.
+
+* Convention over configuration. Related to sensible defaults, we don't let you
+  configure things that aren't absolutely necessary. So you can't keep your test
+  data anywhere except in the `files/` subdirectory of the directory containing
+  your test, for example.
+
+## Non Goals
+
+* Portability. Just being able to run on Linux (host and device) is sufficient
+  for our needs. macOS is probably easy enough if we ever need it, but Windows
+  probably doesn't make sense.
+
+## Syntax
+
+Any all-whitespace line, or line starting with `#` is ignored.
+
+A test looks like this:
+```
+name: unzip -l
+command: unzip -l $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/x.txt ]
+expected-stdout:
+	Archive:  $FILES/example.zip
+	  Length      Date    Time    Name
+	---------  ---------- -----   ----
+	     1024  2017-06-04 08:45   d1/d2/x.txt
+	---------                     -------
+	     1024                     1 file
+---
+```
+
+The `name:` line names the test, and is only for human consumption.
+
+The `command:` line is the command to be run. Additional commands can be
+supplied as zero or more `before:` lines (run before `command:`) and zero or
+more `after:` lines (run after `command:`). These are useful for both
+setup/teardown but also for testing post conditions (as in the example above).
+
+Any `command:`, `before:`, or `after:` line is expected to exit with status 0.
+Anything else is considered a test failure.
+
+The `expected-stdout:` line is followed by zero or more tab-prefixed lines that
+are otherwise the exact output expected from the command. (There's magic behind
+the scenes to rewrite the test files directory to `$FILES` because otherwise any
+path in the output would depend on the temporary directory used to run the test.)
+
+There is currently no `expected-stderr:` line. Standard error is implicitly
+expected to be empty, and any output will cause a test failure. (The support is
+there, but not wired up because we haven't needed it yet.)
+
+The fields can appear in any order, but every test must contain at least a
+`name:` line and a `command:` line.
+
+## Output
+
+The output is intended to resemble gtest.
+
+## Future Directions
+
+* It's often useful to be able to *match* against stdout/stderr/a file rather
+  than give exact expected output. We might want to add explicit support for
+  this. In the meantime, it's possible to use an `after:` with `grep -q` if
+  you redirect in your `command:`.
+
+* In addition to using a `before:` (which will fail a test), it can be useful
+  to be able to specify tests that would cause us to *skip* a test. An example
+  would be "am I running as root?".
+
+* It might be useful to be able to make exit status assertions other than 0?
+
+* There's currently no way (other than the `files/` directory) to share repeated
+  setup between tests.
diff --git a/cli-test/cli-test.cpp b/cli-test/cli-test.cpp
new file mode 100644
index 0000000..d6e27ee
--- /dev/null
+++ b/cli-test/cli-test.cpp
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+// Example:
+
+// name: unzip -n
+// before: mkdir -p d1/d2
+// before: echo b > d1/d2/a.txt
+// command: unzip -q -n $FILES/zip/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+// expected-stdout:
+// 	b
+
+struct Test {
+  std::string test_filename;
+  std::string name;
+  std::string command;
+  std::vector<std::string> befores;
+  std::vector<std::string> afters;
+  std::string expected_stdout;
+  std::string expected_stderr;
+  int exit_status = 0;
+};
+
+static const char* g_progname;
+static bool g_verbose;
+
+static const char* g_file;
+static size_t g_line;
+
+enum Color { kRed, kGreen };
+
+static void Print(Color c, const char* lhs, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  if (isatty(0)) printf("%s", (c == kRed) ? "\e[31m" : "\e[32m");
+  printf("%s%s", lhs, isatty(0) ? "\e[0m" : "");
+  vfprintf(stdout, fmt, ap);
+  putchar('\n');
+  va_end(ap);
+}
+
+static void Die(int error, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  fprintf(stderr, "%s: ", g_progname);
+  vfprintf(stderr, fmt, ap);
+  if (error != 0) fprintf(stderr, ": %s", strerror(error));
+  fprintf(stderr, "\n");
+  va_end(ap);
+  _exit(1);
+}
+
+static void V(const char* fmt, ...) {
+  if (!g_verbose) return;
+
+  va_list ap;
+  va_start(ap, fmt);
+  fprintf(stderr, "           - ");
+  vfprintf(stderr, fmt, ap);
+  fprintf(stderr, "\n");
+  va_end(ap);
+}
+
+static void SetField(const char* what, std::string* field, std::string_view value) {
+  if (!field->empty()) {
+    Die(0, "%s:%zu: %s already set to '%s'", g_file, g_line, what, field->c_str());
+  }
+  field->assign(value);
+}
+
+// Similar to ConsumePrefix, but also trims, so "key:value" and "key: value"
+// are equivalent.
+static bool Match(std::string* s, const std::string& prefix) {
+  if (!android::base::StartsWith(*s, prefix)) return false;
+  s->assign(android::base::Trim(s->substr(prefix.length())));
+  return true;
+}
+
+static void CollectTests(std::vector<Test>* tests, const char* test_filename) {
+  std::string absolute_test_filename;
+  if (!android::base::Realpath(test_filename, &absolute_test_filename)) {
+    Die(errno, "realpath '%s'", test_filename);
+  }
+
+  std::string content;
+  if (!android::base::ReadFileToString(test_filename, &content)) {
+    Die(errno, "couldn't read '%s'", test_filename);
+  }
+
+  size_t count = 0;
+  g_file = test_filename;
+  g_line = 0;
+  auto lines = android::base::Split(content, "\n");
+  std::unique_ptr<Test> test(new Test);
+  while (g_line < lines.size()) {
+    auto line = lines[g_line++];
+    if (line.empty() || line[0] == '#') continue;
+
+    if (line[0] == '-') {
+      if (test->name.empty() || test->command.empty()) {
+        Die(0, "%s:%zu: each test requires both a name and a command", g_file, g_line);
+      }
+      test->test_filename = absolute_test_filename;
+      tests->push_back(*test.release());
+      test.reset(new Test);
+      ++count;
+    } else if (Match(&line, "name:")) {
+      SetField("name", &test->name, line);
+    } else if (Match(&line, "command:")) {
+      SetField("command", &test->command, line);
+    } else if (Match(&line, "before:")) {
+      test->befores.push_back(line);
+    } else if (Match(&line, "after:")) {
+      test->afters.push_back(line);
+    } else if (Match(&line, "expected-stdout:")) {
+      // Collect tab-indented lines.
+      std::string text;
+      while (g_line < lines.size() && !lines[g_line].empty() && lines[g_line][0] == '\t') {
+        text += lines[g_line++].substr(1) + "\n";
+      }
+      SetField("expected stdout", &test->expected_stdout, text);
+    } else {
+      Die(0, "%s:%zu: syntax error: \"%s\"", g_file, g_line, line.c_str());
+    }
+  }
+  if (count == 0) Die(0, "no tests found in '%s'", g_file);
+}
+
+static const char* Plural(size_t n) {
+  return (n == 1) ? "" : "s";
+}
+
+static std::string ExitStatusToString(int status) {
+  if (WIFSIGNALED(status)) {
+    return android::base::StringPrintf("was killed by signal %d (%s)", WTERMSIG(status),
+                                       strsignal(WTERMSIG(status)));
+  }
+  if (WIFSTOPPED(status)) {
+    return android::base::StringPrintf("was stopped by signal %d (%s)", WSTOPSIG(status),
+                                       strsignal(WSTOPSIG(status)));
+  }
+  return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
+}
+
+static bool RunCommands(const char* what, const std::vector<std::string>& commands) {
+  bool result = true;
+  for (auto& command : commands) {
+    V("running %s \"%s\"", what, command.c_str());
+    int exit_status = system(command.c_str());
+    if (exit_status != 0) {
+      result = false;
+      fprintf(stderr, "Command (%s) \"%s\" %s\n", what, command.c_str(),
+              ExitStatusToString(exit_status).c_str());
+    }
+  }
+  return result;
+}
+
+static bool CheckOutput(const char* what, std::string actual_output,
+                        const std::string& expected_output, const std::string& FILES) {
+  // Rewrite the output to reverse any expansion of $FILES.
+  actual_output = android::base::StringReplace(actual_output, FILES, "$FILES", true);
+
+  bool result = (actual_output == expected_output);
+  if (!result) {
+    fprintf(stderr, "Incorrect %s.\nExpected:\n%s\nActual:\n%s\n", what, expected_output.c_str(),
+            actual_output.c_str());
+  }
+  return result;
+}
+
+static int RunTests(const std::vector<Test>& tests) {
+  std::vector<std::string> failures;
+
+  Print(kGreen, "[==========]", " Running %zu tests.", tests.size());
+  android::base::Timer total_timer;
+  for (const auto& test : tests) {
+    bool failed = false;
+
+    Print(kGreen, "[ RUN      ]", " %s", test.name.c_str());
+    android::base::Timer test_timer;
+
+    // Set $FILES for this test.
+    std::string FILES = android::base::Dirname(test.test_filename) + "/files";
+    V("setenv(\"FILES\", \"%s\")", FILES.c_str());
+    setenv("FILES", FILES.c_str(), 1);
+
+    // Make a safe space to run the test.
+    TemporaryDir td;
+    V("chdir(\"%s\")", td.path);
+    if (chdir(td.path)) Die(errno, "chdir(\"%s\")", td.path);
+
+    // Perform any setup specified for this test.
+    if (!RunCommands("before", test.befores)) failed = true;
+
+    if (!failed) {
+      V("running command \"%s\"", test.command.c_str());
+      CapturedStdout test_stdout;
+      CapturedStderr test_stderr;
+      int exit_status = system(test.command.c_str());
+      test_stdout.Stop();
+      test_stderr.Stop();
+
+      V("exit status %d", exit_status);
+      if (exit_status != test.exit_status) {
+        failed = true;
+        fprintf(stderr, "Incorrect exit status: expected %d but %s\n", test.exit_status,
+                ExitStatusToString(exit_status).c_str());
+      }
+
+      if (!CheckOutput("stdout", test_stdout.str(), test.expected_stdout, FILES)) failed = true;
+      if (!CheckOutput("stderr", test_stderr.str(), test.expected_stderr, FILES)) failed = true;
+
+      if (!RunCommands("after", test.afters)) failed = true;
+    }
+
+    std::stringstream duration;
+    duration << test_timer;
+    if (failed) {
+      failures.push_back(test.name);
+      Print(kRed, "[  FAILED  ]", " %s (%s)", test.name.c_str(), duration.str().c_str());
+    } else {
+      Print(kGreen, "[       OK ]", " %s (%s)", test.name.c_str(), duration.str().c_str());
+    }
+  }
+
+  // Summarize the whole run and explicitly list all the failures.
+
+  std::stringstream duration;
+  duration << total_timer;
+  Print(kGreen, "[==========]", " %zu tests ran. (%s total)", tests.size(), duration.str().c_str());
+
+  size_t fail_count = failures.size();
+  size_t pass_count = tests.size() - fail_count;
+  Print(kGreen, "[  PASSED  ]", " %zu test%s.", pass_count, Plural(pass_count));
+  if (!failures.empty()) {
+    Print(kRed, "[  FAILED  ]", " %zu test%s.", fail_count, Plural(fail_count));
+    for (auto& failure : failures) {
+      Print(kRed, "[  FAILED  ]", " %s", failure.c_str());
+    }
+  }
+  return (fail_count == 0) ? 0 : 1;
+}
+
+static void ShowHelp(bool full) {
+  fprintf(full ? stdout : stderr, "usage: %s [-v] FILE...\n", g_progname);
+  if (!full) exit(EXIT_FAILURE);
+
+  printf(
+      "\n"
+      "Run tests.\n"
+      "\n"
+      "-v\tVerbose (show workings)\n");
+  exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char* argv[]) {
+  g_progname = basename(argv[0]);
+
+  static const struct option opts[] = {
+      {"help", no_argument, 0, 'h'},
+      {"verbose", no_argument, 0, 'v'},
+      {},
+  };
+
+  int opt;
+  while ((opt = getopt_long(argc, argv, "hv", opts, nullptr)) != -1) {
+    switch (opt) {
+      case 'h':
+        ShowHelp(true);
+        break;
+      case 'v':
+        g_verbose = true;
+        break;
+      default:
+        ShowHelp(false);
+        break;
+    }
+  }
+
+  argv += optind;
+  if (!*argv) Die(0, "no test files provided");
+  std::vector<Test> tests;
+  for (; *argv; ++argv) CollectTests(&tests, *argv);
+  return RunTests(tests);
+}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 2dc47bb..7b686f0 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -91,6 +91,7 @@
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
 using android::base::Basename;
+using android::base::GetBoolProperty;
 using android::base::Realpath;
 using android::base::StartsWith;
 using android::base::unique_fd;
@@ -1357,14 +1358,33 @@
     return ret;
 }
 
+static std::string GetBlockDeviceForMountPoint(const std::string& mount_point) {
+    Fstab mounts;
+    if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+        LERROR << "Can't read /proc/mounts";
+        return "";
+    }
+    auto entry = GetEntryForMountPoint(&mounts, mount_point);
+    if (entry == nullptr) {
+        LWARNING << mount_point << " is not mounted";
+        return "";
+    }
+    return entry->blk_device;
+}
+
 // TODO(b/143970043): return different error codes based on which step failed.
 int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
-    auto entry = GetMountedEntryForUserdata(fstab);
-    if (entry == nullptr) {
+    std::string block_device = GetBlockDeviceForMountPoint("/data");
+    if (block_device.empty()) {
+        LERROR << "/data is not mounted";
+        return -1;
+    }
+    auto fstab_entry = GetMountedEntryForUserdata(fstab);
+    if (fstab_entry == nullptr) {
         LERROR << "Can't find /data in fstab";
         return -1;
     }
-    if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+    if (!fstab_entry->fs_mgr_flags.checkpoint_blk && !fstab_entry->fs_mgr_flags.checkpoint_fs) {
         LINFO << "Userdata doesn't support checkpointing. Nothing to do";
         return 0;
     }
@@ -1373,34 +1393,43 @@
         LINFO << "Checkpointing not needed. Don't remount";
         return 0;
     }
-    if (entry->fs_mgr_flags.checkpoint_fs) {
+    bool force_umount_for_f2fs =
+            GetBoolProperty("sys.init.userdata_remount.force_umount_f2fs", false);
+    if (fstab_entry->fs_mgr_flags.checkpoint_fs && !force_umount_for_f2fs) {
         // Userdata is f2fs, simply remount it.
-        if (!checkpoint_manager.Update(&(*entry))) {
+        if (!checkpoint_manager.Update(fstab_entry)) {
             LERROR << "Failed to remount userdata in checkpointing mode";
             return -1;
         }
-        if (mount(entry->blk_device.c_str(), entry->mount_point.c_str(), "none",
-                  MS_REMOUNT | entry->flags, entry->fs_options.c_str()) != 0) {
+        if (mount(block_device.c_str(), fstab_entry->mount_point.c_str(), "none",
+                  MS_REMOUNT | fstab_entry->flags, fstab_entry->fs_options.c_str()) != 0) {
             PERROR << "Failed to remount userdata in checkpointing mode";
             return -1;
         }
     } else {
-        // STOPSHIP(b/143970043): support remounting for ext4 + metadata encryption.
-        if (should_use_metadata_encryption(*entry)) {
-            LWARNING << "Remounting into checkpointing is not supported for metadata encrypted "
-                     << "ext4 userdata. Proceed with caution";
-            return 0;
-        }
+        LINFO << "Unmounting /data before remounting into checkpointing mode";
         if (umount2("/data", UMOUNT_NOFOLLOW) != 0) {
             PERROR << "Failed to umount /data";
             return -1;
         }
         DeviceMapper& dm = DeviceMapper::Instance();
-        // TODO(b/143970043): need to delete every dm-device under the one userdata is mounted on.
-        if (!dm.DeleteDeviceIfExists("bow")) {
-            LERROR << "Failed to delete dm-bow";
-            return -1;
+        while (dm.IsDmBlockDevice(block_device)) {
+            auto next_device = dm.GetParentBlockDeviceByPath(block_device);
+            auto name = dm.GetDmDeviceNameByPath(block_device);
+            if (!name) {
+                LERROR << "Failed to get dm-name for " << block_device;
+                return -1;
+            }
+            LINFO << "Deleting " << block_device << " named " << *name;
+            if (!dm.DeleteDevice(*name, 3s)) {
+                return -1;
+            }
+            if (!next_device) {
+                LERROR << "Failed to find parent device for " << block_device;
+            }
+            block_device = *next_device;
         }
+        LINFO << "Remounting /data";
         // TODO(b/143970043): remove this hack after fs_mgr_mount_all is refactored.
         int result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
         return result == FS_MGR_MNTALL_FAIL ? -1 : 0;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index c81a079..9697a4c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -582,8 +582,7 @@
 }  // namespace
 
 void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions) {
-    static constexpr char kGsiKeys[] =
-            "/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey";
+    static constexpr char kDsuKeysDir[] = "/avb";
     // Convert userdata
     // Inherit fstab properties for userdata.
     FstabEntry userdata;
@@ -629,29 +628,18 @@
                     .fs_type = "ext4",
                     .flags = MS_RDONLY,
                     .fs_options = "barrier=1",
-                    .avb_keys = kGsiKeys,
+                    .avb_keys = kDsuKeysDir,
             };
             entry.fs_mgr_flags.wait = true;
             entry.fs_mgr_flags.logical = true;
             entry.fs_mgr_flags.first_stage_mount = true;
-            // Use the system key which may be in the vbmeta or vbmeta_system
-            // TODO: b/141284191
-            entry.vbmeta_partition = "vbmeta";
-            fstab->emplace_back(entry);
-            entry.vbmeta_partition = "vbmeta_system";
-            fstab->emplace_back(entry);
         } else {
             // If the corresponding partition exists, transform all its Fstab
             // by pointing .blk_device to the DSU partition.
             for (auto&& entry : entries) {
                 entry->blk_device = partition;
-                if (entry->avb_keys.size() > 0) {
-                    entry->avb_keys += ":";
-                }
-                // If the DSU is signed by OEM, the original Fstab already has the information
-                // required by avb, otherwise the DSU is GSI and will need the avb_keys as listed
-                // below.
-                entry->avb_keys += kGsiKeys;
+                // AVB keys for DSU should always be under kDsuKeysDir.
+                entry->avb_keys += kDsuKeysDir;
             }
             // Make sure the ext4 is included to support GSI.
             auto partition_ext4 =
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 0579a3d..27971da 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -440,13 +440,9 @@
     rmdir(kScratchMountPoint.c_str());
 }
 
-// reduce 'DM_DEV_STATUS failed for scratch: No such device or address' noise
-std::string scratch_device_cache;
-
 bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change) {
     // umount and delete kScratchMountPoint storage if we have logical partitions
     if (overlay != kScratchMountPoint) return true;
-    scratch_device_cache.erase();
     auto slot_number = fs_mgr_overlayfs_slot_number();
     auto super_device = fs_mgr_overlayfs_super_device(slot_number);
     if (!fs_mgr_rw_access(super_device)) return true;
@@ -815,26 +811,66 @@
     return "auto";
 }
 
-std::string fs_mgr_overlayfs_scratch_device() {
-    if (!scratch_device_cache.empty()) return scratch_device_cache;
+enum class ScratchStrategy {
+    kNone,
+    // DAP device, use logical partitions.
+    kDynamicPartition,
+    // Retrofit DAP device, use super_<other>.
+    kSuperOther,
+    // Pre-DAP device, uses the other slot.
+    kSystemOther
+};
 
-    // Is this a multiple super device (retrofit)?
+// Return the strategy this device must use for creating a scratch partition.
+static ScratchStrategy GetScratchStrategy(std::string* backing_device = nullptr) {
     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)) {
-            // non-DAP A/B device?
-            if (fs_mgr_access(super_device)) return "";
-            auto other_slot = fs_mgr_get_other_slot_suffix();
-            if (other_slot.empty()) return "";
-            path = kPhysicalDevice + "system" + other_slot;
+    if (super_device != path) {
+        // Note: we do not check access() here, since in first-stage init we
+        // wouldn't have registed by-name symlinks for the device as it's
+        // normally not needed. The access checks elsewhere in this function
+        // are safe because system/super are always required.
+        if (backing_device) *backing_device = path;
+        return ScratchStrategy::kSuperOther;
+    }
+    if (fs_mgr_access(super_device)) {
+        if (backing_device) *backing_device = super_device;
+        return ScratchStrategy::kDynamicPartition;
+    }
+
+    auto other_slot = fs_mgr_get_other_slot_suffix();
+    if (!other_slot.empty()) {
+        path = kPhysicalDevice + "system" + other_slot;
+        if (fs_mgr_access(path)) {
+            if (backing_device) *backing_device = path;
+            return ScratchStrategy::kSystemOther;
         }
     }
-    return scratch_device_cache = path;
+    return ScratchStrategy::kNone;
+}
+
+// Return the scratch device if it exists.
+static std::string GetScratchDevice() {
+    std::string device;
+    ScratchStrategy strategy = GetScratchStrategy(&device);
+
+    switch (strategy) {
+        case ScratchStrategy::kSuperOther:
+        case ScratchStrategy::kSystemOther:
+            return device;
+        case ScratchStrategy::kDynamicPartition: {
+            auto& dm = DeviceMapper::Instance();
+            auto partition_name = android::base::Basename(kScratchMountPoint);
+            if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
+                dm.GetDmDevicePathByName(partition_name, &device)) {
+                return device;
+            }
+            return "";
+        }
+        default:
+            return "";
+    }
 }
 
 bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
@@ -878,16 +914,15 @@
     }
 }
 
-// This is where we find and steal backing storage from the system.
-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);
+// Create or update a scratch partition within super.
+static bool CreateDynamicScratch(const Fstab& fstab, std::string* scratch_device,
+                                 bool* partition_exists, bool* change) {
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+
+    auto& dm = DeviceMapper::Instance();
+    *partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
+
     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;
@@ -897,7 +932,6 @@
         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;
@@ -978,6 +1012,25 @@
     return true;
 }
 
+bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
+                                     bool* partition_exists, bool* change) {
+    auto strategy = GetScratchStrategy();
+    if (strategy == ScratchStrategy::kDynamicPartition) {
+        return CreateDynamicScratch(fstab, scratch_device, partition_exists, change);
+    }
+
+    // The scratch partition can only be landed on a physical partition if we
+    // get here. If there are no viable candidates that are R/W, just return
+    // that there is no device.
+    *scratch_device = GetScratchDevice();
+    if (scratch_device->empty()) {
+        errno = ENXIO;
+        return false;
+    }
+    *partition_exists = 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;
@@ -1066,6 +1119,25 @@
     return candidates;
 }
 
+static void TryMountScratch() {
+    auto scratch_device = GetScratchDevice();
+    if (!fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device)) {
+        return;
+    }
+    if (!WaitForFile(scratch_device, 10s)) {
+        return;
+    }
+    const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
+    if (!fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type, true /* readonly */)) {
+        return;
+    }
+    auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
+    fs_mgr_overlayfs_umount_scratch();
+    if (has_overlayfs_dir) {
+        fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
+    }
+}
+
 bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
     auto ret = false;
     if (fs_mgr_overlayfs_invalid()) return ret;
@@ -1080,19 +1152,7 @@
         }
         if (scratch_can_be_mounted) {
             scratch_can_be_mounted = false;
-            auto scratch_device = fs_mgr_overlayfs_scratch_device();
-            if (fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device) &&
-                WaitForFile(scratch_device, 10s)) {
-                const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
-                if (fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type,
-                                                   true /* readonly */)) {
-                    auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
-                    fs_mgr_overlayfs_umount_scratch();
-                    if (has_overlayfs_dir) {
-                        fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
-                    }
-                }
-            }
+            TryMountScratch();
         }
         if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
     }
@@ -1109,7 +1169,7 @@
     for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
         if (fs_mgr_is_verity_enabled(entry)) continue;
         if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) continue;
-        auto device = fs_mgr_overlayfs_scratch_device();
+        auto device = GetScratchDevice();
         if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
         return {device};
     }
@@ -1181,6 +1241,27 @@
     return ret;
 }
 
+static bool GetAndMapScratchDeviceIfNeeded(std::string* device) {
+    *device = GetScratchDevice();
+    if (!device->empty()) {
+        return true;
+    }
+
+    auto strategy = GetScratchStrategy();
+    if (strategy == ScratchStrategy::kDynamicPartition) {
+        auto metadata_slot = fs_mgr_overlayfs_slot_number();
+        CreateLogicalPartitionParams params = {
+                .block_device = fs_mgr_overlayfs_super_device(metadata_slot),
+                .metadata_slot = metadata_slot,
+                .partition_name = android::base::Basename(kScratchMountPoint),
+                .force_writable = true,
+                .timeout_ms = 10s,
+        };
+        return CreateLogicalPartition(params, device);
+    }
+    return false;
+}
+
 // Returns false if teardown not permitted, errno set to last error.
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
@@ -1190,20 +1271,11 @@
     // 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()) {
-            auto metadata_slot = fs_mgr_overlayfs_slot_number();
-            CreateLogicalPartitionParams params = {
-                    .block_device = fs_mgr_overlayfs_super_device(metadata_slot),
-                    .metadata_slot = metadata_slot,
-                    .partition_name = android::base::Basename(kScratchMountPoint),
-                    .force_writable = true,
-                    .timeout_ms = 10s,
-            };
-            CreateLogicalPartition(params, &scratch_device);
+        std::string scratch_device;
+        if (GetAndMapScratchDeviceIfNeeded(&scratch_device)) {
+            mount_scratch = 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(
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 809318c..254fbed 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -575,5 +575,9 @@
     return "/dev/block/" + sub_device_name;
 }
 
+bool DeviceMapper::TargetInfo::IsOverflowSnapshot() const {
+    return spec.target_type == "snapshot"s && data == "Overflow"s;
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 418210c..abe9c4c 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -205,6 +205,8 @@
         TargetInfo() {}
         TargetInfo(const struct dm_target_spec& spec, const std::string& data)
             : spec(spec), data(data) {}
+
+        bool IsOverflowSnapshot() const;
     };
     bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
 
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 32702ae..bf51fe7 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -37,11 +37,9 @@
         "libfstab",
     ],
     shared_libs: [
+        "libbase",
         "libcrypto",
     ],
-    header_libs: [
-        "libbase_headers",
-    ],
     target: {
         darwin: {
             enabled: false,
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index c4d7511..8770a6b 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -22,6 +22,7 @@
 #include <sys/ioctl.h>
 #include <sys/types.h>
 
+#include <algorithm>
 #include <sstream>
 #include <string>
 #include <vector>
@@ -308,7 +309,18 @@
             return nullptr;
     }
 
-    if (!ValidatePublicKeyBlob(public_key_data, Split(fstab_entry.avb_keys, ":"))) {
+    // fstab_entry.avb_keys might be either a directory containing multiple keys,
+    // or a string indicating multiple keys separated by ':'.
+    std::vector<std::string> allowed_avb_keys;
+    auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
+    if (list_avb_keys_in_dir) {
+        std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
+        allowed_avb_keys = *list_avb_keys_in_dir;
+    } else {
+        allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
+    }
+
+    if (!ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
         avb_handle->status_ = AvbHandleStatus::kVerificationError;
         LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
         if (!allow_verification_error) {
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
index 12b5acb..e64282b 100644
--- a/fs_mgr/libfs_avb/tests/util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -16,10 +16,12 @@
 
 #include <unistd.h>
 
+#include <algorithm>
 #include <future>
 #include <string>
 #include <thread>
 
+#include <android-base/strings.h>
 #include <base/files/file_util.h>
 
 #include "fs_avb_test_util.h"
@@ -29,6 +31,7 @@
 using android::fs_mgr::BytesToHex;
 using android::fs_mgr::FileWaitMode;
 using android::fs_mgr::HexToBytes;
+using android::fs_mgr::ListFiles;
 using android::fs_mgr::NibbleValue;
 using android::fs_mgr::WaitForFile;
 
@@ -210,4 +213,102 @@
     ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
 }
 
+TEST(BasicUtilTest, ListFiles) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Creates a test dir for ListFiles testing.
+    base::FilePath test_dir;
+    ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
+
+    // Generates dummy files to list.
+    base::FilePath file_path_1 = test_dir.Append("1.txt");
+    ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
+    base::FilePath file_path_2 = test_dir.Append("2.txt");
+    ASSERT_TRUE(base::WriteFile(file_path_2, "22", 2));
+    base::FilePath file_path_3 = test_dir.Append("3.txt");
+    ASSERT_TRUE(base::WriteFile(file_path_3, "333", 3));
+
+    // List files for comparison.
+    auto result = ListFiles(test_dir.value());
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+    auto files = result.value();
+    EXPECT_EQ(3UL, files.size());
+    // Sort them offline for comparison.
+    std::sort(files.begin(), files.end());
+    EXPECT_EQ(file_path_1.value(), files[0]);
+    EXPECT_EQ(file_path_2.value(), files[1]);
+    EXPECT_EQ(file_path_3.value(), files[2]);
+
+    ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));
+}
+
+TEST(BasicUtilTest, ListFilesShouldDiscardSymlink) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Creates a test dir for ListFiles testing.
+    base::FilePath test_dir;
+    ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
+
+    // Generates dummy files to list.
+    base::FilePath file_path_1 = test_dir.Append("1.txt");
+    ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
+    base::FilePath file_path_2 = test_dir.Append("2.txt");
+    ASSERT_TRUE(base::WriteFile(file_path_2, "22", 2));
+    // Creates a symlink and checks it won't be returned by ListFiles.
+    base::FilePath file_path_3 = test_dir.Append("3.txt");
+    base::FilePath non_existent_target = test_dir.Append("non_existent_target.txt");
+    ASSERT_TRUE(base::CreateSymbolicLink(non_existent_target, file_path_3));
+
+    // List files for comparison.
+    auto result = ListFiles(test_dir.value());
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+    auto files = result.value();
+    EXPECT_EQ(2UL, files.size());  // Should not include the symlink file.
+    // Sort them offline for comparison.
+    std::sort(files.begin(), files.end());
+    EXPECT_EQ(file_path_1.value(), files[0]);
+    EXPECT_EQ(file_path_2.value(), files[1]);
+
+    ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));
+}
+
+TEST(BasicUtilTest, ListFilesOpenDirFailure) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Generates dummy files to list.
+    base::FilePath no_such_dir = tmp_dir.Append("not_such_dir");
+
+    auto fail = ListFiles(no_such_dir.value());
+    ASSERT_FALSE(fail);
+    EXPECT_EQ(ENOENT, fail.error().code());
+    EXPECT_TRUE(android::base::StartsWith(fail.error().message(), "Failed to opendir: "));
+}
+
+TEST(BasicUtilTest, ListFilesEmptyDir) {
+    // Gets system tmp dir.
+    base::FilePath tmp_dir;
+    ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+    // Creates a test dir for ListFiles testing.
+    base::FilePath test_dir;
+    ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
+
+    // List files without sorting.
+    auto result = ListFiles(test_dir.value());
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+    auto files = result.value();
+    EXPECT_EQ(0UL, files.size());
+
+    ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));
+}
+
 }  // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/util.cpp b/fs_mgr/libfs_avb/util.cpp
index d214b5b..7783d04 100644
--- a/fs_mgr/libfs_avb/util.cpp
+++ b/fs_mgr/libfs_avb/util.cpp
@@ -16,10 +16,13 @@
 
 #include "util.h"
 
+#include <dirent.h>
 #include <sys/ioctl.h>
+#include <sys/types.h>
 
 #include <thread>
 
+#include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <linux/fs.h>
 
@@ -122,5 +125,23 @@
     return ioctl(fd, BLKROSET, &ON) == 0;
 }
 
+Result<std::vector<std::string>> ListFiles(const std::string& dir) {
+    struct dirent* de;
+    std::vector<std::string> files;
+
+    std::unique_ptr<DIR, int (*)(DIR*)> dirp(opendir(dir.c_str()), closedir);
+    if (!dirp) {
+        return ErrnoError() << "Failed to opendir: " << dir;
+    }
+
+    while ((de = readdir(dirp.get()))) {
+        if (de->d_type != DT_REG) continue;
+        std::string full_path = android::base::StringPrintf("%s/%s", dir.c_str(), de->d_name);
+        files.emplace_back(std::move(full_path));
+    }
+
+    return files;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/util.h b/fs_mgr/libfs_avb/util.h
index 7763da5..427ab7c 100644
--- a/fs_mgr/libfs_avb/util.h
+++ b/fs_mgr/libfs_avb/util.h
@@ -18,6 +18,7 @@
 
 #include <chrono>
 #include <string>
+#include <vector>
 
 #ifdef HOST_TEST
 #include <base/logging.h>
@@ -25,6 +26,11 @@
 #include <android-base/logging.h>
 #endif
 
+#include <android-base/result.h>
+
+using android::base::ErrnoError;
+using android::base::Result;
+
 #define FS_AVB_TAG "[libfs_avb]"
 
 // Logs a message to kernel
@@ -60,5 +66,8 @@
 
 bool SetBlockDeviceReadOnly(const std::string& blockdev);
 
+// Returns a list of file under the dir, no order is guaranteed.
+Result<std::vector<std::string>> ListFiles(const std::string& dir);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 54350a5..4406696 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -253,7 +253,7 @@
     header_.magic = LP_METADATA_HEADER_MAGIC;
     header_.major_version = LP_METADATA_MAJOR_VERSION;
     header_.minor_version = LP_METADATA_MINOR_VERSION_MIN;
-    header_.header_size = sizeof(header_);
+    header_.header_size = sizeof(LpMetadataHeaderV1_0);
     header_.partitions.entry_size = sizeof(LpMetadataPartition);
     header_.extents.entry_size = sizeof(LpMetadataExtent);
     header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
@@ -264,6 +264,12 @@
     geometry_ = metadata.geometry;
     block_devices_ = metadata.block_devices;
 
+    // Bump the version as necessary to copy any newer fields.
+    if (metadata.header.minor_version >= LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {
+        RequireExpandedMetadataHeader();
+        header_.flags = metadata.header.flags;
+    }
+
     for (const auto& group : metadata.groups) {
         std::string group_name = GetPartitionGroupName(group);
         if (!AddGroup(group_name, group.maximum_size)) {
@@ -883,6 +889,14 @@
     return metadata;
 }
 
+void MetadataBuilder::RequireExpandedMetadataHeader() {
+    if (header_.minor_version >= LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {
+        return;
+    }
+    header_.minor_version = LP_METADATA_VERSION_FOR_EXPANDED_HEADER;
+    header_.header_size = sizeof(LpMetadataHeaderV1_2);
+}
+
 uint64_t MetadataBuilder::AllocatableSpace() const {
     uint64_t total_size = 0;
     for (const auto& block_device : block_devices_) {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index a67ffa7..ca8df61 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -352,6 +352,7 @@
     EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
     EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
     EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION_MIN);
+    EXPECT_EQ(header.header_size, sizeof(LpMetadataHeaderV1_0));
 
     ASSERT_EQ(exported->partitions.size(), 2);
     ASSERT_EQ(exported->extents.size(), 3);
@@ -917,3 +918,22 @@
                                       std::vector<Interval>{Interval(0, 100, 150)})
                           .size());
 }
+
+TEST_F(BuilderTest, ExpandedHeader) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    builder->RequireExpandedMetadataHeader();
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));
+
+    exported->header.flags = 0x5e5e5e5e;
+
+    builder = MetadataBuilder::New(*exported.get());
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));
+    EXPECT_EQ(exported->header.flags, 0x5e5e5e5e);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 1e9d636..7a334fb 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -325,6 +325,10 @@
     bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
     bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
 
+    // Require the expanded metadata header. This is exposed for testing, and
+    // is normally only called as needed by other methods.
+    void RequireExpandedMetadataHeader();
+
     // Attempt to preserve the named partitions from an older metadata. If this
     // is not possible (for example, the block device list has changed) then
     // false is returned.
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 6e928b4..26cbf07 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -40,11 +40,14 @@
 /* Current metadata version. */
 #define LP_METADATA_MAJOR_VERSION 10
 #define LP_METADATA_MINOR_VERSION_MIN 0
-#define LP_METADATA_MINOR_VERSION_MAX 1
+#define LP_METADATA_MINOR_VERSION_MAX 2
 
 /* Metadata version needed to use the UPDATED partition attribute. */
 #define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1
 
+/* Metadata version needed for the new expanded header struct. */
+#define LP_METADATA_VERSION_FOR_EXPANDED_HEADER 2
+
 /* Attributes for the LpMetadataPartition::attributes field.
  *
  * READONLY - The partition should not be considered writable. When used with
@@ -212,6 +215,22 @@
     LpMetadataTableDescriptor groups;
     /* 116: Block device table. */
     LpMetadataTableDescriptor block_devices;
+
+    /* Everything past here is header version 1.2+, and is only included if
+     * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must
+     * zero these additional fields.
+     */
+
+    /* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are
+     * independent of the version number and intended to be informational only.
+     * New flags can be added without bumping the version.
+     *
+     * (Note there are no flags currently defined.)
+     */
+    uint32_t flags;
+
+    /* 132: Reserved (zero), pad to 256 bytes. */
+    uint8_t reserved[124];
 } __attribute__((packed)) LpMetadataHeader;
 
 /* This struct defines a logical partition entry, similar to what would be
@@ -351,6 +370,25 @@
  */
 #define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)
 
+/* For ease of writing compatibility checks, the original metadata header is
+ * preserved below, and typedefs are provided for the current version.
+ */
+typedef struct LpMetadataHeaderV1_0 {
+    uint32_t magic;
+    uint16_t major_version;
+    uint16_t minor_version;
+    uint32_t header_size;
+    uint8_t header_checksum[32];
+    uint32_t tables_size;
+    uint8_t tables_checksum[32];
+    LpMetadataTableDescriptor partitions;
+    LpMetadataTableDescriptor extents;
+    LpMetadataTableDescriptor groups;
+    LpMetadataTableDescriptor block_devices;
+} __attribute__((packed)) LpMetadataHeaderV1_0;
+
+typedef LpMetadataHeader LpMetadataHeaderV1_2;
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 22f6746..e67fb33 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -372,7 +372,7 @@
     // Compute the maximum number of partitions we can fit in 512 bytes of
     // metadata. By default there is the header, one partition group, and a
     // block device entry.
-    static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) -
+    static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeaderV1_0) -
                                                  sizeof(LpMetadataPartitionGroup) -
                                                  sizeof(LpMetadataBlockDevice);
     size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
@@ -742,3 +742,28 @@
     ASSERT_GE(metadata->partitions.size(), 1);
     ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
 }
+
+TEST_F(LiblpTest, ReadExpandedHeader) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+    builder->RequireExpandedMetadataHeader();
+
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+
+    // Export and flash.
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    exported->header.flags = 0x5e5e5e5e;
+    ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
+    ASSERT_NE(imported, nullptr);
+    EXPECT_EQ(imported->header.header_size, sizeof(LpMetadataHeaderV1_2));
+    EXPECT_EQ(imported->header.header_size, exported->header.header_size);
+    EXPECT_EQ(imported->header.flags, exported->header.flags);
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index aecf685..30c17e4 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -31,6 +31,9 @@
 namespace android {
 namespace fs_mgr {
 
+static_assert(sizeof(LpMetadataHeaderV1_0) == offsetof(LpMetadataHeader, flags),
+              "Incorrect LpMetadataHeader v0 size");
+
 // Helper class for reading descriptors and memory buffers in the same manner.
 class Reader {
   public:
@@ -161,30 +164,59 @@
     return true;
 }
 
-static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
-    // To compute the header's checksum, we have to temporarily set its checksum
-    // field to 0.
-    {
-        LpMetadataHeader temp = header;
-        memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
-        SHA256(&temp, sizeof(temp), temp.header_checksum);
-        if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) {
-            LERROR << "Logical partition metadata has invalid checksum.";
-            return false;
-        }
+static bool ReadMetadataHeader(Reader* reader, LpMetadata* metadata) {
+    // Note we zero the struct since older files will result in a partial read.
+    LpMetadataHeader& header = metadata->header;
+    memset(&header, 0, sizeof(header));
+
+    if (!reader->ReadFully(&header, sizeof(LpMetadataHeaderV1_0))) {
+        PERROR << __PRETTY_FUNCTION__ << " read failed";
+        return false;
     }
 
-    // Do basic validation of key metadata bits.
+    // Do basic sanity checks before computing the checksum.
     if (header.magic != LP_METADATA_HEADER_MAGIC) {
         LERROR << "Logical partition metadata has invalid magic value.";
         return false;
     }
-    // Check that the version is compatible.
     if (header.major_version != LP_METADATA_MAJOR_VERSION ||
         header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {
         LERROR << "Logical partition metadata has incompatible version.";
         return false;
     }
+
+    // Validate the header struct size against the reported version.
+    uint32_t expected_struct_size = sizeof(header);
+    if (header.minor_version < LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {
+        expected_struct_size = sizeof(LpMetadataHeaderV1_0);
+    }
+    if (header.header_size != expected_struct_size) {
+        LERROR << "Invalid partition metadata header struct size.";
+        return false;
+    }
+
+    // Read in any remaining fields, the last step needed before checksumming.
+    if (size_t remaining_bytes = header.header_size - sizeof(LpMetadataHeaderV1_0)) {
+        uint8_t* offset = reinterpret_cast<uint8_t*>(&header) + sizeof(LpMetadataHeaderV1_0);
+        if (!reader->ReadFully(offset, remaining_bytes)) {
+            PERROR << __PRETTY_FUNCTION__ << " read failed";
+            return false;
+        }
+    }
+
+    // To compute the header's checksum, we have to temporarily set its checksum
+    // field to 0. Note that we must only compute up to |header_size|.
+    {
+        LpMetadataHeader temp = header;
+        memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
+        SHA256(&temp, temp.header_size, temp.header_checksum);
+        if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) !=
+            0) {
+            LERROR << "Logical partition metadata has invalid checksum.";
+            return false;
+        }
+    }
+
     if (!ValidateTableBounds(header, header.partitions) ||
         !ValidateTableBounds(header, header.extents) ||
         !ValidateTableBounds(header, header.groups) ||
@@ -215,19 +247,22 @@
                                                  Reader* reader) {
     // First read and validate the header.
     std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
-    if (!reader->ReadFully(&metadata->header, sizeof(metadata->header))) {
-        PERROR << __PRETTY_FUNCTION__ << " read " << sizeof(metadata->header) << "bytes failed";
-        return nullptr;
-    }
-    if (!ValidateMetadataHeader(metadata->header)) {
-        return nullptr;
-    }
+
     metadata->geometry = geometry;
+    if (!ReadMetadataHeader(reader, metadata.get())) {
+        return nullptr;
+    }
 
     LpMetadataHeader& header = metadata->header;
 
-    // Read the metadata payload. Allocation is fallible in case the metadata is
-    // corrupt and has some huge value.
+    // Sanity check the table size.
+    if (header.tables_size > geometry.metadata_max_size) {
+        LERROR << "Invalid partition metadata header table size.";
+        return nullptr;
+    }
+
+    // Read the metadata payload. Allocation is fallible since the table size
+    // could be large.
     std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
     if (!buffer) {
         LERROR << "Out of memory reading logical partition tables.";
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index bb24069..8bf1ee9 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -74,10 +74,10 @@
 
     // Compute header checksum.
     memset(header.header_checksum, 0, sizeof(header.header_checksum));
-    SHA256(&header, sizeof(header), header.header_checksum);
+    SHA256(&header, header.header_size, header.header_checksum);
 
     std::string header_blob =
-            std::string(reinterpret_cast<const char*>(&metadata.header), sizeof(metadata.header));
+            std::string(reinterpret_cast<const char*>(&header), header.header_size);
     return header_blob + tables;
 }
 
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 1d72c70..30d01a6 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -121,6 +121,34 @@
     ],
 }
 
+cc_library_static {
+    name: "libsnapshot_test_helpers",
+    defaults: ["libsnapshot_defaults"],
+    export_include_dirs: [
+        "include_test",
+    ],
+    srcs: [
+        "test_helpers.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.boot@1.1",
+        "libcrypto",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.boot@1.1",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+    export_header_lib_headers: [
+        "libstorage_literals_headers",
+    ],
+    static_libs: [
+        "libgtest",
+        "libgmock",
+    ],
+}
+
 cc_test {
     name: "libsnapshot_test",
     defaults: ["libsnapshot_defaults"],
@@ -144,6 +172,7 @@
         "libgmock",
         "liblp",
         "libsnapshot",
+        "libsnapshot_test_helpers",
         "libsparse",
         "libz",
     ],
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 7450d19..5738b96 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -155,6 +155,7 @@
     // Mark snapshot writes as having completed. After this, new snapshots cannot
     // be created, and the device must either cancel the OTA (either before
     // rebooting or after rolling back), or merge the OTA.
+    // Before calling this function, all snapshots must be mapped.
     bool FinishedSnapshotWrites();
 
   private:
@@ -490,6 +491,11 @@
     // This should only be called in recovery.
     bool UnmapAllPartitions();
 
+    // Sanity check no snapshot overflows. Note that this returns false negatives if the snapshot
+    // overflows, then is remapped and not written afterwards. Hence, the function may only serve
+    // as a sanity check.
+    bool EnsureNoOverflowSnapshot(LockedFile* lock);
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
similarity index 100%
rename from fs_mgr/libsnapshot/test_helpers.h
rename to fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index eae6c35..9da3f05 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -18,9 +18,10 @@
 #include <liblp/builder.h>
 #include <liblp/property_fetcher.h>
 
+#include <libsnapshot/test_helpers.h>
+
 #include "dm_snapshot_internals.h"
 #include "partition_cow_creator.h"
-#include "test_helpers.h"
 #include "utility.h"
 
 using namespace android::fs_mgr;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 830495c..f38db43 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -214,6 +214,11 @@
         return false;
     }
 
+    if (!EnsureNoOverflowSnapshot(lock.get())) {
+        LOG(ERROR) << "Cannot ensure there are no overflow snapshots.";
+        return false;
+    }
+
     // This file acts as both a quick indicator for init (it can use access(2)
     // to decide how to do first-stage mounts), and it stores the old slot, so
     // we can tell whether or not we performed a rollback.
@@ -2303,5 +2308,36 @@
     return true;
 }
 
+bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {
+    CHECK(lock);
+
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock, &snapshots)) {
+        LOG(ERROR) << "Could not list snapshots.";
+        return false;
+    }
+
+    auto& dm = DeviceMapper::Instance();
+    for (const auto& snapshot : snapshots) {
+        std::vector<DeviceMapper::TargetInfo> targets;
+        if (!dm.GetTableStatus(snapshot, &targets)) {
+            LOG(ERROR) << "Could not read snapshot device table: " << snapshot;
+            return false;
+        }
+        if (targets.size() != 1) {
+            LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << snapshot
+                       << ", size = " << targets.size();
+            return false;
+        }
+        if (targets[0].IsOverflowSnapshot()) {
+            LOG(ERROR) << "Detected overflow in snapshot " << snapshot
+                       << ", CoW device size computation is wrong!";
+            return false;
+        }
+    }
+
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 4fd8759..337be4f 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -24,7 +24,7 @@
 #include <liblp/builder.h>
 #include <storage_literals/storage_literals.h>
 
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
 
 using namespace android::storage_literals;
 using android::fs_mgr::LpMetadata;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 9e5fef3..964b21a 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -38,7 +38,7 @@
 #include <storage_literals/storage_literals.h>
 
 #include <android/snapshot/snapshot.pb.h>
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
 #include "utility.h"
 
 namespace android {
@@ -273,6 +273,61 @@
         return AssertionSuccess();
     }
 
+    // Prepare A/B slot for a partition named "test_partition".
+    AssertionResult PrepareOneSnapshot(uint64_t device_size,
+                                       std::string* out_snap_device = nullptr) {
+        std::string base_device, cow_device, snap_device;
+        if (!CreatePartition("test_partition_a", device_size)) {
+            return AssertionFailure();
+        }
+        if (!MapUpdatePartitions()) {
+            return AssertionFailure();
+        }
+        if (!dm_.GetDmDevicePathByName("test_partition_b-base", &base_device)) {
+            return AssertionFailure();
+        }
+        SnapshotStatus status;
+        status.set_name("test_partition_b");
+        status.set_device_size(device_size);
+        status.set_snapshot_size(device_size);
+        status.set_cow_file_size(device_size);
+        if (!sm->CreateSnapshot(lock_.get(), &status)) {
+            return AssertionFailure();
+        }
+        if (!CreateCowImage("test_partition_b")) {
+            return AssertionFailure();
+        }
+        if (!MapCowImage("test_partition_b", 10s, &cow_device)) {
+            return AssertionFailure();
+        }
+        if (!sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
+                             &snap_device)) {
+            return AssertionFailure();
+        }
+        if (out_snap_device) {
+            *out_snap_device = std::move(snap_device);
+        }
+        return AssertionSuccess();
+    }
+
+    // Simulate a reboot into the new slot.
+    AssertionResult SimulateReboot() {
+        lock_ = nullptr;
+        if (!sm->FinishedSnapshotWrites()) {
+            return AssertionFailure();
+        }
+        if (!dm_.DeleteDevice("test_partition_b")) {
+            return AssertionFailure();
+        }
+        if (!DestroyLogicalPartition("test_partition_b-base")) {
+            return AssertionFailure();
+        }
+        if (!sm->UnmapCowImage("test_partition_b")) {
+            return AssertionFailure();
+        }
+        return AssertionSuccess();
+    }
+
     DeviceMapper& dm_;
     std::unique_ptr<SnapshotManager::LockedFile> lock_;
     android::fiemap::IImageManager* image_manager_ = nullptr;
@@ -389,21 +444,8 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-
-    std::string base_device, cow_device, snap_device;
-    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
-    ASSERT_TRUE(MapUpdatePartitions());
-    ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b-base", &base_device));
-    SnapshotStatus status;
-    status.set_name("test_partition_b");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test_partition_b"));
-    ASSERT_TRUE(MapCowImage("test_partition_b", 10s, &cow_device));
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
-                                &snap_device));
+    std::string snap_device;
+    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &snap_device));
 
     std::string test_string = "This is a test string.";
     {
@@ -455,21 +497,8 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-
-    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
-    ASSERT_TRUE(MapUpdatePartitions());
-    SnapshotStatus status;
-    status.set_name("test_partition_b");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
-    // Simulate a reboot into the new slot.
-    lock_ = nullptr;
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+    ASSERT_TRUE(SimulateReboot());
 
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
@@ -479,6 +508,7 @@
     ASSERT_TRUE(AcquireLock());
 
     // Validate that we have a snapshot device.
+    SnapshotStatus status;
     ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
     ASSERT_EQ(status.state(), SnapshotState::CREATED);
 
@@ -492,21 +522,8 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-
-    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
-    ASSERT_TRUE(MapUpdatePartitions());
-    SnapshotStatus status;
-    status.set_name("test_partition_b");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
-    // Simulate a reboot into the new slot.
-    lock_ = nullptr;
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+    ASSERT_TRUE(SimulateReboot());
 
     // Reflash the super partition.
     FormatFakeSuper();
@@ -519,6 +536,7 @@
 
     ASSERT_TRUE(AcquireLock());
 
+    SnapshotStatus status;
     ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
 
     // We should not get a snapshot device now.
@@ -535,21 +553,8 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-
-    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
-    ASSERT_TRUE(MapUpdatePartitions());
-    SnapshotStatus status;
-    status.set_name("test_partition_b");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
-    // Simulate a reboot into the new slot.
-    lock_ = nullptr;
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
+    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+    ASSERT_TRUE(SimulateReboot());
 
     auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
@@ -905,6 +910,17 @@
                                   << ", hash: " << hashes_[name];
     }
 
+    AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {"sys_b", "vnd_b",
+                                                                                "prd_b"}) {
+        for (const auto& name : names) {
+            auto res = MapUpdateSnapshot(name);
+            if (!res) {
+                return res;
+            }
+        }
+        return AssertionSuccess();
+    }
+
     std::unique_ptr<TestPartitionOpener> opener_;
     DeltaArchiveManifest manifest_;
     std::unique_ptr<MetadataBuilder> src_;
@@ -1064,9 +1080,7 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Check that target partitions can be mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        EXPECT_TRUE(MapUpdateSnapshot(name));
-    }
+    EXPECT_TRUE(MapUpdateSnapshots());
 }
 
 // Test that the old partitions are not modified.
@@ -1142,6 +1156,7 @@
     // Execute the first update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(MapUpdateSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
 
     // Simulate shutting down the device.
@@ -1277,6 +1292,7 @@
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(MapUpdateSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
 
     // Simulate shutting down the device.
@@ -1379,6 +1395,7 @@
     // Execute the first update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(MapUpdateSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
 
     // Simulate shutting down the device.
@@ -1410,6 +1427,7 @@
     // Execute the first update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(MapUpdateSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
 
     // Simulate shutting down the device.
@@ -1434,6 +1452,7 @@
     // Execute the first update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(MapUpdateSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
 
     // Simulate shutting down the device.
@@ -1480,7 +1499,8 @@
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
-    // Write some data to target partition.
+    // Map and write some data to target partition.
+    ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
     ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
 
     // Finish update.
@@ -1500,6 +1520,32 @@
     ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
 }
 
+// Test for overflow bit after update
+TEST_F(SnapshotUpdateTest, Overflow) {
+    const auto actual_write_size = GetSize(sys_);
+    const auto declared_write_size = actual_write_size - 1_MiB;
+
+    auto e = sys_->add_operations()->add_dst_extents();
+    e->set_start_block(0);
+    e->set_num_blocks(declared_write_size / manifest_.block_size());
+
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    // Map and write some data to target partitions.
+    ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
+    ASSERT_TRUE(WriteSnapshotAndHash("sys_b", actual_write_size));
+
+    std::vector<android::dm::DeviceMapper::TargetInfo> table;
+    ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
+    ASSERT_EQ(1u, table.size());
+    EXPECT_TRUE(table[0].IsOverflowSnapshot());
+
+    ASSERT_FALSE(sm->FinishedSnapshotWrites())
+            << "FinishedSnapshotWrites should detect overflow of CoW device.";
+}
+
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
@@ -1524,7 +1570,7 @@
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
+    ASSERT_TRUE(MapUpdateSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
 
     // Simulate shutting down the device.
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 2d62347..f7f25af 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
diff --git a/init/Android.bp b/init/Android.bp
index 9529617..42d0b33 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -71,6 +71,7 @@
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
         "libsnapshot_init",
+        "lib_apex_manifest_proto_lite",
     ],
     shared_libs: [
         "libbacktrace",
diff --git a/init/Android.mk b/init/Android.mk
index 997b2bc..07b0f95 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -52,7 +52,6 @@
     first_stage_init.cpp \
     first_stage_main.cpp \
     first_stage_mount.cpp \
-    mount_namespace.cpp \
     reboot_utils.cpp \
     selabel.cpp \
     selinux.cpp \
@@ -73,9 +72,8 @@
 LOCAL_REQUIRED_MODULES := \
    adb_debug.prop \
 
-# Set up the same mount points on the ramdisk that system-as-root contains.
+# Set up the directories that first stage init mounts on.
 LOCAL_POST_INSTALL_CMD := mkdir -p \
-    $(TARGET_RAMDISK_OUT)/apex \
     $(TARGET_RAMDISK_OUT)/debug_ramdisk \
     $(TARGET_RAMDISK_OUT)/dev \
     $(TARGET_RAMDISK_OUT)/mnt \
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 6bf3997..62a19ab 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -360,57 +360,61 @@
     return {};
 }
 
-// mkdir <path> [mode] [owner] [group] [<option> ...]
-static Result<void> do_mkdir(const BuiltinArguments& args) {
-    auto options = ParseMkdir(args.args);
-    if (!options) return options.error();
+static Result<void> make_dir_with_options(const MkdirOptions& options) {
     std::string ref_basename;
-    if (options->ref_option == "ref") {
+    if (options.ref_option == "ref") {
         ref_basename = fscrypt_key_ref;
-    } else if (options->ref_option == "per_boot_ref") {
+    } else if (options.ref_option == "per_boot_ref") {
         ref_basename = fscrypt_key_per_boot_ref;
     } else {
-        return Error() << "Unknown key option: '" << options->ref_option << "'";
+        return Error() << "Unknown key option: '" << options.ref_option << "'";
     }
 
     struct stat mstat;
-    if (lstat(options->target.c_str(), &mstat) != 0) {
+    if (lstat(options.target.c_str(), &mstat) != 0) {
         if (errno != ENOENT) {
-            return ErrnoError() << "lstat() failed on " << options->target;
+            return ErrnoError() << "lstat() failed on " << options.target;
         }
-        if (!make_dir(options->target, options->mode)) {
-            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options->target;
+        if (!make_dir(options.target, options.mode)) {
+            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options.target;
         }
-        if (lstat(options->target.c_str(), &mstat) != 0) {
-            return ErrnoError() << "lstat() failed on new " << options->target;
+        if (lstat(options.target.c_str(), &mstat) != 0) {
+            return ErrnoError() << "lstat() failed on new " << options.target;
         }
     }
     if (!S_ISDIR(mstat.st_mode)) {
-        return Error() << "Not a directory on " << options->target;
+        return Error() << "Not a directory on " << options.target;
     }
-    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options->mode;
-    if ((options->uid != static_cast<uid_t>(-1) && options->uid != mstat.st_uid) ||
-        (options->gid != static_cast<gid_t>(-1) && options->gid != mstat.st_gid)) {
-        if (lchown(options->target.c_str(), options->uid, options->gid) == -1) {
-            return ErrnoError() << "lchown failed on " << options->target;
+    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options.mode;
+    if ((options.uid != static_cast<uid_t>(-1) && options.uid != mstat.st_uid) ||
+        (options.gid != static_cast<gid_t>(-1) && options.gid != mstat.st_gid)) {
+        if (lchown(options.target.c_str(), options.uid, options.gid) == -1) {
+            return ErrnoError() << "lchown failed on " << options.target;
         }
         // chown may have cleared S_ISUID and S_ISGID, chmod again
         needs_chmod = true;
     }
     if (needs_chmod) {
-        if (fchmodat(AT_FDCWD, options->target.c_str(), options->mode, AT_SYMLINK_NOFOLLOW) == -1) {
-            return ErrnoError() << "fchmodat() failed on " << options->target;
+        if (fchmodat(AT_FDCWD, options.target.c_str(), options.mode, AT_SYMLINK_NOFOLLOW) == -1) {
+            return ErrnoError() << "fchmodat() failed on " << options.target;
         }
     }
     if (fscrypt_is_native()) {
-        if (!FscryptSetDirectoryPolicy(ref_basename, options->fscrypt_action, options->target)) {
+        if (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) {
             return reboot_into_recovery(
-                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options->target});
+                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options.target});
         }
     }
     return {};
 }
 
+// mkdir <path> [mode] [owner] [group] [<option> ...]
+static Result<void> do_mkdir(const BuiltinArguments& args) {
+    auto options = ParseMkdir(args.args);
+    if (!options) return options.error();
+    return make_dir_with_options(*options);
+}
+
 /* umount <path> */
 static Result<void> do_umount(const BuiltinArguments& args) {
     if (umount(args[1].c_str()) < 0) {
@@ -1172,7 +1176,7 @@
     return {};
 }
 
-static Result<void> do_parse_apex_configs(const BuiltinArguments& args) {
+static Result<void> parse_apex_configs() {
     glob_t glob_result;
     static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
     const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
@@ -1211,6 +1215,45 @@
     }
 }
 
+/*
+ * Creates a directory under /data/misc/apexdata/ for each APEX.
+ */
+static Result<void> create_apex_data_dirs() {
+    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/apex"), closedir);
+    if (!dirp) {
+        return ErrnoError() << "Unable to open apex directory";
+    }
+    struct dirent* entry;
+    while ((entry = readdir(dirp.get())) != nullptr) {
+        if (entry->d_type != DT_DIR) continue;
+
+        const char* name = entry->d_name;
+        // skip any starting with "."
+        if (name[0] == '.') continue;
+
+        if (strchr(name, '@') != nullptr) continue;
+
+        auto path = "/data/misc/apexdata/" + std::string(name);
+        auto system_uid = DecodeUid("system");
+        auto options =
+                MkdirOptions{path, 0700, *system_uid, *system_uid, FscryptAction::kNone, "ref"};
+        make_dir_with_options(options);
+    }
+    return {};
+}
+
+static Result<void> do_perform_apex_config(const BuiltinArguments& args) {
+    auto create_dirs = create_apex_data_dirs();
+    if (!create_dirs) {
+        return create_dirs.error();
+    }
+    auto parse_configs = parse_apex_configs();
+    if (!parse_configs) {
+        return parse_configs.error();
+    }
+    return {};
+}
+
 static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
     if (SwitchToDefaultMountNamespace()) {
         return {};
@@ -1271,7 +1314,7 @@
         // mount and umount are run in the same context as mount_all for symmetry.
         {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
         {"mount",                   {3,     kMax, {false,  do_mount}}},
-        {"parse_apex_configs",      {0,     0,    {false,  do_parse_apex_configs}}},
+        {"perform_apex_config",     {0,     0,    {false,  do_perform_apex_config}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"umount_all",              {1,     1,    {false,  do_umount_all}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index ac44796..bd71cb5 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -204,10 +204,6 @@
     // part of the product partition, e.g. because they are mounted read-write.
     CHECKCALL(mkdir("/mnt/product", 0755));
 
-    // /apex is used to mount APEXes
-    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
-                    "mode=0755,uid=0,gid=0"));
-
     // /debug_ramdisk is used to preserve additional files from the debug ramdisk
     CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 3acc3cc..22de846 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -191,7 +191,7 @@
     }
 
     auto errors = std::vector<std::string>{};
-    ParsePropertyInfoFile(file_contents, property_infos, &errors);
+    ParsePropertyInfoFile(file_contents, true, property_infos, &errors);
     for (const auto& error : errors) {
         LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
     }
diff --git a/init/init.cpp b/init/init.cpp
index 6ba64ee..5f97e44 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -512,10 +512,24 @@
 
 static void UmountDebugRamdisk() {
     if (umount("/debug_ramdisk") != 0) {
-        LOG(ERROR) << "Failed to umount /debug_ramdisk";
+        PLOG(ERROR) << "Failed to umount /debug_ramdisk";
     }
 }
 
+static void MountExtraFilesystems() {
+#define CHECKCALL(x) \
+    if ((x) != 0) PLOG(FATAL) << #x " failed.";
+
+    // /apex is used to mount APEXes
+    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=0"));
+
+    // /linkerconfig is used to keep generated linker configuration
+    CHECKCALL(mount("tmpfs", "/linkerconfig", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                    "mode=0755,uid=0,gid=0"));
+#undef CHECKCALL
+}
+
 static void RecordStageBoottimes(const boot_clock::time_point& second_stage_start_time) {
     int64_t first_stage_start_time_ns = -1;
     if (auto first_stage_start_time_str = getenv(kEnvFirstStageStartedAt);
@@ -656,6 +670,9 @@
         UmountDebugRamdisk();
     }
 
+    // Mount extra filesystems required during second stage init
+    MountExtraFilesystems();
+
     // Now set up SELinux for second stage.
     SelinuxSetupKernelLogging();
     SelabelInitialize();
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 940fb6b..648b3bb 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -27,6 +27,7 @@
 #include <android-base/properties.h>
 #include <android-base/result.h>
 #include <android-base/unique_fd.h>
+#include <apex_manifest.pb.h>
 
 #include "util.h"
 
@@ -90,6 +91,19 @@
     return {};
 }
 
+static Result<std::string> GetApexName(const std::string& apex_dir) {
+    const std::string manifest_path = apex_dir + "/apex_manifest.pb";
+    std::string content;
+    if (!android::base::ReadFileToString(manifest_path, &content)) {
+        return Error() << "Failed to read manifest file: " << manifest_path;
+    }
+    apex::proto::ApexManifest manifest;
+    if (!manifest.ParseFromString(content)) {
+        return Error() << "Can't parse manifest file: " << manifest_path;
+    }
+    return manifest.name();
+}
+
 static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
                                                 const std::string& to_dir) {
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
@@ -101,7 +115,12 @@
         if (entry->d_name[0] == '.') continue;
         if (entry->d_type == DT_DIR) {
             const std::string apex_path = from_dir + "/" + entry->d_name;
-            const std::string mount_path = to_dir + "/" + entry->d_name;
+            const auto apex_name = GetApexName(apex_path);
+            if (!apex_name) {
+                LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_name.error();
+                continue;
+            }
+            const std::string mount_path = to_dir + "/" + (*apex_name);
             if (auto result = MountDir(apex_path, mount_path); !result) {
                 return result;
             }
@@ -129,26 +148,7 @@
             return false;
         }
     }
-    // Special casing for the ART APEX
-    constexpr const char kArtApexMountPath[] = "/apex/com.android.art";
-    static const std::vector<std::string> kArtApexDirNames = {"com.android.art.release",
-                                                              "com.android.art.debug"};
-    bool success = false;
-    for (const auto& name : kArtApexDirNames) {
-        std::string path = kApexTop + "/" + name;
-        if (access(path.c_str(), F_OK) == 0) {
-            if (auto result = MountDir(path, kArtApexMountPath); !result) {
-                LOG(ERROR) << result.error();
-                return false;
-            }
-            success = true;
-            break;
-        }
-    }
-    if (!success) {
-        PLOG(ERROR) << "Failed to bind mount the ART APEX to " << kArtApexMountPath;
-    }
-    return success;
+    return true;
 }
 
 static android::base::unique_fd bootstrap_ns_fd;
@@ -172,6 +172,11 @@
     // the bootstrap namespace get APEXes from the read-only partition.
     if (!(MakePrivate("/apex"))) return false;
 
+    // /linkerconfig is a private mountpoint to give a different linker configuration
+    // based on the mount namespace. Subdirectory will be bind-mounted based on current mount
+    // namespace
+    if (!(MakePrivate("/linkerconfig"))) return false;
+
     bootstrap_ns_fd.reset(OpenMountNamespace());
     bootstrap_ns_id = GetMountNamespaceId();
 
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 3b5a41d..1758cfa 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -198,7 +198,7 @@
     // Note in this case, that the source and destination directories are the same, so only one
     // fsync() is required.
     auto dir = Dirname(persistent_property_filename);
-    auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY)};
+    auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)};
     if (dir_fd < 0) {
         return ErrnoError() << "Unable to open persistent properties directory for fsync()";
     }
diff --git a/init/property_service.cpp b/init/property_service.cpp
index adf8929..5b35ad2 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -925,7 +925,8 @@
     }
 
     auto errors = std::vector<std::string>{};
-    ParsePropertyInfoFile(file_contents, property_infos, &errors);
+    bool require_prefix_or_exact = SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__;
+    ParsePropertyInfoFile(file_contents, require_prefix_or_exact, property_infos, &errors);
     // Individual parsing errors are reported but do not cause a failed boot, which is what
     // returning false would do here.
     for (const auto& error : errors) {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 2fc8f6d..852d6ca 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -531,6 +531,8 @@
     selinux_android_restorecon("/dev/device-mapper", 0);
 
     selinux_android_restorecon("/apex", 0);
+
+    selinux_android_restorecon("/linkerconfig", 0);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/service.cpp b/init/service.cpp
index be46585..a97935e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -326,6 +326,7 @@
                                << (boot_completed ? "in 4 minutes" : "before boot completed");
                     // Notifies update_verifier and apexd
                     SetProperty("sys.init.updatable_crashing", "1");
+                    SetProperty("sys.init.updatable_crashing_process_name", name_);
                 }
             }
         } else {
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 91bd52c..de0c636 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -17,7 +17,6 @@
 liblog_sources = [
     "log_event_list.cpp",
     "log_event_write.cpp",
-    "logger_lock.cpp",
     "logger_name.cpp",
     "logger_read.cpp",
     "logger_write.cpp",
@@ -25,7 +24,6 @@
 ]
 liblog_host_sources = [
     "fake_log_device.cpp",
-    "fake_writer.cpp",
 ]
 liblog_target_sources = [
     "event_tag_map.cpp",
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index f61bbdc..fb3b9bc 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -23,18 +23,20 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
+#include <mutex>
+
 #include <android/log.h>
+#include <log/log_id.h>
+#include <log/logprint.h>
 
 #include "log_portability.h"
+#include "logger.h"
 
 #define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
 
@@ -46,37 +48,21 @@
 #define TRACE(...) ((void)0)
 #endif
 
-/* from the long-dead utils/Log.cpp */
-typedef enum {
-  FORMAT_OFF = 0,
-  FORMAT_BRIEF,
-  FORMAT_PROCESS,
-  FORMAT_TAG,
-  FORMAT_THREAD,
-  FORMAT_RAW,
-  FORMAT_TIME,
-  FORMAT_THREADTIME,
-  FORMAT_LONG
-} LogFormat;
+static void FakeClose();
+static int FakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
 
-/*
- * Log driver state.
- */
+struct android_log_transport_write fakeLoggerWrite = {
+    .close = FakeClose,
+    .write = FakeWrite,
+};
+
 typedef struct LogState {
-  /* the fake fd that's seen by the user */
-  int fakeFd;
-
-  /* a printable name for this fake device */
-  char debugName[sizeof("/dev/log/security")];
-
-  /* nonzero if this is a binary log */
-  int isBinary;
-
+  bool initialized = false;
   /* global minimum priority */
-  int globalMinPriority;
+  int global_min_priority;
 
   /* output format */
-  LogFormat outputFormat;
+  AndroidLogPrintFormat output_format;
 
   /* tags and priorities */
   struct {
@@ -85,82 +71,8 @@
   } tagSet[kTagSetSize];
 } LogState;
 
-#if !defined(_WIN32)
-/*
- * Locking.  Since we're emulating a device, we need to be prepared
- * to have multiple callers at the same time.  This lock is used
- * to both protect the fd list and to prevent LogStates from being
- * freed out from under a user.
- */
-static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
-
-static void lock() {
-  /*
-   * If we trigger a signal handler in the middle of locked activity and the
-   * signal handler logs a message, we could get into a deadlock state.
-   */
-  pthread_mutex_lock(&fakeLogDeviceLock);
-}
-
-static void unlock() {
-  pthread_mutex_unlock(&fakeLogDeviceLock);
-}
-
-#else  // !defined(_WIN32)
-
-#define lock() ((void)0)
-#define unlock() ((void)0)
-
-#endif  // !defined(_WIN32)
-
-/*
- * File descriptor management.
- */
-#define FAKE_FD_BASE 10000
-#define MAX_OPEN_LOGS 8
-static LogState openLogTable[MAX_OPEN_LOGS];
-
-/*
- * Allocate an fd and associate a new LogState with it.
- * The fd is available via the fakeFd field of the return value.
- */
-static LogState* createLogState() {
-  size_t i;
-
-  for (i = 0; i < (sizeof(openLogTable) / sizeof(openLogTable[0])); i++) {
-    if (openLogTable[i].fakeFd == 0) {
-      openLogTable[i].fakeFd = FAKE_FD_BASE + i;
-      return &openLogTable[i];
-    }
-  }
-  return NULL;
-}
-
-/*
- * Translate an fd to a LogState.
- */
-static LogState* fdToLogState(int fd) {
-  if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
-    return &openLogTable[fd - FAKE_FD_BASE];
-  }
-  return NULL;
-}
-
-/*
- * Unregister the fake fd and free the memory it pointed to.
- */
-static void deleteFakeFd(int fd) {
-  LogState* ls;
-
-  lock();
-
-  ls = fdToLogState(fd);
-  if (ls != NULL) {
-    memset(&openLogTable[fd - FAKE_FD_BASE], 0, sizeof(openLogTable[0]));
-  }
-
-  unlock();
-}
+static LogState log_state;
+static std::mutex fake_log_mutex;
 
 /*
  * Configure logging based on ANDROID_LOG_TAGS environment variable.  We
@@ -175,19 +87,11 @@
  * We also want to check ANDROID_PRINTF_LOG to determine how the output
  * will look.
  */
-static void configureInitialState(const char* pathName, LogState* logState) {
-  static const int kDevLogLen = sizeof("/dev/log/") - 1;
-
-  strncpy(logState->debugName, pathName, sizeof(logState->debugName));
-  logState->debugName[sizeof(logState->debugName) - 1] = '\0';
-
-  /* identify binary logs */
-  if (!strcmp(pathName + kDevLogLen, "events") || !strcmp(pathName + kDevLogLen, "security")) {
-    logState->isBinary = 1;
-  }
+void InitializeLogStateLocked() {
+  log_state.initialized = true;
 
   /* global min priority defaults to "info" level */
-  logState->globalMinPriority = ANDROID_LOG_INFO;
+  log_state.global_min_priority = ANDROID_LOG_INFO;
 
   /*
    * This is based on the the long-dead utils/Log.cpp code.
@@ -265,11 +169,11 @@
       }
 
       if (tagName[0] == 0) {
-        logState->globalMinPriority = minPrio;
+        log_state.global_min_priority = minPrio;
         TRACE("+++ global min prio %d\n", logState->globalMinPriority);
       } else {
-        logState->tagSet[entry].minPriority = minPrio;
-        strcpy(logState->tagSet[entry].tag, tagName);
+        log_state.tagSet[entry].minPriority = minPrio;
+        strcpy(log_state.tagSet[entry].tag, tagName);
         TRACE("+++ entry %d: %s:%d\n", entry, logState->tagSet[entry].tag,
               logState->tagSet[entry].minPriority);
         entry++;
@@ -281,7 +185,7 @@
    * Taken from the long-dead utils/Log.cpp
    */
   const char* fstr = getenv("ANDROID_PRINTF_LOG");
-  LogFormat format;
+  AndroidLogPrintFormat format;
   if (fstr == NULL) {
     format = FORMAT_BRIEF;
   } else {
@@ -300,10 +204,10 @@
     else if (strcmp(fstr, "long") == 0)
       format = FORMAT_PROCESS;
     else
-      format = (LogFormat)atoi(fstr);  // really?!
+      format = (AndroidLogPrintFormat)atoi(fstr);  // really?!
   }
 
-  logState->outputFormat = format;
+  log_state.output_format = format;
 }
 
 /*
@@ -348,7 +252,7 @@
  *
  * Log format parsing taken from the long-dead utils/Log.cpp.
  */
-static void showLog(LogState* state, int logPrio, const char* tag, const char* msg) {
+static void ShowLog(int logPrio, const char* tag, const char* msg) {
 #if !defined(_WIN32)
   struct tm tmBuf;
 #endif
@@ -391,7 +295,7 @@
    */
   size_t prefixLen, suffixLen;
 
-  switch (state->outputFormat) {
+  switch (log_state.output_format) {
     case FORMAT_TAG:
       prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
       strcpy(suffixBuf, "\n");
@@ -548,35 +452,28 @@
  *  tag (N bytes -- null-terminated ASCII string)
  *  message (N bytes -- null-terminated ASCII string)
  */
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) {
-  LogState* state;
-
+static int FakeWrite(log_id_t log_id, struct timespec*, struct iovec* vector, size_t count) {
   /* Make sure that no-one frees the LogState while we're using it.
    * Also guarantees that only one thread is in showLog() at a given
    * time (if it matters).
    */
-  lock();
+  auto lock = std::lock_guard{fake_log_mutex};
 
-  state = fdToLogState(fd);
-  if (state == NULL) {
-    errno = EBADF;
-    unlock();
-    return -1;
+  if (!log_state.initialized) {
+    InitializeLogStateLocked();
   }
 
-  if (state->isBinary) {
-    TRACE("%s: ignoring binary log\n", state->debugName);
-    unlock();
+  if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS || log_id == LOG_ID_SECURITY) {
+    TRACE("%s: ignoring binary log\n", android_log_id_to_name(log_id));
     int len = 0;
-    for (int i = 0; i < count; ++i) {
+    for (size_t i = 0; i < count; ++i) {
       len += vector[i].iov_len;
     }
     return len;
   }
 
   if (count != 3) {
-    TRACE("%s: writevLog with count=%d not expected\n", state->debugName, count);
-    unlock();
+    TRACE("%s: writevLog with count=%d not expected\n", android_log_id_to_name(log_id), count);
     return -1;
   }
 
@@ -586,32 +483,30 @@
   const char* msg = (const char*)vector[2].iov_base;
 
   /* see if this log tag is configured */
-  int i;
-  int minPrio = state->globalMinPriority;
-  for (i = 0; i < kTagSetSize; i++) {
-    if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
+  int minPrio = log_state.global_min_priority;
+  for (size_t i = 0; i < kTagSetSize; i++) {
+    if (log_state.tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
       break; /* reached end of configured values */
 
-    if (strcmp(state->tagSet[i].tag, tag) == 0) {
-      minPrio = state->tagSet[i].minPriority;
+    if (strcmp(log_state.tagSet[i].tag, tag) == 0) {
+      minPrio = log_state.tagSet[i].minPriority;
       break;
     }
   }
 
   if (logPrio >= minPrio) {
-    showLog(state, logPrio, tag, msg);
+    ShowLog(logPrio, tag, msg);
   }
 
-  unlock();
   int len = 0;
-  for (i = 0; i < count; ++i) {
+  for (size_t i = 0; i < count; ++i) {
     len += vector[i].iov_len;
   }
   return len;
 }
 
 /*
- * Free up our state and close the fake descriptor.
+ * Reset out state.
  *
  * The logger API has no means or need to 'stop' or 'close' using the logs,
  * and as such, there is no way for that 'stop' or 'close' to translate into
@@ -623,31 +518,10 @@
  * call is in the exit handler. Logging can continue in the exit handler to
  * help debug HOST tools ...
  */
-int fakeLogClose(int fd) {
-  deleteFakeFd(fd);
-  return 0;
-}
+static void FakeClose() {
+  auto lock = std::lock_guard{fake_log_mutex};
 
-/*
- * Open a log output device and return a fake fd.
- */
-int fakeLogOpen(const char* pathName) {
-  LogState* logState;
-  int fd = -1;
-
-  lock();
-
-  logState = createLogState();
-  if (logState != NULL) {
-    configureInitialState(pathName, logState);
-    fd = logState->fakeFd;
-  } else {
-    errno = ENFILE;
-  }
-
-  unlock();
-
-  return fd;
+  memset(&log_state, 0, sizeof(log_state));
 }
 
 int __android_log_is_loggable(int prio, const char*, int def) {
diff --git a/liblog/fake_writer.cpp b/liblog/fake_writer.cpp
deleted file mode 100644
index f1ddff1..0000000
--- a/liblog/fake_writer.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2007-2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "fake_log_device.h"
-#include "log_portability.h"
-#include "logger.h"
-
-static int fakeAvailable(log_id_t);
-static int fakeOpen();
-static void fakeClose();
-static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
-
-static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
-
-struct android_log_transport_write fakeLoggerWrite = {
-    .name = "fake",
-    .logMask = 0,
-    .context.priv = &logFds,
-    .available = fakeAvailable,
-    .open = fakeOpen,
-    .close = fakeClose,
-    .write = fakeWrite,
-};
-
-static int fakeAvailable(log_id_t) {
-  return 0;
-}
-
-static int fakeOpen() {
-  int i;
-
-  for (i = 0; i < LOG_ID_MAX; i++) {
-    /*
-     * Known maximum size string, plus an 8 character margin to deal with
-     * possible independent changes to android_log_id_to_name().
-     */
-    char buf[sizeof("/dev/log_security") + 8];
-    if (logFds[i] >= 0) {
-      continue;
-    }
-    snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(static_cast<log_id_t>(i)));
-    logFds[i] = fakeLogOpen(buf);
-    if (logFds[i] < 0) {
-      fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
-    }
-  }
-  return 0;
-}
-
-static void fakeClose() {
-  int i;
-
-  for (i = 0; i < LOG_ID_MAX; i++) {
-    fakeLogClose(logFds[i]);
-    logFds[i] = -1;
-  }
-}
-
-static int fakeWrite(log_id_t log_id, struct timespec*, struct iovec* vec, size_t nr) {
-  ssize_t ret;
-  size_t i;
-  int logFd, len;
-
-  if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) {
-    return -EINVAL;
-  }
-
-  len = 0;
-  for (i = 0; i < nr; ++i) {
-    len += vec[i].iov_len;
-  }
-
-  if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
-    len = LOGGER_ENTRY_MAX_PAYLOAD;
-  }
-
-  logFd = logFds[(int)log_id];
-  ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr));
-  if (ret < 0) {
-    ret = -errno;
-  } else if (ret > len) {
-    ret = len;
-  }
-
-  return ret;
-}
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index a22c3be..3c6eb69 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -30,97 +30,76 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <shared_mutex>
+
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "log_portability.h"
 #include "logger.h"
+#include "rwlock.h"
 #include "uio.h"
 
-static int logdAvailable(log_id_t LogId);
-static int logdOpen();
-static void logdClose();
-static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+static int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+static void LogdClose();
 
 struct android_log_transport_write logdLoggerWrite = {
-    .name = "logd",
-    .logMask = 0,
-    .context.sock = -EBADF,
-    .available = logdAvailable,
-    .open = logdOpen,
-    .close = logdClose,
-    .write = logdWrite,
+    .close = LogdClose,
+    .write = LogdWrite,
 };
 
-/* log_init_lock assumed */
-static int logdOpen() {
-  int i, ret = 0;
+static int logd_socket;
+static RwLock logd_socket_lock;
 
-  i = atomic_load(&logdLoggerWrite.context.sock);
-  if (i < 0) {
-    int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
-    if (sock < 0) {
-      ret = -errno;
-    } else {
-      struct sockaddr_un un;
-      memset(&un, 0, sizeof(struct sockaddr_un));
-      un.sun_family = AF_UNIX;
-      strcpy(un.sun_path, "/dev/socket/logdw");
-
-      if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) <
-          0) {
-        ret = -errno;
-        switch (ret) {
-          case -ENOTCONN:
-          case -ECONNREFUSED:
-          case -ENOENT:
-            i = atomic_exchange(&logdLoggerWrite.context.sock, ret);
-            [[fallthrough]];
-          default:
-            break;
-        }
-        close(sock);
-      } else {
-        ret = atomic_exchange(&logdLoggerWrite.context.sock, sock);
-        if ((ret >= 0) && (ret != sock)) {
-          close(ret);
-        }
-        ret = 0;
-      }
-    }
+static void OpenSocketLocked() {
+  logd_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+  if (logd_socket <= 0) {
+    return;
   }
 
-  return ret;
-}
+  sockaddr_un un = {};
+  un.sun_family = AF_UNIX;
+  strcpy(un.sun_path, "/dev/socket/logdw");
 
-static void __logdClose(int negative_errno) {
-  int sock = atomic_exchange(&logdLoggerWrite.context.sock, negative_errno);
-  if (sock >= 0) {
-    close(sock);
+  if (TEMP_FAILURE_RETRY(
+          connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un))) < 0) {
+    close(logd_socket);
+    logd_socket = 0;
   }
 }
 
-static void logdClose() {
-  __logdClose(-EBADF);
+static void OpenSocket() {
+  auto lock = std::unique_lock{logd_socket_lock};
+  if (logd_socket > 0) {
+    // Someone raced us and opened the socket already.
+    return;
+  }
+
+  OpenSocketLocked();
 }
 
-static int logdAvailable(log_id_t logId) {
-  if (logId >= LOG_ID_MAX || logId == LOG_ID_KERNEL) {
-    return -EINVAL;
+static void ResetSocket(int old_socket) {
+  auto lock = std::unique_lock{logd_socket_lock};
+  if (old_socket != logd_socket) {
+    // Someone raced us and reset the socket already.
+    return;
   }
-  if (atomic_load(&logdLoggerWrite.context.sock) < 0) {
-    if (access("/dev/socket/logdw", W_OK) == 0) {
-      return 0;
-    }
-    return -EBADF;
-  }
-  return 1;
+  close(logd_socket);
+  logd_socket = 0;
+  OpenSocketLocked();
 }
 
-static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+static void LogdClose() {
+  auto lock = std::unique_lock{logd_socket_lock};
+  if (logd_socket > 0) {
+    close(logd_socket);
+  }
+  logd_socket = 0;
+}
+
+static int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
   ssize_t ret;
-  int sock;
   static const unsigned headerLength = 1;
   struct iovec newVec[nr + headerLength];
   android_log_header_t header;
@@ -128,15 +107,16 @@
   static atomic_int dropped;
   static atomic_int droppedSecurity;
 
-  sock = atomic_load(&logdLoggerWrite.context.sock);
-  if (sock < 0) switch (sock) {
-      case -ENOTCONN:
-      case -ECONNREFUSED:
-      case -ENOENT:
-        break;
-      default:
-        return -EBADF;
-    }
+  auto lock = std::shared_lock{logd_socket_lock};
+  if (logd_socket <= 0) {
+    lock.unlock();
+    OpenSocket();
+    lock.lock();
+  }
+
+  if (logd_socket <= 0) {
+    return -EBADF;
+  }
 
   /* logd, after initialization and priv drop */
   if (__android_log_uid() == AID_LOGD) {
@@ -155,41 +135,39 @@
   newVec[0].iov_base = (unsigned char*)&header;
   newVec[0].iov_len = sizeof(header);
 
-  if (sock >= 0) {
-    int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
-    if (snapshot) {
-      android_log_event_int_t buffer;
+  int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
+  if (snapshot) {
+    android_log_event_int_t buffer;
 
-      header.id = LOG_ID_SECURITY;
-      buffer.header.tag = LIBLOG_LOG_TAG;
-      buffer.payload.type = EVENT_TYPE_INT;
-      buffer.payload.data = snapshot;
+    header.id = LOG_ID_SECURITY;
+    buffer.header.tag = LIBLOG_LOG_TAG;
+    buffer.payload.type = EVENT_TYPE_INT;
+    buffer.payload.data = snapshot;
 
-      newVec[headerLength].iov_base = &buffer;
-      newVec[headerLength].iov_len = sizeof(buffer);
+    newVec[headerLength].iov_base = &buffer;
+    newVec[headerLength].iov_len = sizeof(buffer);
 
-      ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
-      if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
-        atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed);
-      }
+    ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
+    if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+      atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed);
     }
-    snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
-    if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
-                                                  ANDROID_LOG_VERBOSE)) {
-      android_log_event_int_t buffer;
+  }
+  snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+  if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
+                                                ANDROID_LOG_VERBOSE)) {
+    android_log_event_int_t buffer;
 
-      header.id = LOG_ID_EVENTS;
-      buffer.header.tag = LIBLOG_LOG_TAG;
-      buffer.payload.type = EVENT_TYPE_INT;
-      buffer.payload.data = snapshot;
+    header.id = LOG_ID_EVENTS;
+    buffer.header.tag = LIBLOG_LOG_TAG;
+    buffer.payload.type = EVENT_TYPE_INT;
+    buffer.payload.data = snapshot;
 
-      newVec[headerLength].iov_base = &buffer;
-      newVec[headerLength].iov_len = sizeof(buffer);
+    newVec[headerLength].iov_base = &buffer;
+    newVec[headerLength].iov_len = sizeof(buffer);
 
-      ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
-      if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
-        atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
-      }
+    ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
+    if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+      atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
     }
   }
 
@@ -208,49 +186,26 @@
     }
   }
 
-  /*
-   * The write below could be lost, but will never block.
-   *
-   * ENOTCONN occurs if logd has died.
-   * ENOENT occurs if logd is not running and socket is missing.
-   * ECONNREFUSED occurs if we can not reconnect to logd.
-   * EAGAIN occurs if logd is overloaded.
-   */
-  if (sock < 0) {
-    ret = sock;
-  } else {
-    ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
-    if (ret < 0) {
-      ret = -errno;
-    }
+  // The write below could be lost, but will never block.
+  // EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with
+  // the connection, so we reset it and try again.
+  ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
+  if (ret < 0 && errno != EAGAIN) {
+    int old_socket = logd_socket;
+    lock.unlock();
+    ResetSocket(old_socket);
+    lock.lock();
+
+    ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
   }
-  switch (ret) {
-    case -ENOTCONN:
-    case -ECONNREFUSED:
-    case -ENOENT:
-      if (__android_log_trylock()) {
-        return ret; /* in a signal handler? try again when less stressed */
-      }
-      __logdClose(ret);
-      ret = logdOpen();
-      __android_log_unlock();
 
-      if (ret < 0) {
-        return ret;
-      }
-
-      ret = TEMP_FAILURE_RETRY(writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
-      if (ret < 0) {
-        ret = -errno;
-      }
-      [[fallthrough]];
-    default:
-      break;
+  if (ret < 0) {
+    ret = -errno;
   }
 
   if (ret > (ssize_t)sizeof(header)) {
     ret -= sizeof(header);
-  } else if (ret == -EAGAIN) {
+  } else if (ret < 0) {
     atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
     if (logId == LOG_ID_SECURITY) {
       atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
diff --git a/liblog/logger.h b/liblog/logger.h
index 9d74d29..40d5fe5 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -26,20 +26,7 @@
 
 __BEGIN_DECLS
 
-/* Union, sock or fd of zero is not allowed unless static initialized */
-union android_log_context_union {
-  void* priv;
-  atomic_int sock;
-  atomic_int fd;
-};
-
 struct android_log_transport_write {
-  const char* name;                  /* human name to describe the transport */
-  unsigned logMask;                  /* mask cache of available() success */
-  union android_log_context_union context; /* Initialized by static allocation */
-
-  int (*available)(log_id_t logId); /* Does not cause resources to be taken */
-  int (*open)();   /* can be called multiple times, reusing current resources */
   void (*close)(); /* free up resources */
   /* write log to transport, returns number of bytes propagated, or -errno */
   int (*write)(log_id_t logId, struct timespec* ts, struct iovec* vec,
@@ -83,8 +70,4 @@
 }
 #endif
 
-void __android_log_lock();
-int __android_log_trylock();
-void __android_log_unlock();
-
 __END_DECLS
diff --git a/liblog/logger_lock.cpp b/liblog/logger_lock.cpp
deleted file mode 100644
index 4636b00..0000000
--- a/liblog/logger_lock.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2007-2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Some OS specific dribs and drabs (locking etc).
- */
-
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-
-#include "logger.h"
-
-#if !defined(_WIN32)
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-void __android_log_lock() {
-#if !defined(_WIN32)
-  /*
-   * If we trigger a signal handler in the middle of locked activity and the
-   * signal handler logs a message, we could get into a deadlock state.
-   */
-  pthread_mutex_lock(&log_init_lock);
-#endif
-}
-
-int __android_log_trylock() {
-#if !defined(_WIN32)
-  return pthread_mutex_trylock(&log_init_lock);
-#else
-  return 0;
-#endif
-}
-
-void __android_log_unlock() {
-#if !defined(_WIN32)
-  pthread_mutex_unlock(&log_init_lock);
-#endif
-}
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index e1772f1..d38b402 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -24,7 +24,6 @@
 #include <android/set_abort_message.h>
 #endif
 
-#include <log/event_tag_map.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
@@ -47,11 +46,8 @@
 android_log_transport_write* android_log_persist_write = nullptr;
 #endif
 
-static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
-static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init;
-
-static int check_log_uid_permissions() {
 #if defined(__ANDROID__)
+static int check_log_uid_permissions() {
   uid_t uid = __android_log_uid();
 
   /* Matches clientHasLogCredentials() in logd */
@@ -88,51 +84,14 @@
       }
     }
   }
-#endif
   return 0;
 }
-
-static void __android_log_cache_available(struct android_log_transport_write* node) {
-  uint32_t i;
-
-  if (node->logMask) {
-    return;
-  }
-
-  for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-    if (i != LOG_ID_KERNEL && (i != LOG_ID_SECURITY || check_log_uid_permissions() == 0) &&
-        (*node->available)(static_cast<log_id_t>(i)) >= 0) {
-      node->logMask |= 1 << i;
-    }
-  }
-}
-
-#if defined(__ANDROID__)
-static atomic_uintptr_t tagMap;
 #endif
 
 /*
  * Release any logger resources. A new log write will immediately re-acquire.
  */
 void __android_log_close() {
-#if defined(__ANDROID__)
-  EventTagMap* m;
-#endif
-
-  __android_log_lock();
-
-  write_to_log = __write_to_log_init;
-
-  /*
-   * Threads that are actively writing at this point are not held back
-   * by a lock and are at risk of dropping the messages with a return code
-   * -EBADF. Prefer to return error code than add the overhead of a lock to
-   * each log writing call to guarantee delivery. In addition, anyone
-   * calling this is doing so to release the logging resources and shut down,
-   * for them to do so with outstanding log requests in other threads is a
-   * disengenuous use of this function.
-   */
-
   if (android_log_write != nullptr) {
     android_log_write->close();
   }
@@ -141,64 +100,18 @@
     android_log_persist_write->close();
   }
 
-#if defined(__ANDROID__)
-  /*
-   * Additional risk here somewhat mitigated by immediately unlock flushing
-   * the processor cache. The multi-threaded race that we choose to accept,
-   * to minimize locking, is an atomic_load in a writer picking up a value
-   * just prior to entering this routine. There will be an use after free.
-   *
-   * Again, anyone calling this is doing so to release the logging resources
-   * is most probably going to quiesce then shut down; or to restart after
-   * a fork so the risk should be non-existent. For this reason we
-   * choose a mitigation stance for efficiency instead of incuring the cost
-   * of a lock for every log write.
-   */
-  m = (EventTagMap*)atomic_exchange(&tagMap, (uintptr_t)0);
-#endif
-
-  __android_log_unlock();
-
-#if defined(__ANDROID__)
-  if (m != (EventTagMap*)(uintptr_t)-1LL) android_closeEventTagMap(m);
-#endif
 }
 
-static bool transport_initialize(android_log_transport_write* transport) {
-  if (transport == nullptr) {
-    return false;
-  }
-
-  __android_log_cache_available(transport);
-  if (!transport->logMask) {
-    return false;
-  }
-
-  // TODO: Do we actually need to call close() if open() fails?
-  if (transport->open() < 0) {
-    transport->close();
-    return false;
-  }
-
-  return true;
-}
-
-/* log_init_lock assumed */
-static int __write_to_log_initialize() {
-  if (!transport_initialize(android_log_write)) {
-    return -ENODEV;
-  }
-
-  transport_initialize(android_log_persist_write);
-
-  return 1;
-}
-
-static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
+static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
   int ret, save_errno;
   struct timespec ts;
 
   save_errno = errno;
+
+  if (log_id == LOG_ID_KERNEL) {
+    return -EINVAL;
+  }
+
 #if defined(__ANDROID__)
   clock_gettime(android_log_clockid(), &ts);
 
@@ -219,49 +132,10 @@
       return -EPERM;
     }
   } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
-    const char* tag;
-    size_t len;
-    EventTagMap *m, *f;
-
     if (vec[0].iov_len < 4) {
       errno = save_errno;
       return -EINVAL;
     }
-
-    tag = NULL;
-    len = 0;
-    f = NULL;
-    m = (EventTagMap*)atomic_load(&tagMap);
-
-    if (!m) {
-      ret = __android_log_trylock();
-      m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */
-      if (!m) {
-        m = android_openEventTagMap(NULL);
-        if (ret) { /* trylock failed, use local copy, mark for close */
-          f = m;
-        } else {
-          if (!m) { /* One chance to open map file */
-            m = (EventTagMap*)(uintptr_t)-1LL;
-          }
-          atomic_store(&tagMap, (uintptr_t)m);
-        }
-      }
-      if (!ret) { /* trylock succeeded, unlock */
-        __android_log_unlock();
-      }
-    }
-    if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
-      tag = android_lookupEventTag_len(m, &len, *static_cast<uint32_t*>(vec[0].iov_base));
-    }
-    ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len, ANDROID_LOG_VERBOSE);
-    if (f) { /* local copy marked for close */
-      android_closeEventTagMap(f);
-    }
-    if (!ret) {
-      errno = save_errno;
-      return -EPERM;
-    }
   } else {
     int prio = *static_cast<int*>(vec[0].iov_base);
     const char* tag = static_cast<const char*>(vec[1].iov_base);
@@ -283,9 +157,8 @@
 #endif
 
   ret = 0;
-  size_t i = 1 << log_id;
 
-  if (android_log_write != nullptr && (android_log_write->logMask & i)) {
+  if (android_log_write != nullptr) {
     ssize_t retval;
     retval = android_log_write->write(log_id, &ts, vec, nr);
     if (ret >= 0) {
@@ -293,7 +166,7 @@
     }
   }
 
-  if (android_log_persist_write != nullptr && (android_log_persist_write->logMask & i)) {
+  if (android_log_persist_write != nullptr) {
     android_log_persist_write->write(log_id, &ts, vec, nr);
   }
 
@@ -301,29 +174,6 @@
   return ret;
 }
 
-static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) {
-  int ret, save_errno = errno;
-
-  __android_log_lock();
-
-  if (write_to_log == __write_to_log_init) {
-    ret = __write_to_log_initialize();
-    if (ret < 0) {
-      __android_log_unlock();
-      errno = save_errno;
-      return ret;
-    }
-
-    write_to_log = __write_to_log_daemon;
-  }
-
-  __android_log_unlock();
-
-  ret = write_to_log(log_id, vec, nr);
-  errno = save_errno;
-  return ret;
-}
-
 int __android_log_write(int prio, const char* tag, const char* msg) {
   return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
 }
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 54980d9..4f45780 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -25,68 +25,47 @@
 #include <sys/types.h>
 #include <time.h>
 
+#include <shared_mutex>
+
 #include <log/log_properties.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "log_portability.h"
 #include "logger.h"
+#include "rwlock.h"
 #include "uio.h"
 
-static int pmsgOpen();
-static void pmsgClose();
-static int pmsgAvailable(log_id_t logId);
-static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+static void PmsgClose();
+static int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
 
 struct android_log_transport_write pmsgLoggerWrite = {
-    .name = "pmsg",
-    .logMask = 0,
-    .context.fd = -1,
-    .available = pmsgAvailable,
-    .open = pmsgOpen,
-    .close = pmsgClose,
-    .write = pmsgWrite,
+    .close = PmsgClose,
+    .write = PmsgWrite,
 };
 
-static int pmsgOpen() {
-  int fd = atomic_load(&pmsgLoggerWrite.context.fd);
-  if (fd < 0) {
-    int i;
+static int pmsg_fd;
+static RwLock pmsg_fd_lock;
 
-    fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
-    i = atomic_exchange(&pmsgLoggerWrite.context.fd, fd);
-    if ((i >= 0) && (i != fd)) {
-      close(i);
-    }
+static void PmsgOpen() {
+  auto lock = std::unique_lock{pmsg_fd_lock};
+  if (pmsg_fd > 0) {
+    // Someone raced us and opened the socket already.
+    return;
   }
 
-  return fd;
+  pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
 }
 
-static void pmsgClose() {
-  int fd = atomic_exchange(&pmsgLoggerWrite.context.fd, -1);
-  if (fd >= 0) {
-    close(fd);
+static void PmsgClose() {
+  auto lock = std::unique_lock{pmsg_fd_lock};
+  if (pmsg_fd > 0) {
+    close(pmsg_fd);
   }
+  pmsg_fd = 0;
 }
 
-static int pmsgAvailable(log_id_t logId) {
-  if (logId > LOG_ID_SECURITY) {
-    return -EINVAL;
-  }
-  if ((logId != LOG_ID_SECURITY) && (logId != LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
-    return -EINVAL;
-  }
-  if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
-    if (access("/dev/pmsg0", W_OK) == 0) {
-      return 0;
-    }
-    return -EBADF;
-  }
-  return 1;
-}
-
-static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+static int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
   static const unsigned headerLength = 2;
   struct iovec newVec[nr + headerLength];
   android_log_header_t header;
@@ -94,17 +73,31 @@
   size_t i, payloadSize;
   ssize_t ret;
 
-  if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
-    if (vec[0].iov_len < 4) {
-      return -EINVAL;
+  if (!__android_log_is_debuggable()) {
+    if (logId != LOG_ID_EVENTS && logId != LOG_ID_SECURITY) {
+      return -1;
     }
 
-    if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
-      return -EPERM;
+    if (logId == LOG_ID_EVENTS) {
+      if (vec[0].iov_len < 4) {
+        return -EINVAL;
+      }
+
+      if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
+        return -EPERM;
+      }
     }
   }
 
-  if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
+  auto lock = std::shared_lock{pmsg_fd_lock};
+
+  if (pmsg_fd <= 0) {
+    lock.unlock();
+    PmsgOpen();
+    lock.lock();
+  }
+
+  if (pmsg_fd <= 0) {
     return -EBADF;
   }
 
@@ -158,7 +151,7 @@
   }
   pmsgHeader.len += payloadSize;
 
-  ret = TEMP_FAILURE_RETRY(writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i));
+  ret = TEMP_FAILURE_RETRY(writev(pmsg_fd, newVec, i));
   if (ret < 0) {
     ret = errno ? -errno : -ENOTCONN;
   }
@@ -193,7 +186,6 @@
 /* Write a buffer as filename references (tag = <basedir>:<basename>) */
 ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio, const char* filename,
                                       const char* buf, size_t len) {
-  bool weOpened;
   size_t length, packet_len;
   const char* tag;
   char *cp, *slash;
@@ -233,7 +225,6 @@
   vec[1].iov_base = (unsigned char*)tag;
   vec[1].iov_len = length;
 
-  weOpened = false;
   for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
     ssize_t ret;
     size_t transfer;
@@ -254,37 +245,15 @@
     vec[2].iov_base = (unsigned char*)buf;
     vec[2].iov_len = transfer;
 
-    if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
-      if (!weOpened) { /* Impossible for weOpened = true here */
-        __android_log_lock();
-      }
-      weOpened = atomic_load(&pmsgLoggerWrite.context.fd) < 0;
-      if (!weOpened) {
-        __android_log_unlock();
-      } else if (pmsgOpen() < 0) {
-        __android_log_unlock();
-        free(cp);
-        return -EBADF;
-      }
-    }
-
-    ret = pmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
+    ret = PmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
 
     if (ret <= 0) {
-      if (weOpened) {
-        pmsgClose();
-        __android_log_unlock();
-      }
       free(cp);
       return ret ? ret : (len - length);
     }
     length -= transfer;
     buf += transfer;
   }
-  if (weOpened) {
-    pmsgClose();
-    __android_log_unlock();
-  }
   free(cp);
   return len;
 }
diff --git a/liblog/rwlock.h b/liblog/rwlock.h
new file mode 100644
index 0000000..00f1806
--- /dev/null
+++ b/liblog/rwlock.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <pthread.h>
+
+// As of the end of Dec 2019, std::shared_mutex is *not* simply a pthread_rwlock, but rather a
+// combination of std::mutex and std::condition variable, which is obviously less efficient.  This
+// immitates what std::shared_mutex should be doing and is compatible with std::shared_lock and
+// std::unique_lock.
+
+class RwLock {
+ public:
+  RwLock() {}
+  ~RwLock() {}
+
+  void lock() { pthread_rwlock_wrlock(&rwlock_); }
+  void unlock() { pthread_rwlock_unlock(&rwlock_); }
+
+  void lock_shared() { pthread_rwlock_rdlock(&rwlock_); }
+  void unlock_shared() { pthread_rwlock_unlock(&rwlock_); }
+
+ private:
+  pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
+};
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 394fa93..f58c524 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -62,6 +62,7 @@
         "log_time_test.cpp",
         "log_wrap_test.cpp",
         "logprint_test.cpp",
+        "rwlock_test.cpp",
     ],
     shared_libs: [
         "libcutils",
@@ -96,3 +97,11 @@
         "vts",
     ],
 }
+
+cc_test_host {
+    name: "liblog-host-test",
+    static_libs: ["liblog"],
+    shared_libs: ["libbase"],
+    srcs: ["liblog_host_test.cpp"],
+    isolated: true,
+}
diff --git a/liblog/tests/liblog_host_test.cpp b/liblog/tests/liblog_host_test.cpp
new file mode 100644
index 0000000..377550f
--- /dev/null
+++ b/liblog/tests/liblog_host_test.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/log.h>
+#include <private/android_logger.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::StringReplace;
+
+void GenerateLogContent() {
+  __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, "tag", "verbose main");
+  __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, "tag", "info main");
+  __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_ERROR, "tag", "error main");
+
+  __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+  __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, "tag", "info radio");
+  __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, "tag", "error radio");
+
+  __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, "tag", "verbose system");
+  __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, "tag", "info system");
+  __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, "tag", "error system");
+
+  __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+  __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_INFO, "tag", "info crash");
+  __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_ERROR, "tag", "error crash");
+}
+
+std::string GetPidString() {
+  int pid = getpid();
+  return StringPrintf("%5d", pid);
+}
+
+TEST(liblog, default_write) {
+  setenv("ANDROID_PRINTF_LOG", "brief", true);
+  CapturedStderr captured_stderr;
+
+  GenerateLogContent();
+
+  std::string expected_output = StringReplace(R"init(I/tag     (<pid>): info main
+E/tag     (<pid>): error main
+I/tag     (<pid>): info radio
+E/tag     (<pid>): error radio
+I/tag     (<pid>): info system
+E/tag     (<pid>): error system
+I/tag     (<pid>): info crash
+E/tag     (<pid>): error crash
+)init",
+                                              "<pid>", GetPidString(), true);
+
+  EXPECT_EQ(expected_output, captured_stderr.str());
+}
+
+TEST(liblog, format) {
+  setenv("ANDROID_PRINTF_LOG", "process", true);
+  CapturedStderr captured_stderr;
+
+  GenerateLogContent();
+
+  std::string expected_output = StringReplace(R"init(I(<pid>) info main  (tag)
+E(<pid>) error main  (tag)
+I(<pid>) info radio  (tag)
+E(<pid>) error radio  (tag)
+I(<pid>) info system  (tag)
+E(<pid>) error system  (tag)
+I(<pid>) info crash  (tag)
+E(<pid>) error crash  (tag)
+)init",
+                                              "<pid>", GetPidString(), true);
+
+  EXPECT_EQ(expected_output, captured_stderr.str());
+  captured_stderr.Stop();
+  captured_stderr.Reset();
+  captured_stderr.Start();
+
+  // Changing the environment after starting writing doesn't change the format.
+  setenv("ANDROID_PRINTF_LOG", "brief", true);
+  GenerateLogContent();
+  EXPECT_EQ(expected_output, captured_stderr.str());
+  captured_stderr.Stop();
+  captured_stderr.Reset();
+  captured_stderr.Start();
+
+  // However calling __android_log_close() does reset logging and allow changing the format.
+  __android_log_close();
+  GenerateLogContent();
+
+  expected_output = StringReplace(R"init(I/tag     (<pid>): info main
+E/tag     (<pid>): error main
+I/tag     (<pid>): info radio
+E/tag     (<pid>): error radio
+I/tag     (<pid>): info system
+E/tag     (<pid>): error system
+I/tag     (<pid>): info crash
+E/tag     (<pid>): error crash
+)init",
+                                  "<pid>", GetPidString(), true);
+
+  EXPECT_EQ(expected_output, captured_stderr.str());
+}
+
+TEST(liblog, filter) {
+  setenv("ANDROID_PRINTF_LOG", "brief", true);
+  setenv("ANDROID_LOG_TAGS", "*:w verbose_tag:v debug_tag:d", true);
+  CapturedStderr captured_stderr;
+
+  auto generate_logs = [](log_id_t log_id) {
+    // Check that we show verbose logs when requesting for a given tag.
+    __android_log_buf_print(log_id, ANDROID_LOG_VERBOSE, "verbose_tag", "verbose verbose_tag");
+    __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "verbose_tag", "error verbose_tag");
+
+    // Check that we don't show verbose logs when explicitly requesting debug+ for a given tag.
+    __android_log_buf_print(log_id, ANDROID_LOG_VERBOSE, "debug_tag", "verbose debug_tag");
+    __android_log_buf_print(log_id, ANDROID_LOG_DEBUG, "debug_tag", "debug debug_tag");
+    __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "debug_tag", "error debug_tag");
+
+    // Check that we don't show info logs when requesting globally warn+.
+    __android_log_buf_print(log_id, ANDROID_LOG_INFO, "default_tag", "info default_tag");
+    __android_log_buf_print(log_id, ANDROID_LOG_WARN, "default_tag", "warn default_tag");
+    __android_log_buf_print(log_id, ANDROID_LOG_ERROR, "default_tag", "error default_tag");
+  };
+
+  auto expected_output = StringReplace(R"init(V/verbose_tag(<pid>): verbose verbose_tag
+E/verbose_tag(<pid>): error verbose_tag
+D/debug_tag(<pid>): debug debug_tag
+E/debug_tag(<pid>): error debug_tag
+W/default_tag(<pid>): warn default_tag
+E/default_tag(<pid>): error default_tag
+)init",
+                                       "<pid>", GetPidString(), true);
+
+  auto test_all_logs = [&] {
+    for (auto log_id : {LOG_ID_MAIN, LOG_ID_SYSTEM, LOG_ID_RADIO, LOG_ID_CRASH}) {
+      generate_logs(log_id);
+      EXPECT_EQ(expected_output, captured_stderr.str());
+      captured_stderr.Stop();
+      captured_stderr.Reset();
+      captured_stderr.Start();
+    }
+  };
+
+  test_all_logs();
+
+  // Changing the environment after starting writing doesn't change the filter.
+  setenv("ANDROID_LOG_TAGS", "*:e", true);
+  test_all_logs();
+
+  // However calling __android_log_close() does reset logging and allow changing the format.
+  __android_log_close();
+  expected_output = StringReplace(R"init(E/verbose_tag(<pid>): error verbose_tag
+E/debug_tag(<pid>): error debug_tag
+E/default_tag(<pid>): error default_tag
+)init",
+                                  "<pid>", GetPidString(), true);
+  test_all_logs();
+}
+
+TEST(liblog, kernel_no_write) {
+  CapturedStderr captured_stderr;
+  __android_log_buf_print(LOG_ID_KERNEL, ANDROID_LOG_ERROR, "tag", "kernel error");
+  EXPECT_EQ("", captured_stderr.str());
+}
+
+TEST(liblog, binary_no_write) {
+  CapturedStderr captured_stderr;
+  __android_log_buf_print(LOG_ID_EVENTS, ANDROID_LOG_ERROR, "tag", "error events");
+  __android_log_buf_print(LOG_ID_STATS, ANDROID_LOG_ERROR, "tag", "error stats");
+  __android_log_buf_print(LOG_ID_SECURITY, ANDROID_LOG_ERROR, "tag", "error security");
+
+  __android_log_bswrite(0x12, "events");
+  __android_log_stats_bwrite(0x34, "stats", strlen("stats"));
+  __android_log_security_bswrite(0x56, "security");
+
+  EXPECT_EQ("", captured_stderr.str());
+}
diff --git a/liblog/tests/rwlock_test.cpp b/liblog/tests/rwlock_test.cpp
new file mode 100644
index 0000000..617d5c4
--- /dev/null
+++ b/liblog/tests/rwlock_test.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../rwlock.h"
+
+#include <chrono>
+#include <shared_mutex>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+using namespace std::literals;
+
+TEST(rwlock, reader_then_reader_lock) {
+  RwLock lock;
+
+  bool thread_ran = false;
+  auto read_guard = std::shared_lock{lock};
+
+  auto reader_thread = std::thread([&] {
+    auto read_guard = std::shared_lock{lock};
+    thread_ran = true;
+  });
+
+  auto end_time = std::chrono::steady_clock::now() + 1s;
+
+  while (std::chrono::steady_clock::now() < end_time) {
+    if (thread_ran) {
+      break;
+    }
+  }
+
+  EXPECT_EQ(true, thread_ran);
+
+  // Unlock the lock in case something went wrong, to ensure that we can still join() the thread.
+  read_guard.unlock();
+  reader_thread.join();
+}
+
+template <template <typename> typename L1, template <typename> typename L2>
+void TestBlockingLocks() {
+  RwLock lock;
+
+  bool thread_ran = false;
+  auto read_guard = L1{lock};
+
+  auto reader_thread = std::thread([&] {
+    auto read_guard = L2{lock};
+    thread_ran = true;
+  });
+
+  auto end_time = std::chrono::steady_clock::now() + 1s;
+
+  while (std::chrono::steady_clock::now() < end_time) {
+    if (thread_ran) {
+      break;
+    }
+  }
+
+  EXPECT_EQ(false, thread_ran);
+
+  read_guard.unlock();
+  reader_thread.join();
+
+  EXPECT_EQ(true, thread_ran);
+}
+
+TEST(rwlock, reader_then_writer_lock) {
+  TestBlockingLocks<std::shared_lock, std::unique_lock>();
+}
+
+TEST(rwlock, writer_then_reader_lock) {
+  TestBlockingLocks<std::unique_lock, std::shared_lock>();
+}
+
+TEST(rwlock, writer_then_writer_lock) {
+  TestBlockingLocks<std::unique_lock, std::unique_lock>();
+}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 8fe7854..2351afa 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -24,7 +24,6 @@
 #include <linux/if_link.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nfnetlink_log.h>
-#include <linux/netfilter_ipv4/ipt_ULOG.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <net/if.h>
@@ -39,6 +38,23 @@
 const int LOCAL_QLOG_NL_EVENT = 112;
 const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
 
+/* From deprecated ipt_ULOG.h to parse QLOG_NL_EVENT. */
+#define ULOG_MAC_LEN 80
+#define ULOG_PREFIX_LEN 32
+typedef struct ulog_packet_msg {
+    unsigned long mark;
+    long timestamp_sec;
+    long timestamp_usec;
+    unsigned int hook;
+    char indev_name[IFNAMSIZ];
+    char outdev_name[IFNAMSIZ];
+    size_t data_len;
+    char prefix[ULOG_PREFIX_LEN];
+    unsigned char mac_len;
+    unsigned char mac[ULOG_MAC_LEN];
+    unsigned char payload[0];
+} ulog_packet_msg_t;
+
 #include <android-base/parseint.h>
 #include <log/log.h>
 #include <sysutils/NetlinkEvent.h>
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 9dd0cdd..def4088 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -774,6 +774,6 @@
                             cfa_gnu_negative_offset_extended, cfa_register_override);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaLogTest, DwarfCfaLogTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index dd71490..9c6ab05 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -963,6 +963,6 @@
                             cfa_register_override);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaTest, DwarfCfaTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 2b36f17..b6f574a 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -825,6 +825,6 @@
     GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 4792fb5..46a25a4 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -128,6 +128,6 @@
 REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfEhFrameTest, DwarfEhFrameTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 768a808..6aa3867 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -552,6 +552,6 @@
                             GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index f4ade5d..8dbf6e8 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -68,6 +68,6 @@
 REGISTER_TYPED_TEST_SUITE_P(DwarfOpLogTest, multiple_ops);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfOpLogTest, DwarfOpLogTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 0898ec0..0e2d91a 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -1581,6 +1581,6 @@
                             is_dex_pc);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpTest, DwarfOpTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfOpTest, DwarfOpTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index a9d6dad..cac59b7 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -583,6 +583,6 @@
                             GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfSectionImplTest, DwarfSectionImplTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index ae3c349..c58aeff 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -367,6 +367,6 @@
                             symtab_read_cached, get_global);
 
 typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, SymbolsTest, SymbolsTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, SymbolsTest, SymbolsTestTypes);
 
 }  // namespace unwindstack
diff --git a/libutils/Errors.cpp b/libutils/Errors.cpp
index 2dfd138..74f3bef 100644
--- a/libutils/Errors.cpp
+++ b/libutils/Errors.cpp
@@ -45,7 +45,7 @@
 #undef STATUS_CASE
     }
 
-    return std::to_string(s) + ' ' + strerror(-s);
+    return std::to_string(s) + " (" + strerror(-s) + ")";
 }
 
 }  // namespace android
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index e3bb2ab..1bbffaf 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -177,7 +177,7 @@
 cc_binary {
     name: "ziptool",
     defaults: ["libziparchive_flags"],
-    srcs: ["unzip.cpp"],
+    srcs: ["ziptool.cpp"],
     shared_libs: [
         "libbase",
         "libziparchive",
@@ -198,3 +198,15 @@
     host_supported: true,
     corpus: ["testdata/*"],
 }
+
+sh_test {
+    name: "ziptool-tests",
+    src: "run-ziptool-tests-on-android.sh",
+    filename: "run-ziptool-tests-on-android.sh",
+    test_suites: ["general-tests"],
+    host_supported: true,
+    device_supported: false,
+    test_config: "ziptool-tests.xml",
+    data: ["cli-tests/**/*"],
+    target_required: ["cli-test", "ziptool"],
+}
diff --git a/libziparchive/cli-tests/files/example.zip b/libziparchive/cli-tests/files/example.zip
new file mode 100644
index 0000000..c3292e9
--- /dev/null
+++ b/libziparchive/cli-tests/files/example.zip
Binary files differ
diff --git a/libziparchive/cli-tests/unzip.test b/libziparchive/cli-tests/unzip.test
new file mode 100755
index 0000000..6e5cbf2
--- /dev/null
+++ b/libziparchive/cli-tests/unzip.test
@@ -0,0 +1,148 @@
+# unzip tests.
+
+# Note: since "master key", Android uses libziparchive for all zip file
+# handling, and that scans the whole central directory immediately. Not only
+# lookups by name but also iteration is implemented using the resulting hash
+# table, meaning that any test that makes assumptions about iteration order
+# will fail on Android.
+
+name: unzip -l
+command: unzip -l $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/x.txt ]
+expected-stdout:
+	Archive:  $FILES/example.zip
+	  Length      Date    Time    Name
+	---------  ---------- -----   ----
+	     1024  2017-06-04 08:45   d1/d2/x.txt
+	---------                     -------
+	     1024                     1 file
+---
+
+name: unzip -lq
+command: unzip -lq $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/x.txt ]
+expected-stdout:
+	  Length      Date    Time    Name
+	---------  ---------- -----   ----
+	     1024  2017-06-04 08:45   d1/d2/x.txt
+	---------                     -------
+	     1024                     1 file
+---
+
+name: unzip -lv
+command: unzip -lv $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/file ]
+expected-stdout:
+	Archive:  $FILES/example.zip
+	 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
+	--------  ------  ------- ---- ---------- ----- --------  ----
+	    1024  Defl:N       11  99% 2017-06-04 08:45 48d7f063  d1/d2/x.txt
+	--------          -------  ---                            -------
+	    1024               11  99%                            1 file
+---
+
+name: unzip -v
+command: unzip -v $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/file ]
+expected-stdout:
+	Archive:  $FILES/example.zip
+	 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
+	--------  ------  ------- ---- ---------- ----- --------  ----
+	    1024  Defl:N       11  99% 2017-06-04 08:45 48d7f063  d1/d2/x.txt
+	--------          -------  ---                            -------
+	    1024               11  99%                            1 file
+---
+
+name: unzip one file
+command: unzip -q $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+after: [ ! -f d1/d2/b.txt ]
+expected-stdout:
+	a
+---
+
+name: unzip all files
+command: unzip -q $FILES/example.zip
+after: [ -f d1/d2/a.txt ]
+after: [ -f d1/d2/b.txt ]
+after: [ -f d1/d2/c.txt ]
+after: [ -f d1/d2/empty.txt ]
+after: [ -f d1/d2/x.txt ]
+after: [ -d d1/d2/dir ]
+expected-stdout:
+---
+
+name: unzip -o
+before: mkdir -p d1/d2
+before: echo b > d1/d2/a.txt
+command: unzip -q -o $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+expected-stdout:
+	a
+---
+
+name: unzip -n
+before: mkdir -p d1/d2
+before: echo b > d1/d2/a.txt
+command: unzip -q -n $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+expected-stdout:
+	b
+---
+
+# The reference implementation will create *one* level of missing directories,
+# so this succeeds.
+name: unzip -d shallow non-existent
+command: unzip -q -d will-be-created $FILES/example.zip d1/d2/a.txt
+after: [ -d will-be-created ]
+after: [ -f will-be-created/d1/d2/a.txt ]
+---
+
+# The reference implementation will *only* create one level of missing
+# directories, so this fails.
+name: unzip -d deep non-existent
+command: unzip -q -d oh-no/will-not-be-created $FILES/example.zip d1/d2/a.txt 2> stderr ; echo $? > status
+after: [ ! -d oh-no ]
+after: [ ! -d oh-no/will-not-be-created ]
+after: [ ! -f oh-no/will-not-be-created/d1/d2/a.txt ]
+after: grep -q "oh-no/will-not-be-created" stderr
+after: grep -q "No such file or directory" stderr
+# The reference implementation has *lots* of non-zero exit values, but we stick to 0 and 1.
+after: [ $(cat status) -gt 0 ]
+---
+
+name: unzip -d exists
+before: mkdir dir
+command: unzip -q -d dir $FILES/example.zip d1/d2/a.txt && cat dir/d1/d2/a.txt
+after: [ ! -f d1/d2/a.txt ]
+expected-stdout:
+	a
+---
+
+name: unzip -p
+command: unzip -p $FILES/example.zip d1/d2/a.txt
+after: [ ! -f d1/d2/a.txt ]
+expected-stdout:
+	a
+---
+
+name: unzip -x FILE...
+# Note: the RI ignores -x DIR for some reason, but it's not obvious we should.
+command: unzip -q $FILES/example.zip -x d1/d2/a.txt d1/d2/b.txt d1/d2/empty.txt d1/d2/x.txt && cat d1/d2/c.txt
+after: [ ! -f d1/d2/a.txt ]
+after: [ ! -f d1/d2/b.txt ]
+after: [ ! -f d1/d2/empty.txt ]
+after: [ ! -f d1/d2/x.txt ]
+after: [ -d d1/d2/dir ]
+expected-stdout:
+	ccc
+---
+
+name: unzip FILE -x FILE...
+command: unzip -q $FILES/example.zip d1/d2/a.txt d1/d2/b.txt -x d1/d2/a.txt && cat d1/d2/b.txt
+after: [ ! -f d1/d2/a.txt ]
+after: [ -f d1/d2/b.txt ]
+after: [ ! -f d1/d2/c.txt ]
+after: [ ! -f d1/d2/empty.txt ]
+after: [ ! -f d1/d2/x.txt ]
+after: [ ! -d d1/d2/dir ]
+expected-stdout:
+	bb
+---
diff --git a/libziparchive/cli-tests/zipinfo.test b/libziparchive/cli-tests/zipinfo.test
new file mode 100755
index 0000000..d5bce1c
--- /dev/null
+++ b/libziparchive/cli-tests/zipinfo.test
@@ -0,0 +1,53 @@
+# zipinfo tests.
+
+# Note: since "master key", Android uses libziparchive for all zip file
+# handling, and that scans the whole central directory immediately. Not only
+# lookups by name but also iteration is implemented using the resulting hash
+# table, meaning that any test that makes assumptions about iteration order
+# will fail on Android.
+
+name: zipinfo -1
+command: zipinfo -1 $FILES/example.zip | sort
+expected-stdout:
+	d1/
+	d1/d2/a.txt
+	d1/d2/b.txt
+	d1/d2/c.txt
+	d1/d2/dir/
+	d1/d2/empty.txt
+	d1/d2/x.txt
+---
+
+name: zipinfo header
+command: zipinfo $FILES/example.zip | head -2
+expected-stdout:
+	Archive:  $FILES/example.zip
+	Zip file size: 1082 bytes, number of entries: 7
+---
+
+name: zipinfo footer
+command: zipinfo $FILES/example.zip | tail -1
+expected-stdout:
+	7 files, 1033 bytes uncompressed, 20 bytes compressed:  98.1%
+---
+
+name: zipinfo directory
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/ | sed s/17-Jun-/2017-06-/
+expected-stdout:
+	drwxr-x---  3.0 unx        0 bx stor 2017-06-04 08:40 d1/
+---
+
+name: zipinfo stored
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/d2/empty.txt | sed s/17-Jun-/2017-06-/
+expected-stdout:
+	-rw-r-----  3.0 unx        0 bx stor 2017-06-04 08:43 d1/d2/empty.txt
+---
+
+name: zipinfo deflated
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/d2/x.txt | sed s/17-Jun-/2017-06-/
+expected-stdout:
+	-rw-r-----  3.0 unx     1024 tx defN 2017-06-04 08:45 d1/d2/x.txt
+---
diff --git a/libziparchive/run-ziptool-tests-on-android.sh b/libziparchive/run-ziptool-tests-on-android.sh
new file mode 100755
index 0000000..3c23d43
--- /dev/null
+++ b/libziparchive/run-ziptool-tests-on-android.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Copy the tests across.
+adb shell rm -rf /data/local/tmp/ziptool-tests/
+adb shell mkdir /data/local/tmp/ziptool-tests/
+adb push cli-tests/ /data/local/tmp/ziptool-tests/
+#adb push cli-test /data/local/tmp/ziptool-tests/
+
+if tty -s; then
+  dash_t="-t"
+else
+  dash_t=""
+fi
+
+exec adb shell $dash_t cli-test /data/local/tmp/ziptool-tests/cli-tests/*.test
diff --git a/libziparchive/ziptool-tests.xml b/libziparchive/ziptool-tests.xml
new file mode 100644
index 0000000..211119f
--- /dev/null
+++ b/libziparchive/ziptool-tests.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<configuration description="Config for running ziptool-tests through Atest or in Infra">
+    <option name="test-suite-tag" value="ziptool-tests" />
+    <!-- This test requires a device, so it's not annotated with a null-device. -->
+    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+        <option name="binary" value="run-ziptool-tests-on-android.sh" />
+        <!-- Test script assumes a relative path with the cli-tests/ folders. -->
+        <option name="relative-path-execution" value="true" />
+        <!-- Tests shouldn't be that long but set 15m to be safe. -->
+        <option name="per-binary-timeout" value="15m" />
+    </test>
+</configuration>
diff --git a/libziparchive/unzip.cpp b/libziparchive/ziptool.cpp
similarity index 95%
rename from libziparchive/unzip.cpp
rename to libziparchive/ziptool.cpp
index 11b575e..dd42e90 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/ziptool.cpp
@@ -52,7 +52,7 @@
 static Role role;
 static OverwriteMode overwrite_mode = kPrompt;
 static bool flag_1 = false;
-static const char* flag_d = nullptr;
+static std::string flag_d;
 static bool flag_l = false;
 static bool flag_p = false;
 static bool flag_q = false;
@@ -214,12 +214,9 @@
   }
 
   // Where are we actually extracting to (for human-readable output)?
-  std::string dst;
-  if (flag_d) {
-    dst = flag_d;
-    if (!EndsWith(dst, "/")) dst += '/';
-  }
-  dst += name;
+  // flag_d is the empty string if -d wasn't used, or has a trailing '/'
+  // otherwise.
+  std::string dst = flag_d + name;
 
   // Ensure the directory hierarchy exists.
   if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
@@ -463,6 +460,7 @@
       switch (opt) {
         case 'd':
           flag_d = optarg;
+          if (!EndsWith(flag_d, "/")) flag_d += '/';
           break;
         case 'l':
           flag_l = true;
@@ -511,9 +509,17 @@
   }
 
   // Implement -d by changing into that directory.
-  // We'll create implicit directories based on paths in the zip file, but we
-  // require that the -d directory already exists.
-  if (flag_d && chdir(flag_d) == -1) die(errno, "couldn't chdir to %s", flag_d);
+  // We'll create implicit directories based on paths in the zip file, and we'll create
+  // the -d directory itself, but we require that *parents* of the -d directory already exists.
+  // This is pretty arbitrary, but it's the behavior of the original unzip.
+  if (!flag_d.empty()) {
+    if (mkdir(flag_d.c_str(), 0777) == -1 && errno != EEXIST) {
+      die(errno, "couldn't created %s", flag_d.c_str());
+    }
+    if (chdir(flag_d.c_str()) == -1) {
+      die(errno, "couldn't chdir to %s", flag_d.c_str());
+    }
+  }
 
   ProcessAll(zah);
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index cd5d7d4..7b18438 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -104,11 +104,6 @@
     bool debug_ = false;
 };
 
-// logd prefixes records with a length field
-#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
-
-enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
-
 #ifndef F2FS_IOC_SET_PIN_FILE
 #define F2FS_IOCTL_MAGIC       0xf5
 #define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
@@ -1082,50 +1077,45 @@
     }
 
     if (printStatistics || getPruneList) {
-        size_t len = 8192;
-        char* buf;
+        std::string buf(8192, '\0');
+        size_t ret_length = 0;
+        int retry = 32;
 
-        for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
-             delete[] buf, buf = nullptr, --retry) {
+        for (; retry >= 0; --retry) {
             if (getPruneList) {
-                android_logger_get_prune_list(logger_list.get(), buf, len);
+                android_logger_get_prune_list(logger_list.get(), buf.data(), buf.size());
             } else {
-                android_logger_get_statistics(logger_list.get(), buf, len);
+                android_logger_get_statistics(logger_list.get(), buf.data(), buf.size());
             }
-            buf[len - 1] = '\0';
-            if (atol(buf) < 3) {
-                delete[] buf;
-                buf = nullptr;
+
+            ret_length = atol(buf.c_str());
+            if (ret_length < 3) {
+                error(EXIT_FAILURE, 0, "Failed to read data.");
+            }
+
+            if (ret_length < buf.size()) {
                 break;
             }
-            size_t ret = atol(buf) + 1;
-            if (ret <= len) {
-                len = ret;
-                break;
-            }
-            len = ret;
+
+            buf.resize(ret_length + 1);
         }
 
-        if (!buf) {
+        if (retry < 0) {
             error(EXIT_FAILURE, 0, "Failed to read data.");
         }
 
-        // remove trailing FF
-        char* cp = buf + len - 1;
-        *cp = '\0';
-        bool truncated = *--cp != '\f';
-        if (!truncated) *cp = '\0';
-
-        // squash out the byte count
-        cp = buf;
-        if (!truncated) {
-            while (isdigit(*cp)) ++cp;
-            if (*cp == '\n') ++cp;
+        buf.resize(ret_length);
+        if (buf.back() == '\f') {
+            buf.pop_back();
         }
 
-        len = strlen(cp);
+        // Remove the byte count prefix
+        const char* cp = buf.c_str();
+        while (isdigit(*cp)) ++cp;
+        if (*cp == '\n') ++cp;
+
+        size_t len = strlen(cp);
         TEMP_FAILURE_RETRY(write(output_fd_.get(), cp, len));
-        delete[] buf;
         return EXIT_SUCCESS;
     }
 
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 7a843d8..694b5fa 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <math.h>
 #include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
@@ -186,14 +187,26 @@
     : LogCommand("getStatistics"), mBuf(*buf) {
 }
 
-static std::string package_string(const std::string& str) {
-    // Calculate total buffer size prefix, count is the string length w/o nul
-    char fmt[32];
-    for (size_t l = str.length(), y = 0, x = 6; y != x;
-         y = x, x = strlen(fmt) - 2) {
-        snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x);
+// This returns a string with a length prefix with the format <length>\n<data>\n\f.  The length
+// prefix includes the length of the prefix itself.
+static std::string PackageString(const std::string& str) {
+    size_t overhead_length = 3;  // \n \n \f.
+
+    // Number of digits needed to represent length(str + overhead_length).
+    size_t str_size_digits = 1 + static_cast<size_t>(log10(str.size() + overhead_length));
+    // Number of digits needed to represent the total size.
+    size_t total_size_digits =
+            1 + static_cast<size_t>(log10(str.size() + overhead_length + str_size_digits));
+
+    // If adding the size prefix causes a new digit to be required to represent the new total
+    // size, add it to the 'overhead_length'.  This can only happen once, since each new digit
+    // allows for 10x the previous size to be recorded.
+    if (total_size_digits != str_size_digits) {
+        overhead_length++;
     }
-    return android::base::StringPrintf(fmt, str.c_str());
+
+    size_t total_size = str.size() + overhead_length + str_size_digits;
+    return android::base::StringPrintf("%zu\n%s\n\f", total_size, str.c_str());
 }
 
 int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc,
@@ -228,8 +241,7 @@
         }
     }
 
-    cli->sendMsg(
-        package_string(mBuf.formatStatistics(uid, pid, logMask)).c_str());
+    cli->sendMsg(PackageString(mBuf.formatStatistics(uid, pid, logMask)).c_str());
     return 0;
 }
 
@@ -240,7 +252,7 @@
 int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
                                                  int /*argc*/, char** /*argv*/) {
     setname();
-    cli->sendMsg(package_string(mBuf.formatPrune()).c_str());
+    cli->sendMsg(PackageString(mBuf.formatPrune()).c_str());
     return 0;
 }
 
@@ -316,12 +328,11 @@
             cli->sendMsg("can not mix id= with either format= or name=");
             return 0;
         }
-        cli->sendMsg(package_string(mBuf.formatEntry(atoi(id), uid)).c_str());
+        cli->sendMsg(PackageString(mBuf.formatEntry(atoi(id), uid)).c_str());
         return 0;
     }
 
-    cli->sendMsg(
-        package_string(mBuf.formatGetEventTag(uid, name, format)).c_str());
+    cli->sendMsg(PackageString(mBuf.formatGetEventTag(uid, name, format)).c_str());
 
     return 0;
 }
diff --git a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
index 439813d..dfb1d11 100644
--- a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
+++ b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
@@ -14,8 +14,7 @@
 // limitations under the License.
 //
 
-#ifndef PROPERTY_INFO_SERIALIZER_H
-#define PROPERTY_INFO_SERIALIZER_H
+#pragma once
 
 #include <string>
 #include <vector>
@@ -41,11 +40,9 @@
                const std::string& default_context, const std::string& default_type,
                std::string* serialized_trie, std::string* error);
 
-void ParsePropertyInfoFile(const std::string& file_contents,
+void ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,
                            std::vector<PropertyInfoEntry>* property_infos,
                            std::vector<std::string>* errors);
 
 }  // namespace properties
 }  // namespace android
-
-#endif
diff --git a/property_service/libpropertyinfoserializer/property_info_file.cpp b/property_service/libpropertyinfoserializer/property_info_file.cpp
index 2cdc62d..771a9ce 100644
--- a/property_service/libpropertyinfoserializer/property_info_file.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_file.cpp
@@ -56,7 +56,8 @@
   return false;
 }
 
-bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
+bool ParsePropertyInfoLine(const std::string& line, bool require_prefix_or_exact,
+                           PropertyInfoEntry* out, std::string* error) {
   auto tokenizer = SpaceTokenizer(line);
 
   auto property = tokenizer.GetNext();
@@ -72,7 +73,7 @@
   }
 
   // It is not an error to not find exact_match or a type, as older files will not contain them.
-  auto exact_match = tokenizer.GetNext();
+  auto match_operation = tokenizer.GetNext();
   // We reformat type to be space deliminated regardless of the input whitespace for easier storage
   // and subsequent parsing.
   auto type_strings = std::vector<std::string>{};
@@ -82,18 +83,27 @@
     type = tokenizer.GetNext();
   }
 
+  bool exact_match = false;
+  if (match_operation == "exact") {
+    exact_match = true;
+  } else if (match_operation != "prefix" && match_operation != "" && require_prefix_or_exact) {
+    *error = "Match operation '" + match_operation +
+             "' is not valid: must be either 'prefix' or 'exact'";
+    return false;
+  }
+
   if (!type_strings.empty() && !IsTypeValid(type_strings)) {
     *error = "Type '" + Join(type_strings, " ") + "' is not valid";
     return false;
   }
 
-  *out = {property, context, Join(type_strings, " "), exact_match == "exact"};
+  *out = {property, context, Join(type_strings, " "), exact_match};
   return true;
 }
 
 }  // namespace
 
-void ParsePropertyInfoFile(const std::string& file_contents,
+void ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,
                            std::vector<PropertyInfoEntry>* property_infos,
                            std::vector<std::string>* errors) {
   // Do not clear property_infos to allow this function to be called on multiple files, with
@@ -108,7 +118,8 @@
 
     auto property_info_entry = PropertyInfoEntry{};
     auto parse_error = std::string{};
-    if (!ParsePropertyInfoLine(trimmed_line, &property_info_entry, &parse_error)) {
+    if (!ParsePropertyInfoLine(trimmed_line, require_prefix_or_exact, &property_info_entry,
+                               &parse_error)) {
       errors->emplace_back(parse_error);
       continue;
     }
diff --git a/property_service/property_info_checker/property_info_checker.cpp b/property_service/property_info_checker/property_info_checker.cpp
index 52c4383..61b368e 100644
--- a/property_service/property_info_checker/property_info_checker.cpp
+++ b/property_service/property_info_checker/property_info_checker.cpp
@@ -153,7 +153,7 @@
     }
 
     auto errors = std::vector<std::string>{};
-    ParsePropertyInfoFile(file_contents, &property_info_entries, &errors);
+    ParsePropertyInfoFile(file_contents, true, &property_info_entries, &errors);
     if (!errors.empty()) {
       for (const auto& error : errors) {
         std::cerr << "Could not read line from '" << filename << "': " << error << std::endl;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 994d9ae..2dbdb60 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -72,11 +72,12 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
+    linkerconfig $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
-    ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
+    ln -sfn /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
     ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
 ifdef BOARD_USES_VENDORIMAGE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 5dc019c..80573fb 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -16,6 +16,21 @@
 include $(BUILD_PREBUILT)
 
 #######################################
+# q-developer-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := q-developer-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
 # r-gsi.avbpubkey
 include $(CLEAR_VARS)
 
diff --git a/rootdir/avb/q-developer-gsi.avbpubkey b/rootdir/avb/q-developer-gsi.avbpubkey
new file mode 100644
index 0000000..0ace69d
--- /dev/null
+++ b/rootdir/avb/q-developer-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/init.rc b/rootdir/init.rc
index e2ecad4..c2c9df3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -38,12 +38,9 @@
     # Allow up to 32K FDs per process
     setrlimit nofile 32768 32768
 
-    # Create directory to keep ld.config.txt
-    mkdir /dev/linkerconfig 0755
-
     # Generate ld.config.txt for early executed processes
-    exec -- /system/bin/linkerconfig --target /dev/linkerconfig/ld.config.txt
-    chmod 444 /dev/linkerconfig/ld.config.txt
+    exec -- /system/bin/linkerconfig --target /linkerconfig/ld.config.txt
+    chmod 444 /linkerconfig/ld.config.txt
 
     start ueventd
 
@@ -424,6 +421,9 @@
     # Once everything is setup, no need to modify /.
     # The bind+remount combination allows this to work in containers.
     mount rootfs rootfs / remount bind ro nodev
+    # Mount default storage into root namespace
+    mount none /mnt/runtime/default /storage bind rec
+    mount none none /storage slave rec
 
     # Make sure /sys/kernel/debug (if present) is labeled properly
     # Note that tracefs may be mounted under debug, so we need to cross filesystems
@@ -579,6 +579,8 @@
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
     mkdir /data/misc/installd 0700 root root
+    mkdir /data/misc/apexdata 0700 root root
+    mkdir /data/misc/apexrollback 0700 root root
 
     mkdir /data/preloads 0775 system system encryption=None
 
@@ -651,12 +653,35 @@
 
     mkdir /data/user 0711 system system encryption=None
     mkdir /data/user_de 0711 system system encryption=None
-    symlink /data/data /data/user/0
+
+    # Unlink /data/user/0 if we previously symlink it to /data/data
+    rm /data/user/0
+
+    # Bind mount /data/user/0 to /data/data
+    mkdir /data/user/0 0700 system system encryption=None
+    mount none /data/data /data/user/0 bind rec
 
     # Special-case /data/media/obb per b/64566063
     mkdir /data/media 0770 media_rw media_rw encryption=None
     mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
 
+    # A tmpfs directory, which will contain all apps CE DE data directory that
+    # bind mount from the original source.
+    chown root root /data_mirror
+    chmod 0700 /data_mirror
+    mount tmpfs tmpfs /data_mirror mode=0700,uid=0,gid=1000 nodev noexec nosuid
+    restorecon /data_mirror
+    mkdir /data_mirror/data_ce 0700 root root
+    mkdir /data_mirror/data_de 0700 root root
+
+    # Create CE and DE data directory for default volume
+    mkdir /data_mirror/data_ce/null 0700 root root
+    mkdir /data_mirror/data_de/null 0700 root root
+
+    # Bind mount CE and DE data directory to mirror's default volume directory
+    mount none /data/user /data_mirror/data_ce/null bind rec
+    mount none /data/user_de /data_mirror/data_de/null bind rec
+
     mkdir /data/cache 0770 system cache encryption=Require
     mkdir /data/cache/recovery 0770 system cache
     mkdir /data/cache/backup_stage 0700 system system
@@ -668,7 +693,8 @@
 
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
-    parse_apex_configs
+    perform_apex_config
+
     exec_start derive_sdk
 
     init_user0
@@ -695,22 +721,6 @@
     chown root system /dev/fscklogs/log
     chmod 0770 /dev/fscklogs/log
 
-# Switch between sdcardfs and FUSE depending on persist property
-# TODO: Move this to ro property before launch because FDE devices
-# interact with persistent properties differently during boot
-on zygote-start && property:persist.sys.fuse=true
-  # Mount default storage into root namespace
-  mount none /mnt/user/0 /storage bind rec
-  mount none none /storage slave rec
-on zygote-start && property:persist.sys.fuse=false
-  # Mount default storage into root namespace
-  mount none /mnt/runtime/default /storage bind rec
-  mount none none /storage slave rec
-on zygote-start && property:persist.sys.fuse=""
-  # Mount default storage into root namespace
-  mount none /mnt/runtime/default /storage bind rec
-  mount none none /storage slave rec
-
 # It is recommended to put unnecessary data/ initialization from post-fs-data
 # to start-zygote in device's init.rc to unblock zygote start.
 on zygote-start && property:ro.crypto.state=unencrypted
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index a1888fc..02d34ba 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -19,7 +19,9 @@
     updatable
     seclabel u:r:adbd:s0
 
-on boot
+# Set default value on sys.usb.configfs early in boot sequence. It will be
+# overridden in `on boot` action of init.hardware.rc.
+on init
     setprop sys.usb.configfs 0
 
 # Used to disable USB when switching states
@@ -133,3 +135,8 @@
 on property:sys.usb.typec.power_role=sink
     write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
     setprop sys.usb.typec.state ${sys.usb.typec.power_role}
+
+on userspace-reboot-requested
+  setprop sys.usb.config ""
+  setprop sys.usb.configfs ""
+  setprop sys.usb.state ""
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index ec4f6ab..b5a5fb6 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -12,6 +12,7 @@
     required: [
         "auditctl",
         "awk",
+        "bc",
         "bzip2",
         "ldd",
         "logwrapper",
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c61f7d0..5f56408 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -46,6 +46,8 @@
         return MMC_RPMB;
     } else if (!strcmp(dev_type_name, "virt")) {
         return VIRT_RPMB;
+    } else if (!strcmp(dev_type_name, "sock")) {
+        return SOCK_RPMB;
     } else {
         return UNKNOWN_RPMB;
     }
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 29827e2..0bd9e68 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -21,6 +21,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <unistd.h>
 
 #include <linux/major.h>
@@ -192,7 +194,7 @@
             msg->result = STORAGE_ERR_GENERIC;
             goto err_response;
         }
-    } else if (dev_type == VIRT_RPMB) {
+    } else if ((dev_type == VIRT_RPMB) || (dev_type == SOCK_RPMB)) {
         size_t payload_size = req->reliable_write_size + req->write_size;
         rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
         if (rc < 0) {
@@ -234,12 +236,33 @@
     int rc;
     dev_type = open_dev_type;
 
-    rc = open(rpmb_devname, O_RDWR, 0);
-    if (rc < 0) {
-        ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
-        return rc;
+    if (dev_type != SOCK_RPMB) {
+        rc = open(rpmb_devname, O_RDWR, 0);
+        if (rc < 0) {
+            ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
+            return rc;
+        }
+        rpmb_fd = rc;
+    } else {
+        struct sockaddr_un unaddr;
+        struct sockaddr *addr = (struct sockaddr *)&unaddr;
+        rc = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (rc < 0) {
+            ALOGE("unable (%d) to create socket: %s\n", errno, strerror(errno));
+            return rc;
+        }
+        rpmb_fd = rc;
+
+        memset(&unaddr, 0, sizeof(unaddr));
+        unaddr.sun_family = AF_UNIX;
+        // TODO if it overflowed, bail rather than connecting?
+        strncpy(unaddr.sun_path, rpmb_devname, sizeof(unaddr.sun_path)-1);
+        rc = connect(rpmb_fd, addr, sizeof(unaddr));
+        if (rc < 0) {
+            ALOGE("unable (%d) to connect to rpmb socket '%s': %s\n", errno, rpmb_devname, strerror(errno));
+            return rc;
+        }
     }
-    rpmb_fd = rc;
     return 0;
 }
 
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 4c330c9..09af3c5 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <trusty/interface/storage.h>
 
-enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB, SOCK_RPMB };
 
 int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
 int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
new file mode 100644
index 0000000..e923e82
--- /dev/null
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at //
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+    name: "rpmb_dev",
+    vendor: true,
+
+    srcs: [
+        "rpmb_dev.c",
+    ],
+    shared_libs: [
+        "libc",
+        "liblog",
+        "libcrypto",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    init_rc: [
+        "rpmb_dev.rc",
+    ],
+}
diff --git a/trusty/utils/rpmb_dev/rpmb.h b/trusty/utils/rpmb_dev/rpmb.h
new file mode 100644
index 0000000..ab7e8d8
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __RPMB_H__
+#define __RPMB_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct rpmb_key {
+    uint8_t byte[32];
+};
+
+struct rpmb_state;
+
+#define RPMB_BUF_SIZE 256
+
+/* provides */
+int rpmb_init(struct rpmb_state** statep,
+              void* mmc_handle,
+              const struct rpmb_key* key);
+void rpmb_uninit(struct rpmb_state* statep);
+int rpmb_read(struct rpmb_state* state,
+              void* buf,
+              uint16_t addr,
+              uint16_t count);
+/* count must be 1 or 2, addr must be aligned */
+int rpmb_write(struct rpmb_state* state,
+               const void* buf,
+               uint16_t addr,
+               uint16_t count,
+               bool sync);
+
+/* needs */
+int rpmb_send(void* mmc_handle,
+              void* reliable_write_buf,
+              size_t reliable_write_size,
+              void* write_buf,
+              size_t write_buf_size,
+              void* read_buf,
+              size_t read_buf_size,
+              bool sync);
+
+#endif
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
new file mode 100644
index 0000000..af97eba
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2018 The Android Open Source 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.
+ */
+
+#define LOG_TAG "rpmb_mock"
+
+#include "rpmb_protocol.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+/* verbose is an int for getopt */
+static int verbose = false;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+HMAC_CTX* HMAC_CTX_new(void) {
+    HMAC_CTX* ctx = malloc(sizeof(*ctx));
+    if (ctx != NULL) {
+        HMAC_CTX_init(ctx);
+    }
+    return ctx;
+}
+
+void HMAC_CTX_free(HMAC_CTX* ctx) {
+    if (ctx != NULL) {
+        HMAC_CTX_cleanup(ctx);
+        free(ctx);
+    }
+}
+
+#endif
+
+#define MAX_WRITE_COUNTER (0xffffffff)
+
+struct rpmb_data_header {
+    uint32_t write_counter;
+    uint16_t max_block;
+    uint8_t pad1;
+    uint8_t key_programmed;
+    struct rpmb_key key;
+    uint8_t pad[512 - 4 - 2 - 1 - 1 - sizeof(struct rpmb_key)];
+};
+
+#define MAX_PACKET_COUNT (8)
+
+struct rpmb_dev_state {
+    struct rpmb_data_header header;
+    struct rpmb_packet cmd[MAX_PACKET_COUNT];
+    struct rpmb_packet res[MAX_PACKET_COUNT];
+    uint16_t cmd_count;
+    uint16_t res_count;
+    int data_fd;
+};
+
+/* TODO: move to common location */
+static int rpmb_mac(struct rpmb_key key, struct rpmb_packet* packet, size_t packet_count,
+                    struct rpmb_key* mac) {
+    size_t i;
+    int hmac_ret;
+    unsigned int md_len;
+    HMAC_CTX* hmac_ctx;
+
+    hmac_ctx = HMAC_CTX_new();
+    hmac_ret = HMAC_Init_ex(hmac_ctx, &key, sizeof(key), EVP_sha256(), NULL);
+    if (!hmac_ret) {
+        ALOGE("HMAC_Init_ex failed\n");
+        goto err;
+    }
+    for (i = 0; i < packet_count; i++) {
+        hmac_ret = HMAC_Update(hmac_ctx, packet[i].data, 284);
+        if (!hmac_ret) {
+            ALOGE("HMAC_Update failed\n");
+            goto err;
+        }
+    }
+    hmac_ret = HMAC_Final(hmac_ctx, mac->byte, &md_len);
+    if (md_len != sizeof(mac->byte)) {
+        ALOGE("bad md_len %d != %zd\n", md_len, sizeof(mac->byte));
+        exit(1);
+    }
+    if (!hmac_ret) {
+        ALOGE("HMAC_Final failed\n");
+        goto err;
+    }
+
+err:
+    HMAC_CTX_free(hmac_ctx);
+    return hmac_ret ? 0 : -1;
+}
+
+static int rpmb_file_seek(struct rpmb_dev_state* s, uint16_t addr) {
+    int ret;
+    int pos = addr * RPMB_PACKET_DATA_SIZE + sizeof(s->header);
+    ret = lseek(s->data_fd, pos, SEEK_SET);
+    if (ret != pos) {
+        ALOGE("rpmb_dev: seek to %d failed, got %d\n", pos, ret);
+        return -1;
+    }
+    return 0;
+}
+
+static uint16_t rpmb_dev_program_key(struct rpmb_dev_state* s) {
+    int ret;
+
+    if (s->header.key_programmed) {
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    s->header.key = s->cmd[0].key_mac;
+    s->header.key_programmed = 1;
+
+    ret = lseek(s->data_fd, 0, SEEK_SET);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    ret = write(s->data_fd, &s->header, sizeof(s->header));
+    if (ret != sizeof(s->header)) {
+        ALOGE("rpmb_dev: Failed to write rpmb key: %d, %s\n", ret, strerror(errno));
+
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_get_counter(struct rpmb_dev_state* s) {
+    s->res[0].write_counter = rpmb_u32(s->header.write_counter);
+
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_data_write(struct rpmb_dev_state* s) {
+    uint16_t addr = rpmb_get_u16(s->cmd[0].address);
+    uint16_t block_count = s->cmd_count;
+    uint32_t write_counter;
+    int ret;
+
+    if (s->header.write_counter == MAX_WRITE_COUNTER) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Write counter expired\n");
+        }
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    write_counter = rpmb_get_u32(s->cmd[0].write_counter);
+    if (s->header.write_counter != write_counter) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Invalid write counter %u. Expected: %u\n", write_counter,
+                  s->header.write_counter);
+        }
+        return RPMB_RES_COUNT_FAILURE;
+    }
+
+    ret = rpmb_file_seek(s, addr);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    for (int i = 0; i < block_count; i++) {
+        ret = write(s->data_fd, s->cmd[i].data, RPMB_PACKET_DATA_SIZE);
+        if (ret != RPMB_PACKET_DATA_SIZE) {
+            ALOGE("rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret, strerror(errno));
+            return RPMB_RES_WRITE_FAILURE;
+        }
+    }
+
+    s->header.write_counter++;
+
+    ret = lseek(s->data_fd, 0, SEEK_SET);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    ret = write(s->data_fd, &s->header.write_counter, sizeof(s->header.write_counter));
+    if (ret != sizeof(s->header.write_counter)) {
+        ALOGE("rpmb_dev: Failed to write rpmb write counter: %d, %s\n", ret, strerror(errno));
+
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    s->res[0].write_counter = rpmb_u32(s->header.write_counter);
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_data_read(struct rpmb_dev_state* s) {
+    uint16_t addr;
+    uint16_t block_count;
+    int ret;
+
+    addr = rpmb_get_u16(s->cmd[0].address);
+    block_count = s->res_count;
+
+    rpmb_file_seek(s, addr);
+
+    for (int i = 0; i < block_count; i++) {
+        ret = read(s->data_fd, s->res[i].data, RPMB_PACKET_DATA_SIZE);
+        if (ret != 0 && ret != RPMB_PACKET_DATA_SIZE) {
+            ALOGE("rpmb_dev: Failed to read rpmb data file: %d, %s\n", ret, strerror(errno));
+            return RPMB_RES_READ_FAILURE;
+        }
+    }
+
+    return RPMB_RES_OK;
+}
+
+struct rpmb_dev_cmd {
+    uint16_t (*func)(struct rpmb_dev_state* s);
+    uint16_t resp;
+    bool key_mac_is_key;
+    bool check_mac;
+    bool check_result_read;
+    bool check_key_programmed;
+    bool check_addr;
+    bool multi_packet_cmd;
+    bool multi_packet_res;
+    bool res_mac;
+};
+
+static struct rpmb_dev_cmd rpmb_dev_cmd_table[] = {
+        [RPMB_REQ_PROGRAM_KEY] =
+                {
+                        .func = rpmb_dev_program_key,
+                        .resp = RPMB_RESP_PROGRAM_KEY,
+                        .key_mac_is_key = true,
+                        .check_result_read = true,
+                },
+        [RPMB_REQ_GET_COUNTER] =
+                {
+                        .func = rpmb_dev_get_counter,
+                        .resp = RPMB_RESP_GET_COUNTER,
+                        .check_key_programmed = true,
+                        .res_mac = true,
+                },
+        [RPMB_REQ_DATA_WRITE] =
+                {
+                        .func = rpmb_dev_data_write,
+                        .resp = RPMB_RESP_DATA_WRITE,
+                        .check_mac = true,
+                        .check_result_read = true,
+                        .check_key_programmed = true,
+                        .check_addr = true,
+                        .multi_packet_cmd = true,
+                        .res_mac = true,
+                },
+        [RPMB_REQ_DATA_READ] =
+                {
+                        .func = rpmb_dev_data_read,
+                        .resp = RPMB_RESP_DATA_READ,
+                        .check_addr = true,
+                        .multi_packet_res = true,
+                        .res_mac = true,
+                },
+};
+
+#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
+
+static void rpmb_dev_process_cmd(struct rpmb_dev_state* s) {
+    assert(s->cmd_count > 0);
+    assert(s->res_count > 0);
+    uint16_t req_resp = rpmb_get_u16(s->cmd[0].req_resp);
+    uint16_t addr = rpmb_get_u16(s->cmd[0].address);
+    uint16_t sub_req;
+    uint16_t cmd_index = req_resp < countof(rpmb_dev_cmd_table) ? req_resp : 0;
+    struct rpmb_dev_cmd* cmd = &rpmb_dev_cmd_table[cmd_index];
+    uint16_t result = RPMB_RES_GENERAL_FAILURE;
+    struct rpmb_key mac;
+    uint16_t block_count = 0;
+
+    if (cmd->check_result_read) {
+        sub_req = rpmb_get_u16(s->cmd[s->cmd_count - 1].req_resp);
+        if (sub_req != RPMB_REQ_RESULT_READ) {
+            if (verbose) {
+                ALOGE("rpmb_dev: Request %d, missing result read request, got %d, cmd_count %d\n",
+                      req_resp, sub_req, s->cmd_count);
+            }
+            goto err;
+        }
+        assert(s->cmd_count > 1);
+        s->cmd_count--;
+    }
+
+    if (cmd->check_mac) {
+        if (rpmb_mac(s->header.key, s->cmd, s->cmd_count, &mac) != 0) {
+            ALOGE("rpmb_dev: failed to caclulate mac\n");
+            goto err;
+        }
+    } else if (cmd->key_mac_is_key) {
+        mac = s->cmd[s->cmd_count - 1].key_mac;
+    } else {
+        memset(mac.byte, 0, sizeof(mac.byte));
+    }
+
+    if (memcmp(&mac, s->cmd[s->cmd_count - 1].key_mac.byte, sizeof(mac))) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid MAC, cmd_count %d\n", req_resp, s->cmd_count);
+        }
+        if (cmd->check_mac) {
+            result = RPMB_RES_AUTH_FAILURE;
+        }
+        goto err;
+    }
+
+    if (cmd->multi_packet_cmd) {
+        block_count = s->cmd_count;
+    }
+    if (cmd->multi_packet_res) {
+        block_count = s->res_count;
+    }
+
+    if (cmd->check_addr && (addr + block_count > s->header.max_block + 1)) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid addr: 0x%x count 0x%x, Out of bounds. Max addr "
+                  "0x%x\n",
+                  req_resp, addr, block_count, s->header.max_block + 1);
+        }
+        result = RPMB_RES_ADDR_FAILURE;
+        goto err;
+    }
+    if (!cmd->check_addr && addr) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid addr: 0x%x != 0\n", req_resp, addr);
+        }
+        goto err;
+    }
+
+    for (int i = 1; i < s->cmd_count; i++) {
+        sub_req = rpmb_get_u16(s->cmd[i].req_resp);
+        if (sub_req != req_resp) {
+            if (verbose) {
+                ALOGE("rpmb_dev: Request %d, sub-request mismatch, %d, at %d\n", req_resp, i,
+                      sub_req);
+            }
+            goto err;
+        }
+    }
+    if (!cmd->multi_packet_cmd && s->cmd_count != 1) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, bad cmd count %d, expected 1\n", req_resp, s->cmd_count);
+        }
+        goto err;
+    }
+    if (!cmd->multi_packet_res && s->res_count != 1) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, bad res count %d, expected 1\n", req_resp, s->res_count);
+        }
+        goto err;
+    }
+
+    if (cmd->check_key_programmed && !s->header.key_programmed) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, key is not programmed\n", req_resp);
+        }
+        s->res[0].result = rpmb_u16(RPMB_RES_NO_AUTH_KEY);
+        return;
+    }
+
+    if (!cmd->func) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Unsupported request: %d\n", req_resp);
+        }
+        goto err;
+    }
+
+    result = cmd->func(s);
+
+err:
+    if (s->header.write_counter == MAX_WRITE_COUNTER) {
+        result |= RPMB_RES_WRITE_COUNTER_EXPIRED;
+    }
+
+    for (int i = 0; i < s->res_count; i++) {
+        s->res[i].nonce = s->cmd[0].nonce;
+        s->res[i].address = rpmb_u16(addr);
+        s->res[i].block_count = rpmb_u16(block_count);
+        s->res[i].result = rpmb_u16(result);
+        s->res[i].req_resp = rpmb_u16(cmd->resp);
+    }
+    if (cmd->res_mac) {
+        rpmb_mac(s->header.key, s->res, s->res_count, &s->res[s->res_count - 1].key_mac);
+    }
+}
+
+/*
+ * Receives data until one of the following is true:
+ * - The buffer is full (return will be len)
+ * - The connection closed (return > 0, < len)
+ * - An error occurred (return will be the negative error code from recv)
+ */
+ssize_t recv_until(int sock, void* dest_in, size_t len) {
+    size_t bytes_recvd = 0;
+    char* dest = dest_in;
+    while (bytes_recvd < len) {
+        ssize_t ret = recv(sock, dest, len - bytes_recvd, 0);
+        if (ret < 0) {
+            return ret;
+        }
+        dest += ret;
+        bytes_recvd += ret;
+        if (ret == 0) {
+            break;
+        }
+    }
+    return bytes_recvd;
+}
+
+/*
+ * Handles an incoming connection to the rpmb daemon.
+ * Returns 0 if the client disconnects without violating the protocol.
+ * Returns a negative value if we terminated the connection abnormally.
+ *
+ * Arguments:
+ *   conn_sock - an fd to send/recv on
+ *   s - an initialized rpmb device
+ */
+int handle_conn(struct rpmb_dev_state* s, int conn_sock) {
+    int ret;
+
+    while (true) {
+        memset(s->res, 0, sizeof(s->res));
+        ret = recv_until(conn_sock, &s->res_count, sizeof(s->res_count));
+
+        /*
+         * Disconnected while not in the middle of anything.
+         */
+        if (ret <= 0) {
+            return 0;
+        }
+
+        if (s->res_count > MAX_PACKET_COUNT) {
+            ALOGE("rpmb_dev: Receive count too large: %d\n", s->res_count);
+            return -1;
+        }
+        if (s->res_count <= 0) {
+            ALOGE("rpmb_dev: Receive count too small: %d\n", s->res_count);
+            return -1;
+        }
+
+        ret = recv_until(conn_sock, &s->cmd_count, sizeof(s->cmd_count));
+        if (ret != sizeof(s->cmd_count)) {
+            ALOGE("rpmb_dev: Failed to read cmd_count");
+            return -1;
+        }
+
+        if (s->cmd_count == 0) {
+            ALOGE("rpmb_dev: Must contain at least one command\n");
+            return -1;
+        }
+
+        if (s->cmd_count > MAX_PACKET_COUNT) {
+            ALOGE("rpmb_dev: Command count is too large\n");
+            return -1;
+        }
+
+        size_t cmd_size = s->cmd_count * sizeof(s->cmd[0]);
+        ret = recv_until(conn_sock, s->cmd, cmd_size);
+        if (ret != (int)cmd_size) {
+            ALOGE("rpmb_dev: Failed to read command: "
+                  "cmd_size: %zu ret: %d, %s\n",
+                  cmd_size, ret, strerror(errno));
+            return -1;
+        }
+
+        rpmb_dev_process_cmd(s);
+
+        size_t resp_size = sizeof(s->res[0]) * s->res_count;
+        ret = send(conn_sock, s->res, resp_size, 0);
+        if (ret != (int)resp_size) {
+            ALOGE("rpmb_dev: Failed to send response: %d, %s\n", ret, strerror(errno));
+            return -1;
+        }
+    }
+}
+
+void usage(const char* argv0) {
+    fprintf(stderr, "Usage: %s [-d|--dev] <datafile> [--sock] <socket_path>\n", argv0);
+    fprintf(stderr, "or:    %s [-d|--dev] <datafile> [--size <size>] [--key key]\n", argv0);
+}
+
+int main(int argc, char** argv) {
+    struct rpmb_dev_state s;
+    int ret;
+    int cmdres_sock;
+    struct sockaddr_un cmdres_sockaddr;
+    const char* data_file_name = NULL;
+    const char* socket_path = NULL;
+    int open_flags;
+    int init = false;
+
+    struct option long_options[] = {{"size", required_argument, 0, 0},
+                                    {"key", required_argument, 0, 0},
+                                    {"sock", required_argument, 0, 0},
+                                    {"dev", required_argument, 0, 'd'},
+                                    {"init", no_argument, &init, true},
+                                    {"verbose", no_argument, &verbose, true},
+                                    {0, 0, 0, 0}};
+
+    memset(&s.header, 0, sizeof(s.header));
+
+    while (1) {
+        int c;
+        int option_index = 0;
+        c = getopt_long(argc, argv, "d:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            /* long args */
+            case 0:
+                switch (option_index) {
+                    /* size */
+                    case 0:
+                        s.header.max_block = atoi(optarg) - 1;
+                        break;
+                    /* key */
+                    case 1:
+                        for (size_t i = 0; i < sizeof(s.header.key.byte); i++) {
+                            if (!optarg) {
+                                break;
+                            }
+                            s.header.key.byte[i] = strtol(optarg, &optarg, 16);
+                            s.header.key_programmed = 1;
+                        }
+                        break;
+                    /* sock */
+                    case 2:
+                        socket_path = optarg;
+                        break;
+                }
+                break;
+            /* dev */
+            case 'd':
+                data_file_name = optarg;
+                break;
+            default:
+                usage(argv[0]);
+                return EXIT_FAILURE;
+        }
+    }
+
+    /*
+     * We always need a data file, and at exactly one of --init or --sock
+     * must be specified.
+     */
+    if (!data_file_name || (!init == !socket_path)) {
+        usage(argv[0]);
+        return EXIT_FAILURE;
+    }
+
+    /*
+     * If the file is already initialized, exit early.
+     */
+    if (init && !access(data_file_name, F_OK)) {
+        return EXIT_SUCCESS;
+    }
+
+    open_flags = O_RDWR;
+    if (init) {
+        open_flags |= O_CREAT | O_TRUNC;
+    }
+    s.data_fd = open(data_file_name, open_flags, S_IWUSR | S_IRUSR);
+    if (s.data_fd < 0) {
+        ALOGE("rpmb_dev: Failed to open rpmb data file, %s: %s\n", data_file_name, strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    if (init) {
+        /* Create new rpmb data file */
+        if (s.header.max_block == 0) {
+            s.header.max_block = 512 - 1;
+        }
+        ret = write(s.data_fd, &s.header, sizeof(s.header));
+        if (ret != sizeof(s.header)) {
+            ALOGE("rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret, strerror(errno));
+            return EXIT_FAILURE;
+        }
+        return EXIT_SUCCESS;
+    }
+
+    ret = read(s.data_fd, &s.header, sizeof(s.header));
+    if (ret != sizeof(s.header)) {
+        ALOGE("rpmb_dev: Failed to read rpmb data file: %d, %s\n", ret, strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    cmdres_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (cmdres_sock < 0) {
+        ALOGE("rpmb_dev: Failed to create command/response socket: %s\n", strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    cmdres_sockaddr.sun_family = AF_UNIX;
+    strncpy(cmdres_sockaddr.sun_path, socket_path, sizeof(cmdres_sockaddr.sun_path));
+
+    ret = bind(cmdres_sock, (struct sockaddr*)&cmdres_sockaddr, sizeof(struct sockaddr_un));
+    if (ret < 0) {
+        ALOGE("rpmb_dev: Failed to bind command/response socket: %s: %s\n", socket_path,
+              strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    ret = listen(cmdres_sock, 1);
+    if (ret < 0) {
+        ALOGE("rpmb_dev: Failed to listen on command/response socket: %s\n", strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    while (true) {
+        int conn_sock = accept(cmdres_sock, NULL, NULL);
+        if (conn_sock < 0) {
+            ALOGE("rpmb_dev: Could not accept connection: %s\n", strerror(errno));
+            return EXIT_FAILURE;
+        }
+        ret = handle_conn(&s, conn_sock);
+        close(conn_sock);
+        if (ret) {
+            ALOGE("rpmb_dev: Connection terminated: %d", ret);
+        }
+    }
+}
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.rc b/trusty/utils/rpmb_dev/rpmb_dev.rc
new file mode 100644
index 0000000..9f60e81
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.rc
@@ -0,0 +1,29 @@
+# RPMB Mock
+on post-fs-data
+    mkdir /data/vendor/ss
+    chown root system /data/vendor/ss
+    chmod 0770 /data/vendor/ss
+    rm /data/vendor/ss/rpmb_sock
+    start rpmb_mock_init
+    start rpmb_mock
+
+    # Storage proxy
+    start storageproxyd
+
+service storageproxyd /vendor/bin/storageproxyd -d /dev/trusty-ipc-dev0 \
+        -r /data/vendor/ss/rpmb_sock -p /data/vendor/ss -t sock
+    class main
+    disabled
+    user root
+
+service rpmb_mock_init /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --init --key "ea df 64 44 ea 65 5d 1c 87 27 d4 20 71 0d 53 42 dd 73 a3 38 63 e1 d7 94 c3 72 a6 ea e0 64 64 e6" --size 2048
+    disabled
+    user system
+    group system
+    oneshot
+
+service rpmb_mock /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --sock /data/vendor/ss/rpmb_sock
+    class main
+    disabled
+    user system
+    group system
diff --git a/trusty/utils/rpmb_dev/rpmb_protocol.h b/trusty/utils/rpmb_dev/rpmb_protocol.h
new file mode 100644
index 0000000..bfcb806
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_protocol.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "rpmb.h" /* For struct rpmb_key */
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_RELIABLE_WRITE_FLAG (1 << 31)
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_CRC (1 << 2)
+#define MMC_RSP_OPCODE (1 << 4)
+#define MMC_CMD_ADTC (1 << 5)
+#define MMC_RSP_SPI_S1 (1 << 7)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
+
+struct rpmb_nonce {
+    uint8_t byte[16];
+};
+
+struct rpmb_u16 {
+    uint8_t byte[2];
+};
+
+struct rpmb_u32 {
+    uint8_t byte[4];
+};
+
+#define RPMB_PACKET_DATA_SIZE (256)
+
+struct rpmb_packet {
+    uint8_t pad[196];
+    struct rpmb_key key_mac;
+    uint8_t data[RPMB_PACKET_DATA_SIZE];
+    struct rpmb_nonce nonce;
+    struct rpmb_u32 write_counter;
+    struct rpmb_u16 address;
+    struct rpmb_u16 block_count;
+    struct rpmb_u16 result;
+    struct rpmb_u16 req_resp;
+};
+
+enum rpmb_request {
+    RPMB_REQ_PROGRAM_KEY = 0x0001,
+    RPMB_REQ_GET_COUNTER = 0x0002,
+    RPMB_REQ_DATA_WRITE = 0x0003,
+    RPMB_REQ_DATA_READ = 0x0004,
+    RPMB_REQ_RESULT_READ = 0x0005,
+};
+
+enum rpmb_response {
+    RPMB_RESP_PROGRAM_KEY = 0x0100,
+    RPMB_RESP_GET_COUNTER = 0x0200,
+    RPMB_RESP_DATA_WRITE = 0x0300,
+    RPMB_RESP_DATA_READ = 0x0400,
+};
+
+enum rpmb_result {
+    RPMB_RES_OK = 0x0000,
+    RPMB_RES_GENERAL_FAILURE = 0x0001,
+    RPMB_RES_AUTH_FAILURE = 0x0002,
+    RPMB_RES_COUNT_FAILURE = 0x0003,
+    RPMB_RES_ADDR_FAILURE = 0x0004,
+    RPMB_RES_WRITE_FAILURE = 0x0005,
+    RPMB_RES_READ_FAILURE = 0x0006,
+    RPMB_RES_NO_AUTH_KEY = 0x0007,
+
+    RPMB_RES_WRITE_COUNTER_EXPIRED = 0x0080,
+};
+
+static inline struct rpmb_u16 rpmb_u16(uint16_t val) {
+    struct rpmb_u16 ret = {{
+            (uint8_t)(val >> 8),
+            (uint8_t)(val >> 0),
+    }};
+    return ret;
+}
+
+static inline struct rpmb_u32 rpmb_u32(uint32_t val) {
+    struct rpmb_u32 ret = {{
+            (uint8_t)(val >> 24),
+            (uint8_t)(val >> 16),
+            (uint8_t)(val >> 8),
+            (uint8_t)(val >> 0),
+    }};
+    return ret;
+}
+
+static inline uint16_t rpmb_get_u16(struct rpmb_u16 u16) {
+    size_t i;
+    uint16_t val;
+
+    val = 0;
+    for (i = 0; i < sizeof(u16.byte); i++)
+        val = val << 8 | u16.byte[i];
+
+    return val;
+}
+
+static inline uint32_t rpmb_get_u32(struct rpmb_u32 u32) {
+    size_t i;
+    uint32_t val;
+
+    val = 0;
+    for (i = 0; i < sizeof(u32.byte); i++)
+        val = val << 8 | u32.byte[i];
+
+    return val;
+}