Merge "set-verity-state: Link to libfs_mgr_binder."
diff --git a/Android.bp b/Android.bp
index c6f6251..0b4a925 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2,5 +2,3 @@
     name: "android_filesystem_config_header",
     srcs: ["include/private/android_filesystem_config.h"],
 }
-
-subdirs = ["*"]
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 51d5755..d6945e3 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -25,6 +25,9 @@
       "name": "libcutils_test"
     },
     {
+      "name": "libmodprobe_tests"
+    },
+    {
       "name": "libprocinfo_test"
     },
     {
diff --git a/adb/Android.bp b/adb/Android.bp
index bd1f124..7c5e3ea 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -114,6 +114,61 @@
     },
 }
 
+// libadbconnection
+// =========================================================
+// libadbconnection_client/server implement the socket handling for jdwp
+// forwarding and the track-jdwp service.
+cc_library {
+    name: "libadbconnection_server",
+    srcs: ["adbconnection/adbconnection_server.cpp"],
+
+    export_include_dirs: ["adbconnection/include"],
+
+    stl: "libc++_static",
+    shared_libs: ["liblog"],
+    static_libs: ["libbase"],
+
+    defaults: ["adbd_defaults", "host_adbd_supported"],
+
+    // Avoid getting duplicate symbol of android::build::getbuildnumber().
+    use_version_lib: false,
+
+    recovery_available: true,
+    compile_multilib: "both",
+}
+
+cc_library {
+    name: "libadbconnection_client",
+    srcs: ["adbconnection/adbconnection_client.cpp"],
+
+    export_include_dirs: ["adbconnection/include"],
+
+    stl: "libc++_static",
+    shared_libs: ["liblog"],
+    static_libs: ["libbase"],
+
+    defaults: ["adbd_defaults"],
+    visibility: [
+        "//art:__subpackages__",
+        "//system/core/adb/apex:__subpackages__",
+    ],
+    apex_available: [
+        "com.android.adbd",
+        "test_com.android.adbd",
+    ],
+
+    // libadbconnection_client doesn't need an embedded build number.
+    use_version_lib: false,
+
+    stubs: {
+        symbol_file: "adbconnection/libadbconnection_client.map.txt",
+        versions: ["1"],
+    },
+
+    host_supported: true,
+    compile_multilib: "both",
+}
+
 // libadb
 // =========================================================
 // These files are compiled for both the host and the device.
@@ -217,6 +272,7 @@
         "libcutils",
         "libcrypto_utils",
         "libcrypto",
+        "liblog",
         "libmdnssd",
         "libdiagnose_usb",
         "libusb",
@@ -357,11 +413,11 @@
     generated_headers: ["platform_tools_version"],
 
     static_libs: [
+        "libadbconnection_server",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
-        "libadbconnection_server",
         "libadbd_auth",
         "libasyncio",
         "libbase",
@@ -410,12 +466,12 @@
     ],
 
     static_libs: [
+        "libadbconnection_server",
         "libadbd_core",
         "libdiagnose_usb",
     ],
 
     shared_libs: [
-        "libadbconnection_server",
         "libadbd_auth",
         "libasyncio",
         "libbase",
@@ -451,7 +507,7 @@
     defaults: ["adbd_defaults", "host_adbd_supported"],
     recovery_available: true,
 
-    // Avoid getting duplicate symbol of android::build::GetBuildNumber().
+    // avoid getting duplicate symbol of android::build::getbuildnumber().
     use_version_lib: false,
 
     // libminadbd wants both, as it's used to build native tests.
@@ -459,11 +515,11 @@
 
     // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library.
     whole_static_libs: [
+        "libadbconnection_server",
         "libadbd_core",
     ],
 
     shared_libs: [
-        "libadbconnection_server",
         "libadbd_auth",
         "libadbd_services",
         "libasyncio",
@@ -499,6 +555,7 @@
 
     stl: "libc++_static",
     static_libs: [
+        "libadbconnection_server",
         "libadbd",
         "libadbd_auth",
         "libadbd_services",
@@ -515,7 +572,6 @@
     ],
 
     shared_libs: [
-        "libadbconnection_server",
         "libcrypto",
     ],
 }
@@ -729,39 +785,3 @@
         "fastdeploy/testdata/sample.cd",
     ],
 }
-
-prebuilt_etc {
-    name: "com.android.adbd.ld.config.txt",
-    src: "apex/ld.config.txt",
-    filename: "ld.config.txt",
-    installable: false,
-}
-
-apex {
-    name: "com.android.adbd",
-    manifest: "apex/apex_manifest.json",
-
-    binaries: ["adbd"],
-    prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"],
-
-    key: "com.android.adbd.key",
-    certificate: ":com.android.adbd.certificate",
-}
-
-apex_key {
-    name: "com.android.adbd.key",
-    public_key: "apex/com.android.adbd.avbpubkey",
-    private_key: "apex/com.android.adbd.pem",
-}
-
-android_app_certificate {
-    name: "com.android.adbd.certificate",
-    certificate: "apex/com.android.adbd",
-}
-
-prebuilt_etc {
-    name: "com.android.adbd.init.rc",
-    src: "apex/adbd.rc",
-    filename: "init.rc",
-    installable: false,
-}
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 9b663be..460ddde 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1167,7 +1167,7 @@
         std::string host;
         int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
         std::string error;
-        if (address.starts_with("vsock:")) {
+        if (address.starts_with("vsock:") || address.starts_with("localfilesystem:")) {
             serial = address;
         } else if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
             SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
diff --git a/adb/adb.h b/adb/adb.h
index c6cb06a..7f7dd0d 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -185,14 +185,7 @@
     } while (0)
 #endif
 
-#if ADB_HOST_ON_TARGET
-/* adb and adbd are coexisting on the target, so use 5038 for adb
- * to avoid conflicting with adbd's usage of 5037
- */
-#define DEFAULT_ADB_PORT 5038
-#else
 #define DEFAULT_ADB_PORT 5037
-#endif
 
 #define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
 
@@ -200,7 +193,7 @@
 #define ADB_SUBCLASS 0x42
 #define ADB_PROTOCOL 0x1
 
-void local_init(int port);
+void local_init(const std::string& addr);
 bool local_connect(int port);
 int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
 
diff --git a/adb/adbconnection/.clang-format b/adb/adbconnection/.clang-format
new file mode 120000
index 0000000..e545823
--- /dev/null
+++ b/adb/adbconnection/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-2
\ No newline at end of file
diff --git a/adb/adbconnection/adbconnection_client.cpp b/adb/adbconnection/adbconnection_client.cpp
new file mode 100644
index 0000000..ee48abb
--- /dev/null
+++ b/adb/adbconnection/adbconnection_client.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2020 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 "adbconnection/client.h"
+
+#include <pwd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <memory>
+#include <optional>
+
+#include <android-base/cmsg.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+static constexpr char kJdwpControlName[] = "\0jdwp-control";
+
+struct AdbConnectionClientContext {
+  unique_fd control_socket_;
+};
+
+bool SocketPeerIsTrusted(int fd) {
+  ucred cr;
+  socklen_t cr_length = sizeof(cr);
+  if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_length) != 0) {
+    PLOG(ERROR) << "couldn't get socket credentials";
+    return false;
+  }
+
+  passwd* shell = getpwnam("shell");
+  if (cr.uid != 0 && cr.uid != shell->pw_uid) {
+    LOG(ERROR) << "untrusted uid " << cr.uid << " on other end of socket";
+    return false;
+  }
+
+  return true;
+}
+
+AdbConnectionClientContext* adbconnection_client_new(
+    const AdbConnectionClientInfo* const* info_elems, size_t info_count) {
+  auto ctx = std::make_unique<AdbConnectionClientContext>();
+
+  std::optional<uint64_t> pid;
+  std::optional<bool> debuggable;
+
+  for (size_t i = 0; i < info_count; ++i) {
+    auto info = info_elems[i];
+    switch (info->type) {
+      case AdbConnectionClientInfoType::pid:
+        if (pid) {
+          LOG(ERROR) << "multiple pid entries in AdbConnectionClientInfo, ignoring";
+          continue;
+        }
+        pid = info->data.pid;
+        break;
+
+      case AdbConnectionClientInfoType::debuggable:
+        if (debuggable) {
+          LOG(ERROR) << "multiple debuggable entries in AdbConnectionClientInfo, ignoring";
+          continue;
+        }
+        debuggable = info->data.pid;
+        break;
+    }
+  }
+
+  if (!pid) {
+    LOG(ERROR) << "AdbConnectionClientInfo missing required field pid";
+    return nullptr;
+  }
+
+  if (!debuggable) {
+    LOG(ERROR) << "AdbConnectionClientInfo missing required field debuggable";
+    return nullptr;
+  }
+
+  ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0));
+  if (ctx->control_socket_ < 0) {
+    PLOG(ERROR) << "failed to create Unix domain socket";
+    return nullptr;
+  }
+
+  struct timeval timeout;
+  timeout.tv_sec = 1;
+  timeout.tv_usec = 0;
+  setsockopt(ctx->control_socket_.get(), SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
+
+  sockaddr_un addr = {};
+  addr.sun_family = AF_UNIX;
+  memcpy(addr.sun_path, kJdwpControlName, sizeof(kJdwpControlName));
+  size_t addr_len = offsetof(sockaddr_un, sun_path) + sizeof(kJdwpControlName) - 1;
+
+  int rc = connect(ctx->control_socket_.get(), reinterpret_cast<sockaddr*>(&addr), addr_len);
+  if (rc != 0) {
+    PLOG(ERROR) << "failed to connect to jdwp control socket";
+    return nullptr;
+  }
+
+  bool trusted = SocketPeerIsTrusted(ctx->control_socket_.get());
+  if (!trusted) {
+    LOG(ERROR) << "adb socket is not trusted, aborting connection";
+    return nullptr;
+  }
+
+  uint32_t pid_u32 = static_cast<uint32_t>(*pid);
+  rc = TEMP_FAILURE_RETRY(write(ctx->control_socket_.get(), &pid_u32, sizeof(pid_u32)));
+  if (rc != sizeof(pid_u32)) {
+    PLOG(ERROR) << "failed to send JDWP process pid to adbd";
+  }
+
+  return ctx.release();
+}
+
+void adbconnection_client_destroy(AdbConnectionClientContext* ctx) {
+  delete ctx;
+}
+
+int adbconnection_client_pollfd(AdbConnectionClientContext* ctx) {
+  return ctx->control_socket_.get();
+}
+
+int adbconnection_client_receive_jdwp_fd(AdbConnectionClientContext* ctx) {
+  char dummy;
+  unique_fd jdwp_fd;
+  ssize_t rc = android::base::ReceiveFileDescriptors(ctx->control_socket_, &dummy, 1, &jdwp_fd);
+  if (rc != 1) {
+    return rc;
+  }
+  return jdwp_fd.release();
+}
diff --git a/adb/adbconnection/adbconnection_server.cpp b/adb/adbconnection/adbconnection_server.cpp
new file mode 100644
index 0000000..939da2f
--- /dev/null
+++ b/adb/adbconnection/adbconnection_server.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 "adbconnection/server.h"
+
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+#define JDWP_CONTROL_NAME "\0jdwp-control"
+#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
+
+static_assert(JDWP_CONTROL_NAME_LEN <= sizeof(reinterpret_cast<sockaddr_un*>(0)->sun_path));
+
+// Listen for incoming jdwp clients forever.
+void adbconnection_listen(void (*callback)(int fd, pid_t pid)) {
+  sockaddr_un addr = {};
+  socklen_t addrlen = JDWP_CONTROL_NAME_LEN + sizeof(addr.sun_family);
+
+  addr.sun_family = AF_UNIX;
+  memcpy(addr.sun_path, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
+
+  unique_fd s(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
+  if (s < 0) {
+    PLOG(ERROR) << "failed to create JDWP control socket";
+    return;
+  }
+
+  if (bind(s.get(), reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
+    PLOG(ERROR) << "failed to bind JDWP control socket";
+    return;
+  }
+
+  if (listen(s.get(), 4) < 0) {
+    PLOG(ERROR) << "failed to listen on JDWP control socket";
+    return;
+  }
+
+  std::vector<unique_fd> pending_connections;
+
+  unique_fd epfd(epoll_create1(EPOLL_CLOEXEC));
+  std::array<epoll_event, 16> events;
+
+  events[0].events = EPOLLIN;
+  events[0].data.fd = -1;
+  if (epoll_ctl(epfd.get(), EPOLL_CTL_ADD, s.get(), &events[0]) != 0) {
+    LOG(FATAL) << "failed to register event with epoll fd";
+  }
+
+  while (true) {
+    int epoll_rc = TEMP_FAILURE_RETRY(epoll_wait(epfd.get(), events.data(), events.size(), -1));
+    if (epoll_rc == -1) {
+      PLOG(FATAL) << "epoll_wait failed";
+    }
+
+    for (int i = 0; i < epoll_rc; ++i) {
+      const epoll_event& event = events[i];
+      if (event.data.fd == -1) {
+        unique_fd client(
+            TEMP_FAILURE_RETRY(accept4(s.get(), nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC)));
+
+        if (client == -1) {
+          PLOG(WARNING) << "failed to accept client on JDWP control socket";
+          continue;
+        }
+
+        epoll_event register_event;
+        register_event.events = EPOLLIN;
+        register_event.data.fd = client.get();
+
+        if (epoll_ctl(epfd.get(), EPOLL_CTL_ADD, client.get(), &register_event) != 0) {
+          PLOG(FATAL) << "failed to register JDWP client with epoll";
+        }
+
+        pending_connections.emplace_back(std::move(client));
+      } else {
+        // n^2, but the backlog should be short.
+        auto it = std::find_if(pending_connections.begin(), pending_connections.end(),
+                               [&](const unique_fd& fd) { return fd.get() == event.data.fd; });
+
+        if (it == pending_connections.end()) {
+          LOG(FATAL) << "failed to find JDWP client (" << event.data.fd
+                     << ") in pending connections";
+        }
+
+        // Massively oversized buffer: we're expecting an int32_t from the other end.
+        char buf[32];
+        int rc = TEMP_FAILURE_RETRY(recv(it->get(), buf, sizeof(buf), MSG_DONTWAIT));
+        if (rc != 4) {
+          LOG(ERROR) << "received data of incorrect size from JDWP client: read " << rc
+                     << ", expected 4";
+        } else {
+          int32_t pid;
+          memcpy(&pid, buf, sizeof(pid));
+          callback(it->release(), static_cast<pid_t>(pid));
+        }
+
+        if (epoll_ctl(epfd.get(), EPOLL_CTL_DEL, event.data.fd, nullptr) != 0) {
+          LOG(FATAL) << "failed to delete fd from JDWP epoll fd";
+        }
+
+        pending_connections.erase(it);
+      }
+    }
+  }
+}
diff --git a/adb/adbconnection/include/adbconnection/client.h b/adb/adbconnection/include/adbconnection/client.h
new file mode 100644
index 0000000..692fea0
--- /dev/null
+++ b/adb/adbconnection/include/adbconnection/client.h
@@ -0,0 +1,55 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+extern "C" {
+
+struct AdbConnectionClientContext;
+
+enum AdbConnectionClientInfoType {
+  pid,
+  debuggable,
+};
+
+struct AdbConnectionClientInfo {
+  AdbConnectionClientInfoType type;
+  union {
+    uint64_t pid;
+    bool debuggable;
+  } data;
+};
+
+// Construct a context and connect to adbd.
+// Returns null if we fail to connect to adbd.
+AdbConnectionClientContext* adbconnection_client_new(
+    const AdbConnectionClientInfo* const* info_elems, size_t info_count);
+
+void adbconnection_client_destroy(AdbConnectionClientContext* ctx);
+
+// Get an fd which can be polled upon to detect when a jdwp socket is available.
+// You do not own this fd. Do not close it.
+int adbconnection_client_pollfd(AdbConnectionClientContext* ctx);
+
+// Receive a jdwp client fd.
+// Ownership is transferred to the caller of this function.
+int adbconnection_client_receive_jdwp_fd(AdbConnectionClientContext* ctx);
+}
diff --git a/adb/adbconnection/include/adbconnection/server.h b/adb/adbconnection/include/adbconnection/server.h
new file mode 100644
index 0000000..57ca6cd
--- /dev/null
+++ b/adb/adbconnection/include/adbconnection/server.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+extern "C" {
+
+void adbconnection_listen(void (*callback)(int fd, pid_t pid));
+}
diff --git a/adb/adbconnection/libadbconnection_client.map.txt b/adb/adbconnection/libadbconnection_client.map.txt
new file mode 100644
index 0000000..153a0e4
--- /dev/null
+++ b/adb/adbconnection/libadbconnection_client.map.txt
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LIBADBCONNECTION_CLIENT_1 {
+  global:
+    adbconnection_client_new;
+    adbconnection_client_destroy;
+    adbconnection_client_pollfd;
+    adbconnection_client_receive_jdwp_fd;
+  local:
+    *;
+};
diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp
new file mode 100644
index 0000000..75c4ed9
--- /dev/null
+++ b/adb/apex/Android.bp
@@ -0,0 +1,55 @@
+apex_defaults {
+    name: "com.android.adbd-defaults",
+
+    binaries: ["adbd"],
+    compile_multilib: "both",
+    multilib: {
+        both: {
+            native_shared_libs: ["libadbconnection_client"],
+        },
+    },
+    prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"],
+
+    key: "com.android.adbd.key",
+    certificate: ":com.android.adbd.certificate",
+}
+
+apex {
+    name: "com.android.adbd",
+    defaults: ["com.android.adbd-defaults"],
+    manifest: "apex_manifest.json",
+}
+
+// adbd apex with INT_MAX version code, to allow for upgrade/rollback testing.
+apex {
+    name: "test_com.android.adbd",
+    defaults: ["com.android.adbd-defaults"],
+    manifest: "test_apex_manifest.json",
+    file_contexts: ":com.android.adbd-file_contexts",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "com.android.adbd.ld.config.txt",
+    src: "ld.config.txt",
+    filename: "ld.config.txt",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "com.android.adbd.init.rc",
+    src: "adbd.rc",
+    filename: "init.rc",
+    installable: false,
+}
+
+apex_key {
+    name: "com.android.adbd.key",
+    public_key: "com.android.adbd.avbpubkey",
+    private_key: "com.android.adbd.pem",
+}
+
+android_app_certificate {
+    name: "com.android.adbd.certificate",
+    certificate: "com.android.adbd",
+}
diff --git a/adb/apex/ld.config.txt b/adb/apex/ld.config.txt
index 85f9b29..d1858a4 100644
--- a/adb/apex/ld.config.txt
+++ b/adb/apex/ld.config.txt
@@ -5,22 +5,16 @@
 dir.adbd = /apex/com.android.adbd/bin/
 
 [adbd]
-additional.namespaces = platform,art,adbd
+additional.namespaces = platform,art
 
 namespace.default.isolated = true
-namespace.default.links = art,adbd,platform
+namespace.default.search.paths = /apex/com.android.adbd/${LIB}
+namespace.default.asan.search.paths = /apex/com.android.adbd/${LIB}
+namespace.default.permitted.paths = /system/${LIB}
+namespace.default.asan.permitted.paths = /system/${LIB}
+namespace.default.links = art,platform
 namespace.default.link.art.shared_libs = libadbconnection_server.so
-namespace.default.link.platform.allow_all_shared_libs = true
-namespace.default.link.adbd.allow_all_shared_libs = true
-
-###############################################################################
-# "adbd" APEX namespace
-###############################################################################
-namespace.adbd.isolated = true
-namespace.adbd.search.paths = /apex/com.android.adbd/${LIB}
-namespace.adbd.asan.search.paths = /apex/com.android.adbd/${LIB}
-namespace.adbd.links = platform
-namespace.adbd.link.platform.allow_all_shared_libs = true
+namespace.default.link.platform.shared_libs = libc.so:libdl.so:libm.so:libclang_rt.hwasan-aarch64-android.so
 
 ###############################################################################
 # "art" APEX namespace: used for libadbdconnection_server
diff --git a/adb/apex/test_apex_manifest.json b/adb/apex/test_apex_manifest.json
new file mode 100644
index 0000000..7131977
--- /dev/null
+++ b/adb/apex/test_apex_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.adbd",
+  "version": 2147483647
+}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 813a8a9..a6d7e31 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -790,6 +790,15 @@
                        service_string);
 }
 
+static int adb_shell_noinput(int argc, const char** argv) {
+#if !defined(_WIN32)
+    unique_fd fd(adb_open("/dev/null", O_RDONLY));
+    CHECK_NE(STDIN_FILENO, fd.get());
+    dup2(fd.get(), STDIN_FILENO);
+#endif
+    return adb_shell(argc, argv);
+}
+
 static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
     std::string error;
     unique_fd out_fd(adb_connect(android::base::StringPrintf("sideload:%d", size), &error));
@@ -1116,8 +1125,8 @@
         return false;
     }
 
+    fwrite(buf, 1, sizeof(buf) - bytes_left, stdout);
     fflush(stdout);
-    WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
     if (cur != buf && strstr(buf, "restarting") == nullptr) {
         return true;
     }
@@ -1612,7 +1621,7 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) error_exit("usage: adb connect HOST[:PORT>]");
+        if (argc != 2) error_exit("usage: adb connect HOST[:PORT]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
@@ -1711,7 +1720,7 @@
         if (CanUseFeature(features, kFeatureRemountShell)) {
             std::vector<const char*> args = {"shell"};
             args.insert(args.cend(), argv, argv + argc);
-            return adb_shell(args.size(), args.data());
+            return adb_shell_noinput(args.size(), args.data());
         } else if (argc > 1) {
             auto command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
             return adb_connect_command(command);
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 0c5c28f..e5ffe4c 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -129,7 +129,7 @@
     }
 
     if (!getenv("ADB_EMU") || strcmp(getenv("ADB_EMU"), "0") != 0) {
-        local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+        local_init(android::base::StringPrintf("tcp:%d", DEFAULT_ADB_LOCAL_TRANSPORT_PORT));
     }
 
     std::string error;
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 2e84ce6..ec4ab4a 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -16,36 +16,72 @@
 
 #define TRACE_TAG AUTH
 
-#include "adb.h"
-#include "adb_auth.h"
-#include "adb_io.h"
-#include "fdevent/fdevent.h"
 #include "sysdeps.h"
-#include "transport.h"
 
 #include <resolv.h>
 #include <stdio.h>
 #include <string.h>
-#include <iomanip>
 
 #include <algorithm>
+#include <iomanip>
+#include <map>
 #include <memory>
 
 #include <adbd_auth.h>
 #include <android-base/file.h>
+#include <android-base/no_destructor.h>
 #include <android-base/strings.h>
 #include <crypto_utils/android_pubkey.h>
 #include <openssl/obj_mac.h>
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
 
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_io.h"
+#include "fdevent/fdevent.h"
+#include "transport.h"
+#include "types.h"
+
 static AdbdAuthContext* auth_ctx;
 
 static void adb_disconnected(void* unused, atransport* t);
 static struct adisconnect adb_disconnect = {adb_disconnected, nullptr};
 
+static android::base::NoDestructor<std::map<uint32_t, weak_ptr<atransport>>> transports;
+static uint32_t transport_auth_id = 0;
+
 bool auth_required = true;
 
+static void* transport_to_callback_arg(atransport* transport) {
+    uint32_t id = transport_auth_id++;
+    (*transports)[id] = transport->weak();
+    return reinterpret_cast<void*>(id);
+}
+
+static atransport* transport_from_callback_arg(void* id) {
+    uint64_t id_u64 = reinterpret_cast<uint64_t>(id);
+    if (id_u64 > std::numeric_limits<uint32_t>::max()) {
+        LOG(FATAL) << "transport_from_callback_arg called on out of range value: " << id_u64;
+    }
+
+    uint32_t id_u32 = static_cast<uint32_t>(id_u64);
+    auto it = transports->find(id_u32);
+    if (it == transports->end()) {
+        LOG(ERROR) << "transport_from_callback_arg failed to find transport for id " << id_u32;
+        return nullptr;
+    }
+
+    atransport* t = it->second.get();
+    if (!t) {
+        LOG(WARNING) << "transport_from_callback_arg found already destructed transport";
+        return nullptr;
+    }
+
+    transports->erase(it);
+    return t;
+}
+
 static void IteratePublicKeys(std::function<bool(std::string_view public_key)> f) {
     adbd_auth_get_public_keys(
             auth_ctx,
@@ -111,9 +147,16 @@
 
 static void adbd_auth_key_authorized(void* arg, uint64_t id) {
     LOG(INFO) << "adb client authorized";
-    auto* transport = static_cast<atransport*>(arg);
-    transport->auth_id = id;
-    adbd_auth_verified(transport);
+    fdevent_run_on_main_thread([=]() {
+        LOG(INFO) << "arg = " << reinterpret_cast<uintptr_t>(arg);
+        auto* transport = transport_from_callback_arg(arg);
+        if (!transport) {
+            LOG(ERROR) << "authorization received for deleted transport, ignoring";
+            return;
+        }
+        transport->auth_id = id;
+        adbd_auth_verified(transport);
+    });
 }
 
 void adbd_auth_init(void) {
@@ -158,7 +201,8 @@
 void adbd_auth_confirm_key(atransport* t) {
     LOG(INFO) << "prompting user to authorize key";
     t->AddDisconnect(&adb_disconnect);
-    adbd_auth_prompt_user(auth_ctx, t->auth_key.data(), t->auth_key.size(), t);
+    adbd_auth_prompt_user(auth_ctx, t->auth_key.data(), t->auth_key.size(),
+                          transport_to_callback_arg(t));
 }
 
 void adbd_notify_framework_connected_key(atransport* t) {
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 7277cc8..3322574 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -32,11 +32,13 @@
 #include <sys/prctl.h>
 
 #include <memory>
+#include <vector>
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
 #if defined(__ANDROID__)
 #include <libminijail.h>
@@ -51,6 +53,7 @@
 #include "adb_auth.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
+#include "socket_spec.h"
 #include "transport.h"
 
 #include "mdns.h"
@@ -179,12 +182,26 @@
 }
 #endif
 
-static void setup_port(int port) {
-    LOG(INFO) << "adbd listening on port " << port;
-    local_init(port);
+static void setup_adb(const std::vector<std::string>& addrs) {
 #if defined(__ANDROID__)
+    // Get the first valid port from addrs and setup mDNS.
+    int port = -1;
+    std::string error;
+    for (const auto& addr : addrs) {
+        port = get_host_socket_spec_port(addr, &error);
+        if (port != -1) {
+            break;
+        }
+    }
+    if (port == -1) {
+        port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+    }
     setup_mdns(port);
 #endif
+    for (const auto& addr : addrs) {
+        LOG(INFO) << "adbd listening on " << addr;
+        local_init(addr);
+    }
 }
 
 int adbd_main(int server_port) {
@@ -248,25 +265,38 @@
     // If one of these properties is set, also listen on that port.
     // If one of the properties isn't set and we couldn't listen on usb, listen
     // on the default port.
-    std::string prop_port = android::base::GetProperty("service.adb.tcp.port", "");
-    if (prop_port.empty()) {
-        prop_port = android::base::GetProperty("persist.adb.tcp.port", "");
-    }
+    std::vector<std::string> addrs;
+    std::string prop_addr = android::base::GetProperty("service.adb.listen_addrs", "");
+    if (prop_addr.empty()) {
+        std::string prop_port = android::base::GetProperty("service.adb.tcp.port", "");
+        if (prop_port.empty()) {
+            prop_port = android::base::GetProperty("persist.adb.tcp.port", "");
+        }
 
 #if !defined(__ANDROID__)
-    if (prop_port.empty() && getenv("ADBD_PORT")) {
-        prop_port = getenv("ADBD_PORT");
-    }
+        if (prop_port.empty() && getenv("ADBD_PORT")) {
+            prop_port = getenv("ADBD_PORT");
+        }
 #endif
 
-    int port;
-    if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
-        D("using port=%d", port);
-        // Listen on TCP port specified by service.adb.tcp.port property.
-        setup_port(port);
-    } else if (!is_usb) {
-        // Listen on default port.
-        setup_port(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+        int port;
+        if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
+            D("using tcp port=%d", port);
+            // Listen on TCP and VSOCK port specified by service.adb.tcp.port property.
+            addrs.push_back(android::base::StringPrintf("tcp:%d", port));
+            addrs.push_back(android::base::StringPrintf("vsock:%d", port));
+            setup_adb(addrs);
+        } else if (!is_usb) {
+            // Listen on default port.
+            addrs.push_back(
+                    android::base::StringPrintf("tcp:%d", DEFAULT_ADB_LOCAL_TRANSPORT_PORT));
+            addrs.push_back(
+                    android::base::StringPrintf("vsock:%d", DEFAULT_ADB_LOCAL_TRANSPORT_PORT));
+            setup_adb(addrs);
+        }
+    } else {
+        addrs = android::base::Split(prop_addr, ",");
+        setup_adb(addrs);
     }
 
     D("adbd_main(): pre init_jdwp()");
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 0fb14c4..f62032d 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -85,7 +85,6 @@
 #include <paths.h>
 #include <pty.h>
 #include <pwd.h>
-#include <sys/select.h>
 #include <termios.h>
 
 #include <memory>
@@ -141,6 +140,20 @@
     return true;
 }
 
+struct SubprocessPollfds {
+    adb_pollfd pfds[3];
+
+    adb_pollfd* data() { return pfds; }
+    size_t size() { return 3; }
+
+    adb_pollfd* begin() { return pfds; }
+    adb_pollfd* end() { return pfds + size(); }
+
+    adb_pollfd& stdinout_pfd() { return pfds[0]; }
+    adb_pollfd& stderr_pfd() { return pfds[1]; }
+    adb_pollfd& protocol_pfd() { return pfds[2]; }
+};
+
 class Subprocess {
   public:
     Subprocess(std::string command, const char* terminal_type, SubprocessType type,
@@ -176,8 +189,7 @@
     void PassDataStreams();
     void WaitForExit();
 
-    unique_fd* SelectLoop(fd_set* master_read_set_ptr,
-                          fd_set* master_write_set_ptr);
+    unique_fd* PollLoop(SubprocessPollfds* pfds);
 
     // Input/output stream handlers. Success returns nullptr, failure returns
     // a pointer to the failed FD.
@@ -545,23 +557,23 @@
     }
 
     // Start by trying to read from the protocol FD, stdout, and stderr.
-    fd_set master_read_set, master_write_set;
-    FD_ZERO(&master_read_set);
-    FD_ZERO(&master_write_set);
-    for (unique_fd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
-        if (*sfd != -1) {
-            FD_SET(sfd->get(), &master_read_set);
-        }
-    }
+    SubprocessPollfds pfds;
+    pfds.stdinout_pfd() = {.fd = stdinout_sfd_.get(), .events = POLLIN};
+    pfds.stderr_pfd() = {.fd = stderr_sfd_.get(), .events = POLLIN};
+    pfds.protocol_pfd() = {.fd = protocol_sfd_.get(), .events = POLLIN};
 
     // Pass data until the protocol FD or both the subprocess pipes die, at
     // which point we can't pass any more data.
     while (protocol_sfd_ != -1 && (stdinout_sfd_ != -1 || stderr_sfd_ != -1)) {
-        unique_fd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
+        unique_fd* dead_sfd = PollLoop(&pfds);
         if (dead_sfd) {
             D("closing FD %d", dead_sfd->get());
-            FD_CLR(dead_sfd->get(), &master_read_set);
-            FD_CLR(dead_sfd->get(), &master_write_set);
+            auto it = std::find_if(pfds.begin(), pfds.end(), [=](const adb_pollfd& pfd) {
+                return pfd.fd == dead_sfd->get();
+            });
+            CHECK(it != pfds.end());
+            it->fd = -1;
+            it->events = 0;
             if (dead_sfd == &protocol_sfd_) {
                 // Using SIGHUP is a decent general way to indicate that the
                 // controlling process is going away. If specific signals are
@@ -583,30 +595,19 @@
     }
 }
 
-namespace {
-
-inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
-    return sfd != -1 && FD_ISSET(sfd.get(), set);
-}
-
-}   // namespace
-
-unique_fd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
-                                  fd_set* master_write_set_ptr) {
-    fd_set read_set, write_set;
-    int select_n =
-            std::max(std::max(protocol_sfd_.get(), stdinout_sfd_.get()), stderr_sfd_.get()) + 1;
+unique_fd* Subprocess::PollLoop(SubprocessPollfds* pfds) {
     unique_fd* dead_sfd = nullptr;
+    adb_pollfd& stdinout_pfd = pfds->stdinout_pfd();
+    adb_pollfd& stderr_pfd = pfds->stderr_pfd();
+    adb_pollfd& protocol_pfd = pfds->protocol_pfd();
 
-    // Keep calling select() and passing data until an FD closes/errors.
+    // Keep calling poll() and passing data until an FD closes/errors.
     while (!dead_sfd) {
-        memcpy(&read_set, master_read_set_ptr, sizeof(read_set));
-        memcpy(&write_set, master_write_set_ptr, sizeof(write_set));
-        if (select(select_n, &read_set, &write_set, nullptr, nullptr) < 0) {
+        if (adb_poll(pfds->data(), pfds->size(), -1) < 0) {
             if (errno == EINTR) {
                 continue;
             } else {
-                PLOG(ERROR) << "select failed, closing subprocess pipes";
+                PLOG(ERROR) << "poll failed, closing subprocess pipes";
                 stdinout_sfd_.reset(-1);
                 stderr_sfd_.reset(-1);
                 return nullptr;
@@ -614,34 +615,47 @@
         }
 
         // Read stdout, write to protocol FD.
-        if (ValidAndInSet(stdinout_sfd_, &read_set)) {
+        if (stdinout_pfd.fd != -1 && (stdinout_pfd.revents & POLLIN)) {
             dead_sfd = PassOutput(&stdinout_sfd_, ShellProtocol::kIdStdout);
         }
 
         // Read stderr, write to protocol FD.
-        if (!dead_sfd && ValidAndInSet(stderr_sfd_, &read_set)) {
+        if (!dead_sfd && stderr_pfd.fd != 1 && (stderr_pfd.revents & POLLIN)) {
             dead_sfd = PassOutput(&stderr_sfd_, ShellProtocol::kIdStderr);
         }
 
         // Read protocol FD, write to stdin.
-        if (!dead_sfd && ValidAndInSet(protocol_sfd_, &read_set)) {
+        if (!dead_sfd && protocol_pfd.fd != -1 && (protocol_pfd.revents & POLLIN)) {
             dead_sfd = PassInput();
             // If we didn't finish writing, block on stdin write.
             if (input_bytes_left_) {
-                FD_CLR(protocol_sfd_.get(), master_read_set_ptr);
-                FD_SET(stdinout_sfd_.get(), master_write_set_ptr);
+                protocol_pfd.events &= ~POLLIN;
+                stdinout_pfd.events |= POLLOUT;
             }
         }
 
         // Continue writing to stdin; only happens if a previous write blocked.
-        if (!dead_sfd && ValidAndInSet(stdinout_sfd_, &write_set)) {
+        if (!dead_sfd && stdinout_pfd.fd != -1 && (stdinout_pfd.revents & POLLOUT)) {
             dead_sfd = PassInput();
             // If we finished writing, go back to blocking on protocol read.
             if (!input_bytes_left_) {
-                FD_SET(protocol_sfd_.get(), master_read_set_ptr);
-                FD_CLR(stdinout_sfd_.get(), master_write_set_ptr);
+                protocol_pfd.events |= POLLIN;
+                stdinout_pfd.events &= ~POLLOUT;
             }
         }
+
+        // After handling all of the events we've received, check to see if any fds have died.
+        if (stdinout_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+            return &stdinout_sfd_;
+        }
+
+        if (stderr_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+            return &stderr_sfd_;
+        }
+
+        if (protocol_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+            return &protocol_sfd_;
+        }
     }  // while (!dead_sfd)
 
     return dead_sfd;
diff --git a/adb/daemon/transport_qemu.cpp b/adb/daemon/transport_qemu.cpp
index aa760bc..901efee 100644
--- a/adb/daemon/transport_qemu.cpp
+++ b/adb/daemon/transport_qemu.cpp
@@ -18,6 +18,7 @@
 #include <qemu_pipe.h>
 
 #define TRACE_TAG TRANSPORT
+#include "socket_spec.h"
 #include "sysdeps.h"
 #include "transport.h"
 
@@ -55,7 +56,7 @@
  *   the transport registration is completed. That's why we need to send the
  *   'start' request after the transport is registered.
  */
-void qemu_socket_thread(int port) {
+void qemu_socket_thread(std::string_view addr) {
     /* 'accept' request to the adb QEMUD service. */
     static const char _accept_req[] = "accept";
     /* 'start' request to the adb QEMUD service. */
@@ -69,6 +70,12 @@
     adb_thread_setname("qemu socket");
     D("transport: qemu_socket_thread() starting");
 
+    std::string error;
+    int port = get_host_socket_spec_port(addr, &error);
+    if (port == -1) {
+        port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+    }
+
     /* adb QEMUD service connection request. */
     snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
 
@@ -78,7 +85,7 @@
         /* This could be an older version of the emulator, that doesn't
          * implement adb QEMUD service. Fall back to the old TCP way. */
         D("adb service is not available. Falling back to TCP socket.");
-        std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
+        std::thread(server_socket_thread, adb_listen, addr).detach();
         return;
     }
 
diff --git a/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp b/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
index 3dc5e50..932d579 100644
--- a/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
@@ -36,7 +36,7 @@
     FileRegion(borrowed_fd fd, off64_t offset, size_t length)
         : mapped_(android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), offset, length,
                                                           PROT_READ)) {
-        if (mapped_.data() != nullptr) {
+        if (mapped_ != nullptr) {
             return;
         }
 
@@ -50,14 +50,14 @@
         }
     }
 
-    const char* data() const { return mapped_.data() ? mapped_.data() : buffer_.data(); }
-    size_t size() const { return mapped_.data() ? mapped_.size() : buffer_.size(); }
+    const char* data() const { return mapped_ ? mapped_->data() : buffer_.data(); }
+    size_t size() const { return mapped_ ? mapped_->size() : buffer_.size(); }
 
   private:
     FileRegion() = default;
     DISALLOW_COPY_AND_ASSIGN(FileRegion);
 
-    android::base::MappedFile mapped_;
+    std::unique_ptr<android::base::MappedFile> mapped_;
     std::string buffer_;
 };
 }  // namespace
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 9ce443e..d17036c 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -122,6 +122,41 @@
     return true;
 }
 
+int get_host_socket_spec_port(std::string_view spec, std::string* error) {
+    int port;
+    if (spec.starts_with("tcp:")) {
+        if (!parse_tcp_socket_spec(spec, nullptr, &port, nullptr, error)) {
+            return -1;
+        }
+    } else if (spec.starts_with("vsock:")) {
+#if ADB_LINUX
+        std::string spec_str(spec);
+        std::vector<std::string> fragments = android::base::Split(spec_str, ":");
+        if (fragments.size() != 2) {
+            *error = "given vsock server socket string was invalid";
+            return -1;
+        }
+        if (!android::base::ParseInt(fragments[1], &port)) {
+            *error = "could not parse vsock port";
+            errno = EINVAL;
+            return -1;
+        }
+        if (port < 0) {
+            *error = "vsock port was negative.";
+            errno = EINVAL;
+            return -1;
+        }
+#else   // ADB_LINUX
+        *error = "vsock is only supported on linux";
+        return -1;
+#endif  // ADB_LINUX
+    } else {
+        *error = "given socket spec string was invalid";
+        return -1;
+    }
+    return port;
+}
+
 static bool tcp_host_is_local(std::string_view hostname) {
     // FIXME
     return hostname.empty() || hostname == "localhost";
@@ -254,6 +289,14 @@
 
             fd->reset(network_local_client(&address[prefix.length()], it.second.socket_namespace,
                                            SOCK_STREAM, error));
+
+            if (fd->get() < 0) {
+                *error =
+                        android::base::StringPrintf("could not connect to %s address '%s'",
+                                                    it.first.c_str(), std::string(address).c_str());
+                return false;
+            }
+
             if (serial) {
                 *serial = address;
             }
@@ -275,7 +318,11 @@
         }
 
         int result;
+#if ADB_HOST
         if (hostname.empty() && gListenAll) {
+#else
+        if (hostname.empty()) {
+#endif
             result = network_inaddr_any_server(port, SOCK_STREAM, error);
         } else if (tcp_host_is_local(hostname)) {
             result = network_loopback_server(port, SOCK_STREAM, error, true);
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 7cc2fac..94719c8 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -31,3 +31,5 @@
 
 bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
                            std::string* serial, std::string* error);
+
+int get_host_socket_spec_port(std::string_view spec, std::string* error);
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index 3a2f60c..e9d5270 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -18,6 +18,10 @@
 
 #include <string>
 
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 
 TEST(socket_spec, parse_tcp_socket_spec_just_port) {
@@ -88,3 +92,63 @@
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &serial, &error));
 }
+
+TEST(socket_spec, get_host_socket_spec_port) {
+    std::string error;
+    EXPECT_EQ(5555, get_host_socket_spec_port("tcp:5555", &error));
+    EXPECT_EQ(5555, get_host_socket_spec_port("tcp:localhost:5555", &error));
+    EXPECT_EQ(5555, get_host_socket_spec_port("tcp:[::1]:5555", &error));
+    EXPECT_EQ(5555, get_host_socket_spec_port("vsock:5555", &error));
+}
+
+TEST(socket_spec, get_host_socket_spec_port_no_port) {
+    std::string error;
+    EXPECT_EQ(5555, get_host_socket_spec_port("tcp:localhost", &error));
+    EXPECT_EQ(-1, get_host_socket_spec_port("vsock:localhost", &error));
+}
+
+TEST(socket_spec, get_host_socket_spec_port_bad_ports) {
+    std::string error;
+    EXPECT_EQ(-1, get_host_socket_spec_port("tcp:65536", &error));
+    EXPECT_EQ(-1, get_host_socket_spec_port("tcp:-5", &error));
+    EXPECT_EQ(-1, get_host_socket_spec_port("vsock:-5", &error));
+    EXPECT_EQ(-1, get_host_socket_spec_port("vsock:5:5555", &error));
+}
+
+TEST(socket_spec, get_host_socket_spec_port_bad_string) {
+    std::string error;
+    EXPECT_EQ(-1, get_host_socket_spec_port("tcpz:5555", &error));
+    EXPECT_EQ(-1, get_host_socket_spec_port("vsockz:5555", &error));
+    EXPECT_EQ(-1, get_host_socket_spec_port("abcd:5555", &error));
+    EXPECT_EQ(-1, get_host_socket_spec_port("abcd", &error));
+}
+
+TEST(socket_spec, socket_spec_listen_connect_tcp) {
+    std::string error, serial;
+    int port;
+    unique_fd server_fd, client_fd;
+    EXPECT_FALSE(socket_spec_connect(&client_fd, "tcp:localhost:7777", &port, &serial, &error));
+    server_fd.reset(socket_spec_listen("tcp:7777", &error, &port));
+    EXPECT_NE(server_fd.get(), -1);
+    EXPECT_TRUE(socket_spec_connect(&client_fd, "tcp:localhost:7777", &port, &serial, &error));
+    EXPECT_NE(client_fd.get(), -1);
+}
+
+TEST(socket_spec, socket_spec_listen_connect_localfilesystem) {
+    std::string error, serial;
+    int port;
+    unique_fd server_fd, client_fd;
+    TemporaryDir sock_dir;
+
+    // Only run this test if the created directory is writable.
+    int result = access(sock_dir.path, W_OK);
+    if (result == 0) {
+        std::string sock_addr =
+                android::base::StringPrintf("localfilesystem:%s/af_unix_socket", sock_dir.path);
+        EXPECT_FALSE(socket_spec_connect(&client_fd, sock_addr, &port, &serial, &error));
+        server_fd.reset(socket_spec_listen(sock_addr, &error, &port));
+        EXPECT_NE(server_fd.get(), -1);
+        EXPECT_TRUE(socket_spec_connect(&client_fd, sock_addr, &port, &serial, &error));
+        EXPECT_NE(client_fd.get(), -1);
+    }
+}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 7d5bf17..423af67 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -625,7 +625,8 @@
         return true;
     };
 
-    static constexpr std::string_view prefixes[] = {"usb:", "product:", "model:", "device:"};
+    static constexpr std::string_view prefixes[] = {
+            "usb:", "product:", "model:", "device:", "localfilesystem:"};
     for (std::string_view prefix : prefixes) {
         if (command.starts_with(prefix)) {
             consume(prefix.size());
diff --git a/adb/test_device.py b/adb/test_device.py
index 57925e8..083adce 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -536,6 +536,36 @@
         for i, success in result.iteritems():
             self.assertTrue(success)
 
+    def disabled_test_parallel(self):
+        """Spawn a bunch of `adb shell` instances in parallel.
+
+        This was broken historically due to the use of select, which only works
+        for fds that are numerically less than 1024.
+
+        Bug: http://b/141955761"""
+
+        n_procs = 2048
+        procs = dict()
+        for i in xrange(0, n_procs):
+            procs[i] = subprocess.Popen(
+                ['adb', 'shell', 'read foo; echo $foo; read rc; exit $rc'],
+                stdin=subprocess.PIPE,
+                stdout=subprocess.PIPE
+            )
+
+        for i in xrange(0, n_procs):
+            procs[i].stdin.write("%d\n" % i)
+
+        for i in xrange(0, n_procs):
+            response = procs[i].stdout.readline()
+            assert(response == "%d\n" % i)
+
+        for i in xrange(0, n_procs):
+            procs[i].stdin.write("%d\n" % (i % 256))
+
+        for i in xrange(0, n_procs):
+            assert(procs[i].wait() == i % 256)
+
 
 class ArgumentEscapingTest(DeviceTest):
     def test_shell_escaping(self):
diff --git a/adb/transport.h b/adb/transport.h
index ea77117..5a750ee 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -38,6 +38,7 @@
 
 #include "adb.h"
 #include "adb_unique_fd.h"
+#include "types.h"
 #include "usb.h"
 
 typedef std::unordered_set<std::string> FeatureSet;
@@ -223,7 +224,7 @@
     Abort,
 };
 
-class atransport {
+class atransport : public enable_weak_from_this<atransport> {
   public:
     // TODO(danalbert): We expose waaaaaaay too much stuff because this was
     // historically just a struct, but making the whole thing a more idiomatic
@@ -246,7 +247,7 @@
     }
     atransport(ConnectionState state = kCsOffline)
         : atransport([](atransport*) { return ReconnectResult::Abort; }, state) {}
-    virtual ~atransport();
+    ~atransport();
 
     int Write(apacket* p);
     void Reset();
@@ -424,11 +425,12 @@
 asocket* create_device_tracker(bool long_output);
 
 #if !ADB_HOST
-unique_fd tcp_listen_inaddr_any(int port, std::string* error);
-void server_socket_thread(std::function<unique_fd(int, std::string*)> listen_func, int port);
+unique_fd adb_listen(std::string_view addr, std::string* error);
+void server_socket_thread(std::function<unique_fd(std::string_view, std::string*)> listen_func,
+                          std::string_view addr);
 
 #if defined(__ANDROID__)
-void qemu_socket_thread(int port);
+void qemu_socket_thread(std::string_view addr);
 bool use_qemu_goldfish();
 #endif
 
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index b9f738d..c726186 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -85,22 +85,6 @@
     return local_connect_arbitrary_ports(port - 1, port, &dummy) == 0;
 }
 
-std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
-                                                    std::string* response) {
-    unique_fd fd;
-    int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    std::string serial;
-    std::string prefix_addr = address.starts_with("vsock:") ? address : "tcp:" + address;
-    if (socket_spec_connect(&fd, prefix_addr, &port, &serial, response)) {
-        close_on_exec(fd);
-        if (!set_tcp_keepalive(fd, 1)) {
-            D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
-        }
-        return std::make_tuple(std::move(fd), port, serial);
-    }
-    return std::make_tuple(unique_fd(), 0, serial);
-}
-
 void connect_device(const std::string& address, std::string* response) {
     if (address.empty()) {
         *response = "empty address";
@@ -110,17 +94,25 @@
     D("connection requested to '%s'", address.c_str());
     unique_fd fd;
     int port;
-    std::string serial;
-    std::tie(fd, port, serial) = tcp_connect(address, response);
+    std::string serial, prefix_addr;
+
+    // If address does not match any socket type, it should default to TCP.
+    if (address.starts_with("vsock:") || address.starts_with("localfilesystem:")) {
+        prefix_addr = address;
+    } else {
+        prefix_addr = "tcp:" + address;
+    }
+
+    socket_spec_connect(&fd, prefix_addr, &port, &serial, response);
     if (fd.get() == -1) {
         return;
     }
-    auto reconnect = [address](atransport* t) {
+    auto reconnect = [prefix_addr](atransport* t) {
         std::string response;
         unique_fd fd;
         int port;
         std::string serial;
-        std::tie(fd, port, serial) = tcp_connect(address, &response);
+        socket_spec_connect(&fd, prefix_addr, &port, &serial, &response);
         if (fd == -1) {
             D("reconnect failed: %s", response.c_str());
             return ReconnectResult::Retry;
@@ -203,7 +195,7 @@
 std::mutex &retry_ports_lock = *new std::mutex;
 std::condition_variable &retry_ports_cond = *new std::condition_variable;
 
-static void client_socket_thread(int) {
+static void client_socket_thread(std::string_view) {
     adb_thread_setname("client_socket_thread");
     D("transport: client_socket_thread() starting");
     PollAllLocalPortsForEmulator();
@@ -248,7 +240,8 @@
 
 #else  // !ADB_HOST
 
-void server_socket_thread(std::function<unique_fd(int, std::string*)> listen_func, int port) {
+void server_socket_thread(std::function<unique_fd(std::string_view, std::string*)> listen_func,
+                          std::string_view addr) {
     adb_thread_setname("server socket");
 
     unique_fd serverfd;
@@ -256,7 +249,7 @@
 
     while (serverfd == -1) {
         errno = 0;
-        serverfd = listen_func(port, &error);
+        serverfd = listen_func(addr, &error);
         if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) {
             D("unrecoverable error: '%s'", error.c_str());
             return;
@@ -276,7 +269,9 @@
             close_on_exec(fd.get());
             disable_tcp_nagle(fd.get());
             std::string serial = android::base::StringPrintf("host-%d", fd.get());
-            register_socket_transport(std::move(fd), std::move(serial), port, 1,
+            // We don't care about port value in "register_socket_transport" as it is used
+            // only from ADB_HOST. "server_socket_thread" is never called from ADB_HOST.
+            register_socket_transport(std::move(fd), std::move(serial), 0, 1,
                                       [](atransport*) { return ReconnectResult::Abort; });
         }
     }
@@ -285,38 +280,30 @@
 
 #endif
 
-unique_fd tcp_listen_inaddr_any(int port, std::string* error) {
-    return unique_fd{network_inaddr_any_server(port, SOCK_STREAM, error)};
-}
-
 #if !ADB_HOST
-static unique_fd vsock_listen(int port, std::string* error) {
-    return unique_fd{
-        socket_spec_listen(android::base::StringPrintf("vsock:%d", port), error, nullptr)
-    };
+unique_fd adb_listen(std::string_view addr, std::string* error) {
+    return unique_fd{socket_spec_listen(addr, error, nullptr)};
 }
 #endif
 
-void local_init(int port) {
+void local_init(const std::string& addr) {
 #if ADB_HOST
     D("transport: local client init");
-    std::thread(client_socket_thread, port).detach();
+    std::thread(client_socket_thread, addr).detach();
     adb_local_transport_max_port_env_override();
 #elif !defined(__ANDROID__)
     // Host adbd.
     D("transport: local server init");
-    std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
-    std::thread(server_socket_thread, vsock_listen, port).detach();
+    std::thread(server_socket_thread, adb_listen, addr).detach();
 #else
     D("transport: local server init");
     // For the adbd daemon in the system image we need to distinguish
     // between the device, and the emulator.
-    if (use_qemu_goldfish()) {
-        std::thread(qemu_socket_thread, port).detach();
+    if (addr.starts_with("tcp:") && use_qemu_goldfish()) {
+        std::thread(qemu_socket_thread, addr).detach();
     } else {
-        std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
+        std::thread(server_socket_thread, adb_listen, addr).detach();
     }
-    std::thread(server_socket_thread, vsock_listen, port).detach();
 #endif // !ADB_HOST
 }
 
diff --git a/adb/types.h b/adb/types.h
index 6b00224..c619fff 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -25,6 +25,7 @@
 
 #include <android-base/logging.h>
 
+#include "fdevent/fdevent.h"
 #include "sysdeps/uio.h"
 
 // Essentially std::vector<char>, except without zero initialization or reallocation.
@@ -245,3 +246,97 @@
     size_t start_index_ = 0;
     std::vector<block_type> chain_;
 };
+
+// An implementation of weak pointers tied to the fdevent run loop.
+//
+// This allows for code to submit a request for an object, and upon receiving
+// a response, know whether the object is still alive, or has been destroyed
+// because of other reasons. We keep a list of living weak_ptrs in each object,
+// and clear the weak_ptrs when the object is destroyed. This is safe, because
+// we require that both the destructor of the referent and the get method on
+// the weak_ptr are executed on the main thread.
+template <typename T>
+struct enable_weak_from_this;
+
+template <typename T>
+struct weak_ptr {
+    weak_ptr() = default;
+    explicit weak_ptr(T* ptr) { reset(ptr); }
+    weak_ptr(const weak_ptr& copy) { reset(copy.get()); }
+
+    weak_ptr(weak_ptr&& move) {
+        reset(move.get());
+        move.reset();
+    }
+
+    ~weak_ptr() { reset(); }
+
+    weak_ptr& operator=(const weak_ptr& copy) {
+        if (&copy == this) {
+            return *this;
+        }
+
+        reset(copy.get());
+        return *this;
+    }
+
+    weak_ptr& operator=(weak_ptr&& move) {
+        if (&move == this) {
+            return *this;
+        }
+
+        reset(move.get());
+        move.reset();
+        return *this;
+    }
+
+    T* get() {
+        check_main_thread();
+        return ptr_;
+    }
+
+    void reset(T* ptr = nullptr) {
+        check_main_thread();
+
+        if (ptr == ptr_) {
+            return;
+        }
+
+        if (ptr_) {
+            ptr_->weak_ptrs_.erase(
+                    std::remove(ptr_->weak_ptrs_.begin(), ptr_->weak_ptrs_.end(), this));
+        }
+
+        ptr_ = ptr;
+        if (ptr_) {
+            ptr_->weak_ptrs_.push_back(this);
+        }
+    }
+
+  private:
+    friend struct enable_weak_from_this<T>;
+    T* ptr_ = nullptr;
+};
+
+template <typename T>
+struct enable_weak_from_this {
+    ~enable_weak_from_this() {
+        if (!weak_ptrs_.empty()) {
+            check_main_thread();
+            for (auto& weak : weak_ptrs_) {
+                weak->ptr_ = nullptr;
+            }
+            weak_ptrs_.clear();
+        }
+    }
+
+    weak_ptr<T> weak() { return weak_ptr<T>(static_cast<T*>(this)); }
+
+    void schedule_deletion() {
+        fdevent_run_on_main_thread([this]() { delete this; });
+    }
+
+  private:
+    friend struct weak_ptr<T>;
+    std::vector<weak_ptr<T>*> weak_ptrs_;
+};
diff --git a/base/Android.bp b/base/Android.bp
index 8351461..a32959b 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -33,6 +33,7 @@
 cc_library_headers {
     name: "libbase_headers",
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     native_bridge_supported: true,
@@ -56,6 +57,7 @@
         "chrono_utils.cpp",
         "cmsg.cpp",
         "file.cpp",
+        "liblog_symbols.cpp",
         "logging.cpp",
         "mapped_file.cpp",
         "parsebool.cpp",
@@ -68,6 +70,10 @@
         "test_utils.cpp",
     ],
 
+    static: {
+        cflags: ["-DNO_LIBLOG_DLSYM"],
+    },
+
     cppflags: ["-Wexit-time-destructors"],
     shared_libs: ["liblog"],
     target: {
@@ -107,6 +113,7 @@
     name: "libbase",
     defaults: ["libbase_defaults"],
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     native_bridge_supported: true,
diff --git a/base/include/android-base/errno_restorer.h b/base/include/android-base/errno_restorer.h
new file mode 100644
index 0000000..1c8597c
--- /dev/null
+++ b/base/include/android-base/errno_restorer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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 "errno.h"
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+class ErrnoRestorer {
+ public:
+  ErrnoRestorer() : saved_errno_(errno) {}
+
+  ~ErrnoRestorer() { errno = saved_errno_; }
+
+  // Allow this object to be used as part of && operation.
+  operator bool() const { return true; }
+
+ private:
+  const int saved_errno_;
+
+  DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 030ef35e..b3f5adb 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -111,6 +111,7 @@
     !(!std::is_convertible_v<const U&, T> ||
      !std::is_convertible_v<const G&, E>) /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(const expected<U, G>& rhs) {
     if (rhs.has_value()) var_ = rhs.value();
     else var_ = unexpected(rhs.error());
@@ -149,6 +150,7 @@
     !(!std::is_convertible_v<const U&, T> ||
      !std::is_convertible_v<const G&, E>) /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(expected<U, G>&& rhs) {
     if (rhs.has_value()) var_ = std::move(rhs.value());
     else var_ = unexpected(std::move(rhs.error()));
@@ -180,6 +182,7 @@
                 !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                 std::is_convertible_v<U&&, T> /* non-explicit */
                 )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
 
   template <class U = T _ENABLE_IF(
@@ -195,6 +198,7 @@
     std::is_constructible_v<E, const G&> &&
     std::is_convertible_v<const G&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(const unexpected<G>& e)
   : var_(std::in_place_index<1>, e.value()) {}
 
@@ -209,6 +213,7 @@
     std::is_constructible_v<E, G&&> &&
     std::is_convertible_v<G&&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(unexpected<G>&& e)
   : var_(std::in_place_index<1>, std::move(e.value())) {}
 
@@ -457,6 +462,7 @@
     std::is_void_v<U> &&
     std::is_convertible_v<const G&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(const expected<U, G>& rhs) {
     if (!rhs.has_value()) var_ = unexpected(rhs.error());
   }
@@ -473,6 +479,7 @@
     std::is_void_v<U> &&
     std::is_convertible_v<const G&&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(expected<U, G>&& rhs) {
     if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
   }
@@ -489,6 +496,7 @@
     std::is_constructible_v<E, const G&> &&
     std::is_convertible_v<const G&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(const unexpected<G>& e)
   : var_(std::in_place_index<1>, e.value()) {}
 
@@ -503,6 +511,7 @@
     std::is_constructible_v<E, G&&> &&
     std::is_convertible_v<G&&, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr expected(unexpected<G>&& e)
   : var_(std::in_place_index<1>, std::move(e.value())) {}
 
@@ -640,6 +649,7 @@
                 std::is_constructible_v<E, Err> &&
                 !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
                 !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
 
   template<class U, class... Args _ENABLE_IF(
@@ -660,6 +670,7 @@
     !std::is_convertible_v<const unexpected<Err>, E> &&
     std::is_convertible_v<Err, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr unexpected(const unexpected<Err>& rhs)
   : val_(rhs.value()) {}
 
@@ -690,6 +701,7 @@
     !std::is_convertible_v<const unexpected<Err>, E> &&
     std::is_convertible_v<Err, E> /* non-explicit */
   )>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr unexpected(unexpected<Err>&& rhs)
   : val_(std::move(rhs.value())) {}
 
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index ab6476c..accc225 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -64,6 +64,7 @@
 #include <memory>
 #include <ostream>
 
+#include "android-base/errno_restorer.h"
 #include "android-base/macros.h"
 
 // Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
@@ -85,7 +86,7 @@
   INFO,
   WARNING,
   ERROR,
-  FATAL_WITHOUT_ABORT,
+  FATAL_WITHOUT_ABORT,  // For loggability tests, this is considered identical to FATAL.
   FATAL,
 };
 
@@ -93,6 +94,8 @@
   DEFAULT,
   MAIN,
   SYSTEM,
+  RADIO,
+  CRASH,
 };
 
 using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
@@ -113,10 +116,8 @@
 
 void DefaultAborter(const char* abort_message);
 
-std::string GetDefaultTag();
 void SetDefaultTag(const std::string& tag);
 
-#ifdef __ANDROID__
 // We expose this even though it is the default because a user that wants to
 // override the default log buffer will have to construct this themselves.
 class LogdLogger {
@@ -129,7 +130,6 @@
  private:
   LogId default_log_id_;
 };
-#endif
 
 // Configure logging based on ANDROID_LOG_TAGS environment variable.
 // We need to parse a string that looks like
@@ -155,27 +155,6 @@
 // Replace the current aborter.
 void SetAborter(AbortFunction&& aborter);
 
-class ErrnoRestorer {
- public:
-  ErrnoRestorer()
-      : saved_errno_(errno) {
-  }
-
-  ~ErrnoRestorer() {
-    errno = saved_errno_;
-  }
-
-  // Allow this object to be used as part of && operation.
-  operator bool() const {
-    return true;
-  }
-
- private:
-  const int saved_errno_;
-
-  DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
-};
-
 // A helper macro that produces an expression that accepts both a qualified name and an
 // unqualified name for a LogSeverity, and returns a LogSeverity value.
 // Note: DO NOT USE DIRECTLY. This is an implementation detail.
@@ -211,8 +190,8 @@
 #define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
 
 // Defines whether the given severity will be logged or silently swallowed.
-#define WOULD_LOG(severity) \
-  (UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \
+#define WOULD_LOG(severity)                                                              \
+  (UNLIKELY(::android::base::ShouldLog(SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL)) || \
    MUST_LOG_MESSAGE(severity))
 
 // Get an ostream that can be used for logging at the given severity and to the default
@@ -222,20 +201,16 @@
 // 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
 //    usage manually.
 // 2) This does not save and restore errno.
-#define LOG_STREAM(severity) LOG_STREAM_TO(DEFAULT, severity)
-
-// Get an ostream that can be used for logging at the given severity and to the
-// given destination. The same notes as for LOG_STREAM apply.
-#define LOG_STREAM_TO(dest, severity)                                           \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest,        \
-                              SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, -1) \
+#define LOG_STREAM(severity)                                                                    \
+  ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, \
+                              -1)                                                               \
       .stream()
 
 // Logs a message to logcat on Android otherwise to stderr. If the severity is
 // FATAL it also causes an abort. For example:
 //
 //     LOG(FATAL) << "We didn't expect to reach here";
-#define LOG(severity) LOG_TO(DEFAULT, severity)
+#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
 
 // Checks if we want to log something, and sets up appropriate RAII objects if
 // so.
@@ -245,21 +220,12 @@
    ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
    ::android::base::ErrnoRestorer())
 
-// Logs a message to logcat with the specified log ID on Android otherwise to
-// stderr. If the severity is FATAL it also causes an abort.
-// Use an expression here so we can support the << operator following the macro,
-// like "LOG(DEBUG) << xxx;".
-#define LOG_TO(dest, severity) LOGGING_PREAMBLE(severity) && LOG_STREAM_TO(dest, severity)
-
 // A variant of LOG that also logs the current errno value. To be used when
 // library calls fail.
-#define PLOG(severity) PLOG_TO(DEFAULT, severity)
-
-// Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity)                                                        \
-  LOGGING_PREAMBLE(severity) &&                                                        \
-      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest,           \
-                                  SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, errno) \
+#define PLOG(severity)                                                           \
+  LOGGING_PREAMBLE(severity) &&                                                  \
+      ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), \
+                                  _LOG_TAG_INTERNAL, errno)                      \
           .stream()
 
 // Marker that code is yet to be implemented.
@@ -272,24 +238,23 @@
 //
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
-#define CHECK(x)                                                                 \
-  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                            \
-      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,  \
-                                  ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
-              .stream()                                                          \
+#define CHECK(x)                                                                                 \
+  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                                            \
+      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, \
+                                  -1)                                                            \
+              .stream()                                                                          \
           << "Check failed: " #x << " "
 
 // clang-format off
 // Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP)                                                                 \
-  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);                           \
-       UNLIKELY(!(_values.lhs OP _values.rhs));                                                \
-       /* empty */)                                                                            \
-  ABORT_AFTER_LOG_FATAL                                                                        \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,                    \
-                              ::android::base::FATAL, _LOG_TAG_INTERNAL, -1)                   \
-          .stream()                                                                            \
-      << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
+#define CHECK_OP(LHS, RHS, OP)                                                                   \
+  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);                             \
+       UNLIKELY(!(_values.lhs OP _values.rhs));                                                  \
+       /* empty */)                                                                              \
+  ABORT_AFTER_LOG_FATAL                                                                          \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+          .stream()                                                                              \
+      << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs   \
       << ", " #RHS "=" << _values.rhs << ") "
 // clang-format on
 
@@ -311,8 +276,8 @@
 #define CHECK_STROP(s1, s2, sense)                                             \
   while (UNLIKELY((strcmp(s1, s2) == 0) != (sense)))                           \
     ABORT_AFTER_LOG_FATAL                                                      \
-    ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,  \
-                                ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+    ::android::base::LogMessage(__FILE__, __LINE__,  ::android::base::FATAL,   \
+                                 _LOG_TAG_INTERNAL, -1)                        \
         .stream()                                                              \
         << "Check failed: " << "\"" << (s1) << "\""                            \
         << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
@@ -431,8 +396,10 @@
 // of a CHECK. The destructor will abort if the severity is FATAL.
 class LogMessage {
  public:
-  LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, const char* tag,
+  // LogId has been deprecated, but this constructor must exist for prebuilts.
+  LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, const char* tag,
              int error);
+  LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, int error);
 
   ~LogMessage();
 
@@ -441,8 +408,8 @@
   std::ostream& stream();
 
   // The routine that performs the actual logging.
-  static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                      const char* tag, const char* msg);
+  static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                      const char* msg);
 
  private:
   const std::unique_ptr<LogMessageData> data_;
@@ -456,6 +423,9 @@
 // Set the minimum severity level for logging, returning the old severity.
 LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
 
+// Return whether or not a log message with the associated tag should be logged.
+bool ShouldLog(LogSeverity severity, const char* tag);
+
 // Allows to temporarily change the minimum severity level for logging.
 class ScopedLogSeverity {
  public:
@@ -474,9 +444,6 @@
 // Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
 //
 // Note: for this to work, we need to have this in a namespace.
-// Note: lots of ifdef magic to make this work with Clang (platform) vs GCC (windows tools)
-// Note: using diagnose_if(true) under Clang and nothing under GCC/mingw as there is no common
-//       attribute support.
 // Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
 //       diagnose_if.
 // Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
@@ -486,8 +453,8 @@
 #pragma clang diagnostic ignored "-Wgcc-compat"
 #define OSTREAM_STRING_POINTER_USAGE_WARNING \
     __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
-inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
-    OSTREAM_STRING_POINTER_USAGE_WARNING {
+inline OSTREAM_STRING_POINTER_USAGE_WARNING
+std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) {
   return stream << static_cast<const void*>(string_pointer);
 }
 #pragma clang diagnostic pop
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
index 6a19f1b..8c37f43 100644
--- a/base/include/android-base/mapped_file.h
+++ b/base/include/android-base/mapped_file.h
@@ -53,7 +53,8 @@
   /**
    * Same thing, but using the raw OS file handle instead of a CRT wrapper.
    */
-  static MappedFile FromOsHandle(os_handle h, off64_t offset, size_t length, int prot);
+  static std::unique_ptr<MappedFile> FromOsHandle(os_handle h, off64_t offset, size_t length,
+                                                  int prot);
 
   /**
    * Removes the mapping.
@@ -69,10 +70,6 @@
   char* data() const { return base_ + offset_; }
   size_t size() const { return size_; }
 
-  bool isValid() const { return base_ != nullptr; }
-
-  explicit operator bool() const { return isValid(); }
-
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
 
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index 1b763af..b6d26e7 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -90,6 +90,7 @@
   ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
 
   template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   operator android::base::expected<T, ResultError>() {
     return android::base::unexpected(ResultError(message_, code_));
   }
@@ -118,9 +119,11 @@
 class Error {
  public:
   Error() : errno_(0), append_errno_(false) {}
+  // NOLINTNEXTLINE(google-explicit-constructor)
   Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
 
   template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   operator android::base::expected<T, ResultError>() {
     return android::base::unexpected(ResultError(str(), errno_));
   }
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 1605daf..c4a0aad 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -116,6 +116,8 @@
   bool operator<(int rhs) const { return get() < rhs; }
   bool operator==(int rhs) const { return get() == rhs; }
   bool operator!=(int rhs) const { return get() != rhs; }
+  bool operator==(const unique_fd_impl& rhs) const { return get() == rhs.get(); }
+  bool operator!=(const unique_fd_impl& rhs) const { return get() != rhs.get(); }
 
   // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
   bool operator!() const = delete;
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
new file mode 100644
index 0000000..d5dfcd2
--- /dev/null
+++ b/base/liblog_symbols.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 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 "liblog_symbols.h"
+
+#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
+#include <dlfcn.h>
+#endif
+
+namespace android {
+namespace base {
+
+#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM)
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions() {
+  static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
+    void* liblog_handle = dlopen("liblog.so", RTLD_NOW);
+    if (liblog_handle == nullptr) {
+      return {};
+    }
+
+    LibLogFunctions real_liblog_functions = {};
+
+#define DLSYM(name)                                                                   \
+  real_liblog_functions.name =                                                        \
+      reinterpret_cast<decltype(LibLogFunctions::name)>(dlsym(liblog_handle, #name)); \
+  if (real_liblog_functions.name == nullptr) {                                        \
+    return {};                                                                        \
+  }
+
+    DLSYM(__android_log_set_logger)
+    DLSYM(__android_log_write_logger_data)
+    DLSYM(__android_log_logd_logger)
+    DLSYM(__android_log_stderr_logger)
+    DLSYM(__android_log_set_aborter)
+    DLSYM(__android_log_call_aborter)
+    DLSYM(__android_log_default_aborter)
+    DLSYM(__android_log_set_minimum_priority);
+    DLSYM(__android_log_get_minimum_priority);
+    DLSYM(__android_log_set_default_tag);
+#undef DLSYM
+
+    return real_liblog_functions;
+  }();
+
+  return liblog_functions;
+}
+
+#else
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions() {
+  static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
+    return LibLogFunctions{
+        .__android_log_set_logger = __android_log_set_logger,
+        .__android_log_write_logger_data = __android_log_write_logger_data,
+        .__android_log_logd_logger = __android_log_logd_logger,
+        .__android_log_stderr_logger = __android_log_stderr_logger,
+        .__android_log_set_aborter = __android_log_set_aborter,
+        .__android_log_call_aborter = __android_log_call_aborter,
+        .__android_log_default_aborter = __android_log_default_aborter,
+        .__android_log_set_minimum_priority = __android_log_set_minimum_priority,
+        .__android_log_get_minimum_priority = __android_log_get_minimum_priority,
+        .__android_log_set_default_tag = __android_log_set_default_tag,
+    };
+  }();
+  return liblog_functions;
+}
+
+#endif
+
+}  // namespace base
+}  // namespace android
diff --git a/base/liblog_symbols.h b/base/liblog_symbols.h
new file mode 100644
index 0000000..d3134e9
--- /dev/null
+++ b/base/liblog_symbols.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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 <optional>
+
+#include <android/log.h>
+
+namespace android {
+namespace base {
+
+struct LibLogFunctions {
+  void (*__android_log_set_logger)(__android_logger_function logger);
+  void (*__android_log_write_logger_data)(struct __android_logger_data* logger_data,
+                                          const char* msg);
+
+  void (*__android_log_logd_logger)(const struct __android_logger_data* logger_data,
+                                    const char* msg);
+  void (*__android_log_stderr_logger)(const struct __android_logger_data* logger_data,
+                                      const char* message);
+
+  void (*__android_log_set_aborter)(__android_aborter_function aborter);
+  void (*__android_log_call_aborter)(const char* abort_message);
+  void (*__android_log_default_aborter)(const char* abort_message);
+  int (*__android_log_set_minimum_priority)(int priority);
+  int (*__android_log_get_minimum_priority)();
+  void (*__android_log_set_default_tag)(const char* tag);
+};
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions();
+
+}  // namespace base
+}  // namespace android
diff --git a/base/logging.cpp b/base/logging.cpp
index f89168c..a868706 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -36,17 +36,18 @@
 #include <sys/uio.h>
 #endif
 
+#include <atomic>
 #include <iostream>
 #include <limits>
 #include <mutex>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
 
-// Headers for LogMessage::LogLine.
-#ifdef __ANDROID__
 #include <android/log.h>
+#ifdef __ANDROID__
 #include <android/set_abort_message.h>
 #else
 #include <sys/types.h>
@@ -59,6 +60,8 @@
 #include <android-base/strings.h>
 #include <android-base/threads.h>
 
+#include "liblog_symbols.h"
+
 namespace android {
 namespace base {
 
@@ -115,11 +118,84 @@
 }
 #endif
 
+static LogId log_id_tToLogId(int buffer_id) {
+  switch (buffer_id) {
+    case LOG_ID_MAIN:
+      return MAIN;
+    case LOG_ID_SYSTEM:
+      return SYSTEM;
+    case LOG_ID_RADIO:
+      return RADIO;
+    case LOG_ID_CRASH:
+      return CRASH;
+    case LOG_ID_DEFAULT:
+    default:
+      return DEFAULT;
+  }
+}
+
+static int LogIdTolog_id_t(LogId log_id) {
+  switch (log_id) {
+    case MAIN:
+      return LOG_ID_MAIN;
+    case SYSTEM:
+      return LOG_ID_SYSTEM;
+    case RADIO:
+      return LOG_ID_RADIO;
+    case CRASH:
+      return LOG_ID_CRASH;
+    case DEFAULT:
+    default:
+      return LOG_ID_DEFAULT;
+  }
+}
+
+static LogSeverity PriorityToLogSeverity(int priority) {
+  switch (priority) {
+    case ANDROID_LOG_DEFAULT:
+      return INFO;
+    case ANDROID_LOG_VERBOSE:
+      return VERBOSE;
+    case ANDROID_LOG_DEBUG:
+      return DEBUG;
+    case ANDROID_LOG_INFO:
+      return INFO;
+    case ANDROID_LOG_WARN:
+      return WARNING;
+    case ANDROID_LOG_ERROR:
+      return ERROR;
+    case ANDROID_LOG_FATAL:
+      return FATAL;
+    default:
+      return FATAL;
+  }
+}
+
+static android_LogPriority LogSeverityToPriority(LogSeverity severity) {
+  switch (severity) {
+    case VERBOSE:
+      return ANDROID_LOG_VERBOSE;
+    case DEBUG:
+      return ANDROID_LOG_DEBUG;
+    case INFO:
+      return ANDROID_LOG_INFO;
+    case WARNING:
+      return ANDROID_LOG_WARN;
+    case ERROR:
+      return ANDROID_LOG_ERROR;
+    case FATAL_WITHOUT_ABORT:
+    case FATAL:
+    default:
+      return ANDROID_LOG_FATAL;
+  }
+}
+
 static std::mutex& LoggingLock() {
   static auto& logging_lock = *new std::mutex();
   return logging_lock;
 }
 
+// Only used for Q fallback.
 static LogFunction& Logger() {
 #ifdef __ANDROID__
   static auto& logger = *new LogFunction(LogdLogger());
@@ -129,35 +205,39 @@
   return logger;
 }
 
+// Only used for Q fallback.
 static AbortFunction& Aborter() {
   static auto& aborter = *new AbortFunction(DefaultAborter);
   return aborter;
 }
 
+// Only used for Q fallback.
 static std::recursive_mutex& TagLock() {
   static auto& tag_lock = *new std::recursive_mutex();
   return tag_lock;
 }
+// Only used for Q fallback.
 static std::string* gDefaultTag;
-std::string GetDefaultTag() {
-  std::lock_guard<std::recursive_mutex> lock(TagLock());
-  if (gDefaultTag == nullptr) {
-    return "";
-  }
-  return *gDefaultTag;
-}
+
 void SetDefaultTag(const std::string& tag) {
-  std::lock_guard<std::recursive_mutex> lock(TagLock());
-  if (gDefaultTag != nullptr) {
-    delete gDefaultTag;
-    gDefaultTag = nullptr;
-  }
-  if (!tag.empty()) {
-    gDefaultTag = new std::string(tag);
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    liblog_functions->__android_log_set_default_tag(tag.c_str());
+  } else {
+    std::lock_guard<std::recursive_mutex> lock(TagLock());
+    if (gDefaultTag != nullptr) {
+      delete gDefaultTag;
+      gDefaultTag = nullptr;
+    }
+    if (!tag.empty()) {
+      gDefaultTag = new std::string(tag);
+    }
   }
 }
 
 static bool gInitialized = false;
+
+// Only used for Q fallback.
 static LogSeverity gMinimumLogSeverity = INFO;
 
 #if defined(__linux__)
@@ -218,8 +298,13 @@
   static_assert(arraysize(log_characters) - 1 == FATAL + 1,
                 "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
-  fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
-          timestamp, getpid(), GetThreadId(), file, line, message);
+  if (file != nullptr) {
+    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
+            timestamp, getpid(), GetThreadId(), file, line, message);
+  } else {
+    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char,
+            timestamp, getpid(), GetThreadId(), message);
+  }
 }
 
 void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
@@ -242,41 +327,35 @@
 }
 
 
-#ifdef __ANDROID__
 LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
 }
 
 void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
                             const char* file, unsigned int line,
                             const char* message) {
-  static constexpr android_LogPriority kLogSeverityToAndroidLogPriority[] = {
-      ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
-      ANDROID_LOG_WARN,    ANDROID_LOG_ERROR, ANDROID_LOG_FATAL,
-      ANDROID_LOG_FATAL,
-  };
-  static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
-                "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity");
-
-  int priority = kLogSeverityToAndroidLogPriority[severity];
+  android_LogPriority priority = LogSeverityToPriority(severity);
   if (id == DEFAULT) {
     id = default_log_id_;
   }
 
-  static constexpr log_id kLogIdToAndroidLogId[] = {
-    LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM,
-  };
-  static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
-                "Mismatch in size of kLogIdToAndroidLogId and values in LogId");
-  log_id lg_id = kLogIdToAndroidLogId[id];
+  int lg_id = LogIdTolog_id_t(id);
 
-  if (priority == ANDROID_LOG_FATAL) {
-    __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line,
-                            message);
+  char log_message[1024];
+  if (priority == ANDROID_LOG_FATAL && file != nullptr) {
+    snprintf(log_message, sizeof(log_message), "%s:%u] %s", file, line, message);
+  } else {
+    snprintf(log_message, sizeof(log_message), "%s", message);
+  }
+
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    __android_logger_data logger_data = {sizeof(__android_logger_data),     lg_id, priority, tag,
+                                         static_cast<const char*>(nullptr), 0};
+    liblog_functions->__android_log_logd_logger(&logger_data, log_message);
   } else {
     __android_log_buf_print(lg_id, priority, tag, "%s", message);
   }
 }
-#endif
 
 void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
   SetLogger(std::forward<LogFunction>(logger));
@@ -307,27 +386,27 @@
     if (spec.size() == 3 && StartsWith(spec, "*:")) {
       switch (spec[2]) {
         case 'v':
-          gMinimumLogSeverity = VERBOSE;
+          SetMinimumLogSeverity(VERBOSE);
           continue;
         case 'd':
-          gMinimumLogSeverity = DEBUG;
+          SetMinimumLogSeverity(DEBUG);
           continue;
         case 'i':
-          gMinimumLogSeverity = INFO;
+          SetMinimumLogSeverity(INFO);
           continue;
         case 'w':
-          gMinimumLogSeverity = WARNING;
+          SetMinimumLogSeverity(WARNING);
           continue;
         case 'e':
-          gMinimumLogSeverity = ERROR;
+          SetMinimumLogSeverity(ERROR);
           continue;
         case 'f':
-          gMinimumLogSeverity = FATAL_WITHOUT_ABORT;
+          SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
           continue;
         // liblog will even suppress FATAL if you say 's' for silent, but that's
         // crazy!
         case 's':
-          gMinimumLogSeverity = FATAL_WITHOUT_ABORT;
+          SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
           continue;
       }
     }
@@ -337,24 +416,56 @@
 }
 
 void SetLogger(LogFunction&& logger) {
-  std::lock_guard<std::mutex> lock(LoggingLock());
-  Logger() = std::move(logger);
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    // We need to atomically swap the old and new pointers since other threads may be logging.
+    // We know all threads will be using the new logger after __android_log_set_logger() returns,
+    // so we can delete it then.
+    // This leaks one std::function<> per instance of libbase if multiple copies of libbase within a
+    // single process call SetLogger().  That is the same cost as having a static
+    // std::function<>, which is the not-thread-safe alternative.
+    static std::atomic<LogFunction*> logger_function(nullptr);
+    auto* old_logger_function = logger_function.exchange(new LogFunction(logger));
+    liblog_functions->__android_log_set_logger([](const struct __android_logger_data* logger_data,
+                                                  const char* message) {
+      auto log_id = log_id_tToLogId(logger_data->buffer_id);
+      auto severity = PriorityToLogSeverity(logger_data->priority);
+
+      auto& function = *logger_function.load(std::memory_order_acquire);
+      function(log_id, severity, logger_data->tag, logger_data->file, logger_data->line, message);
+    });
+    delete old_logger_function;
+  } else {
+    std::lock_guard<std::mutex> lock(LoggingLock());
+    Logger() = std::move(logger);
+  }
 }
 
 void SetAborter(AbortFunction&& aborter) {
-  std::lock_guard<std::mutex> lock(LoggingLock());
-  Aborter() = std::move(aborter);
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    // See the comment in SetLogger().
+    static std::atomic<AbortFunction*> abort_function(nullptr);
+    auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter));
+    __android_log_set_aborter([](const char* abort_message) {
+      auto& function = *abort_function.load(std::memory_order_acquire);
+      function(abort_message);
+    });
+    delete old_abort_function;
+  } else {
+    std::lock_guard<std::mutex> lock(LoggingLock());
+    Aborter() = std::move(aborter);
+  }
 }
 
 // This indirection greatly reduces the stack impact of having lots of
 // checks/logging in a function.
 class LogMessageData {
  public:
-  LogMessageData(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                 const char* tag, int error)
+  LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                 int error)
       : file_(GetFileBasename(file)),
         line_number_(line),
-        id_(id),
         severity_(severity),
         tag_(tag),
         error_(error) {}
@@ -373,10 +484,6 @@
 
   const char* GetTag() const { return tag_; }
 
-  LogId GetId() const {
-    return id_;
-  }
-
   int GetError() const {
     return error_;
   }
@@ -393,7 +500,6 @@
   std::ostringstream buffer_;
   const char* const file_;
   const unsigned int line_number_;
-  const LogId id_;
   const LogSeverity severity_;
   const char* const tag_;
   const int error_;
@@ -401,9 +507,13 @@
   DISALLOW_COPY_AND_ASSIGN(LogMessageData);
 };
 
-LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
+LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
                        const char* tag, int error)
-    : data_(new LogMessageData(file, line, id, severity, tag, error)) {}
+    : LogMessage(file, line, severity, tag, error) {}
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                       int error)
+    : data_(new LogMessageData(file, line, severity, tag, error)) {}
 
 LogMessage::~LogMessage() {
   // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
@@ -429,16 +539,16 @@
     // Do the actual logging with the lock held.
     std::lock_guard<std::mutex> lock(LoggingLock());
     if (msg.find('\n') == std::string::npos) {
-      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(),
-              data_->GetTag(), msg.c_str());
+      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+              msg.c_str());
     } else {
       msg += '\n';
       size_t i = 0;
       while (i < msg.size()) {
         size_t nl = msg.find('\n', i);
         msg[nl] = '\0';
-        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(),
-                data_->GetTag(), &msg[i]);
+        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+                &msg[i]);
         // Undo the zero-termination so we can give the complete message to the aborter.
         msg[nl] = '\n';
         i = nl + 1;
@@ -448,7 +558,12 @@
 
   // Abort if necessary.
   if (data_->GetSeverity() == FATAL) {
-    Aborter()(msg.c_str());
+    static auto& liblog_functions = GetLibLogFunctions();
+    if (liblog_functions) {
+      liblog_functions->__android_log_call_aborter(msg.c_str());
+    } else {
+      Aborter()(msg.c_str());
+    }
   }
 }
 
@@ -456,27 +571,60 @@
   return data_->GetBuffer();
 }
 
-void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
-                         const char* tag, const char* message) {
-  if (tag == nullptr) {
-    std::lock_guard<std::recursive_mutex> lock(TagLock());
-    if (gDefaultTag == nullptr) {
-      gDefaultTag = new std::string(getprogname());
-    }
-    Logger()(id, severity, gDefaultTag->c_str(), file, line, message);
+void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                         const char* message) {
+  static auto& liblog_functions = GetLibLogFunctions();
+  auto priority = LogSeverityToPriority(severity);
+  if (liblog_functions) {
+    __android_logger_data logger_data = {
+        sizeof(__android_logger_data), LOG_ID_DEFAULT, priority, tag, file, line};
+    __android_log_write_logger_data(&logger_data, message);
   } else {
-    Logger()(id, severity, tag, file, line, message);
+    if (tag == nullptr) {
+      std::lock_guard<std::recursive_mutex> lock(TagLock());
+      if (gDefaultTag == nullptr) {
+        gDefaultTag = new std::string(getprogname());
+      }
+
+      Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message);
+    } else {
+      Logger()(DEFAULT, severity, tag, file, line, message);
+    }
   }
 }
 
 LogSeverity GetMinimumLogSeverity() {
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    return PriorityToLogSeverity(liblog_functions->__android_log_get_minimum_priority());
+  } else {
     return gMinimumLogSeverity;
+  }
+}
+
+bool ShouldLog(LogSeverity severity, const char* tag) {
+  static auto& liblog_functions = GetLibLogFunctions();
+  // Even though we're not using the R liblog functions in this function, if we're running on Q,
+  // we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not
+  // take into consideration the value from SetMinimumLogSeverity().
+  if (liblog_functions) {
+    int priority = LogSeverityToPriority(severity);
+    return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
+  } else {
+    return severity >= gMinimumLogSeverity;
+  }
 }
 
 LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
-  LogSeverity old_severity = gMinimumLogSeverity;
-  gMinimumLogSeverity = new_severity;
-  return old_severity;
+  static auto& liblog_functions = GetLibLogFunctions();
+  if (liblog_functions) {
+    auto priority = LogSeverityToPriority(new_severity);
+    return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority));
+  } else {
+    LogSeverity old_severity = gMinimumLogSeverity;
+    gMinimumLogSeverity = new_severity;
+    return old_severity;
+  }
 }
 
 ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 3113fb4..3a453e6 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -140,10 +140,6 @@
   CHECK_WOULD_LOG_ENABLED(FATAL);
 }
 
-TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_disabled) {
-  CHECK_WOULD_LOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
 TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) {
   CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT);
 }
@@ -266,10 +262,6 @@
     CheckMessage(cap2, android::base::severity, "foobar"); \
   } \
 
-TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_disabled) {
-  CHECK_LOG_STREAM_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
 TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
   ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
 }
@@ -352,10 +344,6 @@
   ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar");
 }
 
-TEST(logging, LOG_FATAL_WITHOUT_ABORT_disabled) {
-  CHECK_LOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
 TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
   ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
 }
@@ -508,10 +496,6 @@
   ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar");
 }
 
-TEST(logging, PLOG_FATAL_WITHOUT_ABORT_disabled) {
-  CHECK_PLOG_DISABLED(FATAL_WITHOUT_ABORT);
-}
-
 TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
   ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
 }
@@ -619,21 +603,6 @@
   LOG(ERROR) << "foobar";
 }
 
-TEST(logging, SetDefaultTag) {
-  constexpr const char* expected_tag = "test_tag";
-  constexpr const char* expected_msg = "foobar";
-  CapturedStderr cap;
-  {
-    std::string old_default_tag = android::base::GetDefaultTag();
-    android::base::SetDefaultTag(expected_tag);
-    android::base::ScopedLogSeverity sls(android::base::LogSeverity::INFO);
-    LOG(INFO) << expected_msg;
-    android::base::SetDefaultTag(old_default_tag);
-  }
-  ASSERT_NO_FATAL_FAILURE(
-      CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag));
-}
-
 TEST(logging, StdioLogger) {
   CapturedStderr cap_err;
   CapturedStdout cap_out;
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index 862b73b..fff3453 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -38,15 +38,14 @@
 std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
                                                int prot) {
 #if defined(_WIN32)
-  auto file =
-      FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
+  return FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
 #else
-  auto file = FromOsHandle(fd.get(), offset, length, prot);
+  return FromOsHandle(fd.get(), offset, length, prot);
 #endif
-  return file ? std::make_unique<MappedFile>(std::move(file)) : std::unique_ptr<MappedFile>{};
 }
 
-MappedFile MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length, int prot) {
+std::unique_ptr<MappedFile> MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length,
+                                                     int prot) {
   static const off64_t page_size = InitPageSize();
   size_t slop = offset % page_size;
   off64_t file_offset = offset - slop;
@@ -59,28 +58,30 @@
     // http://b/119818070 "app crashes when reading asset of zero length".
     // Return a MappedFile that's only valid for reading the size.
     if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) {
-      return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0, nullptr};
+      return std::unique_ptr<MappedFile>(
+          new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0, nullptr));
     }
-    return MappedFile(nullptr, 0, 0, nullptr);
+    return nullptr;
   }
   void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
                              file_offset, file_length);
   if (base == nullptr) {
     CloseHandle(handle);
-    return MappedFile(nullptr, 0, 0, nullptr);
+    return nullptr;
   }
-  return MappedFile{static_cast<char*>(base), length, slop, handle};
+  return std::unique_ptr<MappedFile>(
+      new MappedFile(static_cast<char*>(base), length, slop, handle));
 #else
   void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset);
   if (base == MAP_FAILED) {
     // http://b/119818070 "app crashes when reading asset of zero length".
     // mmap fails with EINVAL for a zero length region.
     if (errno == EINVAL && length == 0) {
-      return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0};
+      return std::unique_ptr<MappedFile>(new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0));
     }
-    return MappedFile(nullptr, 0, 0);
+    return nullptr;
   }
-  return MappedFile{static_cast<char*>(base), length, slop};
+  return std::unique_ptr<MappedFile>(new MappedFile(static_cast<char*>(base), length, slop));
 #endif
 }
 
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
index 3629108..d21703c 100644
--- a/base/mapped_file_test.cpp
+++ b/base/mapped_file_test.cpp
@@ -44,8 +44,6 @@
   ASSERT_TRUE(tf.fd != -1);
 
   auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
-  ASSERT_NE(nullptr, m);
-  EXPECT_TRUE((bool)*m);
   EXPECT_EQ(0u, m->size());
   EXPECT_NE(nullptr, m->data());
 }
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 8979b0c..f379d76 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -636,7 +636,7 @@
   rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
     true
   pushd ${ANDROID_BUILD_TOP} >&2
-  make -j50 >&2
+  build/soong/soong_ui.bash --make-mode >&2
   if [ ${?} != 0 ]; then
     popd >&2
     return 1
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 0602e0a..c8df3e3 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -74,6 +74,7 @@
     header_libs: [
         "libbase_headers",
         "libdebuggerd_common_headers",
+        "bionic_libc_platform_headers",
     ],
 
     whole_static_libs: [
@@ -92,6 +93,9 @@
     defaults: ["debuggerd_defaults"],
     srcs: ["handler/debuggerd_fallback_nop.cpp"],
 
+    header_libs: ["bionic_libc_platform_headers"],
+    export_header_lib_headers: ["bionic_libc_platform_headers"],
+
     whole_static_libs: [
         "libdebuggerd_handler_core",
     ],
@@ -119,6 +123,10 @@
         "liblzma",
         "libcutils",
     ],
+
+    header_libs: ["bionic_libc_platform_headers"],
+    export_header_lib_headers: ["bionic_libc_platform_headers"],
+
     target: {
         recovery: {
             exclude_static_libs: [
@@ -138,15 +146,21 @@
         "util.cpp",
     ],
 
-    header_libs: ["libdebuggerd_common_headers"],
-
     shared_libs: [
         "libbase",
         "libcutils",
         "libprocinfo",
     ],
 
-    export_header_lib_headers: ["libdebuggerd_common_headers"],
+    header_libs: [
+        "libdebuggerd_common_headers",
+        "bionic_libc_platform_headers",
+    ],
+    export_header_lib_headers: [
+        "libdebuggerd_common_headers",
+        "bionic_libc_platform_headers",
+    ],
+
     export_include_dirs: ["include"],
 }
 
@@ -167,6 +181,7 @@
 
     // Needed for private/bionic_fdsan.h
     include_dirs: ["bionic/libc"],
+    header_libs: ["bionic_libc_platform_headers"],
 
     static_libs: [
         "libdexfile_support_static",  // libunwindstack dependency
@@ -176,6 +191,7 @@
         "libcutils",
         "liblog",
     ],
+
     target: {
         recovery: {
             exclude_static_libs: [
@@ -232,6 +248,10 @@
         "libdebuggerd",
     ],
 
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
     local_include_dirs: [
         "libdebuggerd",
     ],
@@ -277,6 +297,10 @@
         },
     },
 
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
     static_libs: [
         "libtombstoned_client_static",
         "libdebuggerd",
@@ -317,7 +341,10 @@
     ],
     defaults: ["debuggerd_defaults"],
 
-    header_libs: ["libdebuggerd_common_headers"],
+    header_libs: [
+        "bionic_libc_platform_headers",
+        "libdebuggerd_common_headers"
+    ],
 
     static_libs: [
         "libbase",
@@ -328,7 +355,3 @@
 
     init_rc: ["tombstoned/tombstoned.rc"],
 }
-
-subdirs = [
-    "crasher",
-]
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 7e35a2f..5c02738 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -35,6 +35,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <bionic/reserved_signals.h>
 #include <cutils/sockets.h>
 #include <procinfo/process.h>
 
@@ -50,7 +51,7 @@
 using android::base::WriteStringToFd;
 
 static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
-  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
+  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER;
   sigval val;
   val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
 
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index cb55745..e8f366f 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -40,6 +40,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <bionic/reserved_signals.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
@@ -511,13 +512,13 @@
 
   // Defer the message until later, for readability.
   bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
-  if (siginfo.si_signo == DEBUGGER_SIGNAL) {
+  if (siginfo.si_signo == BIONIC_SIGNAL_DEBUGGER) {
     wait_for_gdb = false;
   }
 
   // Detach from all of our attached threads before resuming.
   for (const auto& [tid, thread] : thread_info) {
-    int resume_signal = thread.signo == DEBUGGER_SIGNAL ? 0 : thread.signo;
+    int resume_signal = thread.signo == BIONIC_SIGNAL_DEBUGGER ? 0 : thread.signo;
     if (wait_for_gdb) {
       resume_signal = 0;
       if (tgkill(target_process, tid, SIGSTOP) != 0) {
@@ -555,10 +556,10 @@
             << " (target tid = " << g_target_thread << ")";
 
   int signo = siginfo.si_signo;
-  bool fatal_signal = signo != DEBUGGER_SIGNAL;
+  bool fatal_signal = signo != BIONIC_SIGNAL_DEBUGGER;
   bool backtrace = false;
 
-  // si_value is special when used with DEBUGGER_SIGNAL.
+  // si_value is special when used with BIONIC_SIGNAL_DEBUGGER.
   //   0: dump tombstone
   //   1: dump backtrace
   if (!fatal_signal) {
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 7bec470..e86f499 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -44,6 +44,7 @@
     name: "crasher",
 
     defaults: ["crasher-defaults"],
+    header_libs: ["bionic_libc_platform_headers"],
     shared_libs: [
         "libbase",
         "liblog",
@@ -65,6 +66,7 @@
     defaults: ["crasher-defaults"],
     cppflags: ["-DSTATIC_CRASHER"],
     static_executable: true,
+    header_libs: ["bionic_libc_platform_headers"],
     static_libs: [
         "libdebuggerd_handler",
         "libbase",
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 99729dc..6a8cc56 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -31,6 +31,7 @@
 
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
+#include <bionic/reserved_signals.h>
 
 #include <android-base/cmsg.h>
 #include <android-base/file.h>
@@ -398,7 +399,7 @@
   unique_fd output_fd;
   StartProcess([]() {
     android_set_abort_message("not actually aborting");
-    raise(DEBUGGER_SIGNAL);
+    raise(BIONIC_SIGNAL_DEBUGGER);
     exit(0);
   });
   StartIntercept(&output_fd);
@@ -466,7 +467,7 @@
 
   sigval val;
   val.sival_int = 1;
-  ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
+  ASSERT_EQ(0, sigqueue(crasher_pid, BIONIC_SIGNAL_DEBUGGER, val)) << strerror(errno);
   FinishIntercept(&intercept_result);
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
   ConsumeFd(std::move(output_fd), &result);
@@ -734,7 +735,7 @@
 
   siginfo.si_value.sival_int = dump_type == kDebuggerdNativeBacktrace;
 
-  if (syscall(__NR_rt_tgsigqueueinfo, getpid(), gettid(), DEBUGGER_SIGNAL, &siginfo) != 0) {
+  if (syscall(__NR_rt_tgsigqueueinfo, getpid(), gettid(), BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send signal to self";
     return false;
   }
@@ -887,7 +888,7 @@
       errx(2, "first waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
     }
 
-    raise(DEBUGGER_SIGNAL);
+    raise(BIONIC_SIGNAL_DEBUGGER);
 
     errno = 0;
     rc = TEMP_FAILURE_RETRY(waitpid(-1, &status, __WALL | __WNOTHREAD));
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index bbec612..9bcbdb3 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -42,6 +42,7 @@
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <bionic/reserved_signals.h>
 #include <unwindstack/DexFiles.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
@@ -272,7 +273,7 @@
         siginfo.si_pid = getpid();
         siginfo.si_uid = getuid();
 
-        if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
+        if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
           async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
                                 tid, strerror(errno));
           return false;
@@ -340,7 +341,7 @@
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
                                            void* abort_message) {
-  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_ptr != nullptr) {
+  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER && info->si_value.sival_ptr != nullptr) {
     return trace_handler(info, ucontext);
   } else {
     return crash_handler(info, ucontext, abort_message);
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index b90ca80..f8192b5 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -51,6 +51,7 @@
 
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <bionic/reserved_signals.h>
 #include <cutils/properties.h>
 
 #include <libdebuggerd/utility.h>
@@ -175,7 +176,7 @@
     thread_name[MAX_TASK_NAME_LEN] = 0;
   }
 
-  if (info->si_signo == DEBUGGER_SIGNAL) {
+  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
     async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
                           thread_name);
     return;
@@ -307,7 +308,7 @@
 static void* pseudothread_stack;
 
 static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
-  if (thread_info->siginfo->si_signo == DEBUGGER_SIGNAL &&
+  if (thread_info->siginfo->si_signo == BIONIC_SIGNAL_DEBUGGER &&
       thread_info->siginfo->si_value.sival_int) {
     return kDebuggerdNativeBacktrace;
   }
@@ -429,7 +430,7 @@
     async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
   }
 
-  if (thread_info->siginfo->si_signo != DEBUGGER_SIGNAL) {
+  if (thread_info->siginfo->si_signo != BIONIC_SIGNAL_DEBUGGER) {
     // For crashes, we don't need to minimize pause latency.
     // Wait for the dump to complete before having the process exit, to avoid being murdered by
     // ActivityManager or init.
@@ -446,7 +447,7 @@
   // exited with the correct exit status (e.g. so that sh will report
   // "Segmentation fault" instead of "Killed"). For this to work, we need
   // to deregister our signal handler for that signal before continuing.
-  if (info->si_signo != DEBUGGER_SIGNAL) {
+  if (info->si_signo != BIONIC_SIGNAL_DEBUGGER) {
     signal(info->si_signo, SIG_DFL);
     int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info);
     if (rc != 0) {
@@ -485,7 +486,7 @@
 
   void* abort_message = nullptr;
   uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
-  if (signal_number == DEBUGGER_SIGNAL) {
+  if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
     if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
       // Allow for the abort message to be explicitly specified via the sigqueue value.
       // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
@@ -576,7 +577,7 @@
     fatal_errno("failed to restore traceable");
   }
 
-  if (info->si_signo == DEBUGGER_SIGNAL) {
+  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
     // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
     // starting to dump right before our death.
     pthread_mutex_unlock(&crash_mutex);
@@ -591,19 +592,20 @@
     g_callbacks = *callbacks;
   }
 
-  void* thread_stack_allocation =
-    mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  size_t thread_stack_pages = 8;
+  void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * (thread_stack_pages + 2), PROT_NONE,
+                                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
   if (thread_stack_allocation == MAP_FAILED) {
     fatal_errno("failed to allocate debuggerd thread stack");
   }
 
   char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
-  if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
+  if (mprotect(stack, PAGE_SIZE * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) {
     fatal_errno("failed to mprotect debuggerd thread stack");
   }
 
   // Stack grows negatively, set it to the last byte in the page...
-  stack = (stack + PAGE_SIZE - 1);
+  stack = (stack + thread_stack_pages * PAGE_SIZE - 1);
   // and align it.
   stack -= 15;
   pseudothread_stack = stack;
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 7196e0a..cd6fc05 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <bionic/reserved_signals.h>
 #include <signal.h>
 #include <stdint.h>
 #include <sys/cdefs.h>
@@ -33,11 +34,11 @@
 void debuggerd_init(debuggerd_callbacks_t* callbacks);
 
 // DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
-// triggered via DEBUGGER_SIGNAL. The debugger_action_t is sent via si_value
+// triggered via BIONIC_SIGNAL_DEBUGGER. The debugger_action_t is sent via si_value
 // using sigqueue(2) or equivalent. If no si_value is specified (e.g. if the
 // signal is sent by kill(2)), the default behavior is to print the backtrace
 // to the log.
-#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
+#define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER
 
 static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
   sigaction(SIGABRT, action, nullptr);
@@ -50,7 +51,7 @@
 #endif
   sigaction(SIGSYS, action, nullptr);
   sigaction(SIGTRAP, action, nullptr);
-  sigaction(DEBUGGER_SIGNAL, action, nullptr);
+  sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr);
 }
 
 __END_DECLS
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 9dea7ac..b33adf3 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -359,467 +359,3 @@
   dump_timestamp(&log_, 0);
   ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
 }
-
-class MemoryPattern : public unwindstack::Memory {
- public:
-  MemoryPattern() = default;
-  virtual ~MemoryPattern() = default;
-
-  size_t Read(uint64_t, void* dst, size_t size) override {
-    uint8_t* data = reinterpret_cast<uint8_t*>(dst);
-    for (size_t i = 0; i < size; i++) {
-      data[i] = (i % 0xff);
-    }
-    return size;
-  }
-};
-
-TEST_F(TombstoneTest, dump_stack_single_frame) {
-  std::vector<unwindstack::FrameData> frames;
-  unwindstack::Maps maps;
-  MemoryPattern memory;
-
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
-  dump_stack(&log_, frames, &maps, &memory);
-
-  std::string contents;
-  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
-  std::string expected =
-#if defined(__LP64__)
-      "         0000000000001f80  0706050403020100\n"
-      "         0000000000001f88  0f0e0d0c0b0a0908\n"
-      "         0000000000001f90  1716151413121110\n"
-      "         0000000000001f98  1f1e1d1c1b1a1918\n"
-      "         0000000000001fa0  2726252423222120\n"
-      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
-      "         0000000000001fb0  3736353433323130\n"
-      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
-      "         0000000000001fc0  4746454443424140\n"
-      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
-      "         0000000000001fd0  5756555453525150\n"
-      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
-      "         0000000000001fe0  6766656463626160\n"
-      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
-      "         0000000000001ff0  7776757473727170\n"
-      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
-      "    #00  0000000000002000  0706050403020100\n"
-      "         0000000000002008  0f0e0d0c0b0a0908\n"
-      "         0000000000002010  1716151413121110\n"
-      "         0000000000002018  1f1e1d1c1b1a1918\n"
-      "         0000000000002020  2726252423222120\n"
-      "         0000000000002028  2f2e2d2c2b2a2928\n"
-      "         0000000000002030  3736353433323130\n"
-      "         0000000000002038  3f3e3d3c3b3a3938\n"
-      "         0000000000002040  4746454443424140\n"
-      "         0000000000002048  4f4e4d4c4b4a4948\n"
-      "         0000000000002050  5756555453525150\n"
-      "         0000000000002058  5f5e5d5c5b5a5958\n"
-      "         0000000000002060  6766656463626160\n"
-      "         0000000000002068  6f6e6d6c6b6a6968\n"
-      "         0000000000002070  7776757473727170\n"
-      "         0000000000002078  7f7e7d7c7b7a7978\n";
-#else
-      "         00001fc0  03020100\n"
-      "         00001fc4  07060504\n"
-      "         00001fc8  0b0a0908\n"
-      "         00001fcc  0f0e0d0c\n"
-      "         00001fd0  13121110\n"
-      "         00001fd4  17161514\n"
-      "         00001fd8  1b1a1918\n"
-      "         00001fdc  1f1e1d1c\n"
-      "         00001fe0  23222120\n"
-      "         00001fe4  27262524\n"
-      "         00001fe8  2b2a2928\n"
-      "         00001fec  2f2e2d2c\n"
-      "         00001ff0  33323130\n"
-      "         00001ff4  37363534\n"
-      "         00001ff8  3b3a3938\n"
-      "         00001ffc  3f3e3d3c\n"
-      "    #00  00002000  03020100\n"
-      "         00002004  07060504\n"
-      "         00002008  0b0a0908\n"
-      "         0000200c  0f0e0d0c\n"
-      "         00002010  13121110\n"
-      "         00002014  17161514\n"
-      "         00002018  1b1a1918\n"
-      "         0000201c  1f1e1d1c\n"
-      "         00002020  23222120\n"
-      "         00002024  27262524\n"
-      "         00002028  2b2a2928\n"
-      "         0000202c  2f2e2d2c\n"
-      "         00002030  33323130\n"
-      "         00002034  37363534\n"
-      "         00002038  3b3a3938\n"
-      "         0000203c  3f3e3d3c\n";
-#endif
-  EXPECT_EQ(expected, contents);
-}
-
-TEST_F(TombstoneTest, dump_stack_multiple_frames_same_sp) {
-  std::vector<unwindstack::FrameData> frames;
-  unwindstack::Maps maps;
-  MemoryPattern memory;
-
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2000});
-  dump_stack(&log_, frames, &maps, &memory);
-
-  std::string contents;
-  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
-  std::string expected =
-#if defined(__LP64__)
-      "         0000000000001f80  0706050403020100\n"
-      "         0000000000001f88  0f0e0d0c0b0a0908\n"
-      "         0000000000001f90  1716151413121110\n"
-      "         0000000000001f98  1f1e1d1c1b1a1918\n"
-      "         0000000000001fa0  2726252423222120\n"
-      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
-      "         0000000000001fb0  3736353433323130\n"
-      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
-      "         0000000000001fc0  4746454443424140\n"
-      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
-      "         0000000000001fd0  5756555453525150\n"
-      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
-      "         0000000000001fe0  6766656463626160\n"
-      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
-      "         0000000000001ff0  7776757473727170\n"
-      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
-      "    #00  0000000000002000  0706050403020100\n"
-      "         ................  ................\n"
-      "    #01  0000000000002000  0706050403020100\n"
-      "         0000000000002008  0f0e0d0c0b0a0908\n"
-      "         0000000000002010  1716151413121110\n"
-      "         0000000000002018  1f1e1d1c1b1a1918\n"
-      "         0000000000002020  2726252423222120\n"
-      "         0000000000002028  2f2e2d2c2b2a2928\n"
-      "         0000000000002030  3736353433323130\n"
-      "         0000000000002038  3f3e3d3c3b3a3938\n"
-      "         0000000000002040  4746454443424140\n"
-      "         0000000000002048  4f4e4d4c4b4a4948\n"
-      "         0000000000002050  5756555453525150\n"
-      "         0000000000002058  5f5e5d5c5b5a5958\n"
-      "         0000000000002060  6766656463626160\n"
-      "         0000000000002068  6f6e6d6c6b6a6968\n"
-      "         0000000000002070  7776757473727170\n"
-      "         0000000000002078  7f7e7d7c7b7a7978\n";
-#else
-      "         00001fc0  03020100\n"
-      "         00001fc4  07060504\n"
-      "         00001fc8  0b0a0908\n"
-      "         00001fcc  0f0e0d0c\n"
-      "         00001fd0  13121110\n"
-      "         00001fd4  17161514\n"
-      "         00001fd8  1b1a1918\n"
-      "         00001fdc  1f1e1d1c\n"
-      "         00001fe0  23222120\n"
-      "         00001fe4  27262524\n"
-      "         00001fe8  2b2a2928\n"
-      "         00001fec  2f2e2d2c\n"
-      "         00001ff0  33323130\n"
-      "         00001ff4  37363534\n"
-      "         00001ff8  3b3a3938\n"
-      "         00001ffc  3f3e3d3c\n"
-      "    #00  00002000  03020100\n"
-      "         ........  ........\n"
-      "    #01  00002000  03020100\n"
-      "         00002004  07060504\n"
-      "         00002008  0b0a0908\n"
-      "         0000200c  0f0e0d0c\n"
-      "         00002010  13121110\n"
-      "         00002014  17161514\n"
-      "         00002018  1b1a1918\n"
-      "         0000201c  1f1e1d1c\n"
-      "         00002020  23222120\n"
-      "         00002024  27262524\n"
-      "         00002028  2b2a2928\n"
-      "         0000202c  2f2e2d2c\n"
-      "         00002030  33323130\n"
-      "         00002034  37363534\n"
-      "         00002038  3b3a3938\n"
-      "         0000203c  3f3e3d3c\n";
-#endif
-  EXPECT_EQ(expected, contents);
-}
-
-TEST_F(TombstoneTest, dump_stack_multiple_frames) {
-  std::vector<unwindstack::FrameData> frames;
-  unwindstack::Maps maps;
-  MemoryPattern memory;
-
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2100});
-  dump_stack(&log_, frames, &maps, &memory);
-
-  std::string contents;
-  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
-  std::string expected =
-#if defined(__LP64__)
-      "         0000000000001f80  0706050403020100\n"
-      "         0000000000001f88  0f0e0d0c0b0a0908\n"
-      "         0000000000001f90  1716151413121110\n"
-      "         0000000000001f98  1f1e1d1c1b1a1918\n"
-      "         0000000000001fa0  2726252423222120\n"
-      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
-      "         0000000000001fb0  3736353433323130\n"
-      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
-      "         0000000000001fc0  4746454443424140\n"
-      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
-      "         0000000000001fd0  5756555453525150\n"
-      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
-      "         0000000000001fe0  6766656463626160\n"
-      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
-      "         0000000000001ff0  7776757473727170\n"
-      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
-      "    #00  0000000000002000  0706050403020100\n"
-      "         0000000000002008  0f0e0d0c0b0a0908\n"
-      "    #01  0000000000002010  0706050403020100\n"
-      "         0000000000002018  0f0e0d0c0b0a0908\n"
-      "         0000000000002020  1716151413121110\n"
-      "         0000000000002028  1f1e1d1c1b1a1918\n"
-      "         0000000000002030  2726252423222120\n"
-      "         0000000000002038  2f2e2d2c2b2a2928\n"
-      "         0000000000002040  3736353433323130\n"
-      "         0000000000002048  3f3e3d3c3b3a3938\n"
-      "         0000000000002050  4746454443424140\n"
-      "         0000000000002058  4f4e4d4c4b4a4948\n"
-      "         0000000000002060  5756555453525150\n"
-      "         0000000000002068  5f5e5d5c5b5a5958\n"
-      "         0000000000002070  6766656463626160\n"
-      "         0000000000002078  6f6e6d6c6b6a6968\n"
-      "         0000000000002080  7776757473727170\n"
-      "         0000000000002088  7f7e7d7c7b7a7978\n"
-      "         ................  ................\n"
-      "    #02  0000000000002100  0706050403020100\n"
-      "         0000000000002108  0f0e0d0c0b0a0908\n"
-      "         0000000000002110  1716151413121110\n"
-      "         0000000000002118  1f1e1d1c1b1a1918\n"
-      "         0000000000002120  2726252423222120\n"
-      "         0000000000002128  2f2e2d2c2b2a2928\n"
-      "         0000000000002130  3736353433323130\n"
-      "         0000000000002138  3f3e3d3c3b3a3938\n"
-      "         0000000000002140  4746454443424140\n"
-      "         0000000000002148  4f4e4d4c4b4a4948\n"
-      "         0000000000002150  5756555453525150\n"
-      "         0000000000002158  5f5e5d5c5b5a5958\n"
-      "         0000000000002160  6766656463626160\n"
-      "         0000000000002168  6f6e6d6c6b6a6968\n"
-      "         0000000000002170  7776757473727170\n"
-      "         0000000000002178  7f7e7d7c7b7a7978\n";
-#else
-      "         00001fc0  03020100\n"
-      "         00001fc4  07060504\n"
-      "         00001fc8  0b0a0908\n"
-      "         00001fcc  0f0e0d0c\n"
-      "         00001fd0  13121110\n"
-      "         00001fd4  17161514\n"
-      "         00001fd8  1b1a1918\n"
-      "         00001fdc  1f1e1d1c\n"
-      "         00001fe0  23222120\n"
-      "         00001fe4  27262524\n"
-      "         00001fe8  2b2a2928\n"
-      "         00001fec  2f2e2d2c\n"
-      "         00001ff0  33323130\n"
-      "         00001ff4  37363534\n"
-      "         00001ff8  3b3a3938\n"
-      "         00001ffc  3f3e3d3c\n"
-      "    #00  00002000  03020100\n"
-      "         00002004  07060504\n"
-      "         00002008  0b0a0908\n"
-      "         0000200c  0f0e0d0c\n"
-      "    #01  00002010  03020100\n"
-      "         00002014  07060504\n"
-      "         00002018  0b0a0908\n"
-      "         0000201c  0f0e0d0c\n"
-      "         00002020  13121110\n"
-      "         00002024  17161514\n"
-      "         00002028  1b1a1918\n"
-      "         0000202c  1f1e1d1c\n"
-      "         00002030  23222120\n"
-      "         00002034  27262524\n"
-      "         00002038  2b2a2928\n"
-      "         0000203c  2f2e2d2c\n"
-      "         00002040  33323130\n"
-      "         00002044  37363534\n"
-      "         00002048  3b3a3938\n"
-      "         0000204c  3f3e3d3c\n"
-      "         ........  ........\n"
-      "    #02  00002100  03020100\n"
-      "         00002104  07060504\n"
-      "         00002108  0b0a0908\n"
-      "         0000210c  0f0e0d0c\n"
-      "         00002110  13121110\n"
-      "         00002114  17161514\n"
-      "         00002118  1b1a1918\n"
-      "         0000211c  1f1e1d1c\n"
-      "         00002120  23222120\n"
-      "         00002124  27262524\n"
-      "         00002128  2b2a2928\n"
-      "         0000212c  2f2e2d2c\n"
-      "         00002130  33323130\n"
-      "         00002134  37363534\n"
-      "         00002138  3b3a3938\n"
-      "         0000213c  3f3e3d3c\n";
-#endif
-  EXPECT_EQ(expected, contents);
-}
-
-TEST_F(TombstoneTest, dump_stack_multiple_frames_disjoint_frames) {
-  std::vector<unwindstack::FrameData> frames;
-  unwindstack::Maps maps;
-  MemoryPattern memory;
-
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1000});
-  frames.push_back(
-      unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1030});
-  dump_stack(&log_, frames, &maps, &memory);
-
-  std::string contents;
-  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
-
-  std::string expected =
-#if defined(__LP64__)
-      "         0000000000001f80  0706050403020100\n"
-      "         0000000000001f88  0f0e0d0c0b0a0908\n"
-      "         0000000000001f90  1716151413121110\n"
-      "         0000000000001f98  1f1e1d1c1b1a1918\n"
-      "         0000000000001fa0  2726252423222120\n"
-      "         0000000000001fa8  2f2e2d2c2b2a2928\n"
-      "         0000000000001fb0  3736353433323130\n"
-      "         0000000000001fb8  3f3e3d3c3b3a3938\n"
-      "         0000000000001fc0  4746454443424140\n"
-      "         0000000000001fc8  4f4e4d4c4b4a4948\n"
-      "         0000000000001fd0  5756555453525150\n"
-      "         0000000000001fd8  5f5e5d5c5b5a5958\n"
-      "         0000000000001fe0  6766656463626160\n"
-      "         0000000000001fe8  6f6e6d6c6b6a6968\n"
-      "         0000000000001ff0  7776757473727170\n"
-      "         0000000000001ff8  7f7e7d7c7b7a7978\n"
-      "    #00  0000000000002000  0706050403020100\n"
-      "         0000000000002008  0f0e0d0c0b0a0908\n"
-      "    #01  0000000000002010  0706050403020100\n"
-      "         0000000000002018  0f0e0d0c0b0a0908\n"
-      "         0000000000002020  1716151413121110\n"
-      "         0000000000002028  1f1e1d1c1b1a1918\n"
-      "         0000000000002030  2726252423222120\n"
-      "         0000000000002038  2f2e2d2c2b2a2928\n"
-      "         0000000000002040  3736353433323130\n"
-      "         0000000000002048  3f3e3d3c3b3a3938\n"
-      "         0000000000002050  4746454443424140\n"
-      "         0000000000002058  4f4e4d4c4b4a4948\n"
-      "         0000000000002060  5756555453525150\n"
-      "         0000000000002068  5f5e5d5c5b5a5958\n"
-      "         0000000000002070  6766656463626160\n"
-      "         0000000000002078  6f6e6d6c6b6a6968\n"
-      "         0000000000002080  7776757473727170\n"
-      "         0000000000002088  7f7e7d7c7b7a7978\n"
-      "         ................  ................\n"
-      "    #02  0000000000001000  0706050403020100\n"
-      "         0000000000001008  0f0e0d0c0b0a0908\n"
-      "         0000000000001010  1716151413121110\n"
-      "         0000000000001018  1f1e1d1c1b1a1918\n"
-      "         0000000000001020  2726252423222120\n"
-      "         0000000000001028  2f2e2d2c2b2a2928\n"
-      "    #03  0000000000001030  0706050403020100\n"
-      "         0000000000001038  0f0e0d0c0b0a0908\n"
-      "         0000000000001040  1716151413121110\n"
-      "         0000000000001048  1f1e1d1c1b1a1918\n"
-      "         0000000000001050  2726252423222120\n"
-      "         0000000000001058  2f2e2d2c2b2a2928\n"
-      "         0000000000001060  3736353433323130\n"
-      "         0000000000001068  3f3e3d3c3b3a3938\n"
-      "         0000000000001070  4746454443424140\n"
-      "         0000000000001078  4f4e4d4c4b4a4948\n"
-      "         0000000000001080  5756555453525150\n"
-      "         0000000000001088  5f5e5d5c5b5a5958\n"
-      "         0000000000001090  6766656463626160\n"
-      "         0000000000001098  6f6e6d6c6b6a6968\n"
-      "         00000000000010a0  7776757473727170\n"
-      "         00000000000010a8  7f7e7d7c7b7a7978\n";
-#else
-      "         00001fc0  03020100\n"
-      "         00001fc4  07060504\n"
-      "         00001fc8  0b0a0908\n"
-      "         00001fcc  0f0e0d0c\n"
-      "         00001fd0  13121110\n"
-      "         00001fd4  17161514\n"
-      "         00001fd8  1b1a1918\n"
-      "         00001fdc  1f1e1d1c\n"
-      "         00001fe0  23222120\n"
-      "         00001fe4  27262524\n"
-      "         00001fe8  2b2a2928\n"
-      "         00001fec  2f2e2d2c\n"
-      "         00001ff0  33323130\n"
-      "         00001ff4  37363534\n"
-      "         00001ff8  3b3a3938\n"
-      "         00001ffc  3f3e3d3c\n"
-      "    #00  00002000  03020100\n"
-      "         00002004  07060504\n"
-      "         00002008  0b0a0908\n"
-      "         0000200c  0f0e0d0c\n"
-      "    #01  00002010  03020100\n"
-      "         00002014  07060504\n"
-      "         00002018  0b0a0908\n"
-      "         0000201c  0f0e0d0c\n"
-      "         00002020  13121110\n"
-      "         00002024  17161514\n"
-      "         00002028  1b1a1918\n"
-      "         0000202c  1f1e1d1c\n"
-      "         00002030  23222120\n"
-      "         00002034  27262524\n"
-      "         00002038  2b2a2928\n"
-      "         0000203c  2f2e2d2c\n"
-      "         00002040  33323130\n"
-      "         00002044  37363534\n"
-      "         00002048  3b3a3938\n"
-      "         0000204c  3f3e3d3c\n"
-      "         ........  ........\n"
-      "    #02  00001000  03020100\n"
-      "         00001004  07060504\n"
-      "         00001008  0b0a0908\n"
-      "         0000100c  0f0e0d0c\n"
-      "         00001010  13121110\n"
-      "         00001014  17161514\n"
-      "         00001018  1b1a1918\n"
-      "         0000101c  1f1e1d1c\n"
-      "         00001020  23222120\n"
-      "         00001024  27262524\n"
-      "         00001028  2b2a2928\n"
-      "         0000102c  2f2e2d2c\n"
-      "    #03  00001030  03020100\n"
-      "         00001034  07060504\n"
-      "         00001038  0b0a0908\n"
-      "         0000103c  0f0e0d0c\n"
-      "         00001040  13121110\n"
-      "         00001044  17161514\n"
-      "         00001048  1b1a1918\n"
-      "         0000104c  1f1e1d1c\n"
-      "         00001050  23222120\n"
-      "         00001054  27262524\n"
-      "         00001058  2b2a2928\n"
-      "         0000105c  2f2e2d2c\n"
-      "         00001060  33323130\n"
-      "         00001064  37363534\n"
-      "         00001068  3b3a3938\n"
-      "         0000106c  3f3e3d3c\n";
-#endif
-  EXPECT_EQ(expected, contents);
-}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 236fcf7..4e7f35c 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -52,9 +52,6 @@
 #include <unwindstack/Regs.h>
 #include <unwindstack/Unwinder.h>
 
-// Needed to get DEBUGGER_SIGNAL.
-#include "debuggerd/handler.h"
-
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/open_files_list.h"
 #include "libdebuggerd/utility.h"
@@ -191,106 +188,6 @@
   _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
 }
 
-static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
-                               uint64_t* sp, size_t words, int label) {
-  // Read the data all at once.
-  word_t stack_data[words];
-
-  // TODO: Do we need to word align this for crashes caused by a misaligned sp?
-  //       The process_vm_readv implementation of Memory should handle this appropriately?
-  size_t bytes_read = memory->Read(*sp, stack_data, sizeof(word_t) * words);
-  words = bytes_read / sizeof(word_t);
-  std::string line;
-  for (size_t i = 0; i < words; i++) {
-    line = "    ";
-    if (i == 0 && label >= 0) {
-      // Print the label once.
-      line += StringPrintf("#%02d  ", label);
-    } else {
-      line += "     ";
-    }
-    line += StringPrintf("%" PRIPTR "  %" PRIPTR, *sp, static_cast<uint64_t>(stack_data[i]));
-
-    unwindstack::MapInfo* map_info = maps->Find(stack_data[i]);
-    if (map_info != nullptr && !map_info->name.empty()) {
-      line += "  " + map_info->name;
-      std::string func_name;
-      uint64_t func_offset = 0;
-      if (map_info->GetFunctionName(stack_data[i], &func_name, &func_offset)) {
-        line += " (" + func_name;
-        if (func_offset) {
-          line += StringPrintf("+%" PRIu64, func_offset);
-        }
-        line += ')';
-      }
-    }
-    _LOG(log, logtype::STACK, "%s\n", line.c_str());
-
-    *sp += sizeof(word_t);
-  }
-}
-
-static void dump_stack(log_t* log, const std::vector<unwindstack::FrameData>& frames,
-                       unwindstack::Maps* maps, unwindstack::Memory* memory) {
-  size_t first = 0, last;
-  for (size_t i = 0; i < frames.size(); i++) {
-    if (frames[i].sp) {
-      if (!first) {
-        first = i+1;
-      }
-      last = i;
-    }
-  }
-
-  if (!first) {
-    return;
-  }
-  first--;
-
-  // Dump a few words before the first frame.
-  uint64_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
-  dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, -1);
-
-#if defined(__LP64__)
-  static constexpr const char delimiter[] = "         ................  ................\n";
-#else
-  static constexpr const char delimiter[] = "         ........  ........\n";
-#endif
-
-  // Dump a few words from all successive frames.
-  for (size_t i = first; i <= last; i++) {
-    auto* frame = &frames[i];
-    if (sp != frame->sp) {
-      _LOG(log, logtype::STACK, delimiter);
-      sp = frame->sp;
-    }
-    if (i != last) {
-      // Print stack data up to the stack from the next frame.
-      size_t words;
-      uint64_t next_sp = frames[i + 1].sp;
-      if (next_sp < sp) {
-        // The next frame is probably using a completely different stack,
-        // so dump the max from this stack.
-        words = STACK_WORDS;
-      } else {
-        words = (next_sp - sp) / sizeof(word_t);
-        if (words == 0) {
-          // The sp is the same as the next frame, print at least
-          // one line for this frame.
-          words = 1;
-        } else if (words > STACK_WORDS) {
-          words = STACK_WORDS;
-        }
-      }
-      dump_stack_segment(log, maps, memory, &sp, words, i);
-    } else {
-      // Print some number of words past the last stack frame since we
-      // don't know how large the stack is.
-      dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, i);
-    }
-  }
-}
-
 static std::string get_addr_string(uint64_t addr) {
   std::string addr_str;
 #if defined(__LP64__)
@@ -502,9 +399,6 @@
   } else {
     _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
     log_backtrace(log, unwinder, "    ");
-
-    _LOG(log, logtype::STACK, "\nstack:\n");
-    dump_stack(log, unwinder->frames(), unwinder->GetMaps(), unwinder->GetProcessMemory().get());
   }
 
   if (primary_thread) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 5ce26fc..0a1d2a4 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,6 +35,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <bionic/reserved_signals.h>
 #include <debuggerd/handler.h>
 #include <log/log.h>
 #include <unwindstack/Memory.h>
@@ -296,7 +297,8 @@
     case SIGSTOP: return "SIGSTOP";
     case SIGSYS: return "SIGSYS";
     case SIGTRAP: return "SIGTRAP";
-    case DEBUGGER_SIGNAL: return "<debuggerd signal>";
+    case BIONIC_SIGNAL_DEBUGGER:
+      return "<debuggerd signal>";
     default: return "?";
   }
 }
diff --git a/deprecated-adf/Android.bp b/deprecated-adf/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/deprecated-adf/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/deprecated-adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
index 49e3721..70f0a3b 100644
--- a/deprecated-adf/libadf/Android.bp
+++ b/deprecated-adf/libadf/Android.bp
@@ -24,5 +24,3 @@
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
-
-subdirs = ["tests"]
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 17ec392..fd009e7 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,9 +18,9 @@
 # Package fastboot-related executables.
 #
 
-my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
-my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
+my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
 $(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
 my_dist_files :=
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 5a554a0..aefd448 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -47,6 +47,8 @@
 #define FB_VAR_VERSION "version"
 #define FB_VAR_VERSION_BOOTLOADER "version-bootloader"
 #define FB_VAR_VERSION_BASEBAND "version-baseband"
+#define FB_VAR_VERSION_OS "version-os"
+#define FB_VAR_VERSION_VNDK "version-vndk"
 #define FB_VAR_PRODUCT "product"
 #define FB_VAR_SERIALNO "serialno"
 #define FB_VAR_SECURE "secure"
@@ -69,3 +71,9 @@
 #define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
 #define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
 #define FB_VAR_CPU_ABI "cpu-abi"
+#define FB_VAR_SYSTEM_FINGERPRINT "system-fingerprint"
+#define FB_VAR_VENDOR_FINGERPRINT "vendor-fingerprint"
+#define FB_VAR_DYNAMIC_PARTITION "dynamic-partition"
+#define FB_VAR_FIRST_API_LEVEL "first-api-level"
+#define FB_VAR_SECURITY_PATCH_LEVEL "security-patch-level"
+#define FB_VAR_TREBLE_ENABLED "treble-enabled"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b7263d9..2c9dec9 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -106,6 +106,8 @@
             {FB_VAR_VERSION, {GetVersion, nullptr}},
             {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
             {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+            {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
+            {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
             {FB_VAR_PRODUCT, {GetProduct, nullptr}},
             {FB_VAR_SERIALNO, {GetSerial, nullptr}},
             {FB_VAR_VARIANT, {GetVariant, nullptr}},
@@ -127,7 +129,13 @@
             {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
             {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
             {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
-            {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}}};
+            {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
+            {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
+            {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
+            {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
+            {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
+            {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
+            {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}}};
 
     if (args.size() < 2) {
         return device->WriteFail("Missing argument");
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 5066046..9c80765 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -297,3 +297,7 @@
     CloseFunctionFs(handle_.get());
     return 0;
 }
+
+int ClientUsbTransport::Reset() {
+    return 0;
+}
diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h
index 3694f9a..e6a1a8b 100644
--- a/fastboot/device/usb_client.h
+++ b/fastboot/device/usb_client.h
@@ -29,6 +29,7 @@
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
     int Close() override;
+    int Reset() override;
 
   private:
     std::unique_ptr<usb_handle> handle_;
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 10eac01..e7d8bc3 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -62,6 +62,18 @@
     return true;
 }
 
+bool GetOsVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                  std::string* message) {
+    *message = android::base::GetProperty("ro.build.version.release", "");
+    return true;
+}
+
+bool GetVndkVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                    std::string* message) {
+    *message = android::base::GetProperty("ro.vndk.version", "");
+    return true;
+}
+
 bool GetProduct(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
                 std::string* message) {
     *message = android::base::GetProperty("ro.product.device", "");
@@ -458,3 +470,42 @@
     *message = android::base::GetProperty("ro.product.cpu.abi", "");
     return true;
 }
+
+bool GetSystemFingerprint(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                          std::string* message) {
+    *message = android::base::GetProperty("ro.system.build.fingerprint", "");
+    if (message->empty()) {
+        *message = android::base::GetProperty("ro.build.fingerprint", "");
+    }
+    return true;
+}
+
+bool GetVendorFingerprint(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                          std::string* message) {
+    *message = android::base::GetProperty("ro.vendor.build.fingerprint", "");
+    return true;
+}
+
+bool GetDynamicPartition(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                         std::string* message) {
+    *message = android::base::GetProperty("ro.boot.dynamic_partitions", "");
+    return true;
+}
+
+bool GetFirstApiLevel(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                      std::string* message) {
+    *message = android::base::GetProperty("ro.product.first_api_level", "");
+    return true;
+}
+
+bool GetSecurityPatchLevel(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                           std::string* message) {
+    *message = android::base::GetProperty("ro.build.version.security_patch", "");
+    return true;
+}
+
+bool GetTrebleEnabled(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                      std::string* message) {
+    *message = android::base::GetProperty("ro.treble.enabled", "");
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 90840d6..c11e472 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -26,6 +26,10 @@
                           std::string* message);
 bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args,
                         std::string* message);
+bool GetOsVersion(FastbootDevice* device, const std::vector<std::string>& args,
+                  std::string* message);
+bool GetVndkVersion(FastbootDevice* device, const std::vector<std::string>& args,
+                    std::string* message);
 bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
 bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
 bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
@@ -64,6 +68,18 @@
 bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
                              std::string* message);
 bool GetCpuAbi(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSystemFingerprint(FastbootDevice* device, const std::vector<std::string>& args,
+                          std::string* message);
+bool GetVendorFingerprint(FastbootDevice* device, const std::vector<std::string>& args,
+                          std::string* message);
+bool GetDynamicPartition(FastbootDevice* device, const std::vector<std::string>& args,
+                         std::string* message);
+bool GetFirstApiLevel(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message);
+bool GetSecurityPatchLevel(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
+bool GetTrebleEnabled(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message);
 
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 277cc3a..bb54fd9 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -5,7 +5,7 @@
   srcs: [
     "main.cpp",
     "extensions.cpp",
-    "usb_transport_sniffer.cpp",
+    "transport_sniffer.cpp",
     "fixtures.cpp",
     "test_utils.cpp",
   ],
@@ -31,6 +31,8 @@
     "libext4_utils",
   ],
 
+  stl: "libc++_static",
+
   // Static libs (libfastboot2) shared library dependencies are not transitively included
   // This is needed to avoid link time errors when building for mac
   target: {
@@ -40,5 +42,13 @@
           "-framework IOKit",
       ],
     },
-  }
+  },
+
+  // Disable auto-generation of test config as this binary itself is not a test in the test suites,
+  // rather it is used by other tests.
+  auto_gen_config: false,
+  test_suites: [
+    "general-tests",
+    "vts-core",
+  ],
 }
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index bc13a8c..bd76ff4 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -48,12 +48,13 @@
 #include <gtest/gtest.h>
 
 #include "fastboot_driver.h"
+#include "tcp.h"
 #include "usb.h"
 
 #include "extensions.h"
 #include "fixtures.h"
 #include "test_utils.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
 
 using namespace std::literals::chrono_literals;
 
@@ -74,7 +75,14 @@
     return 0;
 }
 
+bool FastBootTest::IsFastbootOverTcp() {
+    // serial contains ":" is treated as host ip and port number
+    return (device_serial.find(":") != std::string::npos);
+}
+
 bool FastBootTest::UsbStillAvailible() {
+    if (IsFastbootOverTcp()) return true;
+
     // For some reason someone decided to prefix the path with "usb:"
     std::string prefix("usb:");
     if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {
@@ -113,15 +121,19 @@
         ASSERT_TRUE(UsbStillAvailible());  // The device disconnected
     }
 
-    const auto matcher = [](usb_ifc_info* info) -> int {
-        return MatchFastboot(info, device_serial);
-    };
-    for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
-        std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
-        if (usb)
-            transport = std::unique_ptr<UsbTransportSniffer>(
-                    new UsbTransportSniffer(std::move(usb), serial_port));
-        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    if (IsFastbootOverTcp()) {
+        ConnectTcpFastbootDevice();
+    } else {
+        const auto matcher = [](usb_ifc_info* info) -> int {
+            return MatchFastboot(info, device_serial);
+        };
+        for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
+            std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+            if (usb)
+                transport = std::unique_ptr<TransportSniffer>(
+                        new TransportSniffer(std::move(usb), serial_port));
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        }
     }
 
     ASSERT_TRUE(transport);  // no nullptr
@@ -154,6 +166,8 @@
 
 // TODO, this should eventually be piped to a file instead of stdout
 void FastBootTest::TearDownSerial() {
+    if (IsFastbootOverTcp()) return;
+
     if (!transport) return;
     // One last read from serial
     transport->ProcessSerial();
@@ -167,9 +181,34 @@
     }
 }
 
+void FastBootTest::ConnectTcpFastbootDevice() {
+    std::size_t found = device_serial.find(":");
+    if (found != std::string::npos) {
+        for (int i = 0; i < MAX_TCP_TRIES && !transport; i++) {
+            std::string error;
+            std::unique_ptr<Transport> tcp(
+                    tcp::Connect(device_serial.substr(0, found), tcp::kDefaultPort, &error)
+                            .release());
+            if (tcp)
+                transport =
+                        std::unique_ptr<TransportSniffer>(new TransportSniffer(std::move(tcp), 0));
+            if (transport != nullptr) break;
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        }
+    }
+}
+
 void FastBootTest::ReconnectFastbootDevice() {
     fb.reset();
     transport.reset();
+
+    if (IsFastbootOverTcp()) {
+        ConnectTcpFastbootDevice();
+        device_path = cb_scratch;
+        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+        return;
+    }
+
     while (UsbStillAvailible())
         ;
     printf("WAITING FOR DEVICE\n");
@@ -180,8 +219,8 @@
     while (!transport) {
         std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
         if (usb) {
-            transport = std::unique_ptr<UsbTransportSniffer>(
-                    new UsbTransportSniffer(std::move(usb), serial_port));
+            transport = std::unique_ptr<TransportSniffer>(
+                    new TransportSniffer(std::move(usb), serial_port));
         }
         std::this_thread::sleep_for(1s);
     }
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index c71c897..2468868 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -31,7 +31,7 @@
 #include "fastboot_driver.h"
 
 #include "extensions.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
 
 namespace fastboot {
 
@@ -45,11 +45,14 @@
     static int serial_port;
     static std::string device_serial;
     static constexpr int MAX_USB_TRIES = 10;
+    static constexpr int MAX_TCP_TRIES = 6000;
 
     static int MatchFastboot(usb_ifc_info* info, const std::string& local_serial = "");
+    static bool IsFastbootOverTcp();
     bool UsbStillAvailible();
     bool UserSpaceFastboot();
     void ReconnectFastbootDevice();
+    void ConnectTcpFastbootDevice();
 
   protected:
     RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
@@ -64,7 +67,7 @@
     void TearDownSerial();
     void SetLockState(bool unlock, bool assert_change = true);
 
-    std::unique_ptr<UsbTransportSniffer> transport;
+    std::unique_ptr<TransportSniffer> transport;
     std::unique_ptr<FastBootDriver> fb;
 
   private:
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index a1d69d2..b9784fe 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -54,7 +54,7 @@
 #include "extensions.h"
 #include "fixtures.h"
 #include "test_utils.h"
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
 
 namespace fastboot {
 
@@ -1756,16 +1756,19 @@
     }
 
     setbuf(stdout, NULL);  // no buffering
-    printf("<Waiting for Device>\n");
-    const auto matcher = [](usb_ifc_info* info) -> int {
-        return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
-    };
-    Transport* transport = nullptr;
-    while (!transport) {
-        transport = usb_open(matcher);
-        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+
+    if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
+        printf("<Waiting for Device>\n");
+        const auto matcher = [](usb_ifc_info* info) -> int {
+            return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+        };
+        Transport* transport = nullptr;
+        while (!transport) {
+            transport = usb_open(matcher);
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        }
+        transport->Close();
     }
-    transport->Close();
 
     if (args.find("serial_port") != args.end()) {
         fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at("serial_port"));
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp b/fastboot/fuzzy_fastboot/transport_sniffer.cpp
similarity index 91%
rename from fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
rename to fastboot/fuzzy_fastboot/transport_sniffer.cpp
index 7c595f4..b55ffd3 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
+++ b/fastboot/fuzzy_fastboot/transport_sniffer.cpp
@@ -1,4 +1,4 @@
-#include "usb_transport_sniffer.h"
+#include "transport_sniffer.h"
 #include <android-base/stringprintf.h>
 #include <sys/select.h>
 #include <sys/time.h>
@@ -8,15 +8,15 @@
 
 namespace fastboot {
 
-UsbTransportSniffer::UsbTransportSniffer(std::unique_ptr<UsbTransport> transport,
+TransportSniffer::TransportSniffer(std::unique_ptr<Transport> transport,
                                          const int serial_fd)
     : transport_(std::move(transport)), serial_fd_(serial_fd) {}
 
-UsbTransportSniffer::~UsbTransportSniffer() {
+TransportSniffer::~TransportSniffer() {
     Close();
 }
 
-ssize_t UsbTransportSniffer::Read(void* data, size_t len) {
+ssize_t TransportSniffer::Read(void* data, size_t len) {
     ProcessSerial();
 
     ssize_t ret = transport_->Read(data, len);
@@ -37,7 +37,7 @@
     return ret;
 }
 
-ssize_t UsbTransportSniffer::Write(const void* data, size_t len) {
+ssize_t TransportSniffer::Write(const void* data, size_t len) {
     ProcessSerial();
 
     size_t ret = transport_->Write(data, len);
@@ -58,11 +58,11 @@
     return ret;
 }
 
-int UsbTransportSniffer::Close() {
+int TransportSniffer::Close() {
     return transport_->Close();
 }
 
-int UsbTransportSniffer::Reset() {
+int TransportSniffer::Reset() {
     ProcessSerial();
     int ret = transport_->Reset();
     std::vector<char> buf;
@@ -72,7 +72,7 @@
     return ret;
 }
 
-const std::vector<UsbTransportSniffer::Event> UsbTransportSniffer::Transfers() {
+const std::vector<TransportSniffer::Event> TransportSniffer::Transfers() {
     return transfers_;
 }
 
@@ -81,7 +81,7 @@
  * the failure. This method will look through its log of captured events, and
  * create a clean printable string of everything that happened.
  */
-std::string UsbTransportSniffer::CreateTrace() {
+std::string TransportSniffer::CreateTrace() {
     std::string ret;
 
     const auto no_print = [](char c) -> bool { return !isprint(c); };
@@ -158,7 +158,7 @@
 
 // This is a quick call to flush any UART logs the device might have sent
 // to our internal event log. It will wait up to 10ms for data to appear
-void UsbTransportSniffer::ProcessSerial() {
+void TransportSniffer::ProcessSerial() {
     if (serial_fd_ <= 0) return;
 
     fd_set set;
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h b/fastboot/fuzzy_fastboot/transport_sniffer.h
similarity index 90%
rename from fastboot/fuzzy_fastboot/usb_transport_sniffer.h
rename to fastboot/fuzzy_fastboot/transport_sniffer.h
index 8119aea..2cbb9fe 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
+++ b/fastboot/fuzzy_fastboot/transport_sniffer.h
@@ -42,12 +42,12 @@
 /* A special class for sniffing reads and writes
  *
  * A useful debugging tool is to see the raw fastboot transactions going between
- * the host and device. This class wraps the UsbTransport class, and snoops and saves
+ * the host and device. This class is a special subclass of Transport that snoops and saves
  * all the transactions going on. Additionally, if there is a console serial port
  * from the device, this class can monitor it as well and capture the interleaving of
  * transport transactions and UART log messages.
  */
-class UsbTransportSniffer : public UsbTransport {
+class TransportSniffer : public Transport {
   public:
     enum EventType {
         READ,
@@ -67,8 +67,8 @@
         const std::vector<char> buf;
     };
 
-    UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, const int serial_fd = 0);
-    ~UsbTransportSniffer() override;
+    TransportSniffer(std::unique_ptr<Transport> transport, const int serial_fd = 0);
+    ~TransportSniffer() override;
 
     virtual ssize_t Read(void* data, size_t len) override;
     virtual ssize_t Write(const void* data, size_t len) override;
@@ -81,7 +81,7 @@
 
   private:
     std::vector<Event> transfers_;
-    std::unique_ptr<UsbTransport> transport_;
+    std::unique_ptr<Transport> transport_;
     const int serial_fd_;
 };
 
diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp
index dd6fbf8..dca306f 100644
--- a/fastboot/tcp.cpp
+++ b/fastboot/tcp.cpp
@@ -64,6 +64,7 @@
     ssize_t Read(void* data, size_t length) override;
     ssize_t Write(const void* data, size_t length) override;
     int Close() override;
+    int Reset() override;
 
   private:
     explicit TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {}
@@ -178,6 +179,10 @@
     return result;
 }
 
+int TcpTransport::Reset() {
+    return 0;
+}
+
 std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
     return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error),
                              error);
diff --git a/fastboot/transport.h b/fastboot/transport.h
index 96b90d2..de0cc92 100644
--- a/fastboot/transport.h
+++ b/fastboot/transport.h
@@ -36,6 +36,8 @@
     // Closes the underlying transport. Returns 0 on success.
     virtual int Close() = 0;
 
+    virtual int Reset() = 0;
+
     // Blocks until the transport disconnects. Transports that don't support
     // this will return immediately. Returns 0 on success.
     virtual int WaitForDisconnect() { return 0; }
diff --git a/fastboot/udp.cpp b/fastboot/udp.cpp
index 53fb347..308c96c 100644
--- a/fastboot/udp.cpp
+++ b/fastboot/udp.cpp
@@ -109,6 +109,7 @@
     ssize_t Read(void* data, size_t length) override;
     ssize_t Write(const void* data, size_t length) override;
     int Close() override;
+    int Reset() override;
 
   private:
     explicit UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {}
@@ -370,6 +371,10 @@
     return result;
 }
 
+int UdpTransport::Reset() {
+    return 0;
+}
+
 std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
     return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error),
                              error);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index eb737bb..7a88aa3 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -26,14 +26,14 @@
     ],
 }
 
-cc_library {
-    // Do not ever allow this library to be vendor_available as a shared library.
-    // It does not have a stable interface.
-    name: "libfs_mgr",
+cc_defaults {
+    name: "libfs_mgr_defaults",
     defaults: ["fs_mgr_defaults"],
-    recovery_available: true,
     export_include_dirs: ["include"],
     include_dirs: ["system/vold"],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+    ],
     srcs: [
         "file_wait.cpp",
         "fs_mgr.cpp",
@@ -43,6 +43,7 @@
         "fs_mgr_overlayfs.cpp",
         "fs_mgr_roots.cpp",
         "fs_mgr_vendor_overlay.cpp",
+        ":libfiemap_srcs",
     ],
     shared_libs: [
         "libbase",
@@ -88,6 +89,43 @@
             ],
         },
     },
+    header_libs: [
+        "libfiemap_headers",
+        "libstorage_literals_headers",
+    ],
+    export_header_lib_headers: [
+        "libfiemap_headers",
+    ],
+}
+
+// Two variants of libfs_mgr are provided: libfs_mgr and libfs_mgr_binder.
+// Use libfs_mgr in recovery, first-stage-init, or when libfiemap or overlayfs
+// is not used.
+//
+// Use libfs_mgr_binder when not in recovery/first-stage init, or when overlayfs
+// or libfiemap is needed. In this case, libfiemap will proxy over binder to
+// gsid.
+cc_library {
+    // Do not ever allow this library to be vendor_available as a shared library.
+    // It does not have a stable interface.
+    name: "libfs_mgr",
+    recovery_available: true,
+    defaults: [
+        "libfs_mgr_defaults",
+    ],
+    srcs: [
+        ":libfiemap_passthrough_srcs",
+    ],
+}
+
+cc_library {
+    // Do not ever allow this library to be vendor_available as a shared library.
+    // It does not have a stable interface.
+    name: "libfs_mgr_binder",
+    defaults: [
+        "libfs_mgr_defaults",
+        "libfiemap_binder_defaults",
+    ],
 }
 
 cc_library_static {
@@ -128,7 +166,7 @@
         "libcrypto",
         "libext4_utils",
         "libfec",
-        "libfs_mgr",
+        "libfs_mgr_binder",
         "liblog",
         "liblp",
         "libselinux",
@@ -150,4 +188,26 @@
             ],
         },
     },
+    required: [
+        "clean_scratch_files",
+    ],
+}
+
+cc_binary {
+    name: "clean_scratch_files",
+    defaults: ["fs_mgr_defaults"],
+    shared_libs: [
+        "libbase",
+        "libfs_mgr_binder",
+    ],
+    srcs: [
+        "clean_scratch_files.cpp",
+    ],
+    product_variables: {
+        debuggable: {
+            init_rc: [
+                "clean_scratch_files.rc",
+            ],
+        },
+    },
 }
diff --git a/fs_mgr/clean_scratch_files.cpp b/fs_mgr/clean_scratch_files.cpp
new file mode 100644
index 0000000..42fe35a
--- /dev/null
+++ b/fs_mgr/clean_scratch_files.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fs_mgr_overlayfs.h>
+
+int main() {
+    android::fs_mgr::CleanupOldScratchFiles();
+    return 0;
+}
diff --git a/fs_mgr/clean_scratch_files.rc b/fs_mgr/clean_scratch_files.rc
new file mode 100644
index 0000000..738d1aa
--- /dev/null
+++ b/fs_mgr/clean_scratch_files.rc
@@ -0,0 +1,2 @@
+on post-fs-data && property:ro.debuggable=1
+    exec_background - root root -- clean_scratch_files
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 5c0401e..9180a06 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -34,6 +34,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <functional>
 #include <map>
 #include <memory>
@@ -42,6 +43,7 @@
 #include <utility>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -84,6 +86,7 @@
 #define ZRAM_BACK_DEV   "/sys/block/zram0/backing_dev"
 
 #define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
+#define SYSFS_EXT4_CASEFOLD "/sys/fs/ext4/features/casefold"
 
 // FIXME: this should be in system/extras
 #define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
@@ -94,9 +97,11 @@
 using android::base::GetBoolProperty;
 using android::base::Realpath;
 using android::base::StartsWith;
+using android::base::Timer;
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
+using android::dm::DmTargetLinear;
 
 // Realistically, this file should be part of the android::fs_mgr namespace;
 using namespace android::fs_mgr;
@@ -120,6 +125,7 @@
     FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
     FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
     FS_STAT_ENABLE_VERITY_FAILED = 0x80000,
+    FS_STAT_ENABLE_CASEFOLD_FAILED = 0x100000,
 };
 
 static void log_fs_stat(const std::string& blk_device, int fs_stat) {
@@ -341,6 +347,7 @@
                        const struct ext4_super_block* sb, int* fs_stat) {
     bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
     bool want_quota = entry.fs_mgr_flags.quota;
+    bool want_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
 
     if (has_quota == want_quota) {
         return;
@@ -357,12 +364,16 @@
     if (want_quota) {
         LINFO << "Enabling quotas on " << blk_device;
         argv[1] = "-Oquota";
-        argv[2] = "-Qusrquota,grpquota";
+        // Once usr/grp unneeded, make just prjquota to save overhead
+        if (want_projid)
+            argv[2] = "-Qusrquota,grpquota,prjquota";
+        else
+            argv[2] = "-Qusrquota,grpquota";
         *fs_stat |= FS_STAT_QUOTA_ENABLED;
     } else {
         LINFO << "Disabling quotas on " << blk_device;
         argv[1] = "-O^quota";
-        argv[2] = "-Q^usrquota,^grpquota";
+        argv[2] = "-Q^usrquota,^grpquota,^prjquota";
     }
 
     if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
@@ -495,6 +506,42 @@
     }
 }
 
+// Enable casefold if needed.
+static void tune_casefold(const std::string& blk_device, const struct ext4_super_block* sb,
+                          int* fs_stat) {
+    bool has_casefold =
+            (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;
+    bool wants_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
+
+    if (!wants_casefold || has_casefold) return;
+
+    std::string casefold_support;
+    if (!android::base::ReadFileToString(SYSFS_EXT4_CASEFOLD, &casefold_support)) {
+        LERROR << "Failed to open " << SYSFS_EXT4_CASEFOLD;
+        return;
+    }
+
+    if (!(android::base::Trim(casefold_support) == "supported")) {
+        LERROR << "Current ext4 casefolding not supported by kernel";
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable ext4 casefold on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    LINFO << "Enabling ext4 casefold on " << blk_device;
+
+    const char* argv[] = {TUNE2FS_BIN, "-O", "casefold", "-E", "encoding=utf8", blk_device.c_str()};
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 casefold on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_CASEFOLD_FAILED;
+    }
+}
+
 // Read the primary superblock from an f2fs filesystem.  On failure return
 // false.  If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
 #define F2FS_BLKSIZE 4096
@@ -592,6 +639,7 @@
             tune_reserved_size(blk_device, entry, &sb, &fs_stat);
             tune_encrypt(blk_device, entry, &sb, &fs_stat);
             tune_verity(blk_device, entry, &sb, &fs_stat);
+            tune_casefold(blk_device, &sb, &fs_stat);
         }
     }
 
@@ -907,6 +955,10 @@
     return true;
 }
 
+static bool SupportsCheckpoint(FstabEntry* entry) {
+    return entry->fs_mgr_flags.checkpoint_blk || entry->fs_mgr_flags.checkpoint_fs;
+}
+
 class CheckpointManager {
   public:
     CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
@@ -923,7 +975,7 @@
     }
 
     bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
-        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+        if (!SupportsCheckpoint(entry)) {
             return true;
         }
 
@@ -944,7 +996,7 @@
     }
 
     bool Revert(FstabEntry* entry) {
-        if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
+        if (!SupportsCheckpoint(entry)) {
             return true;
         }
 
@@ -1063,6 +1115,83 @@
     }
 }
 
+static constexpr const char* kUserdataWrapperName = "userdata-wrapper";
+
+static void WrapUserdata(FstabEntry* entry, dev_t dev, const std::string& block_device) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (dm.GetState(kUserdataWrapperName) != DmDeviceState::INVALID) {
+        // This will report failure for us. If we do fail to get the path,
+        // we leave the device unwrapped.
+        dm.GetDmDevicePathByName(kUserdataWrapperName, &entry->blk_device);
+        return;
+    }
+
+    unique_fd fd(open(block_device.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) {
+        PLOG(ERROR) << "open failed: " << entry->blk_device;
+        return;
+    }
+
+    auto dev_str = android::base::StringPrintf("%u:%u", major(dev), minor(dev));
+    uint64_t sectors = get_block_device_size(fd) / 512;
+
+    android::dm::DmTable table;
+    table.Emplace<DmTargetLinear>(0, sectors, dev_str, 0);
+
+    std::string dm_path;
+    if (!dm.CreateDevice(kUserdataWrapperName, table, &dm_path, 20s)) {
+        LOG(ERROR) << "Failed to create userdata wrapper device";
+        return;
+    }
+    entry->blk_device = dm_path;
+}
+
+// When using Virtual A/B, partitions can be backed by /data and mapped with
+// device-mapper in first-stage init. This can happen when merging an OTA or
+// when using adb remount to house "scratch". In this case, /data cannot be
+// mounted directly off the userdata block device, and e2fsck will refuse to
+// scan it, because the kernel reports the block device as in-use.
+//
+// As a workaround, when mounting /data, we create a trivial dm-linear wrapper
+// if the underlying block device already has dependencies. Note that we make
+// an exception for metadata-encrypted devices, since dm-default-key is already
+// a wrapper.
+static void WrapUserdataIfNeeded(FstabEntry* entry, const std::string& actual_block_device = {}) {
+    const auto& block_device =
+            actual_block_device.empty() ? entry->blk_device : actual_block_device;
+    if (entry->mount_point != "/data" || !entry->key_dir.empty() ||
+        android::base::StartsWith(block_device, "/dev/block/dm-")) {
+        return;
+    }
+
+    struct stat st;
+    if (stat(block_device.c_str(), &st) < 0) {
+        PLOG(ERROR) << "stat failed: " << block_device;
+        return;
+    }
+
+    std::string path = android::base::StringPrintf("/sys/dev/block/%u:%u/holders",
+                                                   major(st.st_rdev), minor(st.st_rdev));
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+    if (!dir) {
+        PLOG(ERROR) << "opendir failed: " << path;
+        return;
+    }
+
+    struct dirent* d;
+    bool has_holders = false;
+    while ((d = readdir(dir.get())) != nullptr) {
+        if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
+            has_holders = true;
+            break;
+        }
+    }
+
+    if (has_holders) {
+        WrapUserdata(entry, st.st_rdev, block_device);
+    }
+}
+
 static bool IsMountPointMounted(const std::string& mount_point) {
     // Check if this is already mounted.
     Fstab fstab;
@@ -1085,7 +1214,9 @@
         return FS_MGR_MNTALL_FAIL;
     }
 
-    for (size_t i = 0; i < fstab->size(); i++) {
+    // Keep i int to prevent unsigned integer overflow from (i = top_idx - 1),
+    // where top_idx is 0. It will give SIGABRT
+    for (int i = 0; i < static_cast<int>(fstab->size()); i++) {
         auto& current_entry = (*fstab)[i];
 
         // If a filesystem should have been mounted in the first stage, we
@@ -1140,6 +1271,8 @@
             }
         }
 
+        WrapUserdataIfNeeded(&current_entry);
+
         if (!checkpoint_manager.Update(&current_entry)) {
             continue;
         }
@@ -1358,6 +1491,49 @@
     return ret;
 }
 
+static bool fs_mgr_unmount_all_data_mounts(const std::string& block_device) {
+    LINFO << __FUNCTION__ << "(): about to umount everything on top of " << block_device;
+    Timer t;
+    // TODO(b/135984674): should be configured via a read-only property.
+    std::chrono::milliseconds timeout = 5s;
+    while (true) {
+        bool umount_done = true;
+        Fstab proc_mounts;
+        if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+            LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
+            return false;
+        }
+        // Now proceed with other bind mounts on top of /data.
+        for (const auto& entry : proc_mounts) {
+            if (entry.blk_device == block_device) {
+                if (umount2(entry.mount_point.c_str(), 0) != 0) {
+                    PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
+                    umount_done = false;
+                }
+            }
+        }
+        if (umount_done) {
+            LINFO << __FUNCTION__ << "(): Unmounting /data took " << t;
+            return true;
+        }
+        if (t.duration() > timeout) {
+            LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on " << block_device;
+            Fstab remaining_mounts;
+            if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
+                LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
+            } else {
+                LERROR << __FUNCTION__ << "(): Following mounts remaining";
+                for (const auto& e : remaining_mounts) {
+                    LERROR << __FUNCTION__ << "(): mount point: " << e.mount_point
+                           << " block device: " << e.blk_device;
+                }
+            }
+            return false;
+        }
+        std::this_thread::sleep_for(50ms);
+    }
+}
+
 // TODO(b/143970043): return different error codes based on which step failed.
 int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
     Fstab proc_mounts;
@@ -1367,6 +1543,9 @@
     }
     std::string block_device;
     if (auto entry = GetEntryForMountPoint(&proc_mounts, "/data"); entry != nullptr) {
+        // Note: we don't care about a userdata wrapper here, since it's safe
+        // to remount on top of the bow device instead, there will be no
+        // conflicts.
         block_device = entry->blk_device;
     } else {
         LERROR << "/data is not mounted";
@@ -1377,18 +1556,20 @@
         LERROR << "Can't find /data in fstab";
         return -1;
     }
-    if (!fstab_entry->fs_mgr_flags.checkpoint_blk && !fstab_entry->fs_mgr_flags.checkpoint_fs) {
+    bool force_umount = GetBoolProperty("sys.init.userdata_remount.force_umount", false);
+    if (force_umount) {
+        LINFO << "Will force an umount of userdata even if it's not required";
+    }
+    if (!force_umount && !SupportsCheckpoint(fstab_entry)) {
         LINFO << "Userdata doesn't support checkpointing. Nothing to do";
         return 0;
     }
     CheckpointManager checkpoint_manager;
-    if (!checkpoint_manager.NeedsCheckpoint()) {
+    if (!force_umount && !checkpoint_manager.NeedsCheckpoint()) {
         LINFO << "Checkpointing not needed. Don't remount";
         return 0;
     }
-    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) {
+    if (!force_umount && fstab_entry->fs_mgr_flags.checkpoint_fs) {
         // Userdata is f2fs, simply remount it.
         if (!checkpoint_manager.Update(fstab_entry)) {
             LERROR << "Failed to remount userdata in checkpointing mode";
@@ -1401,17 +1582,8 @@
         }
     } else {
         LINFO << "Unmounting /data before remounting into checkpointing mode";
-        // First make sure that all the bind-mounts on top of /data are unmounted.
-        for (const auto& entry : proc_mounts) {
-            if (entry.blk_device == block_device && entry.mount_point != "/data") {
-                LINFO << "Unmounting bind-mount " << entry.mount_point;
-                if (umount2(entry.mount_point.c_str(), UMOUNT_NOFOLLOW) != 0) {
-                    PWARNING << "Failed to unmount " << entry.mount_point;
-                }
-            }
-        }
-        if (umount2("/data", UMOUNT_NOFOLLOW) != 0) {
-            PERROR << "Failed to umount /data";
+        if (!fs_mgr_unmount_all_data_mounts(block_device)) {
+            LERROR << "Failed to umount /data";
             return -1;
         }
         DeviceMapper& dm = DeviceMapper::Instance();
@@ -1492,6 +1664,8 @@
             }
         }
 
+        WrapUserdataIfNeeded(&fstab_entry, n_blk_device);
+
         if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) {
             LERROR << "Could not set up checkpoint partition, skipping!";
             continue;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 0dcb9fe..ea9c957 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -151,6 +151,10 @@
             LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
             continue;
         }
+        if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
+            LINFO << "Skipping disabled partition: " << GetPartitionName(partition);
+            continue;
+        }
 
         params.partition = &partition;
 
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 27971da..ca19782 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -48,15 +48,21 @@
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
 #include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
+#include <storage_literals/storage_literals.h>
 
 #include "fs_mgr_priv.h"
+#include "libfiemap/utility.h"
 
 using namespace std::literals;
 using namespace android::dm;
 using namespace android::fs_mgr;
+using namespace android::storage_literals;
+using android::fiemap::FilesystemHasReliablePinning;
+using android::fiemap::IImageManager;
 
 namespace {
 
@@ -104,6 +110,14 @@
     return false;
 }
 
+namespace android {
+namespace fs_mgr {
+
+void MapScratchPartitionIfNeeded(Fstab*, const std::function<bool(const std::string&)>&) {}
+
+}  // namespace fs_mgr
+}  // namespace android
+
 #else  // ALLOW_ADBD_DISABLE_VERITY == 0
 
 namespace {
@@ -153,6 +167,12 @@
 }
 
 const auto kPhysicalDevice = "/dev/block/by-name/"s;
+constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
+
+// Note: this is meant only for recovery/first-stage init.
+bool ScratchIsOnData() {
+    return fs_mgr_access(kScratchImageMetadata);
+}
 
 bool fs_mgr_update_blk_device(FstabEntry* entry) {
     if (entry->fs_mgr_flags.logical) {
@@ -443,20 +463,37 @@
 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;
-    auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    if (!fs_mgr_rw_access(super_device)) return true;
 
     auto save_errno = errno;
     if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         fs_mgr_overlayfs_umount_scratch();
     }
+
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+
+    auto images = IImageManager::Open("remount", 10s);
+    if (images && images->BackingImageExists(partition_name)) {
+#if defined __ANDROID_RECOVERY__
+        if (!images->DisableImage(partition_name)) {
+            return false;
+        }
+#else
+        if (!images->UnmapImageIfExists(partition_name) ||
+            !images->DeleteBackingImage(partition_name)) {
+            return false;
+        }
+#endif
+    }
+
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return true;
+
     auto builder = MetadataBuilder::New(super_device, slot_number);
     if (!builder) {
         errno = save_errno;
         return true;
     }
-    const auto partition_name = android::base::Basename(kScratchMountPoint);
     if (builder->FindPartition(partition_name) == nullptr) {
         errno = save_errno;
         return true;
@@ -811,66 +848,49 @@
     return "auto";
 }
 
-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
-};
-
-// Return the strategy this device must use for creating a scratch partition.
-static ScratchStrategy GetScratchStrategy(std::string* backing_device = nullptr) {
+// Note: we do not check access() here except for the super partition, since
+// in first-stage init we wouldn't have registed by-name symlinks for "other"
+// partitions that won't be mounted.
+static std::string GetPhysicalScratchDevice() {
     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) {
-        // 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;
+        return path;
     }
     if (fs_mgr_access(super_device)) {
-        if (backing_device) *backing_device = super_device;
-        return ScratchStrategy::kDynamicPartition;
+        // Do not try to use system_other on a DAP device.
+        return "";
     }
 
     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 kPhysicalDevice + "system" + other_slot;
     }
-    return ScratchStrategy::kNone;
+    return "";
 }
 
-// Return the scratch device if it exists.
-static std::string GetScratchDevice() {
-    std::string device;
-    ScratchStrategy strategy = GetScratchStrategy(&device);
+// This returns the scratch device that was detected during early boot (first-
+// stage init). If the device was created later, for example during setup for
+// the adb remount command, it can return an empty string since it does not
+// query ImageManager. (Note that ImageManager in first-stage init will always
+// use device-mapper, since /data is not available to use loop devices.)
+static std::string GetBootScratchDevice() {
+    auto& dm = DeviceMapper::Instance();
 
-    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 "";
+    // If there is a scratch partition allocated in /data or on super, we
+    // automatically prioritize that over super_other or system_other.
+    // Some devices, for example, have a write-protected eMMC and the
+    // super partition cannot be used even if it exists.
+    std::string device;
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+    if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
+        dm.GetDmDevicePathByName(partition_name, &device)) {
+        return device;
     }
+
+    // There is no dynamic scratch, so try and find a physical one.
+    return GetPhysicalScratchDevice();
 }
 
 bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
@@ -915,8 +935,8 @@
 }
 
 // Create or update a scratch partition within super.
-static bool CreateDynamicScratch(const Fstab& fstab, std::string* scratch_device,
-                                 bool* partition_exists, bool* change) {
+static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists,
+                                 bool* change) {
     const auto partition_name = android::base::Basename(kScratchMountPoint);
 
     auto& dm = DeviceMapper::Instance();
@@ -925,8 +945,6 @@
     auto partition_create = !*partition_exists;
     auto slot_number = fs_mgr_overlayfs_slot_number();
     auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    if (!fs_mgr_rw_access(super_device)) return false;
-    if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
     auto builder = MetadataBuilder::New(super_device, slot_number);
     if (!builder) {
         LERROR << "open " << super_device << " metadata";
@@ -1012,25 +1030,92 @@
     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);
-    }
+static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
+    *partition_exists = false;
+    *change = false;
 
-    // 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;
+    auto images = IImageManager::Open("remount", 10s);
+    if (!images) {
         return false;
     }
-    *partition_exists = true;
+
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+    if (images->GetMappedImageDevice(partition_name, scratch_device)) {
+        *partition_exists = true;
+        return true;
+    }
+
+    BlockDeviceInfo info;
+    PartitionOpener opener;
+    if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &info)) {
+        LERROR << "could not get block device info for super";
+        return false;
+    }
+
+    *change = true;
+
+    // Note: calling RemoveDisabledImages here ensures that we do not race with
+    // clean_scratch_files and accidentally try to map an image that will be
+    // deleted.
+    if (!images->RemoveDisabledImages()) {
+        return false;
+    }
+    if (!images->BackingImageExists(partition_name)) {
+        static constexpr uint64_t kMinimumSize = 16_MiB;
+        static constexpr uint64_t kMaximumSize = 2_GiB;
+
+        uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
+        auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
+
+        if (!images->CreateBackingImage(partition_name, size, flags)) {
+            LERROR << "could not create scratch image of " << size << " bytes";
+            return false;
+        }
+    }
+    if (!images->MapImageDevice(partition_name, 10s, scratch_device)) {
+        LERROR << "could not map scratch image";
+        return false;
+    }
     return true;
 }
 
+static bool CanUseSuperPartition(const Fstab& fstab, bool* is_virtual_ab) {
+    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) || !fs_mgr_overlayfs_has_logical(fstab)) {
+        return false;
+    }
+    auto metadata = ReadMetadata(super_device, slot_number);
+    if (!metadata) {
+        return false;
+    }
+    *is_virtual_ab = !!(metadata->header.flags & LP_HEADER_FLAG_VIRTUAL_AB_DEVICE);
+    return true;
+}
+
+bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
+                                     bool* partition_exists, bool* change) {
+    // Try a physical partition first.
+    *scratch_device = GetPhysicalScratchDevice();
+    if (!scratch_device->empty() && fs_mgr_rw_access(*scratch_device)) {
+        *partition_exists = true;
+        return true;
+    }
+
+    // If that fails, see if we can land on super.
+    bool is_virtual_ab;
+    if (CanUseSuperPartition(fstab, &is_virtual_ab)) {
+        bool can_use_data = false;
+        if (is_virtual_ab && FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
+            return CreateScratchOnData(scratch_device, partition_exists, change);
+        }
+        return CreateDynamicScratch(scratch_device, partition_exists, change);
+    }
+
+    errno = ENXIO;
+    return false;
+}
+
 // 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;
@@ -1065,19 +1150,6 @@
     return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
 }
 
-bool fs_mgr_overlayfs_scratch_can_be_mounted(const std::string& scratch_device) {
-    if (scratch_device.empty()) return false;
-    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return false;
-    if (android::base::StartsWith(scratch_device, kPhysicalDevice)) return true;
-    if (fs_mgr_rw_access(scratch_device)) return true;
-    auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    if (!fs_mgr_rw_access(super_device)) return false;
-    auto builder = MetadataBuilder::New(super_device, slot_number);
-    if (!builder) return false;
-    return builder->FindPartition(android::base::Basename(kScratchMountPoint)) != nullptr;
-}
-
 bool fs_mgr_overlayfs_invalid() {
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
 
@@ -1120,8 +1192,13 @@
 }
 
 static void TryMountScratch() {
-    auto scratch_device = GetScratchDevice();
-    if (!fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device)) {
+    // Note we get the boot scratch device here, which means if scratch was
+    // just created through ImageManager, this could fail. In practice this
+    // should not happen because "remount" detects this scenario (by checking
+    // if verity is still disabled, i.e. no reboot occurred), and skips calling
+    // fs_mgr_overlayfs_mount_all().
+    auto scratch_device = GetBootScratchDevice();
+    if (!fs_mgr_rw_access(scratch_device)) {
         return;
     }
     if (!WaitForFile(scratch_device, 10s)) {
@@ -1159,23 +1236,6 @@
     return ret;
 }
 
-std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab* fstab) {
-    if (fs_mgr_overlayfs_invalid()) return {};
-
-    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
-        return {};
-    }
-
-    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 = GetScratchDevice();
-        if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
-        return {device};
-    }
-    return {};
-}
-
 // Returns false if setup not permitted, errno set to last error.
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change,
@@ -1241,25 +1301,58 @@
     return ret;
 }
 
-static bool GetAndMapScratchDeviceIfNeeded(std::string* device) {
-    *device = GetScratchDevice();
+static bool EnsureScratchMapped(std::string* device, bool* mapped) {
+    *mapped = false;
+    *device = GetBootScratchDevice();
     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);
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+
+    // Check for scratch on /data first, before looking for a modified super
+    // partition. We should only reach this code in recovery, because scratch
+    // would otherwise always be mapped.
+    auto images = IImageManager::Open("remount", 10s);
+    if (images && images->BackingImageExists(partition_name)) {
+        if (!images->MapImageDevice(partition_name, 10s, device)) {
+            return false;
+        }
+        *mapped = true;
+        return true;
     }
-    return false;
+
+    // Avoid uart spam by first checking for a scratch partition.
+    auto metadata_slot = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
+    auto metadata = ReadCurrentMetadata(super_device);
+    if (!metadata) {
+        return false;
+    }
+
+    auto partition = FindPartition(*metadata.get(), partition_name);
+    if (!partition) {
+        return false;
+    }
+
+    CreateLogicalPartitionParams params = {
+            .block_device = super_device,
+            .metadata = metadata.get(),
+            .partition = partition,
+            .force_writable = true,
+            .timeout_ms = 10s,
+    };
+    if (!CreateLogicalPartition(params, device)) {
+        return false;
+    }
+    *mapped = true;
+    return true;
+}
+
+static void UnmapScratchDevice() {
+    // This should only be reachable in recovery, where scratch is not
+    // automatically mapped and therefore can be unmapped.
+    DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
 }
 
 // Returns false if teardown not permitted, errno set to last error.
@@ -1267,12 +1360,14 @@
 bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
     if (change) *change = false;
     auto ret = true;
+
     // If scratch exists, but is not mounted, lets gain access to clean
     // specific override entries.
     auto mount_scratch = false;
+    bool unmap = false;
     if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         std::string scratch_device;
-        if (GetAndMapScratchDeviceIfNeeded(&scratch_device)) {
+        if (EnsureScratchMapped(&scratch_device, &unmap)) {
             mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
                                                            fs_mgr_overlayfs_scratch_mount_type());
         }
@@ -1294,8 +1389,12 @@
         PERROR << "teardown";
         ret = false;
     }
-    if (mount_scratch) fs_mgr_overlayfs_umount_scratch();
-
+    if (mount_scratch) {
+        fs_mgr_overlayfs_umount_scratch();
+    }
+    if (unmap) {
+        UnmapScratchDevice();
+    }
     return ret;
 }
 
@@ -1313,6 +1412,59 @@
     return false;
 }
 
+namespace android {
+namespace fs_mgr {
+
+void MapScratchPartitionIfNeeded(Fstab* fstab,
+                                 const std::function<bool(const std::set<std::string>&)>& init) {
+    if (fs_mgr_overlayfs_invalid()) {
+        return;
+    }
+    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
+        return;
+    }
+
+    bool want_scratch = false;
+    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;
+        }
+        want_scratch = true;
+        break;
+    }
+    if (!want_scratch) {
+        return;
+    }
+
+    if (ScratchIsOnData()) {
+        if (auto images = IImageManager::Open("remount", 0ms)) {
+            images->MapAllImages(init);
+        }
+    }
+
+    // Physical or logical partitions will have already been mapped here,
+    // so just ensure /dev/block symlinks exist.
+    auto device = GetBootScratchDevice();
+    if (!device.empty()) {
+        init({android::base::Basename(device)});
+    }
+}
+
+void CleanupOldScratchFiles() {
+    if (!ScratchIsOnData()) {
+        return;
+    }
+    if (auto images = IImageManager::Open("remount", 0ms)) {
+        images->RemoveDisabledImages();
+    }
+}
+
+}  // namespace fs_mgr
+}  // namespace android
+
 #endif  // ALLOW_ADBD_DISABLE_VERITY != 0
 
 bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 9a7381f..34aded9 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -16,8 +16,11 @@
 
 #pragma once
 
+#include <functional>
+
 #include <fstab/fstab.h>
 
+#include <set>
 #include <string>
 #include <vector>
 
@@ -38,3 +41,13 @@
     kOverrideCredsRequired,
 };
 OverlayfsValidResult fs_mgr_overlayfs_valid();
+
+namespace android {
+namespace fs_mgr {
+
+void MapScratchPartitionIfNeeded(Fstab* fstab,
+                                 const std::function<bool(const std::set<std::string>&)>& init);
+void CleanupOldScratchFiles();
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 8dbbf4c..1bf457f 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -20,71 +20,42 @@
     export_include_dirs: ["include"],
 }
 
-cc_defaults {
-    name: "libfiemap_defaults",
-    defaults: ["fs_mgr_defaults"],
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
-    ],
-
+filegroup {
+    name: "libfiemap_srcs",
     srcs: [
         "fiemap_writer.cpp",
+        "fiemap_status.cpp",
         "image_manager.cpp",
         "metadata.cpp",
         "split_fiemap_writer.cpp",
         "utility.cpp",
     ],
-
-    static_libs: [
-        "libdm",
-        "libext2_uuid",
-        "libext4_utils",
-        "liblp",
-        "libfs_mgr",
-    ],
-
-    shared_libs: [
-        "libbase",
-    ],
-
-    header_libs: [
-        "libfiemap_headers",
-        "liblog_headers",
-    ],
-
-    export_shared_lib_headers: [
-        "libbase",
-    ],
-
-    export_header_lib_headers: [
-        "libfiemap_headers",
-    ],
 }
 
-// Open up a binder IImageManager interface.
-cc_library_static {
-    name: "libfiemap_binder",
-    defaults: ["libfiemap_defaults"],
+filegroup {
+    name: "libfiemap_binder_srcs",
     srcs: [
         "binder.cpp",
     ],
+}
+
+cc_defaults {
+    name: "libfiemap_binder_defaults",
+    srcs: [":libfiemap_binder_srcs"],
     whole_static_libs: [
         "gsi_aidl_interface-cpp",
         "libgsi",
     ],
     shared_libs: [
         "libbinder",
+        "libutils",
     ],
 }
 
 // Open up a passthrough IImageManager interface. Use libfiemap_binder whenever
 // possible. This should only be used when binder is not available.
-cc_library_static {
-    name: "libfiemap_passthrough",
-    defaults: ["libfiemap_defaults"],
-    recovery_available: true,
+filegroup {
+    name: "libfiemap_passthrough_srcs",
     srcs: [
         "passthrough.cpp",
     ],
@@ -92,10 +63,10 @@
 
 cc_test {
     name: "fiemap_writer_test",
-    defaults: ["libfiemap_defaults"],
     static_libs: [
         "libbase",
         "libdm",
+        "libfs_mgr",
         "liblog",
     ],
 
@@ -108,11 +79,15 @@
     srcs: [
         "fiemap_writer_test.cpp",
     ],
+
+    test_suites: ["vts-core", "device-tests"],
+    auto_gen_config: true,
+    test_min_api_level: 29,
+    require_root: true,
 }
 
 cc_test {
     name: "fiemap_image_test",
-    defaults: ["libfiemap_defaults"],
     static_libs: [
         "libdm",
         "libext4_utils",
@@ -120,6 +95,7 @@
         "liblp",
     ],
     shared_libs: [
+        "libbase",
         "libcrypto",
         "libcrypto_utils",
         "libcutils",
@@ -129,7 +105,3 @@
         "image_test.cpp",
     ],
 }
-
-vts_config {
-    name: "VtsFiemapWriterTest",
-}
diff --git a/fs_mgr/libfiemap/AndroidTest.xml b/fs_mgr/libfiemap/AndroidTest.xml
deleted file mode 100644
index 44c80fc..0000000
--- a/fs_mgr/libfiemap/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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 VTS VtsFiemapWriterTest">
-    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="abort-on-push-failure" value="false"/>
-        <option name="push-group" value="HostDrivenTest.push"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-      <option name="test-module-name" value="VtsFiemapWriterTest"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
-        <option name="binary-test-type" value="gtest"/>
-        <option name="precondition-first-api-level" value="29" />
-        <option name="test-timeout" value="1m"/>
-    </test>
-</configuration>
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 49779f4..96c36ed 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -17,6 +17,7 @@
 #if !defined(__ANDROID_RECOVERY__)
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android/gsi/BnProgressCallback.h>
 #include <android/gsi/IGsiService.h>
 #include <android/gsi/IGsid.h>
 #include <binder/IServiceManager.h>
@@ -29,10 +30,29 @@
 using namespace android::gsi;
 using namespace std::chrono_literals;
 
+class ProgressCallback final : public BnProgressCallback {
+  public:
+    ProgressCallback(std::function<bool(uint64_t, uint64_t)>&& callback)
+        : callback_(std::move(callback)) {
+        CHECK(callback_);
+    }
+    android::binder::Status onProgress(int64_t current, int64_t total) {
+        if (callback_(static_cast<uint64_t>(current), static_cast<uint64_t>(total))) {
+            return android::binder::Status::ok();
+        }
+        return android::binder::Status::fromServiceSpecificError(UNKNOWN_ERROR,
+                                                                 "Progress callback failed");
+    }
+
+  private:
+    std::function<bool(uint64_t, uint64_t)> callback_;
+};
+
 class ImageManagerBinder final : public IImageManager {
   public:
     ImageManagerBinder(android::sp<IGsiService>&& service, android::sp<IImageService>&& manager);
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
+    FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,
+                                    std::function<bool(uint64_t, uint64_t)>&& on_progress) override;
     bool DeleteBackingImage(const std::string& name) override;
     bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
                         std::string* path) override;
@@ -41,8 +61,12 @@
     bool IsImageMapped(const std::string& name) override;
     bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
                                   std::string* dev) override;
-    bool ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
+    FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
     bool RemoveAllImages() override;
+    bool DisableImage(const std::string& name) override;
+    bool RemoveDisabledImages() override;
+    bool GetMappedImageDevice(const std::string& name, std::string* device) override;
+    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
 
     std::vector<std::string> GetAllBackingImages() override;
 
@@ -51,18 +75,31 @@
     android::sp<IImageService> manager_;
 };
 
+static FiemapStatus ToFiemapStatus(const char* func, const binder::Status& status) {
+    if (!status.isOk()) {
+        LOG(ERROR) << func << " binder returned: " << status.toString8().string();
+        if (status.serviceSpecificErrorCode() != 0) {
+            return FiemapStatus::FromErrorCode(status.serviceSpecificErrorCode());
+        } else {
+            return FiemapStatus::Error();
+        }
+    }
+    return FiemapStatus::Ok();
+}
+
 ImageManagerBinder::ImageManagerBinder(android::sp<IGsiService>&& service,
                                        android::sp<IImageService>&& manager)
     : service_(std::move(service)), manager_(std::move(manager)) {}
 
-bool ImageManagerBinder::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
-    auto status = manager_->createBackingImage(name, size, flags);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
+FiemapStatus ImageManagerBinder::CreateBackingImage(
+        const std::string& name, uint64_t size, int flags,
+        std::function<bool(uint64_t, uint64_t)>&& on_progress) {
+    sp<IProgressCallback> callback = nullptr;
+    if (on_progress) {
+        callback = new ProgressCallback(std::move(on_progress));
     }
-    return true;
+    auto status = manager_->createBackingImage(name, size, flags, callback);
+    return ToFiemapStatus(__PRETTY_FUNCTION__, status);
 }
 
 bool ImageManagerBinder::DeleteBackingImage(const std::string& name) {
@@ -143,14 +180,9 @@
     return retval;
 }
 
-bool ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
+FiemapStatus ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
     auto status = manager_->zeroFillNewImage(name, bytes);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return true;
+    return ToFiemapStatus(__PRETTY_FUNCTION__, status);
 }
 
 bool ImageManagerBinder::RemoveAllImages() {
@@ -163,6 +195,36 @@
     return true;
 }
 
+bool ImageManagerBinder::DisableImage(const std::string&) {
+    LOG(ERROR) << __PRETTY_FUNCTION__ << " is not available over binder";
+    return false;
+}
+
+bool ImageManagerBinder::RemoveDisabledImages() {
+    auto status = manager_->removeDisabledImages();
+    if (!status.isOk()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__
+                   << " binder returned: " << status.exceptionMessage().string();
+        return false;
+    }
+    return true;
+}
+
+bool ImageManagerBinder::GetMappedImageDevice(const std::string& name, std::string* device) {
+    auto status = manager_->getMappedImageDevice(name, device);
+    if (!status.isOk()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__
+                   << " binder returned: " << status.exceptionMessage().string();
+        return false;
+    }
+    return !device->empty();
+}
+
+bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {
+    LOG(ERROR) << __PRETTY_FUNCTION__ << " not available over binder";
+    return false;
+}
+
 static android::sp<IGsid> AcquireIGsid(const std::chrono::milliseconds& timeout_ms) {
     if (android::base::GetProperty("init.svc.gsid", "") != "running") {
         if (!android::base::SetProperty("ctl.start", "gsid") ||
diff --git a/fs_mgr/libfiemap/fiemap_status.cpp b/fs_mgr/libfiemap/fiemap_status.cpp
new file mode 100644
index 0000000..92ac935
--- /dev/null
+++ b/fs_mgr/libfiemap/fiemap_status.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 <libfiemap/fiemap_status.h>
+
+namespace android::fiemap {
+
+// FiemapStatus -> string
+std::string FiemapStatus::string() const {
+    if (error_code() == ErrorCode::ERROR) {
+        return "Error";
+    }
+    return strerror(-static_cast<int>(error_code()));
+}
+
+// -errno -> known ErrorCode
+// unknown ErrorCode -> known ErrorCode
+FiemapStatus::ErrorCode FiemapStatus::CastErrorCode(int error_code) {
+    switch (error_code) {
+        case static_cast<int32_t>(ErrorCode::SUCCESS):
+        case static_cast<int32_t>(ErrorCode::NO_SPACE):
+            return static_cast<ErrorCode>(error_code);
+        case static_cast<int32_t>(ErrorCode::ERROR):
+        default:
+            return ErrorCode::ERROR;
+    }
+}
+
+}  // namespace android::fiemap
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 961533e..b911234 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -233,8 +233,7 @@
     return sb.st_size;
 }
 
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
-                              uint32_t* fs_type) {
+static bool PerformFileChecks(const std::string& file_path, uint64_t* blocksz, uint32_t* fs_type) {
     struct statfs64 sfs;
     if (statfs64(file_path.c_str(), &sfs)) {
         PLOG(ERROR) << "Failed to read file system status at: " << file_path;
@@ -258,20 +257,14 @@
             return false;
     }
 
-    uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
-    if (access(file_path.c_str(), F_OK) != 0 && available_bytes <= file_size) {
-        LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
-        return false;
-    }
-
     *blocksz = sfs.f_bsize;
     *fs_type = sfs.f_type;
     return true;
 }
 
-static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
-                              const std::string& file_path,
-                              const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+static FiemapStatus FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
+                                      const std::string& file_path,
+                                      const std::function<bool(uint64_t, uint64_t)>& on_progress) {
     // Even though this is much faster than writing zeroes, it is still slow
     // enough that we need to fire the progress callback periodically. To
     // easily achieve this, we seek in chunks. We use 1000 chunks since
@@ -287,22 +280,22 @@
         auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
         if (rv < 0) {
             PLOG(ERROR) << "Failed to lseek " << file_path;
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
         if (rv != cursor - 1) {
             LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
-            return false;
+            return FiemapStatus::Error();
         }
         char buffer[] = {0};
         if (!android::base::WriteFully(file_fd, buffer, 1)) {
             PLOG(ERROR) << "Write failed: " << file_path;
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
         if (on_progress && !on_progress(cursor, file_size)) {
-            return false;
+            return FiemapStatus::Error();
         }
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
 // F2FS-specific ioctl
@@ -386,53 +379,22 @@
     return true;
 }
 
-// Reserve space for the file on the file system and write it out to make sure the extents
-// don't come back unwritten. Return from this function with the kernel file offset set to 0.
-// If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
-// aren't moved around.
-static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
-                         uint64_t file_size, unsigned int fs_type,
-                         std::function<bool(uint64_t, uint64_t)> on_progress) {
-    switch (fs_type) {
-        case EXT4_SUPER_MAGIC:
-            break;
-        case F2FS_SUPER_MAGIC: {
-            bool supported;
-            if (!F2fsPinBeforeAllocate(file_fd, &supported)) {
-                return false;
-            }
-            if (supported && !PinFile(file_fd, file_path, fs_type)) {
-                return false;
-            }
-            break;
-        }
-        case MSDOS_SUPER_MAGIC:
-            // fallocate() is not supported, and not needed, since VFAT does not support holes.
-            // Instead we can perform a much faster allocation.
-            return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
-        default:
-            LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
-            return false;
-    }
-
-    if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
-        PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
-        return false;
-    }
-
-    // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
-    // blocks are actually written to by the file system and thus getting rid of the holes in the
-    // file.
+// write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
+// blocks are actually written to by the file system and thus getting rid of the holes in the
+// file.
+static FiemapStatus WriteZeroes(int file_fd, const std::string& file_path, size_t blocksz,
+                                uint64_t file_size,
+                                const std::function<bool(uint64_t, uint64_t)>& on_progress) {
     auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
     if (buffer == nullptr) {
         LOG(ERROR) << "failed to allocate memory for writing file";
-        return false;
+        return FiemapStatus::Error();
     }
 
     off64_t offset = lseek64(file_fd, 0, SEEK_SET);
     if (offset < 0) {
         PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
-        return false;
+        return FiemapStatus::FromErrno(errno);
     }
 
     int permille = -1;
@@ -440,7 +402,7 @@
         if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
             PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
                         << " in file " << file_path;
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
 
         offset += blocksz;
@@ -450,7 +412,7 @@
         int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
         if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
             if (on_progress && !on_progress(offset, file_size)) {
-                return false;
+                return FiemapStatus::Error();
             }
             permille = new_permille;
         }
@@ -458,20 +420,67 @@
 
     if (lseek64(file_fd, 0, SEEK_SET) < 0) {
         PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
-        return false;
+        return FiemapStatus::FromErrno(errno);
+    }
+    return FiemapStatus::Ok();
+}
+
+// Reserve space for the file on the file system and write it out to make sure the extents
+// don't come back unwritten. Return from this function with the kernel file offset set to 0.
+// If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
+// aren't moved around.
+static FiemapStatus AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
+                                 uint64_t file_size, unsigned int fs_type,
+                                 std::function<bool(uint64_t, uint64_t)> on_progress) {
+    bool need_explicit_writes = true;
+    switch (fs_type) {
+        case EXT4_SUPER_MAGIC:
+            break;
+        case F2FS_SUPER_MAGIC: {
+            bool supported;
+            if (!F2fsPinBeforeAllocate(file_fd, &supported)) {
+                return FiemapStatus::Error();
+            }
+            if (supported) {
+                if (!PinFile(file_fd, file_path, fs_type)) {
+                    return FiemapStatus::Error();
+                }
+                need_explicit_writes = false;
+            }
+            break;
+        }
+        case MSDOS_SUPER_MAGIC:
+            // fallocate() is not supported, and not needed, since VFAT does not support holes.
+            // Instead we can perform a much faster allocation.
+            return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
+        default:
+            LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
+            return FiemapStatus::Error();
+    }
+
+    if (fallocate(file_fd, 0, 0, file_size)) {
+        PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
+        return FiemapStatus::FromErrno(errno);
+    }
+
+    if (need_explicit_writes) {
+        auto status = WriteZeroes(file_fd, file_path, blocksz, file_size, on_progress);
+        if (!status.is_ok()) {
+            return status;
+        }
     }
 
     // flush all writes here ..
     if (fsync(file_fd)) {
         PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
-        return false;
+        return FiemapStatus::FromErrno(errno);
     }
 
     // Send one last progress notification.
     if (on_progress && !on_progress(file_size, file_size)) {
-        return false;
+        return FiemapStatus::Error();
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
 bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
@@ -665,6 +674,18 @@
 
 FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
                                    std::function<bool(uint64_t, uint64_t)> progress) {
+    FiemapUniquePtr ret;
+    if (!Open(file_path, file_size, &ret, create, progress).is_ok()) {
+        return nullptr;
+    }
+    return ret;
+}
+
+FiemapStatus FiemapWriter::Open(const std::string& file_path, uint64_t file_size,
+                                FiemapUniquePtr* out, bool create,
+                                std::function<bool(uint64_t, uint64_t)> progress) {
+    out->reset();
+
     // if 'create' is false, open an existing file and do not truncate.
     int open_flags = O_RDWR | O_CLOEXEC;
     if (create) {
@@ -677,52 +698,55 @@
             TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
     if (file_fd < 0) {
         PLOG(ERROR) << "Failed to create file at: " << file_path;
-        return nullptr;
+        return FiemapStatus::FromErrno(errno);
     }
 
     std::string abs_path;
     if (!::android::base::Realpath(file_path, &abs_path)) {
+        int saved_errno = errno;
         PLOG(ERROR) << "Invalid file path: " << file_path;
         cleanup(file_path, create);
-        return nullptr;
+        return FiemapStatus::FromErrno(saved_errno);
     }
 
     std::string bdev_path;
     if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
         LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
         cleanup(abs_path, create);
-        return nullptr;
+        return FiemapStatus::Error();
     }
 
     ::android::base::unique_fd bdev_fd(
             TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
     if (bdev_fd < 0) {
+        int saved_errno = errno;
         PLOG(ERROR) << "Failed to open block device: " << bdev_path;
         cleanup(file_path, create);
-        return nullptr;
+        return FiemapStatus::FromErrno(saved_errno);
     }
 
     uint64_t bdevsz;
     if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
+        int saved_errno = errno;
         LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
         cleanup(file_path, create);
-        return nullptr;
+        return FiemapStatus::FromErrno(saved_errno);
     }
 
     if (!create) {
         file_size = GetFileSize(abs_path);
         if (file_size == 0) {
             LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
-            return nullptr;
+            return FiemapStatus::FromErrno(errno);
         }
     }
 
     uint64_t blocksz;
     uint32_t fs_type;
-    if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
+    if (!PerformFileChecks(abs_path, &blocksz, &fs_type)) {
         LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
         cleanup(abs_path, create);
-        return nullptr;
+        return FiemapStatus::Error();
     }
 
     // Align up to the nearest block size.
@@ -731,11 +755,13 @@
     }
 
     if (create) {
-        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
+        auto status =
+                AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress));
+        if (!status.is_ok()) {
             LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
                        << " bytes";
             cleanup(abs_path, create);
-            return nullptr;
+            return status;
         }
     }
 
@@ -743,7 +769,7 @@
     if (!PinFile(file_fd, abs_path, fs_type)) {
         cleanup(abs_path, create);
         LOG(ERROR) << "Failed to pin the file in storage";
-        return nullptr;
+        return FiemapStatus::Error();
     }
 
     // now allocate the FiemapWriter and start setting it up
@@ -754,14 +780,14 @@
             if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
                 LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
                 cleanup(abs_path, create);
-                return nullptr;
+                return FiemapStatus::Error();
             }
             break;
         case MSDOS_SUPER_MAGIC:
             if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
                 LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
                 cleanup(abs_path, create);
-                return nullptr;
+                return FiemapStatus::Error();
             }
             break;
     }
@@ -775,7 +801,8 @@
 
     LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
                  << bdev_path;
-    return fmap;
+    *out = std::move(fmap);
+    return FiemapStatus::Ok();
 }
 
 }  // namespace fiemap
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 4ac7161..22a3722 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -193,7 +193,9 @@
 }
 
 TEST_F(FiemapWriterTest, MaxBlockSize) {
-    ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
+    uint64_t max_piece_size = 0;
+    ASSERT_TRUE(DetermineMaximumFileSize(testfile, &max_piece_size));
+    ASSERT_GT(max_piece_size, 0);
 }
 
 TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index fe2018d..0195716 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -26,6 +26,7 @@
 #include <fs_mgr_dm_linear.h>
 #include <libdm/loop_control.h>
 #include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
 
 #include "metadata.h"
 #include "utility.h"
@@ -34,6 +35,7 @@
 namespace fiemap {
 
 using namespace std::literals;
+using android::base::ReadFileToString;
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
@@ -42,7 +44,10 @@
 using android::dm::LoopControl;
 using android::fs_mgr::CreateLogicalPartition;
 using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::CreateLogicalPartitions;
 using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::GetBlockDevicePartitionName;
+using android::fs_mgr::GetBlockDevicePartitionNames;
 using android::fs_mgr::GetPartitionName;
 
 static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
@@ -50,6 +55,11 @@
 std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
     auto metadata_dir = "/metadata/gsi/" + dir_prefix;
     auto data_dir = "/data/gsi/" + dir_prefix;
+    auto install_dir_file = gsi::DsuInstallDirFile(gsi::GetDsuSlot(dir_prefix));
+    std::string path;
+    if (ReadFileToString(install_dir_file, &path)) {
+        data_dir = path;
+    }
     return Open(metadata_dir, data_dir);
 }
 
@@ -114,7 +124,7 @@
     return images;
 }
 
-bool ImageManager::PartitionExists(const std::string& name) {
+bool ImageManager::BackingImageExists(const std::string& name) {
     if (!MetadataExists(metadata_dir_)) {
         return false;
     }
@@ -125,32 +135,25 @@
     return !!FindPartition(*metadata.get(), name);
 }
 
-bool ImageManager::BackingImageExists(const std::string& name) {
-    auto header_file = GetImageHeaderPath(name);
-    return access(header_file.c_str(), F_OK) == 0;
-}
-
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags) {
-    return CreateBackingImage(name, size, flags, nullptr);
-}
-
 static bool IsUnreliablePinningAllowed(const std::string& path) {
     return android::base::StartsWith(path, "/data/gsi/dsu/") ||
            android::base::StartsWith(path, "/data/gsi/test/") ||
            android::base::StartsWith(path, "/data/gsi/ota/test/");
 }
 
-bool ImageManager::CreateBackingImage(const std::string& name, uint64_t size, int flags,
-                                      std::function<bool(uint64_t, uint64_t)>&& on_progress) {
+FiemapStatus ImageManager::CreateBackingImage(
+        const std::string& name, uint64_t size, int flags,
+        std::function<bool(uint64_t, uint64_t)>&& on_progress) {
     auto data_path = GetImageHeaderPath(name);
-    auto fw = SplitFiemap::Create(data_path, size, 0, on_progress);
-    if (!fw) {
-        return false;
+    std::unique_ptr<SplitFiemap> fw;
+    auto status = SplitFiemap::Create(data_path, size, 0, &fw, on_progress);
+    if (!status.is_ok()) {
+        return status;
     }
 
     bool reliable_pinning;
     if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {
-        return false;
+        return FiemapStatus::Error();
     }
     if (!reliable_pinning && !IsUnreliablePinningAllowed(data_path)) {
         // For historical reasons, we allow unreliable pinning for certain use
@@ -161,7 +164,7 @@
         // proper pinning.
         LOG(ERROR) << "File system does not have reliable block pinning";
         SplitFiemap::RemoveSplitFiles(data_path);
-        return false;
+        return FiemapStatus::Error();
     }
 
     // Except for testing, we do not allow persisting metadata that references
@@ -177,24 +180,25 @@
 
         fw = {};
         SplitFiemap::RemoveSplitFiles(data_path);
-        return false;
+        return FiemapStatus::Error();
     }
 
     bool readonly = !!(flags & CREATE_IMAGE_READONLY);
     if (!UpdateMetadata(metadata_dir_, name, fw.get(), size, readonly)) {
-        return false;
+        return FiemapStatus::Error();
     }
 
     if (flags & CREATE_IMAGE_ZERO_FILL) {
-        if (!ZeroFillNewImage(name, 0)) {
+        auto res = ZeroFillNewImage(name, 0);
+        if (!res.is_ok()) {
             DeleteBackingImage(name);
-            return false;
+            return res;
         }
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
-bool ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
+FiemapStatus ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {
     auto data_path = GetImageHeaderPath(name);
 
     // See the comment in MapImageDevice() about how this works.
@@ -202,13 +206,13 @@
     bool can_use_devicemapper;
     if (!FiemapWriter::GetBlockDeviceForFile(data_path, &block_device, &can_use_devicemapper)) {
         LOG(ERROR) << "Could not determine block device for " << data_path;
-        return false;
+        return FiemapStatus::Error();
     }
 
     if (!can_use_devicemapper) {
         // We've backed with loop devices, and since we store files in an
         // unencrypted folder, the initial zeroes we wrote will suffice.
-        return true;
+        return FiemapStatus::Ok();
     }
 
     // data is dm-crypt, or FBE + dm-default-key. This means the zeroes written
@@ -216,7 +220,7 @@
     // this.
     auto device = MappedDevice::Open(this, 10s, name);
     if (!device) {
-        return false;
+        return FiemapStatus::Error();
     }
 
     static constexpr size_t kChunkSize = 4096;
@@ -229,7 +233,7 @@
         remaining = get_block_device_size(device->fd());
         if (!remaining) {
             PLOG(ERROR) << "Could not get block device size for " << device->path();
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
     }
     while (remaining) {
@@ -237,11 +241,11 @@
         if (!android::base::WriteFully(device->fd(), zeroes.data(),
                                        static_cast<size_t>(to_write))) {
             PLOG(ERROR) << "write failed: " << device->path();
-            return false;
+            return FiemapStatus::FromErrno(errno);
         }
         remaining -= to_write;
     }
-    return true;
+    return FiemapStatus::Ok();
 }
 
 bool ImageManager::DeleteBackingImage(const std::string& name) {
@@ -252,6 +256,10 @@
         return false;
     }
 
+#if defined __ANDROID_RECOVERY__
+    LOG(ERROR) << "Cannot remove images backed by /data in recovery";
+    return false;
+#else
     std::string message;
     auto header_file = GetImageHeaderPath(name);
     if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) {
@@ -265,6 +273,7 @@
         LOG(ERROR) << "Error removing " << status_file << ": " << message;
     }
     return RemoveImageMetadata(metadata_dir_, name);
+#endif
 }
 
 // Create a block device for an image file, using its extents in its
@@ -498,6 +507,7 @@
 
     auto image_header = GetImageHeaderPath(name);
 
+#if !defined __ANDROID_RECOVERY__
     // If there is a device-mapper node wrapping the block device, then we're
     // able to create another node around it; the dm layer does not carry the
     // exclusion lock down the stack when a mount occurs.
@@ -521,6 +531,13 @@
     } else if (!MapWithLoopDevice(name, timeout_ms, path)) {
         return false;
     }
+#else
+    // In recovery, we can *only* use device-mapper, since partitions aren't
+    // mounted. That also means we cannot call GetBlockDeviceForFile.
+    if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
+        return false;
+    }
+#endif
 
     // Set a property so we remember this is mapped.
     auto prop_name = GetStatusPropertyName(name);
@@ -632,6 +649,66 @@
     return true;
 }
 
+bool ImageManager::DisableImage(const std::string& name) {
+    return AddAttributes(metadata_dir_, name, LP_PARTITION_ATTR_DISABLED);
+}
+
+bool ImageManager::RemoveDisabledImages() {
+    if (!MetadataExists(metadata_dir_)) {
+        return true;
+    }
+
+    auto metadata = OpenMetadata(metadata_dir_);
+    if (!metadata) {
+        return false;
+    }
+
+    bool ok = true;
+    for (const auto& partition : metadata->partitions) {
+        if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
+            ok &= DeleteBackingImage(GetPartitionName(partition));
+        }
+    }
+    return ok;
+}
+
+bool ImageManager::GetMappedImageDevice(const std::string& name, std::string* device) {
+    auto prop_name = GetStatusPropertyName(name);
+    *device = android::base::GetProperty(prop_name, "");
+    if (!device->empty()) {
+        return true;
+    }
+
+    auto& dm = DeviceMapper::Instance();
+    if (dm.GetState(name) == DmDeviceState::INVALID) {
+        return false;
+    }
+    return dm.GetDmDevicePathByName(name, device);
+}
+
+bool ImageManager::MapAllImages(const std::function<bool(std::set<std::string>)>& init) {
+    if (!MetadataExists(metadata_dir_)) {
+        return true;
+    }
+
+    auto metadata = OpenMetadata(metadata_dir_);
+    if (!metadata) {
+        return false;
+    }
+
+    std::set<std::string> devices;
+    for (const auto& name : GetBlockDevicePartitionNames(*metadata.get())) {
+        devices.emplace(name);
+    }
+    if (!init(std::move(devices))) {
+        return false;
+    }
+
+    auto data_device = GetMetadataSuperBlockDevice(*metadata.get());
+    auto data_partition_name = GetBlockDevicePartitionName(*data_device);
+    return CreateLogicalPartitions(*metadata.get(), data_partition_name);
+}
+
 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
                                                  const std::chrono::milliseconds& timeout_ms,
                                                  const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index f05825c..80c340f 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -112,6 +112,25 @@
     ASSERT_EQ(android::base::GetProperty(PropertyName(), ""), "");
 }
 
+TEST_F(NativeTest, DisableImage) {
+    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
+    ASSERT_TRUE(manager_->BackingImageExists(base_name_));
+    ASSERT_TRUE(manager_->DisableImage(base_name_));
+    ASSERT_TRUE(manager_->RemoveDisabledImages());
+    ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
+}
+
+TEST_F(NativeTest, GetMappedImageDevice) {
+    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
+
+    std::string path1, path2;
+    ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &path1));
+    ASSERT_TRUE(manager_->GetMappedImageDevice(base_name_, &path2));
+    EXPECT_EQ(path1, path2);
+
+    ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
+}
+
 // This fixture is for tests against a simulated device environment. Rather
 // than use /data, we create an image and then layer a new filesystem within
 // it. Each test then decides how to mount and create layered images. This
@@ -220,9 +239,19 @@
 
     ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
 
+    std::set<std::string> backing_devices;
+    auto init = [&](std::set<std::string> devices) -> bool {
+        backing_devices = std::move(devices);
+        return true;
+    };
+
     std::string path;
     ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
     ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
+    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
+    ASSERT_TRUE(submanager_->MapAllImages(init));
+    ASSERT_FALSE(backing_devices.empty());
+    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
 }
 
 bool Mkdir(const std::string& path) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
new file mode 100644
index 0000000..d7b2cf1
--- /dev/null
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
@@ -0,0 +1,68 @@
+/*
+ * 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 <errno.h>
+#include <stdint.h>
+
+#include <string>
+
+namespace android::fiemap {
+
+// Represent error status of libfiemap classes.
+class FiemapStatus {
+  public:
+    enum class ErrorCode : int32_t {
+        SUCCESS = 0,
+        // Generic non-recoverable failure.
+        ERROR = INT32_MIN,
+        // Not enough space
+        NO_SPACE = -ENOSPC,
+    };
+
+    // Create from a given errno (specified in errno,h)
+    static FiemapStatus FromErrno(int error_num) { return FiemapStatus(CastErrorCode(-error_num)); }
+
+    // Create from an integer error code that is expected to be an ErrorCode
+    // value. If it isn't, Error() is returned.
+    static FiemapStatus FromErrorCode(int32_t error_code) {
+        return FiemapStatus(CastErrorCode(error_code));
+    }
+
+    // Generic error.
+    static FiemapStatus Error() { return FiemapStatus(ErrorCode::ERROR); }
+
+    // Success.
+    static FiemapStatus Ok() { return FiemapStatus(ErrorCode::SUCCESS); }
+
+    ErrorCode error_code() const { return error_code_; }
+    bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }
+    operator bool() const { return is_ok(); }
+
+    // For logging and debugging only.
+    std::string string() const;
+
+  protected:
+    FiemapStatus(ErrorCode code) : error_code_(code) {}
+
+  private:
+    ErrorCode error_code_;
+
+    static ErrorCode CastErrorCode(int error);
+};
+
+}  // namespace android::fiemap
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
index c692265..dd345f6 100644
--- a/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h
@@ -27,6 +27,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include <libfiemap/fiemap_status.h>
+
 namespace android {
 namespace fiemap {
 
@@ -47,6 +49,9 @@
     static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
                                 bool create = true,
                                 std::function<bool(uint64_t, uint64_t)> progress = {});
+    static FiemapStatus Open(const std::string& file_path, uint64_t file_size, FiemapUniquePtr* out,
+                             bool create = true,
+                             std::function<bool(uint64_t, uint64_t)> progress = {});
 
     // Check that a file still has the same extents since it was last opened with FiemapWriter,
     // assuming the file was not resized outside of FiemapWriter. Returns false either on error
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 5ff4628..60b98dc 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -21,9 +21,11 @@
 #include <chrono>
 #include <functional>
 #include <memory>
+#include <set>
 #include <string>
 
 #include <android-base/unique_fd.h>
+#include <libfiemap/fiemap_status.h>
 #include <liblp/partition_opener.h>
 
 namespace android {
@@ -51,7 +53,9 @@
     // of the image is undefined. If zero-fill is requested, and the operation
     // cannot be completed, the image will be deleted and this function will
     // return false.
-    virtual bool CreateBackingImage(const std::string& name, uint64_t size, int flags) = 0;
+    virtual FiemapStatus CreateBackingImage(
+            const std::string& name, uint64_t size, int flags,
+            std::function<bool(uint64_t, uint64_t)>&& on_progress = nullptr) = 0;
 
     // Delete an image created with CreateBackingImage. Its entry will be
     // removed from the associated lp_metadata file.
@@ -72,7 +76,9 @@
     // Unmap a block device previously mapped with mapBackingImage.
     virtual bool UnmapImageDevice(const std::string& name) = 0;
 
-    // Returns true whether the named backing image exists.
+    // Returns true whether the named backing image exists. This does not check
+    // consistency with the /data partition, so that it can return true in
+    // recovery.
     virtual bool BackingImageExists(const std::string& name) = 0;
 
     // Returns true if the specified image is mapped to a device.
@@ -84,12 +90,35 @@
     virtual bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
                                           std::string* dev) = 0;
 
+    // If an image was mapped, return the path to its device. Otherwise, return
+    // false. Errors are not reported in this case, calling IsImageMapped is
+    // not necessary.
+    virtual bool GetMappedImageDevice(const std::string& name, std::string* device) = 0;
+
+    // Map all images owned by this manager. This is only intended to be used
+    // during first-stage init, and as such, it does not provide a timeout
+    // (meaning libdm races can't be resolved, as ueventd is not available),
+    // and is not available over binder.
+    //
+    // The callback provided is given the list of dependent block devices.
+    virtual bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) = 0;
+
+    // Mark an image as disabled. This is useful for marking an image as
+    // will-be-deleted in recovery, since recovery cannot mount /data.
+    //
+    // This is not available in binder, since it is intended for recovery.
+    // When binder is available, images can simply be removed.
+    virtual bool DisableImage(const std::string& name) = 0;
+
+    // Remove all images that been marked as disabled.
+    virtual bool RemoveDisabledImages() = 0;
+
     // Get all backing image names.
     virtual std::vector<std::string> GetAllBackingImages() = 0;
 
     // Writes |bytes| zeros to |name| file. If |bytes| is 0, then the
     // whole file if filled with zeros.
-    virtual bool ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
+    virtual FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
 
     // Find and remove all images and metadata for this manager.
     virtual bool RemoveAllImages() = 0;
@@ -109,7 +138,8 @@
     static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix);
 
     // Methods that must be implemented from IImageManager.
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags) override;
+    FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,
+                                    std::function<bool(uint64_t, uint64_t)>&& on_progress) override;
     bool DeleteBackingImage(const std::string& name) override;
     bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
                         std::string* path) override;
@@ -119,15 +149,12 @@
     bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
                                   std::string* dev) override;
     bool RemoveAllImages() override;
+    bool DisableImage(const std::string& name) override;
+    bool RemoveDisabledImages() override;
+    bool GetMappedImageDevice(const std::string& name, std::string* device) override;
+    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
 
     std::vector<std::string> GetAllBackingImages();
-    // Same as CreateBackingImage, but provides a progress notification.
-    bool CreateBackingImage(const std::string& name, uint64_t size, int flags,
-                            std::function<bool(uint64_t, uint64_t)>&& on_progress);
-
-    // Returns true if the named partition exists. This does not check the
-    // consistency of the backing image/data file.
-    bool PartitionExists(const std::string& name);
 
     // Validates that all images still have pinned extents. This will be removed
     // once b/134588268 is fixed.
@@ -136,7 +163,7 @@
     void set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener);
 
     // Writes |bytes| zeros at the beginning of the passed image
-    bool ZeroFillNewImage(const std::string& name, uint64_t bytes);
+    FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
 
   private:
     ImageManager(const std::string& metadata_dir, const std::string& data_dir);
diff --git a/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h b/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
index feffb3d..d739fcf 100644
--- a/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
+++ b/fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h
@@ -25,7 +25,8 @@
 
 #include <android-base/unique_fd.h>
 
-#include "fiemap_writer.h"
+#include <libfiemap/fiemap_status.h>
+#include <libfiemap/fiemap_writer.h>
 
 namespace android {
 namespace fiemap {
@@ -43,6 +44,9 @@
     static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
                                                uint64_t max_piece_size,
                                                ProgressCallback progress = {});
+    static FiemapStatus Create(const std::string& file_path, uint64_t file_size,
+                               uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,
+                               ProgressCallback progress = {});
 
     // Open an existing split fiemap file.
     static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
diff --git a/fs_mgr/libfiemap/metadata.cpp b/fs_mgr/libfiemap/metadata.cpp
index 597efe9..ea1f508 100644
--- a/fs_mgr/libfiemap/metadata.cpp
+++ b/fs_mgr/libfiemap/metadata.cpp
@@ -192,5 +192,23 @@
     return SaveMetadata(builder.get(), metadata_dir);
 }
 
+bool AddAttributes(const std::string& metadata_dir, const std::string& partition_name,
+                   uint32_t attributes) {
+    auto metadata = OpenMetadata(metadata_dir);
+    if (!metadata) {
+        return false;
+    }
+    auto builder = MetadataBuilder::New(*metadata.get());
+    if (!builder) {
+        return false;
+    }
+    auto partition = builder->FindPartition(partition_name);
+    if (!partition) {
+        return false;
+    }
+    partition->set_attributes(partition->attributes() | attributes);
+    return SaveMetadata(builder.get(), metadata_dir);
+}
+
 }  // namespace fiemap
 }  // namespace android
diff --git a/fs_mgr/libfiemap/metadata.h b/fs_mgr/libfiemap/metadata.h
index f0ce23e..4eb3ad5 100644
--- a/fs_mgr/libfiemap/metadata.h
+++ b/fs_mgr/libfiemap/metadata.h
@@ -29,6 +29,8 @@
 std::unique_ptr<android::fs_mgr::LpMetadata> OpenMetadata(const std::string& metadata_dir);
 bool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,
                     SplitFiemap* file, uint64_t partition_size, bool readonly);
+bool AddAttributes(const std::string& metadata_dir, const std::string& partition_name,
+                   uint32_t attributes);
 bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
 bool RemoveAllMetadata(const std::string& dir);
 
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index cc54f20..12c7397 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -45,16 +45,28 @@
 std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
                                                  uint64_t max_piece_size,
                                                  ProgressCallback progress) {
+    std::unique_ptr<SplitFiemap> ret;
+    if (!Create(file_path, file_size, max_piece_size, &ret, progress).is_ok()) {
+        return nullptr;
+    }
+    return ret;
+}
+
+FiemapStatus SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
+                                 uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,
+                                 ProgressCallback progress) {
+    out_val->reset();
+
     if (!file_size) {
         LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
-        return nullptr;
+        return FiemapStatus::Error();
     }
 
     if (!max_piece_size) {
-        max_piece_size = DetermineMaximumFileSize(file_path);
-        if (!max_piece_size) {
+        auto status = DetermineMaximumFileSize(file_path, &max_piece_size);
+        if (!status.is_ok()) {
             LOG(ERROR) << "Could not determine maximum file size for " << file_path;
-            return nullptr;
+            return status;
         }
     }
 
@@ -75,7 +87,6 @@
         }
         return true;
     };
-
     std::unique_ptr<SplitFiemap> out(new SplitFiemap());
     out->creating_ = true;
     out->list_file_ = file_path;
@@ -85,14 +96,17 @@
     while (remaining_bytes) {
         if (out->files_.size() >= kMaxFilePieces) {
             LOG(ERROR) << "Requested size " << file_size << " created too many split files";
-            return nullptr;
+            out.reset();
+            return FiemapStatus::Error();
         }
         std::string chunk_path =
                 android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
         uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
-        auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
-        if (!writer) {
-            return nullptr;
+        FiemapUniquePtr writer;
+        auto status = FiemapWriter::Open(chunk_path, chunk_size, &writer, true, on_progress);
+        if (!status.is_ok()) {
+            out.reset();
+            return status;
         }
 
         // To make sure the alignment doesn't create too much inconsistency, we
@@ -110,20 +124,23 @@
     unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
     if (fd < 0) {
         PLOG(ERROR) << "Failed to open " << file_path;
-        return nullptr;
+        out.reset();
+        return FiemapStatus::FromErrno(errno);
     }
 
     for (const auto& writer : out->files_) {
         std::string line = android::base::Basename(writer->file_path()) + "\n";
         if (!android::base::WriteFully(fd, line.data(), line.size())) {
             PLOG(ERROR) << "Write failed " << file_path;
-            return nullptr;
+            out.reset();
+            return FiemapStatus::FromErrno(errno);
         }
     }
 
     // Unset this bit, so we don't unlink on destruction.
     out->creating_ = false;
-    return out;
+    *out_val = std::move(out);
+    return FiemapStatus::Ok();
 }
 
 std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
diff --git a/fs_mgr/libfiemap/utility.cpp b/fs_mgr/libfiemap/utility.cpp
index 955e544..bbb0510 100644
--- a/fs_mgr/libfiemap/utility.cpp
+++ b/fs_mgr/libfiemap/utility.cpp
@@ -37,29 +37,30 @@
 
 static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
 
-uint64_t DetermineMaximumFileSize(const std::string& file_path) {
+FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result) {
     // Create the smallest file possible (one block).
-    auto writer = FiemapWriter::Open(file_path, 1);
-    if (!writer) {
-        return 0;
+    FiemapUniquePtr writer;
+    auto status = FiemapWriter::Open(file_path, 1, &writer);
+    if (!status.is_ok()) {
+        return status;
     }
 
-    uint64_t result = 0;
+    *result = 0;
     switch (writer->fs_type()) {
         case EXT4_SUPER_MAGIC:
             // The minimum is 16GiB, so just report that. If we wanted we could parse the
             // superblock and figure out if 64-bit support is enabled.
-            result = 17179869184ULL;
+            *result = 17179869184ULL;
             break;
         case F2FS_SUPER_MAGIC:
             // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
             // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
-            result = 4329690886144ULL;
+            *result = 4329690886144ULL;
             break;
         case MSDOS_SUPER_MAGIC:
             // 4GB-1, which we want aligned to the block size.
-            result = 4294967295;
-            result -= (result % writer->block_size());
+            *result = 4294967295;
+            *result -= (*result % writer->block_size());
             break;
         default:
             LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
@@ -70,7 +71,7 @@
     writer = nullptr;
     unlink(file_path.c_str());
 
-    return result;
+    return FiemapStatus::Ok();
 }
 
 // Given a SplitFiemap, this returns a device path that will work during first-
diff --git a/fs_mgr/libfiemap/utility.h b/fs_mgr/libfiemap/utility.h
index 24ebc57..4c0bc2b 100644
--- a/fs_mgr/libfiemap/utility.h
+++ b/fs_mgr/libfiemap/utility.h
@@ -28,7 +28,7 @@
 // Given a file that will be created, determine the maximum size its containing
 // filesystem allows. Note this is a theoretical maximum size; free space is
 // ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
+FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result);
 
 // Given a SplitFiemap, this returns a device path that will work during first-
 // stage init (i.e., its path can be found by InitRequiredDevices).
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 4406696..d496466 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -852,7 +852,7 @@
             return nullptr;
         }
 
-        if (partition->attributes() & LP_PARTITION_ATTR_UPDATED) {
+        if (partition->attributes() & LP_PARTITION_ATTRIBUTE_MASK_V1) {
             static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;
             metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);
         }
@@ -1125,6 +1125,11 @@
     auto_slot_suffixing_ = true;
 }
 
+void MetadataBuilder::SetVirtualABDeviceFlag() {
+    RequireExpandedMetadataHeader();
+    header_.flags |= LP_HEADER_FLAG_VIRTUAL_AB_DEVICE;
+}
+
 bool MetadataBuilder::IsABDevice() {
     return !IPropertyFetcher::GetInstance()->GetProperty("ro.boot.slot_suffix", "").empty();
 }
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 7a334fb..f7738fb 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -145,6 +145,7 @@
     std::vector<std::unique_ptr<Extent>> extents_;
     uint32_t attributes_;
     uint64_t size_;
+    bool disabled_;
 };
 
 // An interval in the metadata. This is similar to a LinearExtent with one difference.
@@ -318,6 +319,8 @@
 
     // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
     void SetAutoSlotSuffixing();
+    // Set the LP_HEADER_FLAG_VIRTUAL_AB_DEVICE flag.
+    void SetVirtualABDeviceFlag();
 
     // If set, checks for slot suffixes will be ignored internally.
     void IgnoreSlotSuffixing();
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 26cbf07..41d8b0c 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -72,13 +72,17 @@
  */
 #define LP_PARTITION_ATTR_UPDATED (1 << 2)
 
+/* This flag marks a partition as disabled. It should not be used or mapped. */
+#define LP_PARTITION_ATTR_DISABLED (1 << 3)
+
 /* Mask that defines all valid attributes. When changing this, make sure to
  * update ParseMetadata().
  */
 #define LP_PARTITION_ATTRIBUTE_MASK_V0 \
     (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
-#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTR_UPDATED)
-#define LP_PARTITION_ATTRIBUTE_MASK LP_PARTITION_ATTRIBUTE_MASK_V1
+#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTR_UPDATED | LP_PARTITION_ATTR_DISABLED)
+#define LP_PARTITION_ATTRIBUTE_MASK \
+    (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTRIBUTE_MASK_V1)
 
 /* Default name of the physical partition that holds logical partition entries.
  * The layout of this partition will look like:
@@ -224,8 +228,6 @@
     /* 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;
 
@@ -233,6 +235,11 @@
     uint8_t reserved[124];
 } __attribute__((packed)) LpMetadataHeader;
 
+/* This device uses Virtual A/B. Note that on retrofit devices, the expanded
+ * header may not be present.
+ */
+#define LP_HEADER_FLAG_VIRTUAL_AB_DEVICE 0x1
+
 /* This struct defines a logical partition entry, similar to what would be
  * present in a GUID Partition Table.
  */
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 30c17e4..e6fd9f7 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -280,11 +280,9 @@
         return nullptr;
     }
 
-    uint32_t valid_attributes = 0;
+    uint32_t valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
     if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
-        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V1;
-    } else {
-        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
+        valid_attributes |= LP_PARTITION_ATTRIBUTE_MASK_V1;
     }
 
     // ValidateTableSize ensured that |cursor| is valid for the number of
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 30d01a6..c67e33d 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -30,7 +30,6 @@
     static_libs: [
         "libcutils",
         "libdm",
-        "libfs_mgr",
         "libfstab",
         "liblp",
         "update_metadata-protos",
@@ -76,6 +75,7 @@
         "snapshot.cpp",
         "snapshot_metadata_updater.cpp",
         "partition_cow_creator.cpp",
+        "return.cpp",
         "utility.cpp",
     ],
 }
@@ -93,8 +93,8 @@
         "libsnapshot_hal_deps",
     ],
     srcs: [":libsnapshot_sources"],
-    whole_static_libs: [
-        "libfiemap_binder",
+    static_libs: [
+        "libfs_mgr_binder"
     ],
 }
 
@@ -103,8 +103,8 @@
     defaults: ["libsnapshot_defaults"],
     srcs: [":libsnapshot_sources"],
     recovery_available: true,
-    whole_static_libs: [
-        "libfiemap_passthrough",
+    static_libs: [
+        "libfs_mgr",
     ],
 }
 
@@ -116,8 +116,8 @@
     ],
     srcs: [":libsnapshot_sources"],
     recovery_available: true,
-    whole_static_libs: [
-        "libfiemap_passthrough",
+    static_libs: [
+        "libfs_mgr",
     ],
 }
 
@@ -128,6 +128,7 @@
         "include_test",
     ],
     srcs: [
+        "android/snapshot/snapshot.proto",
         "test_helpers.cpp",
     ],
     shared_libs: [
@@ -144,6 +145,7 @@
         "libstorage_literals_headers",
     ],
     static_libs: [
+        "libfs_mgr",
         "libgtest",
         "libgmock",
     ],
@@ -170,6 +172,7 @@
         "android.hardware.boot@1.1",
         "libfs_mgr",
         "libgmock",
+        "libgsi",
         "liblp",
         "libsnapshot",
         "libsnapshot_test_helpers",
@@ -189,7 +192,6 @@
     static_libs: [
         "libdm",
         "libext2_uuid",
-        "libfiemap_binder",
         "libfstab",
         "libsnapshot",
     ],
@@ -200,7 +202,7 @@
         "libbinder",
         "libbinderthreadstate",
         "libext4_utils",
-        "libfs_mgr",
+        "libfs_mgr_binder",
         "libhidlbase",
         "liblog",
         "liblp",
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 0cfa7e4..801c446 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,2 +1,3 @@
+balsini@google.com
 dvander@google.com
 elsk@google.com
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 629c3a4..a3a518d 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -85,3 +85,49 @@
     // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
     uint64 metadata_sectors = 8;
 }
+
+// Next: 8
+enum UpdateState {
+    // No update or merge is in progress.
+    None = 0;
+
+    // An update is applying; snapshots may already exist.
+    Initiated = 1;
+
+    // An update is pending, but has not been successfully booted yet.
+    Unverified = 2;
+
+    // The kernel is merging in the background.
+    Merging = 3;
+
+    // Post-merge cleanup steps could not be completed due to a transient
+    // error, but the next reboot will finish any pending operations.
+    MergeNeedsReboot = 4;
+
+    // Merging is complete, and needs to be acknowledged.
+    MergeCompleted = 5;
+
+    // Merging failed due to an unrecoverable error.
+    MergeFailed = 6;
+
+    // The update was implicitly cancelled, either by a rollback or a flash
+    // operation via fastboot. This state can only be returned by WaitForMerge.
+    Cancelled = 7;
+};
+
+// Next: 5
+message SnapshotUpdateStatus {
+    UpdateState state = 1;
+
+    // Total number of sectors allocated in the COW files before performing the
+    // merge operation.  This field is used to keep track of the total number
+    // of sectors modified to monitor and show the progress of the merge during
+    // an update.
+    uint64 sectors_allocated = 2;
+
+    // Total number of sectors of all the snapshot devices.
+    uint64 total_sectors = 3;
+
+    // Sectors allocated for metadata in all the snapshot devices.
+    uint64 metadata_sectors = 4;
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/return.h b/fs_mgr/libsnapshot/include/libsnapshot/return.h
new file mode 100644
index 0000000..1f132fa
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/return.h
@@ -0,0 +1,61 @@
+// 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 <stdint.h>
+#include <string.h>
+
+#include <libfiemap/fiemap_status.h>
+
+namespace android::snapshot {
+
+// SnapshotManager functions return either bool or Return objects. "Return" types provides
+// more information about the reason of the failure.
+class Return {
+    using FiemapStatus = android::fiemap::FiemapStatus;
+
+  public:
+    enum class ErrorCode : int32_t {
+        SUCCESS = static_cast<int32_t>(FiemapStatus::ErrorCode::SUCCESS),
+        ERROR = static_cast<int32_t>(FiemapStatus::ErrorCode::ERROR),
+        NEEDS_REBOOT = ERROR + 1,
+        NO_SPACE = static_cast<int32_t>(FiemapStatus::ErrorCode::NO_SPACE),
+    };
+    ErrorCode error_code() const { return error_code_; }
+    bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }
+    operator bool() const { return is_ok(); }
+    // Total required size on /userdata.
+    uint64_t required_size() const { return required_size_; }
+    std::string string() const;
+
+    static Return Ok() { return Return(ErrorCode::SUCCESS); }
+    static Return Error() { return Return(ErrorCode::ERROR); }
+    static Return NoSpace(uint64_t size) { return Return(ErrorCode::NO_SPACE, size); }
+    static Return NeedsReboot() { return Return(ErrorCode::NEEDS_REBOOT); }
+    // Does not set required_size_ properly even when status.error_code() == NO_SPACE.
+    explicit Return(const FiemapStatus& status)
+        : error_code_(FromFiemapStatusErrorCode(status.error_code())), required_size_(0) {}
+
+  private:
+    ErrorCode error_code_;
+    uint64_t required_size_;
+    Return(ErrorCode error_code, uint64_t required_size = 0)
+        : error_code_(error_code), required_size_(required_size) {}
+
+    // FiemapStatus::ErrorCode -> ErrorCode
+    static ErrorCode FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code);
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 445e6db..959d8a7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -26,6 +26,7 @@
 #include <vector>
 
 #include <android-base/unique_fd.h>
+#include <android/snapshot/snapshot.pb.h>
 #include <fs_mgr_dm_linear.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
@@ -34,6 +35,7 @@
 #include <update_engine/update_metadata.pb.h>
 
 #include <libsnapshot/auto_device.h>
+#include <libsnapshot/return.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -74,34 +76,11 @@
 
 bool SourceCopyOperationIsClone(const chromeos_update_engine::InstallOperation& operation);
 
-enum class UpdateState : unsigned int {
-    // No update or merge is in progress.
-    None,
-
-    // An update is applying; snapshots may already exist.
-    Initiated,
-
-    // An update is pending, but has not been successfully booted yet.
-    Unverified,
-
-    // The kernel is merging in the background.
-    Merging,
-
-    // Post-merge cleanup steps could not be completed due to a transient
-    // error, but the next reboot will finish any pending operations.
-    MergeNeedsReboot,
-
-    // Merging is complete, and needs to be acknowledged.
-    MergeCompleted,
-
-    // Merging failed due to an unrecoverable error.
-    MergeFailed,
-
-    // The update was implicitly cancelled, either by a rollback or a flash
-    // operation via fastboot. This state can only be returned by WaitForMerge.
-    Cancelled
+enum class CreateResult : unsigned int {
+    ERROR,
+    CREATED,
+    NOT_CREATED,
 };
-std::ostream& operator<<(std::ostream& os, UpdateState state);
 
 class SnapshotManager final {
     using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
@@ -110,6 +89,7 @@
     using MetadataBuilder = android::fs_mgr::MetadataBuilder;
     using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
     using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+    using FiemapStatus = android::fiemap::FiemapStatus;
 
   public:
     // Dependency injection for testing.
@@ -201,9 +181,11 @@
     // Wait for the merge if rebooted into the new slot. Does NOT initiate a
     // merge. If the merge has not been initiated (but should be), wait.
     // Returns:
-    //   - true there is no merge or merge finishes
-    //   - false indicating an error has occurred
-    bool WaitForMerge();
+    //   - Return::Ok(): there is no merge or merge finishes
+    //   - Return::NeedsReboot(): merge finishes but need a reboot before
+    //     applying the next update.
+    //   - Return::Error(): other irrecoverable errors
+    Return WaitForMerge();
 
     // Find the status of the current update, if any.
     //
@@ -216,7 +198,7 @@
     // Create necessary COW device / files for OTA clients. New logical partitions will be added to
     // group "cow" in target_metadata. Regions of partitions of current_metadata will be
     // "write-protected" and snapshotted.
-    bool CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
+    Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
 
     // Map a snapshotted partition for OTA clients to write to. Write-protected regions are
     // determined previously in CreateSnapshots.
@@ -246,6 +228,17 @@
     // optional callback fires periodically to query progress via GetUpdateState.
     bool HandleImminentDataWipe(const std::function<void()>& callback = {});
 
+    // This method is only allowed in recovery and is used as a helper to
+    // initialize the snapshot devices as a requirement to mount a snapshotted
+    // /system in recovery.
+    // This function returns:
+    // - CreateResult::CREATED if snapshot devices were successfully created;
+    // - CreateResult::NOT_CREATED if it was not necessary to create snapshot
+    // devices;
+    // - CreateResult::ERROR if a fatal error occurred, mounting /system should
+    // be aborted.
+    CreateResult RecoveryCreateSnapshotDevices();
+
     // Dump debug information.
     bool Dump(std::ostream& os);
 
@@ -342,7 +335,7 @@
 
     // |name| should be the base partition name (e.g. "system_a"). Create the
     // backing COW image using the size previously passed to CreateSnapshot().
-    bool CreateCowImage(LockedFile* lock, const std::string& name);
+    Return CreateCowImage(LockedFile* lock, const std::string& name);
 
     // Map a snapshot device that was previously created with CreateSnapshot.
     // If a merge was previously initiated, the device-mapper table will have a
@@ -394,7 +387,9 @@
 
     // Interact with /metadata/ota/state.
     UpdateState ReadUpdateState(LockedFile* file);
+    SnapshotUpdateStatus ReadSnapshotUpdateStatus(LockedFile* file);
     bool WriteUpdateState(LockedFile* file, UpdateState state);
+    bool WriteSnapshotUpdateStatus(LockedFile* file, const SnapshotUpdateStatus& status);
     std::string GetStateFilePath() const;
 
     // Helpers for merging.
@@ -482,14 +477,14 @@
 
     // Helper for CreateUpdateSnapshots.
     // Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
-    bool CreateUpdateSnapshotsInternal(LockedFile* lock, const DeltaArchiveManifest& manifest,
-                                       PartitionCowCreator* cow_creator,
-                                       AutoDeviceList* created_devices,
-                                       std::map<std::string, SnapshotStatus>* all_snapshot_status);
+    Return CreateUpdateSnapshotsInternal(
+            LockedFile* lock, const DeltaArchiveManifest& manifest,
+            PartitionCowCreator* cow_creator, AutoDeviceList* created_devices,
+            std::map<std::string, SnapshotStatus>* all_snapshot_status);
 
     // Initialize snapshots so that they can be mapped later.
     // Map the COW partition and zero-initialize the header.
-    bool InitializeUpdateSnapshots(
+    Return InitializeUpdateSnapshots(
             LockedFile* lock, MetadataBuilder* target_metadata,
             const LpMetadata* exported_target_metadata, const std::string& target_suffix,
             const std::map<std::string, SnapshotStatus>& all_snapshot_status);
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 2bf1b57..98bf56a 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -14,10 +14,12 @@
 
 #pragma once
 
+#include <memory>
 #include <optional>
 #include <string>
 #include <unordered_set>
 
+#include <android-base/file.h>
 #include <android/hardware/boot/1.1/IBootControl.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -40,7 +42,6 @@
 using testing::_;
 using testing::AssertionResult;
 using testing::NiceMock;
-using testing::Return;
 
 using namespace android::storage_literals;
 using namespace std::string_literals;
@@ -115,6 +116,7 @@
 class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
   public:
     SnapshotTestPropertyFetcher(const std::string& slot_suffix) {
+        using testing::Return;
         ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix));
         ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _))
                 .WillByDefault(Return(true));
@@ -155,5 +157,28 @@
 // Get partition size from update package metadata.
 uint64_t GetSize(PartitionUpdate* partition_update);
 
+// Util class for test cases on low space scenario. These tests assumes image manager
+// uses /data as backup device.
+class LowSpaceUserdata {
+  public:
+    // Set the maximum free space allowed for this test. If /userdata has more space than the given
+    // number, a file is allocated to consume space.
+    AssertionResult Init(uint64_t max_free_space);
+
+    uint64_t free_space() const;
+    uint64_t available_space() const;
+    uint64_t bsize() const;
+
+  private:
+    AssertionResult ReadUserdataStats();
+
+    static constexpr const char* kUserDataDevice = "/data";
+    std::unique_ptr<TemporaryFile> big_file_;
+    bool initialized_ = false;
+    uint64_t free_space_ = 0;
+    uint64_t available_space_ = 0;
+    uint64_t bsize_ = 0;
+};
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/return.cpp b/fs_mgr/libsnapshot/return.cpp
new file mode 100644
index 0000000..6559c12
--- /dev/null
+++ b/fs_mgr/libsnapshot/return.cpp
@@ -0,0 +1,46 @@
+// 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 <libsnapshot/return.h>
+
+#include <string.h>
+
+using android::fiemap::FiemapStatus;
+
+namespace android::snapshot {
+
+std::string Return::string() const {
+    switch (error_code()) {
+        case ErrorCode::ERROR:
+            return "Error";
+        case ErrorCode::NEEDS_REBOOT:
+            return "Retry after reboot";
+        case ErrorCode::SUCCESS:
+            [[fallthrough]];
+        case ErrorCode::NO_SPACE:
+            return strerror(-static_cast<int>(error_code()));
+    }
+}
+
+Return::ErrorCode Return::FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code) {
+    switch (error_code) {
+        case FiemapStatus::ErrorCode::SUCCESS:
+        case FiemapStatus::ErrorCode::ERROR:
+        case FiemapStatus::ErrorCode::NO_SPACE:
+            return static_cast<ErrorCode>(error_code);
+        default:
+            return ErrorCode::ERROR;
+    }
+}
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index a0ec068..88d6b8d 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -54,6 +54,7 @@
 using android::dm::DmTargetSnapshot;
 using android::dm::kSectorSize;
 using android::dm::SnapshotStorageMode;
+using android::fiemap::FiemapStatus;
 using android::fiemap::IImageManager;
 using android::fs_mgr::CreateDmTable;
 using android::fs_mgr::CreateLogicalPartition;
@@ -289,14 +290,14 @@
     return true;
 }
 
-bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
+Return SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
     CHECK(lock);
     CHECK(lock->lock_mode() == LOCK_EX);
-    if (!EnsureImageManager()) return false;
+    if (!EnsureImageManager()) return Return::Error();
 
     SnapshotStatus status;
     if (!ReadSnapshotStatus(lock, name, &status)) {
-        return false;
+        return Return::Error();
     }
 
     // The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
@@ -304,12 +305,12 @@
     if (status.cow_file_size() % kSectorSize != 0) {
         LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
                    << status.cow_file_size();
-        return false;
+        return Return::Error();
     }
 
     std::string cow_image_name = GetCowImageDeviceName(name);
     int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
-    return images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags);
+    return Return(images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags));
 }
 
 bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
@@ -559,9 +560,26 @@
         }
     }
 
+    DmTargetSnapshot::Status initial_target_values = {};
+    for (const auto& snapshot : snapshots) {
+        DmTargetSnapshot::Status current_status;
+        if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) {
+            return false;
+        }
+        initial_target_values.sectors_allocated += current_status.sectors_allocated;
+        initial_target_values.total_sectors += current_status.total_sectors;
+        initial_target_values.metadata_sectors += current_status.metadata_sectors;
+    }
+
+    SnapshotUpdateStatus initial_status;
+    initial_status.set_state(UpdateState::Merging);
+    initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
+    initial_status.set_total_sectors(initial_target_values.total_sectors);
+    initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
+
     // Point of no return - mark that we're starting a merge. From now on every
     // snapshot must be a merge target.
-    if (!WriteUpdateState(lock.get(), UpdateState::Merging)) {
+    if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {
         return false;
     }
 
@@ -810,7 +828,8 @@
                 cancelled = true;
                 break;
             default:
-                LOG(ERROR) << "Unknown merge status: " << static_cast<uint32_t>(snapshot_state);
+                LOG(ERROR) << "Unknown merge status for \"" << snapshot << "\": "
+                           << "\"" << snapshot_state << "\"";
                 failed = true;
                 break;
         }
@@ -1200,7 +1219,8 @@
         if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) {
             // Remember whether or not we were able to unmap the cow image.
             auto cow_image_device = GetCowImageDeviceName(name);
-            has_mapped_cow_images |= images_->IsImageMapped(cow_image_device);
+            has_mapped_cow_images |=
+                    (EnsureImageManager() && images_->IsImageMapped(cow_image_device));
 
             ok = false;
         }
@@ -1231,15 +1251,45 @@
         return UpdateState::None;
     }
 
-    auto state = ReadUpdateState(lock.get());
-    if (progress) {
-        *progress = 0.0;
-        if (state == UpdateState::Merging) {
-            // :TODO: When merging is implemented, set progress_val.
-        } else if (state == UpdateState::MergeCompleted) {
-            *progress = 100.0;
-        }
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    auto state = update_status.state();
+    if (progress == nullptr) {
+        return state;
     }
+
+    if (state == UpdateState::MergeCompleted) {
+        *progress = 100.0;
+        return state;
+    }
+
+    *progress = 0.0;
+    if (state != UpdateState::Merging) {
+        return state;
+    }
+
+    // Sum all the snapshot states as if the system consists of a single huge
+    // snapshots device, then compute the merge completion percentage of that
+    // device.
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots)) {
+        LOG(ERROR) << "Could not list snapshots";
+        return state;
+    }
+
+    DmTargetSnapshot::Status fake_snapshots_status = {};
+    for (const auto& snapshot : snapshots) {
+        DmTargetSnapshot::Status current_status;
+
+        if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) continue;
+
+        fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;
+        fake_snapshots_status.total_sectors += current_status.total_sectors;
+        fake_snapshots_status.metadata_sectors += current_status.metadata_sectors;
+    }
+
+    *progress = DmTargetSnapshot::MergePercent(fake_snapshots_status,
+                                               update_status.sectors_allocated());
+
     return state;
 }
 
@@ -1642,15 +1692,7 @@
     return OpenLock(LOCK_EX);
 }
 
-UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
-    CHECK(lock);
-
-    std::string contents;
-    if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
-        PLOG(ERROR) << "Read state file failed";
-        return UpdateState::None;
-    }
-
+static UpdateState UpdateStateFromString(const std::string& contents) {
     if (contents.empty() || contents == "none") {
         return UpdateState::None;
     } else if (contents == "initiated") {
@@ -1666,7 +1708,7 @@
     } else if (contents == "merge-failed") {
         return UpdateState::MergeFailed;
     } else {
-        LOG(ERROR) << "Unknown merge state in update state file";
+        LOG(ERROR) << "Unknown merge state in update state file: \"" << contents << "\"";
         return UpdateState::None;
     }
 }
@@ -1688,23 +1730,59 @@
         case UpdateState::MergeFailed:
             return os << "merge-failed";
         default:
-            LOG(ERROR) << "Unknown update state";
+            LOG(ERROR) << "Unknown update state: " << static_cast<uint32_t>(state);
             return os;
     }
 }
 
+UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
+    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock);
+    return status.state();
+}
+
+SnapshotUpdateStatus SnapshotManager::ReadSnapshotUpdateStatus(LockedFile* lock) {
+    CHECK(lock);
+
+    SnapshotUpdateStatus status = {};
+    std::string contents;
+    if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {
+        PLOG(ERROR) << "Read state file failed";
+        status.set_state(UpdateState::None);
+        return status;
+    }
+
+    if (!status.ParseFromString(contents)) {
+        LOG(WARNING) << "Unable to parse state file as SnapshotUpdateStatus, using the old format";
+
+        // Try to rollback to legacy file to support devices that are
+        // currently using the old file format.
+        // TODO(b/147409432)
+        status.set_state(UpdateStateFromString(contents));
+    }
+
+    return status;
+}
+
 bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
+    SnapshotUpdateStatus status = {};
+    status.set_state(state);
+    return WriteSnapshotUpdateStatus(lock, status);
+}
+
+bool SnapshotManager::WriteSnapshotUpdateStatus(LockedFile* lock,
+                                                const SnapshotUpdateStatus& status) {
     CHECK(lock);
     CHECK(lock->lock_mode() == LOCK_EX);
 
-    std::stringstream ss;
-    ss << state;
-    std::string contents = ss.str();
-    if (contents.empty()) return false;
+    std::string contents;
+    if (!status.SerializeToString(&contents)) {
+        LOG(ERROR) << "Unable to serialize SnapshotUpdateStatus.";
+        return false;
+    }
 
 #ifdef LIBSNAPSHOT_USE_HAL
     auto merge_status = MergeStatus::UNKNOWN;
-    switch (state) {
+    switch (status.state()) {
         // The needs-reboot and completed cases imply that /data and /metadata
         // can be safely wiped, so we don't report a merge status.
         case UpdateState::None:
@@ -1723,7 +1801,7 @@
         default:
             // Note that Cancelled flows to here - it is never written, since
             // it only communicates a transient state to the caller.
-            LOG(ERROR) << "Unexpected update status: " << state;
+            LOG(ERROR) << "Unexpected update status: " << status.state();
             break;
     }
 
@@ -1844,9 +1922,21 @@
     }
 }
 
-bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
+static Return AddRequiredSpace(Return orig,
+                               const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
+    if (orig.error_code() != Return::ErrorCode::NO_SPACE) {
+        return orig;
+    }
+    uint64_t sum = 0;
+    for (auto&& [name, status] : all_snapshot_status) {
+        sum += status.cow_file_size();
+    }
+    return Return::NoSpace(sum);
+}
+
+Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
     auto lock = LockExclusive();
-    if (!lock) return false;
+    if (!lock) return Return::Error();
 
     // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
     // partition takes up a big chunk of space in super, causing COW images to be created on
@@ -1854,7 +1944,7 @@
     if (device_->IsOverlayfsSetup()) {
         LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
                    << ", reboot, then try again.";
-        return false;
+        return Return::Error();
     }
 
     const auto& opener = device_->GetPartitionOpener();
@@ -1879,7 +1969,7 @@
     SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
     if (!metadata_updater.Update()) {
         LOG(ERROR) << "Cannot calculate new metadata.";
-        return false;
+        return Return::Error();
     }
 
     // Delete previous COW partitions in current_metadata so that PartitionCowCreator marks those as
@@ -1911,36 +2001,34 @@
             .extra_extents = {},
     };
 
-    if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
-                                       &all_snapshot_status)) {
-        return false;
-    }
+    auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
+                                             &all_snapshot_status);
+    if (!ret.is_ok()) return ret;
 
     auto exported_target_metadata = target_metadata->Export();
     if (exported_target_metadata == nullptr) {
         LOG(ERROR) << "Cannot export target metadata";
-        return false;
+        return Return::Error();
     }
 
-    if (!InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
-                                   exported_target_metadata.get(), target_suffix,
-                                   all_snapshot_status)) {
-        return false;
-    }
+    ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
+                                    exported_target_metadata.get(), target_suffix,
+                                    all_snapshot_status);
+    if (!ret.is_ok()) return ret;
 
     if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
                               *exported_target_metadata, target_slot)) {
         LOG(ERROR) << "Cannot write target metadata";
-        return false;
+        return Return::Error();
     }
 
     created_devices.Release();
     LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
 
-    return true;
+    return Return::Ok();
 }
 
-bool SnapshotManager::CreateUpdateSnapshotsInternal(
+Return SnapshotManager::CreateUpdateSnapshotsInternal(
         LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,
         AutoDeviceList* created_devices,
         std::map<std::string, SnapshotStatus>* all_snapshot_status) {
@@ -1951,7 +2039,7 @@
 
     if (!target_metadata->AddGroup(kCowGroupName, 0)) {
         LOG(ERROR) << "Cannot add group " << kCowGroupName;
-        return false;
+        return Return::Error();
     }
 
     std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
@@ -1963,7 +2051,7 @@
         if (!inserted) {
             LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
                        << " in update manifest.";
-            return false;
+            return Return::Error();
         }
 
         auto& extra_extents = extra_extents_map[suffixed_name];
@@ -1992,7 +2080,7 @@
         // Compute the device sizes for the partition.
         auto cow_creator_ret = cow_creator->Run();
         if (!cow_creator_ret.has_value()) {
-            return false;
+            return Return::Error();
         }
 
         LOG(INFO) << "For partition " << target_partition->name()
@@ -2006,7 +2094,7 @@
         if (!DeleteSnapshot(lock, target_partition->name())) {
             LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
                        << target_partition->name();
-            return false;
+            return Return::Error();
         }
 
         // It is possible that the whole partition uses free space in super, and snapshot / COW
@@ -2024,7 +2112,7 @@
 
         // Store these device sizes to snapshot status file.
         if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
-            return false;
+            return Return::Error();
         }
         created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());
 
@@ -2038,7 +2126,7 @@
             auto cow_partition = target_metadata->AddPartition(GetCowName(target_partition->name()),
                                                                kCowGroupName, 0 /* flags */);
             if (cow_partition == nullptr) {
-                return false;
+                return Return::Error();
             }
 
             if (!target_metadata->ResizePartition(
@@ -2046,28 +2134,34 @@
                         cow_creator_ret->cow_partition_usable_regions)) {
                 LOG(ERROR) << "Cannot create COW partition on metadata with size "
                            << cow_creator_ret->snapshot_status.cow_partition_size();
-                return false;
+                return Return::Error();
             }
             // Only the in-memory target_metadata is modified; nothing to clean up if there is an
             // error in the future.
         }
 
-        // Create the backing COW image if necessary.
-        if (cow_creator_ret->snapshot_status.cow_file_size() > 0) {
-            if (!CreateCowImage(lock, target_partition->name())) {
-                return false;
-            }
-        }
-
         all_snapshot_status->emplace(target_partition->name(),
                                      std::move(cow_creator_ret->snapshot_status));
 
-        LOG(INFO) << "Successfully created snapshot for " << target_partition->name();
+        LOG(INFO) << "Successfully created snapshot partition for " << target_partition->name();
     }
-    return true;
+
+    LOG(INFO) << "Allocating CoW images.";
+
+    for (auto&& [name, snapshot_status] : *all_snapshot_status) {
+        // Create the backing COW image if necessary.
+        if (snapshot_status.cow_file_size() > 0) {
+            auto ret = CreateCowImage(lock, name);
+            if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
+        }
+
+        LOG(INFO) << "Successfully created snapshot for " << name;
+    }
+
+    return Return::Ok();
 }
 
-bool SnapshotManager::InitializeUpdateSnapshots(
+Return SnapshotManager::InitializeUpdateSnapshots(
         LockedFile* lock, MetadataBuilder* target_metadata,
         const LpMetadata* exported_target_metadata, const std::string& target_suffix,
         const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
@@ -2086,7 +2180,7 @@
         if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {
             LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
                        << target_partition->name();
-            return false;
+            return Return::Error();
         }
 
         auto it = all_snapshot_status.find(target_partition->name());
@@ -2094,23 +2188,24 @@
         cow_params.partition_name = target_partition->name();
         std::string cow_name;
         if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {
-            return false;
+            return Return::Error();
         }
 
         std::string cow_path;
         if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
             LOG(ERROR) << "Cannot determine path for " << cow_name;
-            return false;
+            return Return::Error();
         }
 
-        if (!InitializeCow(cow_path)) {
+        auto ret = InitializeCow(cow_path);
+        if (!ret.is_ok()) {
             LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
                        << cow_path;
-            return false;
+            return AddRequiredSpace(ret, all_snapshot_status);
         }
         // Let destructor of created_devices_for_cow to unmap the COW devices.
     };
-    return true;
+    return Return::Ok();
 }
 
 bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
@@ -2221,9 +2316,19 @@
         }
     }
 
+    unsigned int last_progress = 0;
+    auto callback = [&]() -> void {
+        double progress;
+        GetUpdateState(&progress);
+        if (last_progress < static_cast<unsigned int>(progress)) {
+            last_progress = progress;
+            LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%.";
+        }
+    };
+
     LOG(INFO) << "Waiting for any previous merge request to complete. "
               << "This can take up to several minutes.";
-    auto state = ProcessUpdateState();
+    auto state = ProcessUpdateState(callback);
     if (state == UpdateState::None) {
         LOG(INFO) << "Can't find any snapshot to merge.";
         return state;
@@ -2235,14 +2340,15 @@
         }
         // All other states can be handled by ProcessUpdateState.
         LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
-        state = ProcessUpdateState();
+        last_progress = 0;
+        state = ProcessUpdateState(callback);
     }
 
     LOG(INFO) << "Merge finished with state \"" << state << "\".";
     return state;
 }
 
-bool SnapshotManager::WaitForMerge() {
+Return SnapshotManager::WaitForMerge() {
     LOG(INFO) << "Waiting for any previous merge request to complete. "
               << "This can take up to several minutes.";
     while (true) {
@@ -2253,7 +2359,18 @@
             continue;
         }
         LOG(INFO) << "Wait for merge exits with state " << state;
-        return state == UpdateState::None || state == UpdateState::MergeCompleted;
+        switch (state) {
+            case UpdateState::None:
+                [[fallthrough]];
+            case UpdateState::MergeCompleted:
+                [[fallthrough]];
+            case UpdateState::Cancelled:
+                return Return::Ok();
+            case UpdateState::MergeNeedsReboot:
+                return Return::NeedsReboot();
+            default:
+                return Return::Error();
+        }
     }
 }
 
@@ -2356,5 +2473,37 @@
     return true;
 }
 
+CreateResult SnapshotManager::RecoveryCreateSnapshotDevices() {
+    if (!device_->IsRecovery()) {
+        LOG(ERROR) << __func__ << " is only allowed in recovery.";
+        return CreateResult::NOT_CREATED;
+    }
+
+    auto mount = EnsureMetadataMounted();
+    if (!mount || !mount->HasDevice()) {
+        LOG(ERROR) << "Couldn't mount Metadata.";
+        return CreateResult::NOT_CREATED;
+    }
+
+    auto state_file = GetStateFilePath();
+    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
+        LOG(ERROR) << "Couldn't access state file.";
+        return CreateResult::NOT_CREATED;
+    }
+
+    if (!NeedSnapshotsInFirstStageMount()) {
+        return CreateResult::NOT_CREATED;
+    }
+
+    auto slot_suffix = device_->GetOtherSlotSuffix();
+    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    auto super_path = device_->GetSuperDevice(slot_number);
+    if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+        LOG(ERROR) << "Unable to map partitions.";
+        return CreateResult::ERROR;
+    }
+    return CreateResult::CREATED;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 0f5af14..47ac474 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -47,6 +47,7 @@
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
+using android::fiemap::FiemapStatus;
 using android::fiemap::IImageManager;
 using android::fs_mgr::BlockDeviceInfo;
 using android::fs_mgr::CreateLogicalPartitionParams;
@@ -606,17 +607,17 @@
 std::ostream& operator<<(std::ostream& os, Request request) {
     switch (request) {
         case Request::LOCK_SHARED:
-            return os << "LOCK_SHARED";
+            return os << "Shared";
         case Request::LOCK_EXCLUSIVE:
-            return os << "LOCK_EXCLUSIVE";
+            return os << "Exclusive";
         case Request::UNLOCK:
-            return os << "UNLOCK";
+            return os << "Unlock";
         case Request::EXIT:
-            return os << "EXIT";
+            return os << "Exit";
         case Request::UNKNOWN:
             [[fallthrough]];
         default:
-            return os << "UNKNOWN";
+            return os << "Unknown";
     }
 }
 
@@ -746,7 +747,7 @@
                         LockTestParam{Request::LOCK_SHARED, Request::LOCK_EXCLUSIVE}),
         [](const testing::TestParamInfo<LockTestP::ParamType>& info) {
             std::stringstream ss;
-            ss << info.param.first << "_" << info.param.second;
+            ss << info.param.first << info.param.second;
             return ss.str();
         });
 
@@ -1585,6 +1586,29 @@
     ASSERT_THAT(merger.get(), AnyOf(UpdateState::None, UpdateState::MergeCompleted));
 }
 
+TEST_F(SnapshotUpdateTest, LowSpace) {
+    static constexpr auto kMaxFree = 10_MiB;
+    auto userdata = std::make_unique<LowSpaceUserdata>();
+    ASSERT_TRUE(userdata->Init(kMaxFree));
+
+    // Grow all partitions to 5_MiB, total 15_MiB. This requires 15 MiB of CoW space. After
+    // using the empty space in super (< 1 MiB), it uses at least 14 MiB of /userdata space.
+    constexpr uint64_t partition_size = 5_MiB;
+    SetSize(sys_, partition_size);
+    SetSize(vnd_, partition_size);
+    SetSize(prd_, partition_size);
+
+    AddOperationForPartitions();
+
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    auto res = sm->CreateUpdateSnapshots(manifest_);
+    ASSERT_FALSE(res);
+    ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
+    ASSERT_GE(res.required_size(), 14_MiB);
+    ASSERT_LT(res.required_size(), 15_MiB);
+}
+
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
@@ -1700,6 +1724,55 @@
                                     "Merge"s;
                          });
 
+// Test behavior of ImageManager::Create on low space scenario. These tests assumes image manager
+// uses /data as backup device.
+class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t> {
+  public:
+    void SetUp() override {
+        SnapshotTest::SetUp();
+        userdata_ = std::make_unique<LowSpaceUserdata>();
+        ASSERT_TRUE(userdata_->Init(GetParam()));
+    }
+    void TearDown() override {
+        EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
+                    image_manager_->DeleteBackingImage(kImageName));
+    }
+    static constexpr const char* kImageName = "my_image";
+    std::unique_ptr<LowSpaceUserdata> userdata_;
+};
+
+TEST_P(ImageManagerTest, CreateImageEnoughAvailSpace) {
+    if (userdata_->available_space() == 0) {
+        GTEST_SKIP() << "/data is full (" << userdata_->available_space()
+                     << " bytes available), skipping";
+    }
+    ASSERT_TRUE(image_manager_->CreateBackingImage(kImageName, userdata_->available_space(),
+                                                   IImageManager::CREATE_IMAGE_DEFAULT))
+            << "Should be able to create image with size = " << userdata_->available_space()
+            << " bytes";
+    ASSERT_TRUE(image_manager_->DeleteBackingImage(kImageName))
+            << "Should be able to delete created image";
+}
+
+TEST_P(ImageManagerTest, CreateImageNoSpace) {
+    uint64_t to_allocate = userdata_->free_space() + userdata_->bsize();
+    auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
+                                                  IImageManager::CREATE_IMAGE_DEFAULT);
+    ASSERT_FALSE(res) << "Should not be able to create image with size = " << to_allocate
+                      << " bytes because only " << userdata_->free_space() << " bytes are free";
+    ASSERT_EQ(FiemapStatus::ErrorCode::NO_SPACE, res.error_code()) << res.string();
+}
+
+std::vector<uint64_t> ImageManagerTestParams() {
+    std::vector<uint64_t> ret;
+    for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
+        ret.push_back(size);
+    }
+    return ret;
+}
+
+INSTANTIATE_TEST_SUITE_P(ImageManagerTest, ImageManagerTest, ValuesIn(ImageManagerTestParams()));
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index f7f25af..b036606 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -14,8 +14,11 @@
 
 #include <libsnapshot/test_helpers.h>
 
+#include <sys/statvfs.h>
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <openssl/sha.h>
@@ -167,5 +170,67 @@
     return partition_update->mutable_new_partition_info()->size();
 }
 
+AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) {
+    auto res = ReadUserdataStats();
+    if (!res) return res;
+
+    // Try to fill up the disk as much as possible until free_space_ <= max_free_space.
+    big_file_ = std::make_unique<TemporaryFile>();
+    if (big_file_->fd == -1) {
+        return AssertionFailure() << strerror(errno);
+    }
+    if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) {
+        return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
+                                  << kUserDataDevice;
+    }
+    uint64_t next_consume =
+            std::min(free_space_ - max_free_space, (uint64_t)std::numeric_limits<off_t>::max());
+    off_t allocated = 0;
+    while (next_consume > 0 && free_space_ > max_free_space) {
+        int status = fallocate(big_file_->fd, 0, allocated, next_consume);
+        if (status == -1 && errno == ENOSPC) {
+            next_consume /= 2;
+            continue;
+        }
+        if (status == -1) {
+            return AssertionFailure() << strerror(errno);
+        }
+        allocated += next_consume;
+
+        res = ReadUserdataStats();
+        if (!res) return res;
+    }
+
+    LOG(INFO) << allocated << " bytes allocated to " << big_file_->path;
+    initialized_ = true;
+    return AssertionSuccess();
+}
+
+AssertionResult LowSpaceUserdata::ReadUserdataStats() {
+    struct statvfs buf;
+    if (statvfs(kUserDataDevice, &buf) == -1) {
+        return AssertionFailure() << strerror(errno);
+    }
+    bsize_ = buf.f_bsize;
+    free_space_ = buf.f_bsize * buf.f_bfree;
+    available_space_ = buf.f_bsize * buf.f_bavail;
+    return AssertionSuccess();
+}
+
+uint64_t LowSpaceUserdata::free_space() const {
+    CHECK(initialized_);
+    return free_space_;
+}
+
+uint64_t LowSpaceUserdata::available_space() const {
+    CHECK(initialized_);
+    return available_space_;
+}
+
+uint64_t LowSpaceUserdata::bsize() const {
+    CHECK(initialized_);
+    return bsize_;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index fa1d7f0..3a64448 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -14,12 +14,15 @@
 
 #include "utility.h"
 
+#include <errno.h>
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <fs_mgr/roots.h>
 
 using android::dm::kSectorSize;
+using android::fiemap::FiemapStatus;
 using android::fs_mgr::EnsurePathMounted;
 using android::fs_mgr::EnsurePathUnmounted;
 using android::fs_mgr::Fstab;
@@ -83,7 +86,7 @@
     }
 }
 
-bool InitializeCow(const std::string& device) {
+Return InitializeCow(const std::string& device) {
     // When the kernel creates a persistent dm-snapshot, it requires a CoW file
     // to store the modifications. The kernel interface does not specify how
     // the CoW is used, and there is no standard associated.
@@ -103,15 +106,15 @@
     android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
     if (fd < 0) {
         PLOG(ERROR) << "Can't open COW device: " << device;
-        return false;
+        return Return(FiemapStatus::FromErrno(errno));
     }
 
     LOG(INFO) << "Zero-filling COW device: " << device;
     if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
         PLOG(ERROR) << "Can't zero-fill COW device for " << device;
-        return false;
+        return Return(FiemapStatus::FromErrno(errno));
     }
-    return true;
+    return Return::Ok();
 }
 
 std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 5cc572e..ad46090 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -26,6 +26,7 @@
 #include <update_engine/update_metadata.pb.h>
 
 #include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
 
 namespace android {
 namespace snapshot {
@@ -110,7 +111,7 @@
         android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
 
 // Initialize a device before using it as the COW device for a dm-snapshot device.
-bool InitializeCow(const std::string& device);
+Return InitializeCow(const std::string& device);
 
 // "Atomically" write string to file. This is done by a series of actions:
 // 1. Write to path + ".tmp"
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index 11611dd..beb18ef 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -2,5 +2,6 @@
 cc_library_headers {
     name: "libstorage_literals_headers",
     host_supported: true,
+    recovery_available: true,
     export_include_dirs: ["."],
 }
diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp
index 937e0f3..bceabab 100644
--- a/fs_mgr/libvbmeta/Android.bp
+++ b/fs_mgr/libvbmeta/Android.bp
@@ -37,6 +37,7 @@
 cc_test_host {
     name: "libvbmeta_test",
     static_libs: [
+        "liblog",
         "libsparse",
         "libvbmeta",
         "libz",
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
index 91c3fb9..0ff8995 100644
--- a/fs_mgr/tests/AndroidTest.xml
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -15,6 +15,7 @@
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 4226e95..c66f307 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1160,13 +1160,16 @@
 ret=${?}
 uses_dynamic_scratch=false
 scratch_partition=
+virtual_ab=`get_property ro.virtual_ab.enabled`
 if ${overlayfs_needed}; then
   if [ ${ret} != 0 ]; then
     die -t ${T} "overlay takeover failed"
   fi
   echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
    echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover not complete" >&2
-  scratch_partition=scratch
+  if [ -z "${virtual_ab}" ]; then
+    scratch_partition=scratch
+  fi
   if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
     echo "${BLUE}[     INFO ]${NORMAL} using ${scratch_partition} dynamic partition for overrides" >&2
   fi
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index bdf4aac..7caf468 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -126,6 +126,26 @@
     return *ret;
 }
 
+BatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {
+    static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {
+            {"Unknown", BatteryCapacityLevel::UNKNOWN},
+            {"Critical", BatteryCapacityLevel::CRITICAL},
+            {"Low", BatteryCapacityLevel::LOW},
+            {"Normal", BatteryCapacityLevel::NORMAL},
+            {"High", BatteryCapacityLevel::HIGH},
+            {"Full", BatteryCapacityLevel::FULL},
+            {NULL, BatteryCapacityLevel::UNKNOWN},
+    };
+
+    auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
+    if (!ret) {
+        KLOG_WARNING(LOG_TAG, "Unknown battery capacity level '%s'\n", capacityLevel);
+        *ret = BatteryCapacityLevel::UNKNOWN;
+    }
+
+    return *ret;
+}
+
 BatteryHealth getBatteryHealth(const char* status) {
     static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
             {"Unknown", BatteryHealth::UNKNOWN},
@@ -241,9 +261,10 @@
         mHealthInfo->legacy.batteryCurrentAverage =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
 
-    // TODO(b/142260281): Retrieve these values correctly.
-    mHealthInfo->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
-    mHealthInfo->batteryChargeTimeToFullNowSeconds = 0;
+    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+        mHealthInfo->batteryChargeTimeToFullNowSeconds =
+                getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
+
     mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
 
     props.batteryTemperature = mBatteryFixedTemperature ?
@@ -252,6 +273,9 @@
 
     std::string buf;
 
+    if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)
+        mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
+
     if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
         props.batteryStatus = getBatteryStatus(buf.c_str());
 
@@ -585,6 +609,19 @@
                         mHealthdConfig->batteryCycleCountPath = path;
                 }
 
+                if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+                }
+
+                if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryChargeTimeToFullNowPath = path;
+                }
+
                 if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_avg",
@@ -653,6 +690,10 @@
             KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
         if (mHealthdConfig->batteryCycleCountPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
+        if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
+        if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
     }
 
     if (property_get("ro.boot.fake_battery", pval, NULL) > 0
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index a900071..8ffb114 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -69,6 +69,8 @@
     android::String8 batteryChargeCounterPath;
     android::String8 batteryFullChargePath;
     android::String8 batteryCycleCountPath;
+    android::String8 batteryCapacityLevelPath;
+    android::String8 batteryChargeTimeToFullNowPath;
 
     int (*energyCounter)(int64_t *);
     int boot_min_cap;
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 667911d..920dc6c 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
diff --git a/init/README.md b/init/README.md
index b8300fa..4f0a7ec 100644
--- a/init/README.md
+++ b/init/README.md
@@ -487,6 +487,25 @@
   -f: force installation of the module even if the version of the running kernel
   and the version of the kernel for which the module was compiled do not match.
 
+`interface_start <name>` \
+`interface_restart <name>` \
+`interface_stop <name>`
+> Find the service that provides the interface _name_ if it exists and run the `start`, `restart`,
+or `stop` commands on it respectively.  _name_ may be either a fully qualified HIDL name, in which
+case it is specified as `<interface>/<instance>`, or an AIDL name, in which case it is specified as
+`aidl/<interface>` for example `android.hardware.secure_element@1.1::ISecureElement/eSE1` or
+`aidl/aidl_lazy_test_1`.
+
+> Note that these commands only act on interfaces specified by the `interface` service option, not
+on interfaces registered at runtime.
+
+> Example usage of these commands: \
+`interface_start android.hardware.secure_element@1.1::ISecureElement/eSE1`
+will start the HIDL Service that provides the `android.hardware.secure_element@1.1` and `eSI1`
+instance. \
+`interface_start aidl/aidl_lazy_test_1` will start the AIDL service that
+provides the `aidl_lazy_test_1` interface.
+
 `load_system_props`
 > (This action is deprecated and no-op.)
 
@@ -700,6 +719,26 @@
   `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
   characteristics in a device agnostic manner.
 
+Init responds to properties that begin with `ctl.`.  These properties take the format of
+`ctl.<command>` and the _value_ of the system property is used as a parameter, for example:
+`SetProperty("ctl.start", "logd")` will run the `start` command on `logd`.  Note that these
+properties are only settable; they will have no value when read.
+
+`ctl.start` \
+`ctl.restart` \
+`ctl.stop`
+> These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified
+by the _value_ of the property.
+
+`ctl.interface_start` \
+`ctl.interface_restart` \
+`ctl.interface_stop`
+> These are equivalent to using the `interface_start`, `interface_restart`, and `interface_stop`
+commands on the interface specified by the _value_ of the property.
+
+`ctl.sigstop_on` and `ctl.sigstop_off` will turn on or off the _sigstop_ feature for the service
+specified by the _value_ of the property.  See the _Debugging init_ section below for more details
+about this feature.
 
 Boot timing
 -----------
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 62a19ab..c877590 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -59,6 +59,8 @@
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
 #include <libgsi/libgsi.h>
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -1117,19 +1119,28 @@
 }
 
 static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
+    bool should_reboot_into_recovery = true;
     auto reboot_reason = vdc_arg + "_failed";
+    if (android::sysprop::InitProperties::userspace_reboot_in_progress().value_or(false)) {
+        should_reboot_into_recovery = false;
+    }
 
-    auto reboot = [reboot_reason](const std::string& message) {
+    auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
         // TODO (b/122850122): support this in gsi
-        if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
-            LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
-            if (auto result = reboot_into_recovery(
-                        {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
-                !result) {
-                LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+        if (should_reboot_into_recovery) {
+            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+                LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
+                if (auto result = reboot_into_recovery(
+                            {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+                    !result) {
+                    LOG(FATAL) << "Could not reboot into recovery: " << result.error();
+                }
+            } else {
+                LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
             }
         } else {
-            LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason;
+            LOG(ERROR) << message << ": rebooting, reason: " << reboot_reason;
+            trigger_shutdown("reboot," + reboot_reason);
         }
     };
 
@@ -1176,6 +1187,37 @@
     return {};
 }
 
+static Result<void> GenerateLinkerConfiguration() {
+    const char* linkerconfig_binary = "/system/bin/linkerconfig";
+    const char* linkerconfig_target = "/linkerconfig";
+    const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target};
+
+    if (logwrap_fork_execvp(arraysize(arguments), arguments, nullptr, false, LOG_KLOG, false,
+                            nullptr) != 0) {
+        return ErrnoError() << "failed to execute linkerconfig";
+    }
+
+    LOG(INFO) << "linkerconfig generated " << linkerconfig_target
+              << " with mounted APEX modules info";
+
+    return {};
+}
+
+static bool IsApexUpdatable() {
+    static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+    return updatable;
+}
+
+static Result<void> do_update_linker_config(const BuiltinArguments&) {
+    // If APEX is not updatable, then all APEX information are already included in the first
+    // linker config generation, so there is no need to update linker configuration again.
+    if (IsApexUpdatable()) {
+        return GenerateLinkerConfiguration();
+    }
+
+    return {};
+}
+
 static Result<void> parse_apex_configs() {
     glob_t glob_result;
     static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
@@ -1234,9 +1276,7 @@
         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"};
+        auto options = MkdirOptions{path, 0771, AID_ROOT, AID_SYSTEM, FscryptAction::kNone, "ref"};
         make_dir_with_options(options);
     }
     return {};
@@ -1251,6 +1291,12 @@
     if (!parse_configs) {
         return parse_configs.error();
     }
+
+    auto update_linker_config = do_update_linker_config(args);
+    if (!update_linker_config) {
+        return update_linker_config.error();
+    }
+
     return {};
 }
 
@@ -1317,6 +1363,7 @@
         {"perform_apex_config",     {0,     0,    {false,  do_perform_apex_config}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"umount_all",              {1,     1,    {false,  do_umount_all}}},
+        {"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
         {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
         {"restart",                 {1,     1,    {false,  do_restart}}},
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 9121bac..d8c4843 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -34,6 +34,7 @@
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
+#include <libfiemap/image_manager.h>
 #include <libgsi/libgsi.h>
 #include <liblp/liblp.h>
 #include <libsnapshot/snapshot.h>
@@ -46,6 +47,7 @@
 
 using android::base::Split;
 using android::base::Timer;
+using android::fiemap::IImageManager;
 using android::fs_mgr::AvbHandle;
 using android::fs_mgr::AvbHandleStatus;
 using android::fs_mgr::AvbHashtreeResult;
@@ -56,7 +58,6 @@
 using android::fs_mgr::ReadFstabFromDt;
 using android::fs_mgr::SkipMountingPartitions;
 using android::fs_mgr::TransformFstabForDsu;
-using android::init::WriteFile;
 using android::snapshot::SnapshotManager;
 
 using namespace std::literals;
@@ -93,7 +94,7 @@
     bool IsDmLinearEnabled();
     void GetDmLinearMetadataDevice(std::set<std::string>* devices);
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
-    void UseGsiIfPresent();
+    void UseDsuIfPresent();
 
     ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);
 
@@ -102,7 +103,7 @@
     virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
 
     bool need_dm_verity_;
-    bool gsi_not_on_userdata_ = false;
+    bool dsu_not_on_userdata_ = false;
 
     Fstab fstab_;
     std::string lp_metadata_partition_;
@@ -511,7 +512,7 @@
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
 bool FirstStageMount::TrySwitchSystemAsRoot() {
-    UseGsiIfPresent();
+    UseDsuIfPresent();
 
     auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
         return entry.mount_point == "/system";
@@ -520,7 +521,7 @@
     if (system_partition == fstab_.end()) return true;
 
     if (MountPartition(system_partition, false /* erase_same_mounts */)) {
-        if (gsi_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+        if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
             LOG(ERROR) << "check_most_at_once forbidden on external media";
             return false;
         }
@@ -556,6 +557,14 @@
             continue;
         }
 
+        // Skip raw partition entries such as boot, dtbo, etc.
+        // Having emmc fstab entries allows us to probe current->vbmeta_partition
+        // in InitDevices() when they are AVB chained partitions.
+        if (current->fs_type == "emmc") {
+            ++current;
+            continue;
+        }
+
         Fstab::iterator end;
         if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
             if (current->fs_mgr_flags.no_fail) {
@@ -582,63 +591,64 @@
     }
 
     // heads up for instantiating required device(s) for overlayfs logic
-    const auto devices = fs_mgr_overlayfs_required_devices(&fstab_);
-    for (auto const& device : devices) {
-        if (android::base::StartsWith(device, "/dev/block/by-name/")) {
-            InitRequiredDevices({basename(device.c_str())});
-        } else {
-            InitMappedDevice(device);
+    auto init_devices = [this](std::set<std::string> devices) -> bool {
+        for (auto iter = devices.begin(); iter != devices.end();) {
+            if (android::base::StartsWith(*iter, "/dev/block/dm-")) {
+                if (!InitMappedDevice(*iter)) return false;
+                iter = devices.erase(iter);
+            } else {
+                iter++;
+            }
         }
-    }
+        return InitRequiredDevices(std::move(devices));
+    };
+    MapScratchPartitionIfNeeded(&fstab_, init_devices);
 
     fs_mgr_overlayfs_mount_all(&fstab_);
 
     return true;
 }
 
-void FirstStageMount::UseGsiIfPresent() {
+void FirstStageMount::UseDsuIfPresent() {
     std::string error;
 
     if (!android::gsi::CanBootIntoGsi(&error)) {
-        LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
+        LOG(INFO) << "DSU " << error << ", proceeding with normal boot";
         return;
     }
 
-    auto metadata = android::fs_mgr::ReadFromImageFile(gsi::kDsuLpMetadataFile);
-    if (!metadata) {
-        LOG(ERROR) << "GSI partition layout could not be read";
+    auto init_devices = [this](std::set<std::string> devices) -> bool {
+        if (devices.count("userdata") == 0 || devices.size() > 1) {
+            dsu_not_on_userdata_ = true;
+        }
+        return InitRequiredDevices(std::move(devices));
+    };
+    std::string active_dsu;
+    if (!gsi::GetActiveDsu(&active_dsu)) {
+        LOG(ERROR) << "Failed to GetActiveDsu";
         return;
     }
-
-    if (!InitDmLinearBackingDevices(*metadata.get())) {
-        return;
-    }
-
-    // Find the super name. PartitionOpener will ensure this translates to the
-    // correct block device path.
-    auto super = GetMetadataSuperBlockDevice(*metadata.get());
-    auto super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
-    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_name)) {
-        LOG(ERROR) << "GSI partition layout could not be instantiated";
+    LOG(INFO) << "DSU slot: " << active_dsu;
+    auto images = IImageManager::Open("dsu/" + active_dsu, 0ms);
+    if (!images || !images->MapAllImages(init_devices)) {
+        LOG(ERROR) << "DSU partition layout could not be instantiated";
         return;
     }
 
     if (!android::gsi::MarkSystemAsGsi()) {
-        PLOG(ERROR) << "GSI indicator file could not be written";
+        PLOG(ERROR) << "DSU indicator file could not be written";
         return;
     }
 
     std::string lp_names = "";
     std::vector<std::string> dsu_partitions;
-    for (auto&& partition : metadata->partitions) {
-        auto name = fs_mgr::GetPartitionName(partition);
+    for (auto&& name : images->GetAllBackingImages()) {
         dsu_partitions.push_back(name);
         lp_names += name + ",";
     }
     // Publish the logical partition names for TransformFstabForDsu
     WriteFile(gsi::kGsiLpNamesFile, lp_names);
     TransformFstabForDsu(&fstab_, dsu_partitions);
-    gsi_not_on_userdata_ = (super_name != "userdata");
 }
 
 bool FirstStageMountVBootV1::GetDmVerityDevices(std::set<std::string>* devices) {
diff --git a/init/init.cpp b/init/init.cpp
index 5f97e44..a25bf6c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -730,8 +730,8 @@
     }
 
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
-
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
+    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 648b3bb..1a474fb 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -35,6 +35,19 @@
 namespace init {
 namespace {
 
+static bool BindMount(const std::string& source, const std::string& mount_point,
+                      bool recursive = false) {
+    unsigned long mountflags = MS_BIND;
+    if (recursive) {
+        mountflags |= MS_REC;
+    }
+    if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+        PLOG(ERROR) << "Failed to bind mount " << source;
+        return false;
+    }
+    return true;
+}
+
 static bool MakeShared(const std::string& mount_point, bool recursive = false) {
     unsigned long mountflags = MS_SHARED;
     if (recursive) {
@@ -47,6 +60,18 @@
     return true;
 }
 
+static bool MakeSlave(const std::string& mount_point, bool recursive = false) {
+    unsigned long mountflags = MS_SLAVE;
+    if (recursive) {
+        mountflags |= MS_REC;
+    }
+    if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+        PLOG(ERROR) << "Failed to change propagation type to slave";
+        return false;
+    }
+    return true;
+}
+
 static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
     unsigned long mountflags = MS_PRIVATE;
     if (recursive) {
@@ -82,7 +107,7 @@
 }
 
 static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
-    if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && ret != EEXIST) {
+    if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
         return ErrnoError() << "Could not create mount point " << mount_path;
     }
     if (mount(path.c_str(), mount_path.c_str(), nullptr, MS_BIND, nullptr) != 0) {
@@ -151,6 +176,20 @@
     return true;
 }
 
+static Result<void> MountLinkerConfigForDefaultNamespace() {
+    // No need to mount linkerconfig for default mount namespace if the path does not exist (which
+    // would mean it is already mounted)
+    if (access("/linkerconfig/default", 0) != 0) {
+        return {};
+    }
+
+    if (mount("/linkerconfig/default", "/linkerconfig", nullptr, MS_BIND | MS_REC, nullptr) != 0) {
+        return ErrnoError() << "Failed to mount linker configuration for default mount namespace.";
+    }
+
+    return {};
+}
+
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -177,6 +216,39 @@
     // namespace
     if (!(MakePrivate("/linkerconfig"))) return false;
 
+    // The two mount namespaces present challenges for scoped storage, because
+    // vold, which is responsible for most of the mounting, lives in the
+    // bootstrap mount namespace, whereas most other daemons and all apps live
+    // in the default namespace.  Scoped storage has a need for a
+    // /mnt/installer view that is a slave bind mount of /mnt/user - in other
+    // words, all mounts under /mnt/user should automatically show up under
+    // /mnt/installer. However, additional mounts done under /mnt/installer
+    // should not propagate back to /mnt/user. In a single mount namespace
+    // this is easy to achieve, by simply marking the /mnt/installer a slave
+    // bind mount. Unfortunately, if /mnt/installer is only created and
+    // bind mounted after the two namespaces are created below, we end up
+    // with the following situation:
+    // /mnt/user and /mnt/installer share the same peer group in both the
+    // bootstrap and default namespaces. Marking /mnt/installer slave in either
+    // namespace means that it won't propagate events to the /mnt/installer in
+    // the other namespace, which is still something we require - vold is the
+    // one doing the mounting under /mnt/installer, and those mounts should
+    // show up in the default namespace as well.
+    //
+    // The simplest solution is to do the bind mount before the two namespaces
+    // are created: the effect is that in both namespaces, /mnt/installer is a
+    // slave to the /mnt/user mount, and at the same time /mnt/installer in the
+    // bootstrap namespace shares a peer group with /mnt/installer in the
+    // default namespace.
+    if (!mkdir_recursive("/mnt/user", 0755)) return false;
+    if (!mkdir_recursive("/mnt/installer", 0755)) return false;
+    if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
+    // First, make /mnt/installer a slave bind mount
+    if (!(MakeSlave("/mnt/installer"))) return false;
+    // Then, make it shared again - effectively creating a new peer group, that
+    // will be inherited by new mount namespaces.
+    if (!(MakeShared("/mnt/installer"))) return false;
+
     bootstrap_ns_fd.reset(OpenMountNamespace());
     bootstrap_ns_id = GetMountNamespaceId();
 
@@ -222,6 +294,11 @@
             PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
             return false;
         }
+
+        if (auto result = MountLinkerConfigForDefaultNamespace(); !result) {
+            LOG(ERROR) << result.error();
+            return false;
+        }
     }
 
     LOG(INFO) << "Switched to default mount namespace";
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 0e61234..8c90714 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -741,12 +741,15 @@
     });
     // Triggering userspace-reboot-requested will result in a bunch of setprop
     // actions. We should make sure, that all of them are propagated before
-    // proceeding with userspace reboot. Synchronously setting kUserspaceRebootInProgress property
-    // is not perfect, but it should do the trick.
+    // proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
+    // property is not perfect, but it should do the trick.
     if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
         return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
     }
     EnterShutdown();
+    if (!SetProperty("sys.powerctl", "")) {
+        return Error() << "Failed to reset sys.powerctl property";
+    }
     std::vector<Service*> stop_first;
     // Remember the services that were enabled. We will need to manually enable them again otherwise
     // triggers like class_start won't restart them.
@@ -834,6 +837,10 @@
 }
 
 static void HandleUserspaceReboot() {
+    if (!android::sysprop::InitProperties::is_userspace_reboot_supported().value_or(false)) {
+        LOG(ERROR) << "Attempted a userspace reboot on a device that doesn't support it";
+        return;
+    }
     // Spinnig up a separate thread will fail the setns call later in the boot sequence.
     // Fork a new process to monitor userspace reboot while we are investigating a better solution.
     pid_t pid = fork();
@@ -856,6 +863,30 @@
     am.QueueBuiltinAction(handler, "userspace-reboot");
 }
 
+/**
+ * Check if "command" field is set in bootloader message.
+ *
+ * If "command" field is broken (contains non-printable characters prior to
+ * terminating zero), it will be zeroed.
+ *
+ * @param[in,out] boot Bootloader message (BCB) structure
+ * @return true if "command" field is already set, and false if it's empty
+ */
+static bool CommandIsPresent(bootloader_message* boot) {
+    if (boot->command[0] == '\0')
+        return false;
+
+    for (size_t i = 0; i < arraysize(boot->command); ++i) {
+        if (boot->command[i] == '\0')
+            return true;
+        if (!isprint(boot->command[i]))
+            break;
+    }
+
+    memset(boot->command, 0, sizeof(boot->command));
+    return false;
+}
+
 void HandlePowerctlMessage(const std::string& command) {
     unsigned int cmd = 0;
     std::vector<std::string> cmd_params = Split(command, ",");
@@ -908,7 +939,7 @@
                 }
                 // Update the boot command field if it's empty, and preserve
                 // the other arguments in the bootloader message.
-                if (boot.command[0] == '\0') {
+                if (!CommandIsPresent(&boot)) {
                     strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
                     if (std::string err; !write_bootloader_message(boot, &err)) {
                         LOG(ERROR) << "Failed to set bootloader message: " << err;
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index dac0cf4..485188b 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -34,12 +34,16 @@
 namespace init {
 
 static std::string init_fatal_reboot_target = "bootloader";
+static bool init_fatal_panic = false;
 
 void SetFatalRebootTarget() {
     std::string cmdline;
     android::base::ReadFileToString("/proc/cmdline", &cmdline);
     cmdline = android::base::Trim(cmdline);
 
+    const char kInitFatalPanicString[] = "androidboot.init_fatal_panic=true";
+    init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
+
     const char kRebootTargetString[] = "androidboot.init_fatal_reboot_target=";
     auto start_pos = cmdline.find(kRebootTargetString);
     if (start_pos == std::string::npos) {
@@ -133,6 +137,9 @@
     for (size_t i = 0; i < backtrace->NumFrames(); i++) {
         LOG(ERROR) << backtrace->FormatFrameData(i);
     }
+    if (init_fatal_panic) {
+        _exit(signal_number);
+    }
     RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
 }
 
diff --git a/init/security.cpp b/init/security.cpp
index 586d0c7..6cbe642 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -18,14 +18,19 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/perf_event.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
 #include <unistd.h>
 
 #include <fstream>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
 
 using android::base::unique_fd;
+using android::base::SetProperty;
 
 namespace android {
 namespace init {
@@ -197,5 +202,61 @@
     return {};
 }
 
+// Test for whether the kernel has SELinux hooks for the perf_event_open()
+// syscall. If the hooks are present, we can stop using the other permission
+// mechanism (perf_event_paranoid sysctl), and use only the SELinux policy to
+// control access to the syscall. The hooks are expected on all Android R
+// release kernels, but might be absent on devices that upgrade while keeping an
+// older kernel.
+//
+// There is no direct/synchronous way of finding out that a syscall failed due
+// to SELinux. Therefore we test for a combination of a success and a failure
+// that are explained by the platform's SELinux policy for the "init" domain:
+// * cpu-scoped perf_event is allowed
+// * ioctl() on the event fd is disallowed with EACCES
+//
+// Since init has CAP_SYS_ADMIN, these tests are not affected by the system-wide
+// perf_event_paranoid sysctl.
+//
+// If the SELinux hooks are detected, a special sysprop
+// (sys.init.perf_lsm_hooks) is set, which translates to a modification of
+// perf_event_paranoid (through init.rc sysprop actions).
+//
+// TODO(b/137092007): this entire test can be removed once the platform stops
+// supporting kernels that precede the perf_event_open hooks (Android common
+// kernels 4.4 and 4.9).
+Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&) {
+    // Use a trivial event that will be configured, but not started.
+    struct perf_event_attr pe = {
+            .type = PERF_TYPE_SOFTWARE,
+            .size = sizeof(struct perf_event_attr),
+            .config = PERF_COUNT_SW_TASK_CLOCK,
+            .disabled = 1,
+            .exclude_kernel = 1,
+    };
+
+    // Open the above event targeting cpu 0. (EINTR not possible.)
+    unique_fd fd(static_cast<int>(syscall(__NR_perf_event_open, &pe, /*pid=*/-1,
+                                          /*cpu=*/0,
+                                          /*group_fd=*/-1, /*flags=*/0)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Unexpected perf_event_open error";
+        return {};
+    }
+
+    int ioctl_ret = ioctl(fd, PERF_EVENT_IOC_RESET);
+    if (ioctl_ret != -1) {
+        // Success implies that the kernel doesn't have the hooks.
+        return {};
+    } else if (errno != EACCES) {
+        PLOG(ERROR) << "Unexpected perf_event ioctl error";
+        return {};
+    }
+
+    // Conclude that the SELinux hooks are present.
+    SetProperty("sys.init.perf_lsm_hooks", "1");
+    return {};
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/security.h b/init/security.h
index b081a05..43c2739 100644
--- a/init/security.h
+++ b/init/security.h
@@ -29,6 +29,7 @@
 Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
 Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
 Result<void> SetKptrRestrictAction(const BuiltinArguments&);
+Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/service.cpp b/init/service.cpp
index a97935e..0e27ff1 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -325,8 +325,8 @@
                     LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
                                << (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_);
+                    SetProperty("sys.init.updatable_crashing", "1");
                 }
             }
         } else {
diff --git a/init/sysprop/InitProperties.sysprop b/init/sysprop/InitProperties.sysprop
index d6a1ab6..b876dc0 100644
--- a/init/sysprop/InitProperties.sysprop
+++ b/init/sysprop/InitProperties.sysprop
@@ -25,3 +25,12 @@
     integer_as_bool: true
 }
 
+# Shows whenever the device supports userspace reboot or not.
+prop {
+    api_name: "is_userspace_reboot_supported"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.init.userspace_reboot.is_supported"
+    integer_as_bool: true
+}
diff --git a/init/sysprop/api/com.android.sysprop.init-current.txt b/init/sysprop/api/com.android.sysprop.init-current.txt
index 8da50e0..b8bcef9 100644
--- a/init/sysprop/api/com.android.sysprop.init-current.txt
+++ b/init/sysprop/api/com.android.sysprop.init-current.txt
@@ -1,6 +1,11 @@
 props {
   module: "android.sysprop.InitProperties"
   prop {
+    api_name: "is_userspace_reboot_supported"
+    prop_name: "ro.init.userspace_reboot.is_supported"
+    integer_as_bool: true
+  }
+  prop {
     api_name: "userspace_reboot_in_progress"
     access: ReadWrite
     prop_name: "sys.init.userspace_reboot.in_progress"
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 334364e..65af2b3 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -250,9 +250,8 @@
     require_root: true,
 }
 
-cc_test {
-    name: "libcutils_test_static",
-    test_suites: ["device-tests"],
+cc_defaults {
+    name: "libcutils_test_static_defaults",
     defaults: ["libcutils_test_default"],
     static_libs: [
         "libc",
@@ -272,3 +271,16 @@
         },
     },
 }
+
+cc_test {
+    name: "libcutils_test_static",
+    test_suites: ["device-tests"],
+    defaults: ["libcutils_test_static_defaults"],
+}
+
+cc_test {
+    name: "KernelLibcutilsTest",
+    test_suites: ["general-tests", "vts-core"],
+    defaults: ["libcutils_test_static_defaults"],
+    test_config: "KernelLibcutilsTest.xml",
+}
diff --git a/libcutils/KernelLibcutilsTest.xml b/libcutils/KernelLibcutilsTest.xml
new file mode 100644
index 0000000..e27fac6
--- /dev/null
+++ b/libcutils/KernelLibcutilsTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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="Runs libcutils_test_static.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="libcutils_test_static->/data/local/tmp/libcutils_test_static" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="libcutils_test_static" />
+        <option name="include-filter" value="*AshmemTest*" />
+    </test>
+</configuration>
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 340572c..8c232f0 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -203,19 +203,23 @@
 {
     static const std::string ashmem_device_path = get_ashmem_device_path();
 
-    int ret;
-    struct stat st;
-
     if (ashmem_device_path.empty()) {
         return -1;
     }
 
     int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
+
+    // fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
+    if (fd < 0) {
+        fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
+    }
+
     if (fd < 0) {
         return fd;
     }
 
-    ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
+    struct stat st;
+    int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
     if (ret < 0) {
         int save_errno = errno;
         close(fd);
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 2b39ca6..fd9a3eb 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -86,6 +86,7 @@
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/bin" },
+    { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/apex/*/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/bin" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "vendor" },
     { 00755, AID_ROOT,         AID_ROOT,         0, 0 },
@@ -195,10 +196,7 @@
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/simpleperf_app_runner" },
-
-    // Support FIFO scheduling mode in SurfaceFlinger.
-    { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
-                                              "system/bin/surfaceflinger" },
+    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/e2fsck" },
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
@@ -209,6 +207,7 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/apex/*/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system_ext/bin/*" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system_ext/apex/*/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 79b4b35..e12c32c 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -25,7 +25,6 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #include <unistd.h>
-
 #include <cutils/compiler.h>
 
 __BEGIN_DECLS
@@ -89,6 +88,12 @@
 #error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
 #endif
 
+// Set this to 0 to revert to the old Binder-based atrace implementation.
+// This is only here in case rollbacks do not apply cleanly.
+// TODO(fmayer): Remove this once we are confident this won't need to be
+// rolled back, no later than 2020-03-01.
+#define ATRACE_SHMEM 1
+
 /**
  * Opens the trace file for writing and reads the property for initial tags.
  * The atrace.tags.enableflags property sets the tags to trace.
@@ -116,11 +121,15 @@
  * prevent tracing within the Zygote process.
  */
 void atrace_set_tracing_enabled(bool enabled);
-
 /**
- * Flag indicating whether setup has been completed, initialized to 0.
- * Nonzero indicates setup has completed.
- * Note: This does NOT indicate whether or not setup was successful.
+ * If !ATRACE_SHMEM:
+ *   Flag indicating whether setup has been completed, initialized to 0.
+ *   Nonzero indicates setup has completed.
+ *   Note: This does NOT indicate whether or not setup was successful.
+ * If ATRACE_SHMEM:
+ *   This is always set to false. This forces code that uses an old version
+ *   of this header to always call into atrace_setup, in which we call
+ *   atrace_init unconditionally.
  */
 extern atomic_bool atrace_is_ready;
 
@@ -143,6 +152,12 @@
  * This can be explicitly run to avoid setup delay on first trace function.
  */
 #define ATRACE_INIT() atrace_init()
+#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
+
+#if ATRACE_SHMEM
+void atrace_init();
+uint64_t atrace_get_enabled_tags();
+#else
 static inline void atrace_init()
 {
     if (CC_UNLIKELY(!atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
@@ -155,12 +170,12 @@
  * It can be used as a guard condition around more expensive trace calculations.
  * Every trace function calls this, which ensures atrace_init is run.
  */
-#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()
 static inline uint64_t atrace_get_enabled_tags()
 {
     atrace_init();
     return atrace_enabled_tags;
 }
+#endif
 
 /**
  * Test if a given tag is currently enabled.
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index e1e8230..ff6b036 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -129,6 +129,8 @@
 #define AID_NETWORK_STACK 1073   /* network stack service */
 #define AID_GSID 1074            /* GSI service daemon */
 #define AID_FSVERITY_CERT 1075   /* fs-verity key ownership in keystore */
+#define AID_CREDSTORE 1076       /* identity credential manager service */
+#define AID_EXTERNAL_STORAGE 1077 /* Full external storage access including USB OTG volumes */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index d981f8f..c23d5e2 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -39,6 +39,11 @@
 static pthread_mutex_t  atrace_enabling_mutex        = PTHREAD_MUTEX_INITIALIZER;
 static pthread_rwlock_t atrace_container_sock_rwlock = PTHREAD_RWLOCK_INITIALIZER;
 
+static void atrace_seq_number_changed(uint32_t, uint32_t seq_no) {
+    pthread_once(&atrace_once_control, atrace_init_once);
+    atomic_store_explicit(&last_sequence_number, seq_no, memory_order_relaxed);
+}
+
 static bool atrace_init_container_sock()
 {
     pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index bff16c1..2ee39d3 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -37,12 +37,39 @@
     } else {
       atrace_enabled_tags = atrace_get_property();
     }
+#if !ATRACE_SHMEM
     atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+#endif
+}
+
+static void atrace_seq_number_changed(uint32_t prev_seq_no, uint32_t seq_no) {
+    if (!atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+        return;
+    }
+
+    // Someone raced us.
+    if (!atomic_compare_exchange_strong(&last_sequence_number, &prev_seq_no, seq_no)) {
+        return;
+    }
+
+    if (CC_UNLIKELY(prev_seq_no == kSeqNoNotInit)) {
+#if defined(__BIONIC__)
+        const prop_info* new_pi = __system_property_find("debug.atrace.tags.enableflags");
+        if (new_pi) atrace_property_info = new_pi;
+#endif
+        pthread_once(&atrace_once_control, atrace_init_once);
+    }
+
+    atrace_update_tags();
 }
 
 void atrace_setup()
 {
+#if ATRACE_SHMEM
+    atrace_init();
+#else
     pthread_once(&atrace_once_control, atrace_init_once);
+#endif
 }
 
 void atrace_begin_body(const char* name)
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index e3da77b..a57a4c5 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -34,6 +34,11 @@
 #include <log/log.h>
 #include <log/log_properties.h>
 
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+#endif
+
 /**
  * Maximum size of a message that can be logged to the trace buffer.
  * Note this message includes a tag, the pid, and the string given as the name.
@@ -41,12 +46,57 @@
  */
 #define ATRACE_MESSAGE_LENGTH 1024
 
-atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(false);
-int                     atrace_marker_fd     = -1;
-uint64_t                atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
-static bool             atrace_is_debuggable = false;
-static atomic_bool      atrace_is_enabled    = ATOMIC_VAR_INIT(true);
-static pthread_mutex_t  atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
+constexpr uint32_t kSeqNoNotInit = static_cast<uint32_t>(-1);
+
+atomic_bool              atrace_is_ready      = ATOMIC_VAR_INIT(false);
+int                      atrace_marker_fd     = -1;
+uint64_t                 atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
+static bool              atrace_is_debuggable = false;
+static atomic_bool       atrace_is_enabled    = ATOMIC_VAR_INIT(true);
+static pthread_mutex_t   atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Sequence number of debug.atrace.tags.enableflags the last time the enabled
+ * tags were reloaded.
+ **/
+static _Atomic(uint32_t) last_sequence_number = ATOMIC_VAR_INIT(kSeqNoNotInit);
+
+#if defined(__BIONIC__)
+// All zero prop_info that has a sequence number of 0. This is easier than
+// depending on implementation details of the property implementation.
+//
+// prop_info is static_assert-ed to be 96 bytes, which cannot change due to
+// ABI compatibility.
+alignas(uint64_t) static char empty_pi[96];
+static const prop_info* atrace_property_info = reinterpret_cast<const prop_info*>(empty_pi);
+#endif
+
+#if ATRACE_SHMEM
+
+/**
+ * This is called when the sequence number of debug.atrace.tags.enableflags
+ * changes and we need to reload the enabled tags.
+ **/
+static void atrace_seq_number_changed(uint32_t prev_seq_no, uint32_t seq_no);
+
+void atrace_init() {
+#if defined(__BIONIC__)
+    uint32_t seq_no = __system_property_serial(atrace_property_info);  // Acquire semantics.
+#else
+    uint32_t seq_no = 0;
+#endif
+    uint32_t prev_seq_no = atomic_load_explicit(&last_sequence_number, memory_order_relaxed);
+    if (CC_UNLIKELY(seq_no != prev_seq_no)) {
+        atrace_seq_number_changed(prev_seq_no, seq_no);
+    }
+}
+
+uint64_t atrace_get_enabled_tags()
+{
+    atrace_init();
+    return atrace_enabled_tags;
+}
+#endif
 
 // Set whether this process is debuggable, which determines whether
 // application-level tracing is allowed when the ro.debuggable system property
@@ -136,7 +186,7 @@
 void atrace_update_tags()
 {
     uint64_t tags;
-    if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+    if (ATRACE_SHMEM || CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
         if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
             tags = atrace_get_property();
             pthread_mutex_lock(&atrace_tags_mutex);
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index d47cc18..c21d0ee 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -30,3 +30,10 @@
 void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
 void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
 void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
+#if ATRACE_SHMEM
+void atrace_init() {}
+uint64_t atrace_get_enabled_tags()
+{
+    return ATRACE_TAG_NOT_READY;
+}
+#endif
diff --git a/liblog/Android.bp b/liblog/Android.bp
index de0c636..2cf60e0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -21,14 +21,11 @@
     "logger_read.cpp",
     "logger_write.cpp",
     "logprint.cpp",
-]
-liblog_host_sources = [
-    "fake_log_device.cpp",
+    "properties.cpp",
 ]
 liblog_target_sources = [
     "event_tag_map.cpp",
     "log_time.cpp",
-    "properties.cpp",
     "pmsg_reader.cpp",
     "pmsg_writer.cpp",
     "logd_reader.cpp",
@@ -39,6 +36,7 @@
     name: "liblog_headers",
     host_supported: true,
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     export_include_dirs: ["include"],
@@ -62,15 +60,12 @@
 cc_library {
     name: "liblog",
     host_supported: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     srcs: liblog_sources,
 
     target: {
-        host: {
-            srcs: liblog_host_sources,
-            cflags: ["-DFAKE_LOG_DEVICE=1"],
-        },
         android: {
             version_script: "liblog.map.txt",
             srcs: liblog_target_sources,
@@ -95,12 +90,15 @@
         },
     },
 
-    header_libs: ["liblog_headers"],
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
     export_header_lib_headers: ["liblog_headers"],
 
     stubs: {
         symbol_file: "liblog.map.txt",
-        versions: ["10000"],
+        versions: ["29", "30"],
     },
 
     // TODO(tomcherry): Renable this before release branch is cut
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2886289..51c5e60 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -36,7 +36,6 @@
 #include <utils/FastStrcmp.h>
 #include <utils/RWLock.h>
 
-#include "log_portability.h"
 #include "logd_reader.h"
 
 #define OUT_TAG "EventTagMap"
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
deleted file mode 100644
index fb3b9bc..0000000
--- a/liblog/fake_log_device.cpp
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Copyright (C) 2008-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Intercepts log messages intended for the Android log device.
- * Messages are printed to stderr.
- */
-
-#include "fake_log_device.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#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 */
-
-#define kTagSetSize 16 /* arbitrary */
-
-#if 0
-#define TRACE(...) printf("fake_log_device: " __VA_ARGS__)
-#else
-#define TRACE(...) ((void)0)
-#endif
-
-static void FakeClose();
-static int FakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
-
-struct android_log_transport_write fakeLoggerWrite = {
-    .close = FakeClose,
-    .write = FakeWrite,
-};
-
-typedef struct LogState {
-  bool initialized = false;
-  /* global minimum priority */
-  int global_min_priority;
-
-  /* output format */
-  AndroidLogPrintFormat output_format;
-
-  /* tags and priorities */
-  struct {
-    char tag[kMaxTagLen];
-    int minPriority;
-  } tagSet[kTagSetSize];
-} LogState;
-
-static LogState log_state;
-static std::mutex fake_log_mutex;
-
-/*
- * Configure logging based on ANDROID_LOG_TAGS environment variable.  We
- * need to parse a string that looks like
- *
- *   *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
- *
- * The tag (or '*' for the global level) comes first, followed by a colon
- * and a letter indicating the minimum priority level we're expected to log.
- * This can be used to reveal or conceal logs with specific tags.
- *
- * We also want to check ANDROID_PRINTF_LOG to determine how the output
- * will look.
- */
-void InitializeLogStateLocked() {
-  log_state.initialized = true;
-
-  /* global min priority defaults to "info" level */
-  log_state.global_min_priority = ANDROID_LOG_INFO;
-
-  /*
-   * This is based on the the long-dead utils/Log.cpp code.
-   */
-  const char* tags = getenv("ANDROID_LOG_TAGS");
-  TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
-  if (tags != NULL) {
-    int entry = 0;
-
-    while (*tags != '\0') {
-      char tagName[kMaxTagLen];
-      int i, minPrio;
-
-      while (isspace(*tags)) tags++;
-
-      i = 0;
-      while (*tags != '\0' && !isspace(*tags) && *tags != ':' && i < kMaxTagLen) {
-        tagName[i++] = *tags++;
-      }
-      if (i == kMaxTagLen) {
-        TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen - 1);
-        return;
-      }
-      tagName[i] = '\0';
-
-      /* default priority, if there's no ":" part; also zero out '*' */
-      minPrio = ANDROID_LOG_VERBOSE;
-      if (tagName[0] == '*' && tagName[1] == '\0') {
-        minPrio = ANDROID_LOG_DEBUG;
-        tagName[0] = '\0';
-      }
-
-      if (*tags == ':') {
-        tags++;
-        if (*tags >= '0' && *tags <= '9') {
-          if (*tags >= ('0' + ANDROID_LOG_SILENT))
-            minPrio = ANDROID_LOG_VERBOSE;
-          else
-            minPrio = *tags - '\0';
-        } else {
-          switch (*tags) {
-            case 'v':
-              minPrio = ANDROID_LOG_VERBOSE;
-              break;
-            case 'd':
-              minPrio = ANDROID_LOG_DEBUG;
-              break;
-            case 'i':
-              minPrio = ANDROID_LOG_INFO;
-              break;
-            case 'w':
-              minPrio = ANDROID_LOG_WARN;
-              break;
-            case 'e':
-              minPrio = ANDROID_LOG_ERROR;
-              break;
-            case 'f':
-              minPrio = ANDROID_LOG_FATAL;
-              break;
-            case 's':
-              minPrio = ANDROID_LOG_SILENT;
-              break;
-            default:
-              minPrio = ANDROID_LOG_DEFAULT;
-              break;
-          }
-        }
-
-        tags++;
-        if (*tags != '\0' && !isspace(*tags)) {
-          TRACE("ERROR: garbage in tag env; expected whitespace\n");
-          TRACE("       env='%s'\n", tags);
-          return;
-        }
-      }
-
-      if (tagName[0] == 0) {
-        log_state.global_min_priority = minPrio;
-        TRACE("+++ global min prio %d\n", logState->globalMinPriority);
-      } else {
-        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++;
-      }
-    }
-  }
-
-  /*
-   * Taken from the long-dead utils/Log.cpp
-   */
-  const char* fstr = getenv("ANDROID_PRINTF_LOG");
-  AndroidLogPrintFormat format;
-  if (fstr == NULL) {
-    format = FORMAT_BRIEF;
-  } else {
-    if (strcmp(fstr, "brief") == 0)
-      format = FORMAT_BRIEF;
-    else if (strcmp(fstr, "process") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "tag") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "thread") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "raw") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "time") == 0)
-      format = FORMAT_PROCESS;
-    else if (strcmp(fstr, "long") == 0)
-      format = FORMAT_PROCESS;
-    else
-      format = (AndroidLogPrintFormat)atoi(fstr);  // really?!
-  }
-
-  log_state.output_format = format;
-}
-
-/*
- * Return a human-readable string for the priority level.  Always returns
- * a valid string.
- */
-static const char* getPriorityString(int priority) {
-  /* the first character of each string should be unique */
-  static const char* priorityStrings[] = {"Verbose", "Debug", "Info", "Warn", "Error", "Assert"};
-  int idx;
-
-  idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
-  if (idx < 0 || idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
-    return "?unknown?";
-  return priorityStrings[idx];
-}
-
-#if defined(_WIN32)
-/*
- * WIN32 does not have writev().
- * Make up something to replace it.
- */
-static ssize_t fake_writev(int fd, const struct iovec* iov, int iovcnt) {
-  ssize_t result = 0;
-  const struct iovec* end = iov + iovcnt;
-  for (; iov < end; iov++) {
-    ssize_t w = write(fd, iov->iov_base, iov->iov_len);
-    if (w != (ssize_t)iov->iov_len) {
-      if (w < 0) return w;
-      return result + w;
-    }
-    result += w;
-  }
-  return result;
-}
-
-#define writev fake_writev
-#endif
-
-/*
- * Write a filtered log message to stderr.
- *
- * Log format parsing taken from the long-dead utils/Log.cpp.
- */
-static void ShowLog(int logPrio, const char* tag, const char* msg) {
-#if !defined(_WIN32)
-  struct tm tmBuf;
-#endif
-  struct tm* ptm;
-  char timeBuf[32];
-  char prefixBuf[128], suffixBuf[128];
-  char priChar;
-  time_t when;
-#if !defined(_WIN32)
-  pid_t pid, tid;
-#else
-  uint32_t pid, tid;
-#endif
-
-  TRACE("LOG %d: %s %s", logPrio, tag, msg);
-
-  priChar = getPriorityString(logPrio)[0];
-  when = time(NULL);
-  pid = tid = getpid();  // find gettid()?
-
-/*
- * Get the current date/time in pretty form
- *
- * It's often useful when examining a log with "less" to jump to
- * a specific point in the file by searching for the date/time stamp.
- * For this reason it's very annoying to have regexp meta characters
- * in the time stamp.  Don't use forward slashes, parenthesis,
- * brackets, asterisks, or other special chars here.
- */
-#if !defined(_WIN32)
-  ptm = localtime_r(&when, &tmBuf);
-#else
-  ptm = localtime(&when);
-#endif
-  // strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
-  strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
-
-  /*
-   * Construct a buffer containing the log header and log message.
-   */
-  size_t prefixLen, suffixLen;
-
-  switch (log_state.output_format) {
-    case FORMAT_TAG:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_PROCESS:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d) ", priChar, pid);
-      suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "  (%s)\n", tag);
-      break;
-    case FORMAT_THREAD:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d:%5d) ", priChar, pid, tid);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_RAW:
-      prefixBuf[0] = 0;
-      prefixLen = 0;
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_TIME:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %-8s\n\t", timeBuf, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_THREADTIME:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d %5d %c %-8s \n\t", timeBuf, pid,
-                           tid, priChar, tag);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-    case FORMAT_LONG:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "[ %s %5d:%5d %c/%-8s ]\n", timeBuf, pid,
-                           tid, priChar, tag);
-      strcpy(suffixBuf, "\n\n");
-      suffixLen = 2;
-      break;
-    default:
-      prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s(%5d): ", priChar, tag, pid);
-      strcpy(suffixBuf, "\n");
-      suffixLen = 1;
-      break;
-  }
-
-  /*
-   * Figure out how many lines there will be.
-   */
-  const char* end = msg + strlen(msg);
-  size_t numLines = 0;
-  const char* p = msg;
-  while (p < end) {
-    if (*p++ == '\n') numLines++;
-  }
-  if (p > msg && *(p - 1) != '\n') {
-    numLines++;
-  }
-
-  /*
-   * Create an array of iovecs large enough to write all of
-   * the lines with a prefix and a suffix.
-   */
-  const size_t INLINE_VECS = 64;
-  const size_t MAX_LINES = ((size_t)~0) / (3 * sizeof(struct iovec*));
-  struct iovec stackVec[INLINE_VECS];
-  struct iovec* vec = stackVec;
-  size_t numVecs;
-
-  if (numLines > MAX_LINES) numLines = MAX_LINES;
-
-  numVecs = numLines * 3;  // 3 iovecs per line.
-  if (numVecs > INLINE_VECS) {
-    vec = (struct iovec*)malloc(sizeof(struct iovec) * numVecs);
-    if (vec == NULL) {
-      msg = "LOG: write failed, no memory";
-      numVecs = INLINE_VECS;
-      numLines = numVecs / 3;
-      vec = stackVec;
-    }
-  }
-
-  /*
-   * Fill in the iovec pointers.
-   */
-  p = msg;
-  struct iovec* v = vec;
-  int totalLen = 0;
-  while (numLines > 0 && p < end) {
-    if (prefixLen > 0) {
-      v->iov_base = prefixBuf;
-      v->iov_len = prefixLen;
-      totalLen += prefixLen;
-      v++;
-    }
-    const char* start = p;
-    while (p < end && *p != '\n') {
-      p++;
-    }
-    if ((p - start) > 0) {
-      v->iov_base = (void*)start;
-      v->iov_len = p - start;
-      totalLen += p - start;
-      v++;
-    }
-    if (*p == '\n') p++;
-    if (suffixLen > 0) {
-      v->iov_base = suffixBuf;
-      v->iov_len = suffixLen;
-      totalLen += suffixLen;
-      v++;
-    }
-    numLines -= 1;
-  }
-
-  /*
-   * Write the entire message to the log file with a single writev() call.
-   * We need to use this rather than a collection of printf()s on a FILE*
-   * because of multi-threading and multi-process issues.
-   *
-   * If the file was not opened with O_APPEND, this will produce interleaved
-   * output when called on the same file from multiple processes.
-   *
-   * If the file descriptor is actually a network socket, the writev()
-   * call may return with a partial write.  Putting the writev() call in
-   * a loop can result in interleaved data.  This can be alleviated
-   * somewhat by wrapping the writev call in the Mutex.
-   */
-
-  for (;;) {
-    int cc = writev(fileno(stderr), vec, v - vec);
-
-    if (cc == totalLen) break;
-
-    if (cc < 0) {
-      if (errno == EINTR) continue;
-
-      /* can't really log the failure; for now, throw out a stderr */
-      fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
-      break;
-    } else {
-      /* shouldn't happen when writing to file or tty */
-      fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
-      break;
-    }
-  }
-
-  /* if we allocated storage for the iovecs, free it */
-  if (vec != stackVec) free(vec);
-}
-
-/*
- * Receive a log message.  We happen to know that "vector" has three parts:
- *
- *  priority (1 byte)
- *  tag (N bytes -- null-terminated ASCII string)
- *  message (N bytes -- null-terminated ASCII string)
- */
-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).
-   */
-  auto lock = std::lock_guard{fake_log_mutex};
-
-  if (!log_state.initialized) {
-    InitializeLogStateLocked();
-  }
-
-  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 (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", android_log_id_to_name(log_id), count);
-    return -1;
-  }
-
-  /* pull out the three fields */
-  int logPrio = *(const char*)vector[0].iov_base;
-  const char* tag = (const char*)vector[1].iov_base;
-  const char* msg = (const char*)vector[2].iov_base;
-
-  /* see if this log tag is configured */
-  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(log_state.tagSet[i].tag, tag) == 0) {
-      minPrio = log_state.tagSet[i].minPriority;
-      break;
-    }
-  }
-
-  if (logPrio >= minPrio) {
-    ShowLog(logPrio, tag, msg);
-  }
-
-  int len = 0;
-  for (size_t i = 0; i < count; ++i) {
-    len += vector[i].iov_len;
-  }
-  return len;
-}
-
-/*
- * 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
- * a close operation to the fake log handler. fakeLogClose is provided for
- * completeness only.
- *
- * We have no intention of adding a log close operation as it would complicate
- * every user of the logging API with no gain since the only valid place to
- * call is in the exit handler. Logging can continue in the exit handler to
- * help debug HOST tools ...
- */
-static void FakeClose() {
-  auto lock = std::lock_guard{fake_log_mutex};
-
-  memset(&log_state, 0, sizeof(log_state));
-}
-
-int __android_log_is_loggable(int prio, const char*, int def) {
-  int logLevel = def;
-  return logLevel >= 0 && prio >= logLevel;
-}
-
-int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
-  int logLevel = def;
-  return logLevel >= 0 && prio >= logLevel;
-}
-
-int __android_log_is_debuggable() {
-  return 1;
-}
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
deleted file mode 100644
index bd2256c..0000000
--- a/liblog/fake_log_device.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/types.h>
-
-#include "log_portability.h"
-#include "uio.h"
-
-struct iovec;
-
-__BEGIN_DECLS
-
-int fakeLogOpen(const char* pathName);
-int fakeLogClose(int fd);
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
-
-int __android_log_is_loggable(int prio, const char*, int def);
-int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
-int __android_log_is_debuggable();
-
-__END_DECLS
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 7290789..c84ddf7 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -55,6 +55,7 @@
  */
 
 #include <stdarg.h>
+#include <stddef.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -152,6 +153,12 @@
 } log_id_t;
 
 /**
+ * Let the logging function choose the best log target.
+ * This is not part of the enum since adding either -1 or 0xFFFFFFFF forces the enum to be signed or
+ * unsigned, which breaks unfortunately common arithmetic against LOG_ID_MIN and LOG_ID_MAX. */
+#define LOG_ID_DEFAULT -1
+
+/**
  * Writes the constant string `text` to the log buffer `id`,
  * with priority `prio` and tag `tag`.
  *
@@ -170,6 +177,112 @@
 int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
     __attribute__((__format__(printf, 4, 5)));
 
+/**
+ * Logger data struct used for writing log messages to liblog via __android_log_write_logger_data()
+ * and sending log messages to user defined loggers specified in __android_log_set_logger().
+ */
+struct __android_logger_data {
+  size_t struct_size; /* Must be set to sizeof(__android_logger_data) and is used for versioning. */
+  int buffer_id;      /* log_id_t or -1 to represent 'default'. */
+  int priority;       /* android_LogPriority values. */
+  const char* tag;
+  const char* file;  /* Optional file name, may be set to nullptr. */
+  unsigned int line; /* Optional line number, ignore if file is nullptr. */
+};
+
+/**
+ * Writes the log message specified with logger_data and msg to the log.  logger_data includes
+ * additional file name and line number information that a logger may use.  logger_data is versioned
+ * for backwards compatibility.
+ * This assumes that loggability has already been checked through __android_log_is_loggable().
+ * Higher level logging libraries, such as libbase, first check loggability, then format their
+ * buffers, then pass the message to liblog via this function, and therefore we do not want to
+ * duplicate the loggability check here.
+ */
+void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg);
+
+/**
+ * Prototype for the 'logger' function that is called for every log message.
+ */
+typedef void (*__android_logger_function)(const struct __android_logger_data* logger_data,
+                                          const char* message);
+
+/**
+ * Sets a user defined logger function.  All log messages sent to liblog will be set to the
+ * function pointer specified by logger for processing.
+ */
+void __android_log_set_logger(__android_logger_function logger);
+
+/**
+ * Writes the log message to logd.  This is an __android_logger_function and can be provided to
+ * __android_log_set_logger().  It is the default logger when running liblog on a device.
+ */
+void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg);
+
+/**
+ * Writes the log message to stderr.  This is an __android_logger_function and can be provided to
+ * __android_log_set_logger().  It is the default logger when running liblog on host.
+ */
+void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
+                                 const char* message);
+
+/**
+ * Prototype for the 'abort' function that is called when liblog will abort due to
+ * __android_log_assert() failures.
+ */
+typedef void (*__android_aborter_function)(const char* abort_message);
+
+/**
+ * Sets a user defined aborter function that is called for __android_log_assert() failures.
+ */
+void __android_log_set_aborter(__android_aborter_function aborter);
+
+/**
+ * Calls the stored aborter function.  This allows for other logging libraries to use the same
+ * aborter function by calling this function in liblog.
+ */
+void __android_log_call_aborter(const char* abort_message);
+
+/**
+ * Sets android_set_abort_message() on device then aborts().  This is the default aborter.
+ */
+void __android_log_default_aborter(const char* abort_message);
+
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed.  A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log.  If only one is set, then that value is used to determine the
+ * minimum priority needed.  If none are set, then default_priority is used.
+ *
+ * prio is ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+
+/**
+ * Sets the minimum priority that will be logged for this process.
+ *
+ * This returns the previous set minimum priority, or ANDROID_LOG_DEFAULT if none was set.
+ */
+int __android_log_set_minimum_priority(int priority);
+
+/**
+ * Gets the minimum priority that will be logged for this process.  If none has been set by a
+ * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
+ */
+int __android_log_get_minimum_priority();
+
+/**
+ * Sets the default tag if no tag is provided when writing a log message.  Defaults to
+ * getprogname().  This truncates tag to the maximum log message size, though appropriate tags
+ * should be much smaller.
+ */
+void __android_log_set_default_tag(const char* tag);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 2dd8059..198cdae 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -54,18 +54,32 @@
     __android_log_is_debuggable; # apex llndk
 };
 
-LIBLOG_Q {
+LIBLOG_Q { # introduced=29
   global:
     __android_log_bswrite; # apex
     __android_log_btwrite; # apex
     __android_log_bwrite; # apex
     __android_log_close; # apex
     __android_log_security; # apex
-    __android_log_security_bswrite; # apex
     android_log_reset; # llndk
     android_log_parser_reset; # llndk
 };
 
+LIGLOG_R { # introduced=30
+  global:
+    __android_log_call_aborter;
+    __android_log_default_aborter;
+    __android_log_get_minimum_priority;
+    __android_log_logd_logger;
+    __android_log_security_bswrite; # apex
+    __android_log_set_aborter;
+    __android_log_set_default_tag;
+    __android_log_set_logger;
+    __android_log_set_minimum_priority;
+    __android_log_stderr_logger;
+    __android_log_write_logger_data;
+};
+
 LIBLOG_PRIVATE {
   global:
     __android_log_pmsg_file_read;
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index e9f4a32..cb70d48 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -25,8 +25,6 @@
 #include <log/log_event_list.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
-
 #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
 
 enum ReadWriteFlag {
diff --git a/liblog/log_event_write.cpp b/liblog/log_event_write.cpp
index d04ba90..39afd0c 100644
--- a/liblog/log_event_write.cpp
+++ b/liblog/log_event_write.cpp
@@ -20,8 +20,6 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 
-#include "log_portability.h"
-
 #define MAX_SUBTAG_LEN 32
 
 int __android_log_error_write(int tag, const char* subTag, int32_t uid, const char* data,
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
deleted file mode 100644
index b7279d1..0000000
--- a/liblog/log_portability.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#pragma once
-
-#include <sys/cdefs.h>
-#include <unistd.h>
-
-/* possible missing definitions in sys/cdefs.h */
-
-/* DECLS */
-#ifndef __BEGIN_DECLS
-#if defined(__cplusplus)
-#define __BEGIN_DECLS extern "C" {
-#define __END_DECLS }
-#else
-#define __BEGIN_DECLS
-#define __END_DECLS
-#endif
-#endif
-
-/* possible missing definitions in unistd.h */
-
-#ifndef TEMP_FAILURE_RETRY
-/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp)            \
-  ({                                       \
-    __typeof__(exp) _rc;                   \
-    do {                                   \
-      _rc = (exp);                         \
-    } while (_rc == -1 && errno == EINTR); \
-    _rc;                                   \
-  })
-#endif
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 3ae250f..3fbe1cb 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -21,8 +21,6 @@
 
 #include <private/android_logger.h>
 
-#include "log_portability.h"
-
 const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
 const timespec log_time::EPOCH = {0, 0};
 
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 6865c14..82ed6b2 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -35,8 +35,6 @@
 
 #include <string>
 
-#include <cutils/sockets.h>
-#include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "logger.h"
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 2d032fa..68eef02 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -16,10 +16,10 @@
 
 #pragma once
 
+#include <sys/cdefs.h>
 #include <unistd.h>
 
 #include "log/log_read.h"
-#include "log_portability.h"
 
 __BEGIN_DECLS
 
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 3c6eb69..67376f4 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "logd_writer.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -32,23 +34,13 @@
 
 #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 LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
-static void LogdClose();
-
-struct android_log_transport_write logdLoggerWrite = {
-    .close = LogdClose,
-    .write = LogdWrite,
-};
-
 static int logd_socket;
 static RwLock logd_socket_lock;
 
@@ -90,7 +82,7 @@
   OpenSocketLocked();
 }
 
-static void LogdClose() {
+void LogdClose() {
   auto lock = std::unique_lock{logd_socket_lock};
   if (logd_socket > 0) {
     close(logd_socket);
@@ -98,7 +90,7 @@
   logd_socket = 0;
 }
 
-static int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
   ssize_t ret;
   static const unsigned headerLength = 1;
   struct iovec newVec[nr + headerLength];
@@ -119,7 +111,7 @@
   }
 
   /* logd, after initialization and priv drop */
-  if (__android_log_uid() == AID_LOGD) {
+  if (getuid() == AID_LOGD) {
     /*
      * ignore log messages we send to ourself (logd).
      * Such log messages are often generated by libraries we depend on
diff --git a/liblog/logd_writer.h b/liblog/logd_writer.h
new file mode 100644
index 0000000..41197b5
--- /dev/null
+++ b/liblog/logd_writer.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 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 <stddef.h>
+
+#include <android/log.h>
+
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void LogdClose();
diff --git a/liblog/logger.h b/liblog/logger.h
index 40d5fe5..ddff19d 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -17,22 +17,14 @@
 #pragma once
 
 #include <stdatomic.h>
+#include <sys/cdefs.h>
 
-#include <cutils/list.h>
 #include <log/log.h>
 
-#include "log_portability.h"
 #include "uio.h"
 
 __BEGIN_DECLS
 
-struct android_log_transport_write {
-  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,
-               size_t nr);
-};
-
 struct logger_list {
   atomic_int fd;
   int mode;
@@ -56,18 +48,4 @@
   return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
 }
 
-/* OS specific dribs and drabs */
-
-#if defined(_WIN32)
-#include <private/android_filesystem_config.h>
-typedef uint32_t uid_t;
-static inline uid_t __android_log_uid() {
-  return AID_SYSTEM;
-}
-#else
-static inline uid_t __android_log_uid() {
-  return getuid();
-}
-#endif
-
 __END_DECLS
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
index ece0550..7d676f4 100644
--- a/liblog/logger_name.cpp
+++ b/liblog/logger_name.cpp
@@ -19,8 +19,6 @@
 
 #include <log/log.h>
 
-#include "log_portability.h"
-
 /* In the future, we would like to make this list extensible */
 static const char* LOG_NAME[LOG_ID_MAX] = {
     /* clang-format off */
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 0d383ff..a0c526b 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -27,10 +27,7 @@
 #include <unistd.h>
 
 #include <android/log.h>
-#include <cutils/list.h>
-#include <private/android_filesystem_config.h>
 
-#include "log_portability.h"
 #include "logd_reader.h"
 #include "logger.h"
 #include "pmsg_reader.h"
@@ -100,7 +97,7 @@
 
   int ret = 0;
 
-#if (FAKE_LOG_DEVICE == 0)
+#ifdef __ANDROID__
   if (logger_list->mode & ANDROID_LOG_PSTORE) {
     ret = PmsgRead(logger_list, log_msg);
   } else {
@@ -138,7 +135,7 @@
     return;
   }
 
-#if (FAKE_LOG_DEVICE == 0)
+#ifdef __ANDROID__
   if (logger_list->mode & ANDROID_LOG_PSTORE) {
     PmsgClose(logger_list);
   } else {
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index d38b402..cf82e0f 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+#include "logger_write.h"
+
 #include <errno.h>
-#include <stdatomic.h>
+#include <inttypes.h>
+#include <libgen.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
@@ -24,31 +27,39 @@
 #include <android/set_abort_message.h>
 #endif
 
+#include <shared_mutex>
+
+#include <android-base/errno_restorer.h>
+#include <android-base/macros.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
+#include "android/log.h"
+#include "log/log_read.h"
 #include "logger.h"
+#include "rwlock.h"
 #include "uio.h"
 
+#ifdef __ANDROID__
+#include "logd_writer.h"
+#include "pmsg_writer.h"
+#endif
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+using android::base::ErrnoRestorer;
+
 #define LOG_BUF_SIZE 1024
 
-#if (FAKE_LOG_DEVICE == 0)
-extern struct android_log_transport_write logdLoggerWrite;
-extern struct android_log_transport_write pmsgLoggerWrite;
-
-android_log_transport_write* android_log_write = &logdLoggerWrite;
-android_log_transport_write* android_log_persist_write = &pmsgLoggerWrite;
-#else
-extern android_log_transport_write fakeLoggerWrite;
-
-android_log_transport_write* android_log_write = &fakeLoggerWrite;
-android_log_transport_write* android_log_persist_write = nullptr;
-#endif
-
 #if defined(__ANDROID__)
 static int check_log_uid_permissions() {
-  uid_t uid = __android_log_uid();
+  uid_t uid = getuid();
 
   /* Matches clientHasLogCredentials() in logd */
   if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
@@ -92,121 +103,268 @@
  * Release any logger resources. A new log write will immediately re-acquire.
  */
 void __android_log_close() {
-  if (android_log_write != nullptr) {
-    android_log_write->close();
-  }
-
-  if (android_log_persist_write != nullptr) {
-    android_log_persist_write->close();
-  }
-
+#ifdef __ANDROID__
+  LogdClose();
+  PmsgClose();
+#endif
 }
 
-static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
-  int ret, save_errno;
-  struct timespec ts;
+#if defined(__GLIBC__) || defined(_WIN32)
+static const char* getprogname() {
+#if defined(__GLIBC__)
+  return program_invocation_short_name;
+#elif defined(_WIN32)
+  static bool first = true;
+  static char progname[MAX_PATH] = {};
 
-  save_errno = errno;
+  if (first) {
+    char path[PATH_MAX + 1];
+    DWORD result = GetModuleFileName(nullptr, path, sizeof(path) - 1);
+    if (result == 0 || result == sizeof(path) - 1) return "";
+    path[PATH_MAX - 1] = 0;
+
+    char* path_basename = basename(path);
+
+    snprintf(progname, sizeof(progname), "%s", path_basename);
+    first = false;
+  }
+
+  return progname;
+#endif
+}
+#endif
+
+// It's possible for logging to happen during static initialization before our globals are
+// initialized, so we place this std::string in a function such that it is initialized on the first
+// call.
+std::string& GetDefaultTag() {
+  static std::string default_tag = getprogname();
+  return default_tag;
+}
+RwLock default_tag_lock;
+
+void __android_log_set_default_tag(const char* tag) {
+  auto lock = std::unique_lock{default_tag_lock};
+  GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
+}
+
+static int minimum_log_priority = ANDROID_LOG_DEFAULT;
+int __android_log_set_minimum_priority(int priority) {
+  int old_minimum_log_priority = minimum_log_priority;
+  minimum_log_priority = priority;
+  return old_minimum_log_priority;
+}
+
+int __android_log_get_minimum_priority() {
+  return minimum_log_priority;
+}
+
+#ifdef __ANDROID__
+static __android_logger_function logger_function = __android_log_logd_logger;
+#else
+static __android_logger_function logger_function = __android_log_stderr_logger;
+#endif
+static RwLock logger_function_lock;
+
+void __android_log_set_logger(__android_logger_function logger) {
+  auto lock = std::unique_lock{logger_function_lock};
+  logger_function = logger;
+}
+
+void __android_log_default_aborter(const char* abort_message) {
+#ifdef __ANDROID__
+  android_set_abort_message(abort_message);
+#else
+  UNUSED(abort_message);
+#endif
+  abort();
+}
+
+static __android_aborter_function aborter_function = __android_log_default_aborter;
+static RwLock aborter_function_lock;
+
+void __android_log_set_aborter(__android_aborter_function aborter) {
+  auto lock = std::unique_lock{aborter_function_lock};
+  aborter_function = aborter;
+}
+
+void __android_log_call_aborter(const char* abort_message) {
+  auto lock = std::shared_lock{aborter_function_lock};
+  aborter_function(abort_message);
+}
+
+#ifdef __ANDROID__
+static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
+  int ret;
+  struct timespec ts;
 
   if (log_id == LOG_ID_KERNEL) {
     return -EINVAL;
   }
 
-#if defined(__ANDROID__)
   clock_gettime(android_log_clockid(), &ts);
 
   if (log_id == LOG_ID_SECURITY) {
     if (vec[0].iov_len < 4) {
-      errno = save_errno;
       return -EINVAL;
     }
 
     ret = check_log_uid_permissions();
     if (ret < 0) {
-      errno = save_errno;
       return ret;
     }
     if (!__android_log_security()) {
       /* If only we could reset downstream logd counter */
-      errno = save_errno;
       return -EPERM;
     }
   } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
     if (vec[0].iov_len < 4) {
-      errno = save_errno;
       return -EINVAL;
     }
-  } else {
-    int prio = *static_cast<int*>(vec[0].iov_base);
-    const char* tag = static_cast<const char*>(vec[1].iov_base);
-    size_t len = vec[1].iov_len;
+  }
 
-    if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
-      errno = save_errno;
-      return -EPERM;
-    }
-  }
+  ret = LogdWrite(log_id, &ts, vec, nr);
+  PmsgWrite(log_id, &ts, vec, nr);
+
+  return ret;
+}
 #else
-  /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
-  {
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    ts.tv_sec = tv.tv_sec;
-    ts.tv_nsec = tv.tv_usec * 1000;
-  }
+static int write_to_log(log_id_t, struct iovec*, size_t) {
+  // Non-Android text logs should go to __android_log_stderr_logger, not here.
+  // Non-Android binary logs are always dropped.
+  return 1;
+}
 #endif
 
-  ret = 0;
+// Copied from base/threads.cpp
+static uint64_t GetThreadId() {
+#if defined(__BIONIC__)
+  return gettid();
+#elif defined(__APPLE__)
+  uint64_t tid;
+  pthread_threadid_np(NULL, &tid);
+  return tid;
+#elif defined(__linux__)
+  return syscall(__NR_gettid);
+#elif defined(_WIN32)
+  return GetCurrentThreadId();
+#endif
+}
 
-  if (android_log_write != nullptr) {
-    ssize_t retval;
-    retval = android_log_write->write(log_id, &ts, vec, nr);
-    if (ret >= 0) {
-      ret = retval;
-    }
+void __android_log_stderr_logger(const struct __android_logger_data* logger_data,
+                                 const char* message) {
+  struct tm now;
+  time_t t = time(nullptr);
+
+#if defined(_WIN32)
+  localtime_s(&now, &t);
+#else
+  localtime_r(&t, &now);
+#endif
+
+  char timestamp[32];
+  strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+  static const char log_characters[] = "XXVDIWEF";
+  static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
+                "Mismatch in size of log_characters and values in android_LogPriority");
+  int priority =
+      logger_data->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : logger_data->priority;
+  char priority_char = log_characters[priority];
+  uint64_t tid = GetThreadId();
+
+  if (logger_data->file != nullptr) {
+    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n",
+            logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(),
+            tid, logger_data->file, logger_data->line, message);
+  } else {
+    fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n",
+            logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(),
+            tid, message);
   }
+}
 
-  if (android_log_persist_write != nullptr) {
-    android_log_persist_write->write(log_id, &ts, vec, nr);
-  }
+void __android_log_logd_logger(const struct __android_logger_data* logger_data,
+                               const char* message) {
+  int buffer_id = logger_data->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : logger_data->buffer_id;
 
-  errno = save_errno;
-  return ret;
+  struct iovec vec[3];
+  vec[0].iov_base =
+      const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&logger_data->priority));
+  vec[0].iov_len = 1;
+  vec[1].iov_base = const_cast<void*>(static_cast<const void*>(logger_data->tag));
+  vec[1].iov_len = strlen(logger_data->tag) + 1;
+  vec[2].iov_base = const_cast<void*>(static_cast<const void*>(message));
+  vec[2].iov_len = strlen(message) + 1;
+
+  write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
 }
 
 int __android_log_write(int prio, const char* tag, const char* msg) {
   return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
 }
 
-int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
-  if (!tag) tag = "";
+void __android_log_write_logger_data(__android_logger_data* logger_data, const char* msg) {
+  ErrnoRestorer errno_restorer;
+
+  if (logger_data->buffer_id != LOG_ID_DEFAULT && logger_data->buffer_id != LOG_ID_MAIN &&
+      logger_data->buffer_id != LOG_ID_SYSTEM && logger_data->buffer_id != LOG_ID_RADIO &&
+      logger_data->buffer_id != LOG_ID_CRASH) {
+    return;
+  }
+
+  auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
+  if (logger_data->tag == nullptr) {
+    tag_lock.lock();
+    logger_data->tag = GetDefaultTag().c_str();
+  }
 
 #if __BIONIC__
-  if (prio == ANDROID_LOG_FATAL) {
+  if (logger_data->priority == ANDROID_LOG_FATAL) {
     android_set_abort_message(msg);
   }
 #endif
 
-  struct iovec vec[3];
-  vec[0].iov_base = (unsigned char*)&prio;
-  vec[0].iov_len = 1;
-  vec[1].iov_base = (void*)tag;
-  vec[1].iov_len = strlen(tag) + 1;
-  vec[2].iov_base = (void*)msg;
-  vec[2].iov_len = strlen(msg) + 1;
+  auto lock = std::shared_lock{logger_function_lock};
+  logger_function(logger_data, msg);
+}
 
-  return write_to_log(static_cast<log_id_t>(bufID), vec, 3);
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
+  __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, msg);
+  return 1;
 }
 
 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   char buf[LOG_BUF_SIZE];
 
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
 
-  return __android_log_write(prio, tag, buf);
+  __android_logger_data logger_data = {
+      sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   va_list ap;
   char buf[LOG_BUF_SIZE];
 
@@ -214,10 +372,19 @@
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
   va_end(ap);
 
-  return __android_log_write(prio, tag, buf);
+  __android_logger_data logger_data = {
+      sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
+  ErrnoRestorer errno_restorer;
+
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   va_list ap;
   char buf[LOG_BUF_SIZE];
 
@@ -225,7 +392,9 @@
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
   va_end(ap);
 
-  return __android_log_buf_write(bufID, prio, tag, buf);
+  __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
@@ -253,11 +422,13 @@
   TEMP_FAILURE_RETRY(write(2, "\n", 1));
 
   __android_log_write(ANDROID_LOG_FATAL, tag, buf);
-  abort(); /* abort so we have a chance to debug the situation */
-           /* NOTREACHED */
+  __android_log_call_aborter(buf);
+  abort();
 }
 
 int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -269,6 +440,8 @@
 }
 
 int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -280,6 +453,8 @@
 }
 
 int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[2];
 
   vec[0].iov_base = &tag;
@@ -296,6 +471,8 @@
  * handy if we just want to dump an integer into the log.
  */
 int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[3];
 
   vec[0].iov_base = &tag;
@@ -313,6 +490,8 @@
  * event log.
  */
 int __android_log_bswrite(int32_t tag, const char* payload) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[4];
   char type = EVENT_TYPE_STRING;
   uint32_t len = strlen(payload);
@@ -334,6 +513,8 @@
  * security log.
  */
 int __android_log_security_bswrite(int32_t tag, const char* payload) {
+  ErrnoRestorer errno_restorer;
+
   struct iovec vec[4];
   char type = EVENT_TYPE_STRING;
   uint32_t len = strlen(payload);
diff --git a/liblog/logger_write.h b/liblog/logger_write.h
new file mode 100644
index 0000000..065fd55
--- /dev/null
+++ b/liblog/logger_write.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "rwlock.h"
+
+std::string& GetDefaultTag();  // Must read lock default_tag_lock
+extern RwLock default_tag_lock;
\ No newline at end of file
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 4b61828..e32878a 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -40,8 +40,6 @@
 #include <log/logprint.h>
 #include <private/android_logger.h>
 
-#include "log_portability.h"
-
 #define MS_PER_NSEC 1000000
 #define US_PER_NSEC 1000
 
@@ -1522,12 +1520,7 @@
  * This code is Android specific, bionic guarantees that
  * calls to non-reentrant getpwuid() are thread safe.
  */
-#if !defined(__MINGW32__)
-#if (FAKE_LOG_DEVICE == 0)
-#ifndef __BIONIC__
-#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
-#endif
-#endif
+#ifdef __ANDROID__
       struct passwd* pwd = getpwuid(entry->uid);
       if (pwd && (strlen(pwd->pw_name) <= 5)) {
         snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index 9390fec..64a92b7 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -23,7 +23,7 @@
 #include <string.h>
 #include <sys/types.h>
 
-#include <private/android_filesystem_config.h>
+#include <cutils/list.h>
 #include <private/android_logger.h>
 
 #include "logger.h"
diff --git a/liblog/pmsg_reader.h b/liblog/pmsg_reader.h
index 53746d8..b784f9f 100644
--- a/liblog/pmsg_reader.h
+++ b/liblog/pmsg_reader.h
@@ -16,10 +16,10 @@
 
 #pragma once
 
+#include <sys/cdefs.h>
 #include <unistd.h>
 
 #include "log/log_read.h"
-#include "log_portability.h"
 
 __BEGIN_DECLS
 
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 4f45780..06e5e04 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-/*
- * pmsg write handler
- */
+#include "pmsg_writer.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -28,22 +26,12 @@
 #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 void PmsgClose();
-static int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
-
-struct android_log_transport_write pmsgLoggerWrite = {
-    .close = PmsgClose,
-    .write = PmsgWrite,
-};
-
 static int pmsg_fd;
 static RwLock pmsg_fd_lock;
 
@@ -57,7 +45,7 @@
   pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
 }
 
-static void PmsgClose() {
+void PmsgClose() {
   auto lock = std::unique_lock{pmsg_fd_lock};
   if (pmsg_fd > 0) {
     close(pmsg_fd);
@@ -65,7 +53,7 @@
   pmsg_fd = 0;
 }
 
-static int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+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;
@@ -123,7 +111,7 @@
 
   pmsgHeader.magic = LOGGER_MAGIC;
   pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
-  pmsgHeader.uid = __android_log_uid();
+  pmsgHeader.uid = getuid();
   pmsgHeader.pid = getpid();
 
   header.id = logId;
diff --git a/liblog/pmsg_writer.h b/liblog/pmsg_writer.h
new file mode 100644
index 0000000..d5e1a1c
--- /dev/null
+++ b/liblog/pmsg_writer.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 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 <stddef.h>
+
+#include <android/log.h>
+
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void PmsgClose();
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 2e0a8c9..f30058a 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -20,13 +20,18 @@
 #include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <shared_mutex>
+
 #include <private/android_logger.h>
 
-#include "log_portability.h"
+#include "logger_write.h"
+
+#ifdef __ANDROID__
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
 
 static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
 
@@ -89,14 +94,21 @@
   }
 }
 
-static int __android_log_level(const char* tag, size_t len, int default_prio) {
+static int __android_log_level(const char* tag, size_t len) {
   /* sizeof() is used on this array below */
   static const char log_namespace[] = "persist.log.tag.";
   static const size_t base_offset = 8; /* skip "persist." */
-  /* calculate the size of our key temporary buffer */
-  const size_t taglen = tag ? len : 0;
+
+  auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
+  if (tag == nullptr || len == 0) {
+    tag_lock.lock();
+    auto& tag_string = GetDefaultTag();
+    tag = tag_string.c_str();
+    len = tag_string.size();
+  }
+
   /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
-  char key[sizeof(log_namespace) + taglen];
+  char key[sizeof(log_namespace) + len];
   char* kp;
   size_t i;
   char c = 0;
@@ -146,7 +158,7 @@
     }
   }
 
-  if (taglen) {
+  if (len) {
     int local_change_detected = change_detected;
     if (!not_locked) {
       if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
@@ -258,20 +270,30 @@
     case 'F': /* FALLTHRU */ /* Not officially supported */
     case 'A': return ANDROID_LOG_FATAL;
     case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
-    case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
+    case 'S': return ANDROID_LOG_SILENT;
       /* clang-format on */
   }
-  return default_prio;
+  return -1;
 }
 
 int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
-  int logLevel = __android_log_level(tag, len, default_prio);
-  return logLevel >= 0 && prio >= logLevel;
+  int minimum_log_priority = __android_log_get_minimum_priority();
+  int property_log_level = __android_log_level(tag, len);
+
+  if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) {
+    return prio >= std::min(property_log_level, minimum_log_priority);
+  } else if (property_log_level >= 0) {
+    return prio >= property_log_level;
+  } else if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
+    return prio >= minimum_log_priority;
+  } else {
+    return prio >= default_prio;
+  }
 }
 
 int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
-  int logLevel = __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
-  return logLevel >= 0 && prio >= logLevel;
+  auto len = tag ? strlen(tag) : 0;
+  return __android_log_is_loggable_len(prio, tag, len, default_prio);
 }
 
 int __android_log_is_debuggable() {
@@ -627,3 +649,23 @@
 
   return property_size;
 }
+
+#else
+
+int __android_log_is_loggable(int prio, const char*, int) {
+  int minimum_priority = __android_log_get_minimum_priority();
+  if (minimum_priority == ANDROID_LOG_DEFAULT) {
+    minimum_priority = ANDROID_LOG_INFO;
+  }
+  return prio >= minimum_priority;
+}
+
+int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
+  return __android_log_is_loggable(prio, nullptr, def);
+}
+
+int __android_log_is_debuggable() {
+  return 1;
+}
+
+#endif
\ No newline at end of file
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index f58c524..b4bb77f 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -54,6 +54,8 @@
     ],
     srcs: [
         "libc_test.cpp",
+        "liblog_default_tag.cpp",
+        "liblog_global_state.cpp",
         "liblog_test.cpp",
         "log_id_test.cpp",
         "log_radio_test.cpp",
@@ -102,6 +104,11 @@
     name: "liblog-host-test",
     static_libs: ["liblog"],
     shared_libs: ["libbase"],
-    srcs: ["liblog_host_test.cpp"],
+    srcs: [
+        "liblog_host_test.cpp",
+        "liblog_default_tag.cpp",
+        "liblog_global_state.cpp",
+        "rwlock_test.cpp",
+    ],
     isolated: true,
 }
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
index c167478..fcb46b1 100644
--- a/liblog/tests/AndroidTest.xml
+++ b/liblog/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
diff --git a/liblog/tests/liblog_default_tag.cpp b/liblog/tests/liblog_default_tag.cpp
new file mode 100644
index 0000000..31b7467
--- /dev/null
+++ b/liblog/tests/liblog_default_tag.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+// LOG_TAG must be unset for android-base's logging to use a default tag.
+#undef LOG_TAG
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android/log.h>
+
+#include <gtest/gtest.h>
+
+#ifndef __ANDROID__
+static const char* getprogname() {
+  return program_invocation_short_name;
+}
+#endif
+
+TEST(liblog_default_tag, no_default_tag_libbase_write_first) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+
+  expected_tag = getprogname();
+  LOG(WARNING) << "message";
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, no_default_tag_liblog_write_first) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+
+  expected_tag = getprogname();
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(WARNING) << "message";
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, libbase_sets_default_tag) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "libbase_test_tag";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+  SetDefaultTag(expected_tag);
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(WARNING) << "message";
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, liblog_sets_default_tag) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "liblog_test_tag";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+  __android_log_set_default_tag(expected_tag.c_str());
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(WARNING) << "message";
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, default_tag_plus_log_severity) {
+#ifdef __ANDROID__
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = "liblog_test_tag";
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+  __android_log_set_default_tag(expected_tag.c_str());
+
+  auto log_tag_property = "log.tag." + expected_tag;
+  SetProperty(log_tag_property, "V");
+  auto reset_tag_property_guard = make_scope_guard([=] { SetProperty(log_tag_property, ""); });
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(VERBOSE) << "message";
+  EXPECT_TRUE(message_seen);
+#else
+  GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
+
+TEST(liblog_default_tag, generated_default_tag_plus_log_severity) {
+#ifdef __ANDROID__
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_tag = getprogname();
+  SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+    message_seen = true;
+    EXPECT_EQ(expected_tag, tag);
+  });
+
+  // Even without any calls to SetDefaultTag(), the first message that attempts to log, will
+  // generate a default tag from getprogname() and check log.tag.<default tag> for loggability. This
+  // case checks that we can log a Verbose message when log.tag.<getprogname()> is set to 'V'.
+  auto log_tag_property = "log.tag." + expected_tag;
+  SetProperty(log_tag_property, "V");
+  auto reset_tag_property_guard = make_scope_guard([=] { SetProperty(log_tag_property, ""); });
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, nullptr, "message");
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  LOG(VERBOSE) << "message";
+  EXPECT_TRUE(message_seen);
+#else
+  GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
\ No newline at end of file
diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp
new file mode 100644
index 0000000..9a181ef
--- /dev/null
+++ b/liblog/tests/liblog_global_state.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "global_state_test_tag"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/log.h>
+
+#include <gtest/gtest.h>
+
+TEST(liblog_global_state, libbase_logs_with_libbase_SetLogger) {
+  using namespace android::base;
+  bool message_seen = false;
+  LogSeverity expected_severity = WARNING;
+  std::string expected_file = Basename(__FILE__);
+  unsigned int expected_line;
+  std::string expected_message = "libbase test message";
+
+  auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
+                            unsigned int line, const char* message) {
+    message_seen = true;
+    EXPECT_EQ(DEFAULT, log_id);
+    EXPECT_EQ(expected_severity, severity);
+    EXPECT_STREQ(LOG_TAG, tag);
+    EXPECT_EQ(expected_file, file);
+    EXPECT_EQ(expected_line, line);
+    EXPECT_EQ(expected_message, message);
+  };
+
+  SetLogger(LoggerFunction);
+
+  expected_line = __LINE__ + 1;
+  LOG(expected_severity) << expected_message;
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, libbase_logs_with_liblog_set_logger) {
+  using namespace android::base;
+  // These must be static since they're used by the liblog logger function, which only accepts
+  // lambdas without captures.  The items used by the libbase logger are explicitly not static, to
+  // ensure that lambdas with captures do work there.
+  static bool message_seen = false;
+  static std::string expected_file = Basename(__FILE__);
+  static unsigned int expected_line;
+  static std::string expected_message = "libbase test message";
+
+  auto liblog_logger_function = [](const struct __android_logger_data* logger_data,
+                                   const char* message) {
+    message_seen = true;
+    EXPECT_EQ(sizeof(__android_logger_data), logger_data->struct_size);
+    EXPECT_EQ(LOG_ID_DEFAULT, logger_data->buffer_id);
+    EXPECT_EQ(ANDROID_LOG_WARN, logger_data->priority);
+    EXPECT_STREQ(LOG_TAG, logger_data->tag);
+    EXPECT_EQ(expected_file, logger_data->file);
+    EXPECT_EQ(expected_line, logger_data->line);
+    EXPECT_EQ(expected_message, message);
+  };
+
+  __android_log_set_logger(liblog_logger_function);
+
+  expected_line = __LINE__ + 1;
+  LOG(WARNING) << expected_message;
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, liblog_logs_with_libbase_SetLogger) {
+  using namespace android::base;
+  bool message_seen = false;
+  std::string expected_message = "libbase test message";
+
+  auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
+                            unsigned int line, const char* message) {
+    message_seen = true;
+    EXPECT_EQ(MAIN, log_id);
+    EXPECT_EQ(WARNING, severity);
+    EXPECT_STREQ(LOG_TAG, tag);
+    EXPECT_EQ(nullptr, file);
+    EXPECT_EQ(0U, line);
+    EXPECT_EQ(expected_message, message);
+  };
+
+  SetLogger(LoggerFunction);
+
+  __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, LOG_TAG, expected_message.c_str());
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+}
+
+TEST(liblog_global_state, liblog_logs_with_liblog_set_logger) {
+  using namespace android::base;
+  // These must be static since they're used by the liblog logger function, which only accepts
+  // lambdas without captures.  The items used by the libbase logger are explicitly not static, to
+  // ensure that lambdas with captures do work there.
+  static bool message_seen = false;
+  static int expected_buffer_id = LOG_ID_MAIN;
+  static int expected_priority = ANDROID_LOG_WARN;
+  static std::string expected_message = "libbase test message";
+
+  auto liblog_logger_function = [](const struct __android_logger_data* logger_data,
+                                   const char* message) {
+    message_seen = true;
+    EXPECT_EQ(sizeof(__android_logger_data), logger_data->struct_size);
+    EXPECT_EQ(expected_buffer_id, logger_data->buffer_id);
+    EXPECT_EQ(expected_priority, logger_data->priority);
+    EXPECT_STREQ(LOG_TAG, logger_data->tag);
+    EXPECT_STREQ(nullptr, logger_data->file);
+    EXPECT_EQ(0U, logger_data->line);
+    EXPECT_EQ(expected_message, message);
+  };
+
+  __android_log_set_logger(liblog_logger_function);
+
+  __android_log_buf_write(expected_buffer_id, expected_priority, LOG_TAG, expected_message.c_str());
+  EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, SetAborter_with_liblog) {
+  using namespace android::base;
+
+  std::string expected_message = "libbase test message";
+  static bool message_seen = false;
+  auto aborter_function = [&](const char* message) {
+    message_seen = true;
+    EXPECT_EQ(expected_message, message);
+  };
+
+  SetAborter(aborter_function);
+  LOG(FATAL) << expected_message;
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+
+  static std::string expected_message_static = "libbase test message";
+  auto liblog_aborter_function = [](const char* message) {
+    message_seen = true;
+    EXPECT_EQ(expected_message_static, message);
+  };
+  __android_log_set_aborter(liblog_aborter_function);
+  LOG(FATAL) << expected_message_static;
+  EXPECT_TRUE(message_seen);
+  message_seen = false;
+}
+
+TEST(liblog_global_state, is_loggable_both_default) {
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
+#ifdef __ANDROID__
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+  android::base::SetProperty(log_tag_property, "d");
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "w");
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "");
+#else
+  GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
+
+TEST(liblog_global_state, is_loggable_both_set) {
+#ifdef __ANDROID__
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // When both a tag and a minimum priority are set, we use the lower value of the two.
+
+  // tag = warning, minimum_priority = debug, expect 'debug'
+  auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+  android::base::SetProperty(log_tag_property, "w");
+  EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = warning, minimum_priority = warning, expect 'warning'
+  EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = debug, minimum_priority = warning, expect 'debug'
+  android::base::SetProperty(log_tag_property, "d");
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  // tag = debug, minimum_priority = debug, expect 'debug'
+  EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+  EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+  android::base::SetProperty(log_tag_property, "");
+#else
+  GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
diff --git a/liblog/tests/liblog_host_test.cpp b/liblog/tests/liblog_host_test.cpp
index 377550f..ec186d4 100644
--- a/liblog/tests/liblog_host_test.cpp
+++ b/liblog/tests/liblog_host_test.cpp
@@ -20,15 +20,37 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <regex>
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
-#include <android-base/strings.h>
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
+using android::base::InitLogging;
+using android::base::StderrLogger;
 using android::base::StringPrintf;
-using android::base::StringReplace;
 
-void GenerateLogContent() {
+static std::string MakeLogPattern(int priority, const char* tag, const char* message) {
+  static const char log_characters[] = "XXVDIWEF";
+  static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
+                "Mismatch in size of log_characters and values in android_LogPriority");
+  priority = priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : priority;
+  char log_char = log_characters[priority];
+
+  return StringPrintf("%s %c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s", tag, log_char,
+                      message);
+}
+
+static void CheckMessage(bool expected, const std::string& output, int priority, const char* tag,
+                         const char* message) {
+  std::regex message_regex(MakeLogPattern(priority, tag, message));
+  EXPECT_EQ(expected, std::regex_search(output, message_regex)) << message;
+}
+
+static 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");
@@ -52,137 +74,86 @@
 }
 
 TEST(liblog, default_write) {
-  setenv("ANDROID_PRINTF_LOG", "brief", true);
   CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
 
   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);
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
 
-  EXPECT_EQ(expected_output, captured_stderr.str());
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
 }
 
-TEST(liblog, format) {
-  setenv("ANDROID_PRINTF_LOG", "process", true);
+TEST(liblog, verbose_write) {
+  setenv("ANDROID_LOG_TAGS", "*:v", true);
   CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
 
   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);
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
 
-  EXPECT_EQ(expected_output, captured_stderr.str());
-  captured_stderr.Stop();
-  captured_stderr.Reset();
-  captured_stderr.Start();
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
 
-  // 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();
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
 
-  // 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());
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
 }
 
-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.
+TEST(liblog, error_write) {
   setenv("ANDROID_LOG_TAGS", "*:e", true);
-  test_all_logs();
+  CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
 
-  // 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();
+  GenerateLogContent();
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
+
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+  CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+  CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
 }
 
 TEST(liblog, kernel_no_write) {
   CapturedStderr captured_stderr;
+  InitLogging(nullptr, StderrLogger);
   __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;
+  InitLogging(nullptr, StderrLogger);
   __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");
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index c402e20..75a26bf 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1074,7 +1074,6 @@
 
     // Once we've found our expected entry, break.
     if (len == LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag)) {
-      EXPECT_EQ(ret, len + static_cast<ssize_t>(sizeof(big_payload_tag)));
       *found = true;
     }
   };
@@ -1259,14 +1258,10 @@
     int level;
     char type;
   } levels[] = {
-    { ANDROID_LOG_VERBOSE, 'v' },
-    { ANDROID_LOG_DEBUG, 'd' },
-    { ANDROID_LOG_INFO, 'i' },
-    { ANDROID_LOG_WARN, 'w' },
-    { ANDROID_LOG_ERROR, 'e' },
-    { ANDROID_LOG_FATAL, 'a' },
-    { -1, 's' },
-    { -2, 'g' },  // Illegal value, resort to default
+      {ANDROID_LOG_VERBOSE, 'v'}, {ANDROID_LOG_DEBUG, 'd'},
+      {ANDROID_LOG_INFO, 'i'},    {ANDROID_LOG_WARN, 'w'},
+      {ANDROID_LOG_ERROR, 'e'},   {ANDROID_LOG_FATAL, 'a'},
+      {ANDROID_LOG_SILENT, 's'},  {-2, 'g'},  // Illegal value, resort to default
   };
 
   // Set up initial test condition
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 333fc55..ee6ae7a 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -44,6 +44,9 @@
     bool Rmmod(const std::string& module_name);
     std::vector<std::string> GetDependencies(const std::string& module);
     bool ModuleExists(const std::string& module_name);
+    void AddOption(const std::string& module_name, const std::string& option_name,
+                   const std::string& value);
+    std::string GetKernelCmdline();
 
     bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
     bool ParseAliasCallback(const std::vector<std::string>& args);
@@ -51,6 +54,7 @@
     bool ParseLoadCallback(const std::vector<std::string>& args);
     bool ParseOptionsCallback(const std::vector<std::string>& args);
     bool ParseBlacklistCallback(const std::vector<std::string>& args);
+    void ParseKernelCmdlineOptions();
     void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
 
     std::vector<std::pair<std::string, std::string>> module_aliases_;
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 6b9107f..f22bbf1 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -238,6 +238,80 @@
     return;
 }
 
+void Modprobe::AddOption(const std::string& module_name, const std::string& option_name,
+                         const std::string& value) {
+    auto canonical_name = MakeCanonical(module_name);
+    auto options_iter = module_options_.find(canonical_name);
+    auto option_str = option_name + "=" + value;
+    if (options_iter != module_options_.end()) {
+        options_iter->second = options_iter->second + " " + option_str;
+    } else {
+        module_options_.emplace(canonical_name, option_str);
+    }
+}
+
+void Modprobe::ParseKernelCmdlineOptions(void) {
+    std::string cmdline = GetKernelCmdline();
+    std::string module_name = "";
+    std::string option_name = "";
+    std::string value = "";
+    bool in_module = true;
+    bool in_option = false;
+    bool in_value = false;
+    bool in_quotes = false;
+    int start = 0;
+
+    for (int i = 0; i < cmdline.size(); i++) {
+        if (cmdline[i] == '"') {
+            in_quotes = !in_quotes;
+        }
+
+        if (in_quotes) continue;
+
+        if (cmdline[i] == ' ') {
+            if (in_value) {
+                value = cmdline.substr(start, i - start);
+                if (!module_name.empty() && !option_name.empty()) {
+                    AddOption(module_name, option_name, value);
+                }
+            }
+            module_name = "";
+            option_name = "";
+            value = "";
+            in_value = false;
+            start = i + 1;
+            in_module = true;
+            continue;
+        }
+
+        if (cmdline[i] == '.') {
+            if (in_module) {
+                module_name = cmdline.substr(start, i - start);
+                start = i + 1;
+                in_module = false;
+            }
+            in_option = true;
+            continue;
+        }
+
+        if (cmdline[i] == '=') {
+            if (in_option) {
+                option_name = cmdline.substr(start, i - start);
+                start = i + 1;
+                in_option = false;
+            }
+            in_value = true;
+            continue;
+        }
+    }
+    if (in_value && !in_quotes) {
+        value = cmdline.substr(start, cmdline.size() - start);
+        if (!module_name.empty() && !option_name.empty()) {
+            AddOption(module_name, option_name, value);
+        }
+    }
+}
+
 Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
     using namespace std::placeholders;
 
@@ -261,6 +335,7 @@
         ParseCfg(base_path + "/modules.blacklist", blacklist_callback);
     }
 
+    ParseKernelCmdlineOptions();
     android::base::SetMinimumLogSeverity(android::base::INFO);
 }
 
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 8bebe4c..99472c1 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -17,11 +17,20 @@
 #include <sys/stat.h>
 #include <sys/syscall.h>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
 #include <modprobe/modprobe.h>
 
+std::string Modprobe::GetKernelCmdline(void) {
+    std::string cmdline;
+    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+        return "";
+    }
+    return cmdline;
+}
+
 bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
     android::base::unique_fd fd(
             TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
index 7d817b1..057dea3 100644
--- a/libmodprobe/libmodprobe_ext_test.cpp
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -29,6 +29,10 @@
 
 #include "libmodprobe_test.h"
 
+std::string Modprobe::GetKernelCmdline(void) {
+    return kernel_cmdline;
+}
+
 bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
     auto deps = GetDependencies(MakeCanonical(path_name));
     if (deps.empty()) {
@@ -57,7 +61,7 @@
 
 bool Modprobe::Rmmod(const std::string& module_name) {
     for (auto it = modules_loaded.begin(); it != modules_loaded.end(); it++) {
-        if (*it == module_name) {
+        if (*it == module_name || android::base::StartsWith(*it, module_name + " ")) {
             modules_loaded.erase(it);
             return true;
         }
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index a711631..879c7f2 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -31,7 +31,13 @@
 // Used by libmodprobe_ext_test to report which modules would have been loaded.
 std::vector<std::string> modules_loaded;
 
+// Used by libmodprobe_ext_test to fake a kernel commandline
+std::string kernel_cmdline;
+
 TEST(libmodprobe, Test) {
+    kernel_cmdline =
+            "flag1 flag2 test1.option1=50 test4.option3=\"set x\" test1.option2=60 "
+            "test8. test5.option1= test10.option1=1";
     test_modules = {
             "/test1.ko",  "/test2.ko",  "/test3.ko",  "/test4.ko",  "/test5.ko",
             "/test6.ko",  "/test7.ko",  "/test8.ko",  "/test9.ko",  "/test10.ko",
@@ -42,25 +48,33 @@
             "/test14.ko",
             "/test15.ko",
             "/test3.ko",
-            "/test4.ko",
-            "/test1.ko",
+            "/test4.ko option3=\"set x\"",
+            "/test1.ko option1=50 option2=60",
             "/test6.ko",
             "/test2.ko",
-            "/test5.ko",
+            "/test5.ko option1=",
             "/test8.ko",
             "/test7.ko param1=4",
             "/test9.ko param_x=1 param_y=2 param_z=3",
-            "/test10.ko",
+            "/test10.ko option1=1",
             "/test12.ko",
             "/test11.ko",
             "/test13.ko",
     };
 
     std::vector<std::string> expected_after_remove = {
-            "/test14.ko", "/test15.ko",         "/test1.ko",
-            "/test6.ko",  "/test2.ko",          "/test5.ko",
-            "/test8.ko",  "/test7.ko param1=4", "/test9.ko param_x=1 param_y=2 param_z=3",
-            "/test10.ko", "/test12.ko",         "/test11.ko",
+            "/test14.ko",
+            "/test15.ko",
+            "/test1.ko option1=50 option2=60",
+            "/test6.ko",
+            "/test2.ko",
+            "/test5.ko option1=",
+            "/test8.ko",
+            "/test7.ko param1=4",
+            "/test9.ko param_x=1 param_y=2 param_z=3",
+            "/test10.ko option1=1",
+            "/test12.ko",
+            "/test11.ko",
             "/test13.ko",
     };
 
diff --git a/libmodprobe/libmodprobe_test.h b/libmodprobe/libmodprobe_test.h
index a001b69..e7b949f 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/libmodprobe/libmodprobe_test.h
@@ -19,5 +19,6 @@
 #include <string>
 #include <vector>
 
+extern std::string kernel_cmdline;
 extern std::vector<std::string> test_modules;
 extern std::vector<std::string> modules_loaded;
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 8212eba..5999e39 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -257,8 +257,8 @@
  *
  * Returns zero on success and negative errno on failure.
  */
-int ifc_act_on_address(int action, const char *name, const char *address,
-                       int prefixlen) {
+int ifc_act_on_address(int action, const char* name, const char* address, int prefixlen,
+                       bool nodad) {
     int ifindex, s, len, ret;
     struct sockaddr_storage ss;
     int saved_errno;
@@ -311,6 +311,7 @@
 
     // Interface address message header.
     req.r.ifa_family = ss.ss_family;
+    req.r.ifa_flags = nodad ? IFA_F_NODAD : 0;
     req.r.ifa_prefixlen = prefixlen;
     req.r.ifa_index = ifindex;
 
@@ -363,12 +364,12 @@
 
 // Returns zero on success and negative errno on failure.
 int ifc_add_address(const char *name, const char *address, int prefixlen) {
-    return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen);
+    return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen, /*nodad*/ false);
 }
 
 // Returns zero on success and negative errno on failure.
 int ifc_del_address(const char *name, const char * address, int prefixlen) {
-    return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen);
+    return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen, /*nodad*/ false);
 }
 
 /*
diff --git a/libnetutils/include/netutils/ifc.h b/libnetutils/include/netutils/ifc.h
index 3b27234..ee896ac 100644
--- a/libnetutils/include/netutils/ifc.h
+++ b/libnetutils/include/netutils/ifc.h
@@ -17,8 +17,9 @@
 #ifndef _NETUTILS_IFC_H_
 #define _NETUTILS_IFC_H_
 
-#include <sys/cdefs.h>
 #include <arpa/inet.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -42,6 +43,8 @@
 
 extern int ifc_get_addr(const char *name, in_addr_t *addr);
 extern int ifc_set_addr(const char *name, in_addr_t addr);
+extern int ifc_act_on_address(int action, const char* name, const char* address, int prefixlen,
+                              bool nodad);
 extern int ifc_add_address(const char *name, const char *address,
                            int prefixlen);
 extern int ifc_del_address(const char *name, const char *address,
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 16339d3..698e74d 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -138,8 +138,17 @@
     return enabled;
 }
 
+static bool schedtune_enabled() {
+    return (CgroupMap::GetInstance().FindController("schedtune").IsUsable());
+}
+
+static bool cpuctl_enabled() {
+    return (CgroupMap::GetInstance().FindController("cpu").IsUsable());
+}
+
 bool schedboost_enabled() {
-    static bool enabled = (CgroupMap::GetInstance().FindController("schedtune").IsUsable());
+    static bool enabled = schedtune_enabled() || cpuctl_enabled();
+
     return enabled;
 }
 
@@ -162,7 +171,9 @@
 
     std::string group;
     if (schedboost_enabled()) {
-        if (getCGroupSubsys(tid, "schedtune", group) < 0) return -1;
+        if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
+            (getCGroupSubsys(tid, "cpu", group) < 0))
+		return -1;
     }
     if (group.empty() && cpusets_enabled()) {
         if (getCGroupSubsys(tid, "cpuset", group) < 0) return -1;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 9447f86..72f01af 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -288,6 +288,11 @@
     return true;
 }
 
+void TaskProfile::MoveTo(TaskProfile* profile) {
+    profile->elements_ = std::move(elements_);
+    profile->res_cached_ = res_cached_;
+}
+
 bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& element : elements_) {
         if (!element->ExecuteForProcess(uid, pid)) {
@@ -458,7 +463,15 @@
                 LOG(WARNING) << "Unknown profile action: " << action_name;
             }
         }
-        profiles_[profile_name] = profile;
+        auto iter = profiles_.find(profile_name);
+        if (iter == profiles_.end()) {
+            profiles_[profile_name] = profile;
+        } else {
+            // Move the content rather that replace the profile because old profile might be
+            // referenced from an aggregate profile if vendor overrides task profiles
+            profile->MoveTo(iter->second.get());
+            profile.reset();
+        }
     }
 
     const Json::Value& aggregateprofiles_val = root["AggregateProfiles"];
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 9f2308c..a64ca50 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -143,6 +143,7 @@
     TaskProfile() : res_cached_(false) {}
 
     void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
+    void MoveTo(TaskProfile* profile);
 
     bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     bool ExecuteForTask(int tid) const;
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index 9194cf3..2efd49c 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -59,7 +59,6 @@
     case 'Z':
       return kProcessStateZombie;
     default:
-      LOG(ERROR) << "unknown process state: " << *state;
       return kProcessStateUnknown;
   }
 }
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 88146e9..135904b 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,6 +3,7 @@
 cc_library {
     name: "libsparse",
     host_supported: true,
+    ramdisk_available: true,
     recovery_available: true,
     unique_host_soname: true,
     srcs: [
diff --git a/libstats/Android.bp b/libstats/Android.bp
deleted file mode 100644
index f5ee1da..0000000
--- a/libstats/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// ==========================================================
-// Native library to write stats log to statsd socket
-// ==========================================================
-cc_library {
-    name: "libstatssocket",
-    srcs: [
-        "stats_event_list.c",
-        "statsd_writer.c",
-    ],
-    host_supported: true,
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-DLIBLOG_LOG_TAG=1006",
-        "-DWRITE_TO_STATSD=1",
-        "-DWRITE_TO_LOGD=0",
-    ],
-    export_include_dirs: ["include"],
-    shared_libs: [
-        "libcutils",
-        "liblog",
-    ],
-}
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
new file mode 100644
index 0000000..465c05a
--- /dev/null
+++ b/libstats/push_compat/Android.bp
@@ -0,0 +1,59 @@
+//
+// 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.
+//
+
+// =========================================================================
+// Native library that toggles between the old and new statsd socket
+// protocols. This library should only be used by DNS resolver or other
+// native modules on Q that log pushed atoms to statsd.
+// =========================================================================
+cc_defaults {
+    name: "libstatspush_compat_defaults",
+    srcs: [
+        "statsd_writer.c",
+        "stats_event_list.c",
+        "StatsEventCompat.cpp"
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DWRITE_TO_STATSD=1",
+        "-DWRITE_TO_LOGD=0",
+    ],
+    header_libs: ["libstatssocket_headers"],
+    static_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libstatspush_compat",
+    defaults: ["libstatspush_compat_defaults"],
+    export_include_dirs: ["include"],
+    static_libs: ["libgtest_prod"],
+}
+
+cc_test {
+    name: "libstatspush_compat_test",
+    defaults: ["libstatspush_compat_defaults"],
+    test_suites: ["device_tests"],
+    srcs: [
+        "tests/StatsEventCompat_test.cpp",
+    ],
+    static_libs: ["libgmock"],
+}
+
diff --git a/libstats/push_compat/StatsEventCompat.cpp b/libstats/push_compat/StatsEventCompat.cpp
new file mode 100644
index 0000000..edfa070
--- /dev/null
+++ b/libstats/push_compat/StatsEventCompat.cpp
@@ -0,0 +1,221 @@
+/*
+ * 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 "include/StatsEventCompat.h"
+#include <android-base/properties.h>
+#include <android/api-level.h>
+#include <android/log.h>
+#include <dlfcn.h>
+#include <utils/SystemClock.h>
+
+using android::base::GetProperty;
+
+const static int kStatsEventTag = 1937006964;
+
+/* Checking ro.build.version.release is fragile, as the release field is
+ * an opaque string without structural guarantees. However, testing confirms
+ * that on Q devices, the property is "10," and on R, it is "R." Until
+ * android_get_device_api_level() is updated, this is the only solution.
+ *
+ * TODO(b/146019024): migrate to android_get_device_api_level()
+ */
+const bool StatsEventCompat::mPlatformAtLeastR =
+        GetProperty("ro.build.version.codename", "") == "R" ||
+        android_get_device_api_level() > __ANDROID_API_Q__;
+
+// definitions of static class variables
+bool StatsEventCompat::mAttemptedLoad = false;
+struct stats_event_api_table* StatsEventCompat::mStatsEventApi = nullptr;
+std::mutex StatsEventCompat::mLoadLock;
+
+StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
+    // guard loading because StatsEventCompat might be called from multithreaded
+    // environment
+    {
+        std::lock_guard<std::mutex> lg(mLoadLock);
+        if (!mAttemptedLoad) {
+            void* handle = dlopen("libstatssocket.so", RTLD_NOW);
+            if (handle) {
+                mStatsEventApi = (struct stats_event_api_table*)dlsym(handle, "table");
+            } else {
+                ALOGE("dlopen failed: %s\n", dlerror());
+            }
+        }
+        mAttemptedLoad = true;
+    }
+
+    if (mStatsEventApi) {
+        mEventR = mStatsEventApi->obtain();
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << android::elapsedRealtimeNano();
+    }
+}
+
+StatsEventCompat::~StatsEventCompat() {
+    if (mStatsEventApi) mStatsEventApi->release(mEventR);
+}
+
+void StatsEventCompat::setAtomId(int32_t atomId) {
+    if (mStatsEventApi) {
+        mStatsEventApi->set_atom_id(mEventR, (uint32_t)atomId);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << atomId;
+    }
+}
+
+void StatsEventCompat::writeInt32(int32_t value) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_int32(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeInt64(int64_t value) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_int64(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeFloat(float value) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_float(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeBool(bool value) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_bool(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_byte_array(mEventR, (const uint8_t*)buffer, length);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ.AppendCharArray(buffer, length);
+    }
+}
+
+void StatsEventCompat::writeString(const char* value) {
+    if (value == nullptr) value = "";
+
+    if (mStatsEventApi) {
+        mStatsEventApi->write_string8(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
+                                             const vector<const char*>& tags) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_attribution_chain(mEventR, (const uint32_t*)uids, tags.data(),
+                                                (uint8_t)numUids);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ.begin();
+        for (size_t i = 0; i < numUids; i++) {
+            mEventQ.begin();
+            mEventQ << uids[i];
+            const char* tag = tags[i] ? tags[i] : "";
+            mEventQ << tag;
+            mEventQ.end();
+        }
+        mEventQ.end();
+    }
+}
+
+void StatsEventCompat::writeKeyValuePairs(const map<int, int32_t>& int32Map,
+                                          const map<int, int64_t>& int64Map,
+                                          const map<int, const char*>& stringMap,
+                                          const map<int, float>& floatMap) {
+    if (mStatsEventApi) {
+        vector<struct key_value_pair> pairs;
+
+        for (const auto& it : int32Map) {
+            pairs.push_back({.key = it.first, .valueType = INT32_TYPE, .int32Value = it.second});
+        }
+        for (const auto& it : int64Map) {
+            pairs.push_back({.key = it.first, .valueType = INT64_TYPE, .int64Value = it.second});
+        }
+        for (const auto& it : stringMap) {
+            pairs.push_back({.key = it.first, .valueType = STRING_TYPE, .stringValue = it.second});
+        }
+        for (const auto& it : floatMap) {
+            pairs.push_back({.key = it.first, .valueType = FLOAT_TYPE, .floatValue = it.second});
+        }
+
+        mStatsEventApi->write_key_value_pairs(mEventR, pairs.data(), (uint8_t)pairs.size());
+    }
+
+    else if (!mPlatformAtLeastR) {
+        mEventQ.begin();
+        writeKeyValuePairMap(int32Map);
+        writeKeyValuePairMap(int64Map);
+        writeKeyValuePairMap(stringMap);
+        writeKeyValuePairMap(floatMap);
+        mEventQ.end();
+    }
+}
+
+template <class T>
+void StatsEventCompat::writeKeyValuePairMap(const map<int, T>& keyValuePairMap) {
+    for (const auto& it : keyValuePairMap) {
+        mEventQ.begin();
+        mEventQ << it.first;
+        mEventQ << it.second;
+        mEventQ.end();
+    }
+}
+
+// explicitly specify which types we're going to use
+template void StatsEventCompat::writeKeyValuePairMap<int32_t>(const map<int, int32_t>&);
+template void StatsEventCompat::writeKeyValuePairMap<int64_t>(const map<int, int64_t>&);
+template void StatsEventCompat::writeKeyValuePairMap<float>(const map<int, float>&);
+template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);
+
+void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {
+    if (mStatsEventApi) mStatsEventApi->add_bool_annotation(mEventR, annotationId, value);
+    // Don't do anything if on Q.
+}
+
+void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
+    if (mStatsEventApi) mStatsEventApi->add_int32_annotation(mEventR, annotationId, value);
+    // Don't do anything if on Q.
+}
+
+int StatsEventCompat::writeToSocket() {
+    if (mStatsEventApi) {
+        mStatsEventApi->build(mEventR);
+        return mStatsEventApi->write(mEventR);
+    }
+
+    if (!mPlatformAtLeastR) return mEventQ.write(LOG_ID_STATS);
+
+    // We reach here only if we're on R, but libstatspush_compat was unable to
+    // be loaded using dlopen.
+    return -ENOLINK;
+}
+
+bool StatsEventCompat::usesNewSchema() {
+    return mStatsEventApi != nullptr;
+}
diff --git a/libstats/push_compat/include/StatsEventCompat.h b/libstats/push_compat/include/StatsEventCompat.h
new file mode 100644
index 0000000..a8cde68
--- /dev/null
+++ b/libstats/push_compat/include/StatsEventCompat.h
@@ -0,0 +1,71 @@
+/*
+ * 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 <gtest/gtest_prod.h>
+#include <map>
+#include <mutex>
+#include <vector>
+#include "stats_event.h"
+#include "stats_event_list.h"
+
+using std::map;
+using std::vector;
+
+class StatsEventCompat {
+  public:
+    StatsEventCompat();
+    ~StatsEventCompat();
+
+    void setAtomId(int32_t atomId);
+    void writeInt32(int32_t value);
+    void writeInt64(int64_t value);
+    void writeFloat(float value);
+    void writeBool(bool value);
+    void writeByteArray(const char* buffer, size_t length);
+    void writeString(const char* value);
+
+    // Pre-condition: numUids == tags.size()
+    void writeAttributionChain(const int32_t* uids, size_t numUids,
+                               const vector<const char*>& tags);
+
+    void writeKeyValuePairs(const map<int, int32_t>& int32Map, const map<int, int64_t>& int64Map,
+                            const map<int, const char*>& stringMap,
+                            const map<int, float>& floatMap);
+
+    void addBoolAnnotation(uint8_t annotationId, bool value);
+    void addInt32Annotation(uint8_t annotationId, int32_t value);
+
+    int writeToSocket();
+
+  private:
+    // static member variables
+    const static bool mPlatformAtLeastR;
+    static bool mAttemptedLoad;
+    static std::mutex mLoadLock;
+    static struct stats_event_api_table* mStatsEventApi;
+
+    // non-static member variables
+    struct stats_event* mEventR = nullptr;
+    stats_event_list mEventQ;
+
+    template <class T>
+    void writeKeyValuePairMap(const map<int, T>& keyValuePairMap);
+
+    bool usesNewSchema();
+    FRIEND_TEST(StatsEventCompatTest, TestDynamicLoading);
+};
diff --git a/libstats/include/stats_event_list.h b/libstats/push_compat/include/stats_event_list.h
similarity index 100%
rename from libstats/include/stats_event_list.h
rename to libstats/push_compat/include/stats_event_list.h
diff --git a/libstats/stats_event_list.c b/libstats/push_compat/stats_event_list.c
similarity index 100%
rename from libstats/stats_event_list.c
rename to libstats/push_compat/stats_event_list.c
diff --git a/libstats/statsd_writer.c b/libstats/push_compat/statsd_writer.c
similarity index 98%
rename from libstats/statsd_writer.c
rename to libstats/push_compat/statsd_writer.c
index 073b67f..04d3b46 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/push_compat/statsd_writer.c
@@ -101,7 +101,7 @@
             strcpy(un.sun_path, "/dev/socket/statsdw");
 
             if (TEMP_FAILURE_RETRY(
-                    connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
+                        connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
                 ret = -errno;
                 switch (ret) {
                     case -ENOTCONN:
diff --git a/libstats/statsd_writer.h b/libstats/push_compat/statsd_writer.h
similarity index 100%
copy from libstats/statsd_writer.h
copy to libstats/push_compat/statsd_writer.h
diff --git a/libstats/push_compat/tests/StatsEventCompat_test.cpp b/libstats/push_compat/tests/StatsEventCompat_test.cpp
new file mode 100644
index 0000000..2be24ec
--- /dev/null
+++ b/libstats/push_compat/tests/StatsEventCompat_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/StatsEventCompat.h"
+#include <android-base/properties.h>
+#include <android/api-level.h>
+#include <gtest/gtest.h>
+
+using android::base::GetProperty;
+
+/* Checking ro.build.version.release is fragile, as the release field is
+ * an opaque string without structural guarantees. However, testing confirms
+ * that on Q devices, the property is "10," and on R, it is "R." Until
+ * android_get_device_api_level() is updated, this is the only solution.
+ *
+ *
+ * TODO(b/146019024): migrate to android_get_device_api_level()
+ */
+const static bool mPlatformAtLeastR = GetProperty("ro.build.version.release", "") == "R" ||
+                                      android_get_device_api_level() > __ANDROID_API_Q__;
+
+TEST(StatsEventCompatTest, TestDynamicLoading) {
+    StatsEventCompat event;
+    EXPECT_EQ(mPlatformAtLeastR, event.usesNewSchema());
+}
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
new file mode 100644
index 0000000..9fd9fbc
--- /dev/null
+++ b/libstats/socket/Android.bp
@@ -0,0 +1,96 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// =========================================================================
+// Native library to write stats log to statsd socket on Android R and later
+// =========================================================================
+cc_library {
+    name: "libstatssocket",
+    srcs: [
+        "stats_buffer_writer.c",
+        "stats_event.c",
+        // TODO(b/145573568): Remove stats_event_list once stats_event
+        // migration is complete.
+        "stats_event_list.c",
+        "statsd_writer.c",
+    ],
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DLIBLOG_LOG_TAG=1006",
+        "-DWRITE_TO_STATSD=1",
+        "-DWRITE_TO_LOGD=0",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+
+    // enumerate stable entry points for APEX use
+    stubs: {
+        symbol_file: "libstatssocket.map.txt",
+        versions: [
+            "1",
+        ],
+    }
+}
+
+cc_library_headers {
+    name: "libstatssocket_headers",
+    export_include_dirs: ["include"],
+    host_supported: true,
+}
+
+cc_benchmark {
+    name: "libstatssocket_benchmark",
+    srcs: [
+        "benchmark/main.cpp",
+        "benchmark/stats_event_benchmark.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    static_libs: [
+        "libstatssocket",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libgtest_prod",
+    ],
+}
+
+cc_test {
+    name: "libstatssocket_test",
+    srcs: ["tests/stats_event_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    static_libs: [
+        "libgmock",
+        "libstatssocket",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    test_suites: ["device_tests"],
+}
diff --git a/libstats/socket/benchmark/main.cpp b/libstats/socket/benchmark/main.cpp
new file mode 100644
index 0000000..5ebdf6e
--- /dev/null
+++ b/libstats/socket/benchmark/main.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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 <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/libstats/socket/benchmark/stats_event_benchmark.cpp b/libstats/socket/benchmark/stats_event_benchmark.cpp
new file mode 100644
index 0000000..9488168
--- /dev/null
+++ b/libstats/socket/benchmark/stats_event_benchmark.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "benchmark/benchmark.h"
+#include "stats_event.h"
+
+static struct stats_event* constructStatsEvent() {
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, 100);
+
+    // randomly sample atom size
+    int numElements = rand() % 800;
+    for (int i = 0; i < numElements; i++) {
+        stats_event_write_int32(event, i);
+    }
+
+    return event;
+}
+
+static void BM_stats_event_truncate_buffer(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        struct stats_event* event = constructStatsEvent();
+        stats_event_build(event);
+        stats_event_write(event);
+        stats_event_release(event);
+    }
+}
+
+BENCHMARK(BM_stats_event_truncate_buffer);
+
+static void BM_stats_event_full_buffer(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        struct stats_event* event = constructStatsEvent();
+        stats_event_truncate_buffer(event, false);
+        stats_event_build(event);
+        stats_event_write(event);
+        stats_event_release(event);
+    }
+}
+
+BENCHMARK(BM_stats_event_full_buffer);
diff --git a/libstats/socket/include/stats_buffer_writer.h b/libstats/socket/include/stats_buffer_writer.h
new file mode 100644
index 0000000..de4a5e2
--- /dev/null
+++ b/libstats/socket/include/stats_buffer_writer.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __CPLUSPLUS
+void stats_log_close();
+int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId);
+#ifdef __cplusplus
+}
+#endif  // __CPLUSPLUS
diff --git a/libstats/socket/include/stats_event.h b/libstats/socket/include/stats_event.h
new file mode 100644
index 0000000..080e957
--- /dev/null
+++ b/libstats/socket/include/stats_event.h
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_STATS_LOG_STATS_EVENT_H
+#define ANDROID_STATS_LOG_STATS_EVENT_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * Functionality to build and store the buffer sent over the statsd socket.
+ * This code defines and encapsulates the socket protocol.
+ *
+ * Usage:
+ *      struct stats_event* event = stats_event_obtain();
+ *
+ *      stats_event_set_atom_id(event, atomId);
+ *      stats_event_write_int32(event, 24);
+ *      stats_event_add_bool_annotation(event, 1, true); // annotations apply to the previous field
+ *      stats_event_add_int32_annotation(event, 2, 128);
+ *      stats_event_write_float(event, 2.0);
+ *
+ *      stats_event_build(event);
+ *      stats_event_write(event);
+ *      stats_event_release(event);
+ *
+ * Notes:
+ *    (a) write_<type>() and add_<type>_annotation() should be called in the order that fields
+ *        and annotations are defined in the atom.
+ *    (b) set_atom_id() can be called anytime before stats_event_write().
+ *    (c) add_<type>_annotation() calls apply to the previous field.
+ *    (d) If errors occur, stats_event_write() will write a bitmask of the errors to the socket.
+ *    (e) All strings should be encoded using UTF8.
+ */
+
+/* ERRORS */
+#define ERROR_NO_TIMESTAMP 0x1
+#define ERROR_NO_ATOM_ID 0x2
+#define ERROR_OVERFLOW 0x4
+#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
+#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
+#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
+#define ERROR_INVALID_ANNOTATION_ID 0x40
+#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
+#define ERROR_TOO_MANY_ANNOTATIONS 0x100
+#define ERROR_TOO_MANY_FIELDS 0x200
+#define ERROR_INVALID_VALUE_TYPE 0x400
+#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
+
+/* TYPE IDS */
+#define INT32_TYPE 0x00
+#define INT64_TYPE 0x01
+#define STRING_TYPE 0x02
+#define LIST_TYPE 0x03
+#define FLOAT_TYPE 0x04
+#define BOOL_TYPE 0x05
+#define BYTE_ARRAY_TYPE 0x06
+#define OBJECT_TYPE 0x07
+#define KEY_VALUE_PAIRS_TYPE 0x08
+#define ATTRIBUTION_CHAIN_TYPE 0x09
+#define ERROR_TYPE 0x0F
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __CPLUSPLUS
+
+struct stats_event;
+
+/* SYSTEM API */
+struct stats_event* stats_event_obtain();
+// The build function can be called multiple times without error. If the event
+// has been built before, this function is a no-op.
+void stats_event_build(struct stats_event* event);
+int stats_event_write(struct stats_event* event);
+void stats_event_release(struct stats_event* event);
+
+void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId);
+
+void stats_event_write_int32(struct stats_event* event, int32_t value);
+void stats_event_write_int64(struct stats_event* event, int64_t value);
+void stats_event_write_float(struct stats_event* event, float value);
+void stats_event_write_bool(struct stats_event* event, bool value);
+
+void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf, size_t numBytes);
+
+// Buf must be null-terminated.
+void stats_event_write_string8(struct stats_event* event, const char* value);
+
+// Tags must be null-terminated.
+void stats_event_write_attribution_chain(struct stats_event* event, const uint32_t* uids,
+                                         const char* const* tags, uint8_t numNodes);
+
+/* key_value_pair struct can be constructed as follows:
+ *    struct key_value_pair pair = {.key = key, .valueType = STRING_TYPE,
+ *                                  .stringValue = buf};
+ */
+struct key_value_pair {
+    int32_t key;
+    uint8_t valueType;  // expected to be INT32_TYPE, INT64_TYPE, FLOAT_TYPE, or STRING_TYPE
+    union {
+        int32_t int32Value;
+        int64_t int64Value;
+        float floatValue;
+        const char* stringValue;  // must be null terminated
+    };
+};
+
+void stats_event_write_key_value_pairs(struct stats_event* event, struct key_value_pair* pairs,
+                                       uint8_t numPairs);
+
+void stats_event_add_bool_annotation(struct stats_event* event, uint8_t annotationId, bool value);
+void stats_event_add_int32_annotation(struct stats_event* event, uint8_t annotationId,
+                                      int32_t value);
+
+uint32_t stats_event_get_atom_id(struct stats_event* event);
+// Size is an output parameter.
+uint8_t* stats_event_get_buffer(struct stats_event* event, size_t* size);
+uint32_t stats_event_get_errors(struct stats_event* event);
+
+// This table is used by StatsEventCompat to access the stats_event API.
+struct stats_event_api_table {
+    struct stats_event* (*obtain)(void);
+    void (*build)(struct stats_event*);
+    int (*write)(struct stats_event*);
+    void (*release)(struct stats_event*);
+    void (*set_atom_id)(struct stats_event*, uint32_t);
+    void (*write_int32)(struct stats_event*, int32_t);
+    void (*write_int64)(struct stats_event*, int64_t);
+    void (*write_float)(struct stats_event*, float);
+    void (*write_bool)(struct stats_event*, bool);
+    void (*write_byte_array)(struct stats_event*, const uint8_t*, size_t);
+    void (*write_string8)(struct stats_event*, const char*);
+    void (*write_attribution_chain)(struct stats_event*, const uint32_t*, const char* const*,
+                                    uint8_t);
+    void (*write_key_value_pairs)(struct stats_event*, struct key_value_pair*, uint8_t);
+    void (*add_bool_annotation)(struct stats_event*, uint8_t, bool);
+    void (*add_int32_annotation)(struct stats_event*, uint8_t, int32_t);
+    uint32_t (*get_atom_id)(struct stats_event*);
+    uint8_t* (*get_buffer)(struct stats_event*, size_t*);
+    uint32_t (*get_errors)(struct stats_event*);
+};
+
+// exposed for benchmarking only
+void stats_event_truncate_buffer(struct stats_event* event, bool truncate);
+
+#ifdef __cplusplus
+}
+#endif  // __CPLUSPLUS
+
+#endif  // ANDROID_STATS_LOG_STATS_EVENT_H
diff --git a/libstats/socket/include/stats_event_list.h b/libstats/socket/include/stats_event_list.h
new file mode 100644
index 0000000..7a26536
--- /dev/null
+++ b/libstats/socket/include/stats_event_list.h
@@ -0,0 +1,248 @@
+/*
+ * 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 <log/log_event_list.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void reset_log_context(android_log_context ctx);
+int write_to_logger(android_log_context context, log_id_t id);
+void note_log_drop(int error, int atomId);
+void stats_log_close();
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+/**
+ * A copy of android_log_event_list class.
+ *
+ * android_log_event_list is going to be deprecated soon, so copy it here to
+ * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this
+ * code.
+ */
+class stats_event_list {
+  private:
+    android_log_context ctx;
+    int ret;
+
+    stats_event_list(const stats_event_list&) = delete;
+    void operator=(const stats_event_list&) = delete;
+
+  public:
+    explicit stats_event_list(int tag) : ret(0) {
+        ctx = create_android_logger(static_cast<uint32_t>(tag));
+    }
+    ~stats_event_list() { android_log_destroy(&ctx); }
+
+    int close() {
+        int retval = android_log_destroy(&ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return retval;
+    }
+
+    /* To allow above C calls to use this class as parameter */
+    operator android_log_context() const { return ctx; }
+
+    /* return errors or transmit status */
+    int status() const { return ret; }
+
+    int begin() {
+        int retval = android_log_write_list_begin(ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+    int end() {
+        int retval = android_log_write_list_end(ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    stats_event_list& operator<<(int32_t value) {
+        int retval = android_log_write_int32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(uint32_t value) {
+        int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(bool value) {
+        int retval = android_log_write_int32(ctx, value ? 1 : 0);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(int64_t value) {
+        int retval = android_log_write_int64(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(uint64_t value) {
+        int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(const char* value) {
+        int retval = android_log_write_string8(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(float value) {
+        int retval = android_log_write_float32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    int write(log_id_t id = LOG_ID_EVENTS) {
+        /* facilitate -EBUSY retry */
+        if ((ret == -EBUSY) || (ret > 0)) {
+            ret = 0;
+        }
+        int retval = write_to_logger(ctx, id);
+        /* existing errors trump transmission errors */
+        if (!ret) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    /*
+     * Append<Type> methods removes any integer promotion
+     * confusion, and adds access to string with length.
+     * Append methods are also added for all types for
+     * convenience.
+     */
+
+    bool AppendInt(int32_t value) {
+        int retval = android_log_write_int32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendLong(int64_t value) {
+        int retval = android_log_write_int64(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const char* value) {
+        int retval = android_log_write_string8(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const char* value, size_t len) {
+        int retval = android_log_write_string8_len(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    bool Append(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    bool AppendFloat(float value) {
+        int retval = android_log_write_float32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    template <typename Tvalue>
+    bool Append(Tvalue value) {
+        *this << value;
+        return ret >= 0;
+    }
+
+    bool Append(const char* value, size_t len) {
+        int retval = android_log_write_string8_len(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendCharArray(const char* value, size_t len) {
+        int retval = android_log_write_char_array(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+};
+
+#endif
diff --git a/libstats/socket/libstatssocket.map.txt b/libstats/socket/libstatssocket.map.txt
new file mode 100644
index 0000000..55bfbda
--- /dev/null
+++ b/libstats/socket/libstatssocket.map.txt
@@ -0,0 +1,23 @@
+LIBSTATSSOCKET {
+    global:
+        stats_event_obtain; # apex # introduced=1
+        stats_event_build; # apex # introduced=1
+        stats_event_write; # apex # introduced=1
+        stats_event_release; # apex # introduced=1
+        stats_event_set_atom_id; # apex # introduced=1
+        stats_event_write_int32; # apex # introduced=1
+        stats_event_write_int64; # apex # introduced=1
+        stats_event_write_float; # apex # introduced=1
+        stats_event_write_bool; # apex # introduced=1
+        stats_event_write_byte_array; # apex # introduced=1
+        stats_event_write_string8; # apex # introduced=1
+        stats_event_write_attribution_chain; # apex # introduced=1
+        stats_event_write_key_value_pairs; # apex # introduced=1
+        stats_event_add_bool_annotation; # apex # introduced=1
+        stats_event_add_int32_annotation; # apex # introduced=1
+        stats_event_get_atom_id; # apex # introduced=1
+        stats_event_get_buffer; # apex # introduced=1
+        stats_event_get_errors; # apex # introduced=1
+    local:
+        *;
+};
diff --git a/libstats/socket/stats_buffer_writer.c b/libstats/socket/stats_buffer_writer.c
new file mode 100644
index 0000000..c5c591d
--- /dev/null
+++ b/libstats/socket/stats_buffer_writer.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "include/stats_buffer_writer.h"
+#ifdef __ANDROID__
+#include <cutils/properties.h>
+#endif
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include "statsd_writer.h"
+
+static const uint32_t kStatsEventTag = 1937006964;
+
+extern struct android_log_transport_write statsdLoggerWrite;
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr);
+static int (*__write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+
+void note_log_drop(int error, int atomId) {
+    statsdLoggerWrite.noteDrop(error, atomId);
+}
+
+void stats_log_close() {
+    statsd_writer_init_lock();
+    __write_to_statsd = __write_to_statsd_init;
+    if (statsdLoggerWrite.close) {
+        (*statsdLoggerWrite.close)();
+    }
+    statsd_writer_init_unlock();
+}
+
+int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
+    int ret = 1;
+
+#ifdef __ANDROID__
+    bool statsdEnabled = property_get_bool("ro.statsd.enable", true);
+#else
+    bool statsdEnabled = false;
+#endif
+
+    if (statsdEnabled) {
+        struct iovec vecs[2];
+        vecs[0].iov_base = (void*)&kStatsEventTag;
+        vecs[0].iov_len = sizeof(kStatsEventTag);
+        vecs[1].iov_base = buffer;
+        vecs[1].iov_len = size;
+
+        ret = __write_to_statsd(vecs, 2);
+
+        if (ret < 0) {
+            note_log_drop(ret, atomId);
+        }
+    }
+
+    return ret;
+}
+
+static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
+    int save_errno;
+    struct timespec ts;
+    size_t len, i;
+
+    for (len = i = 0; i < nr; ++i) {
+        len += vec[i].iov_len;
+    }
+    if (!len) {
+        return -EINVAL;
+    }
+
+    save_errno = errno;
+#if defined(__ANDROID__)
+    clock_gettime(CLOCK_REALTIME, &ts);
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ts.tv_sec = tv.tv_sec;
+    ts.tv_nsec = tv.tv_usec * 1000;
+#endif
+
+    int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
+    errno = save_errno;
+    return ret;
+}
+
+static int __write_to_statsd_initialize_locked() {
+    if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
+        if (statsdLoggerWrite.close) {
+            (*statsdLoggerWrite.close)();
+            return -ENODEV;
+        }
+    }
+    return 1;
+}
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
+    int ret, save_errno = errno;
+
+    statsd_writer_init_lock();
+
+    if (__write_to_statsd == __write_to_statsd_init) {
+        ret = __write_to_statsd_initialize_locked();
+        if (ret < 0) {
+            statsd_writer_init_unlock();
+            errno = save_errno;
+            return ret;
+        }
+
+        __write_to_statsd = __write_to_stats_daemon;
+    }
+
+    statsd_writer_init_unlock();
+
+    ret = __write_to_statsd(vec, nr);
+    errno = save_errno;
+    return ret;
+}
diff --git a/libstats/socket/stats_event.c b/libstats/socket/stats_event.c
new file mode 100644
index 0000000..15039c6
--- /dev/null
+++ b/libstats/socket/stats_event.c
@@ -0,0 +1,354 @@
+/*
+ * 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 "include/stats_event.h"
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "stats_buffer_writer.h"
+
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+// Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
+// See android_util_Stats_Log.cpp
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - 4)
+
+/* POSITIONS */
+#define POS_NUM_ELEMENTS 1
+#define POS_TIMESTAMP (POS_NUM_ELEMENTS + sizeof(uint8_t))
+#define POS_ATOM_ID (POS_TIMESTAMP + sizeof(uint8_t) + sizeof(uint64_t))
+#define POS_FIRST_FIELD (POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t))
+
+/* LIMITS */
+#define MAX_ANNOTATION_COUNT 15
+#define MAX_BYTE_VALUE 127  // parsing side requires that lengths fit in 7 bits
+
+// The stats_event struct holds the serialized encoding of an event
+// within a buf. Also includes other required fields.
+struct stats_event {
+    uint8_t* buf;
+    size_t lastFieldPos;  // location of last field within the buf
+    size_t size;          // number of valid bytes within buffer
+    uint32_t numElements;
+    uint32_t atomId;
+    uint32_t errors;
+    bool truncate;
+    bool built;
+};
+
+static int64_t get_elapsed_realtime_ns() {
+    struct timespec t;
+    t.tv_sec = t.tv_nsec = 0;
+    clock_gettime(CLOCK_BOOTTIME, &t);
+    return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
+}
+
+struct stats_event* stats_event_obtain() {
+    struct stats_event* event = malloc(sizeof(struct stats_event));
+    event->buf = (uint8_t*)calloc(MAX_EVENT_PAYLOAD, 1);
+    event->buf[0] = OBJECT_TYPE;
+    event->atomId = 0;
+    event->errors = 0;
+    event->truncate = true;  // truncate for both pulled and pushed atoms
+    event->built = false;
+
+    // place the timestamp
+    uint64_t timestampNs = get_elapsed_realtime_ns();
+    event->buf[POS_TIMESTAMP] = INT64_TYPE;
+    memcpy(&event->buf[POS_TIMESTAMP + sizeof(uint8_t)], &timestampNs, sizeof(timestampNs));
+
+    event->numElements = 1;
+    event->lastFieldPos = 0;  // 0 since we haven't written a field yet
+    event->size = POS_FIRST_FIELD;
+
+    return event;
+}
+
+void stats_event_release(struct stats_event* event) {
+    free(event->buf);
+    free(event);
+}
+
+void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId) {
+    event->atomId = atomId;
+    event->buf[POS_ATOM_ID] = INT32_TYPE;
+    memcpy(&event->buf[POS_ATOM_ID + sizeof(uint8_t)], &atomId, sizeof(atomId));
+    event->numElements++;
+}
+
+// Side-effect: modifies event->errors if the buffer would overflow
+static bool overflows(struct stats_event* event, size_t size) {
+    if (event->size + size > MAX_EVENT_PAYLOAD) {
+        event->errors |= ERROR_OVERFLOW;
+        return true;
+    }
+    return false;
+}
+
+// Side-effect: all append functions increment event->size if there is
+// sufficient space within the buffer to place the value
+static void append_byte(struct stats_event* event, uint8_t value) {
+    if (!overflows(event, sizeof(value))) {
+        event->buf[event->size] = value;
+        event->size += sizeof(value);
+    }
+}
+
+static void append_bool(struct stats_event* event, bool value) {
+    append_byte(event, (uint8_t)value);
+}
+
+static void append_int32(struct stats_event* event, int32_t value) {
+    if (!overflows(event, sizeof(value))) {
+        memcpy(&event->buf[event->size], &value, sizeof(value));
+        event->size += sizeof(value);
+    }
+}
+
+static void append_int64(struct stats_event* event, int64_t value) {
+    if (!overflows(event, sizeof(value))) {
+        memcpy(&event->buf[event->size], &value, sizeof(value));
+        event->size += sizeof(value);
+    }
+}
+
+static void append_float(struct stats_event* event, float value) {
+    if (!overflows(event, sizeof(value))) {
+        memcpy(&event->buf[event->size], &value, sizeof(value));
+        event->size += sizeof(float);
+    }
+}
+
+static void append_byte_array(struct stats_event* event, const uint8_t* buf, size_t size) {
+    if (!overflows(event, size)) {
+        memcpy(&event->buf[event->size], buf, size);
+        event->size += size;
+    }
+}
+
+// Side-effect: modifies event->errors if buf is not properly null-terminated
+static void append_string(struct stats_event* event, const char* buf) {
+    size_t size = strnlen(buf, MAX_EVENT_PAYLOAD);
+    if (size == MAX_EVENT_PAYLOAD) {
+        event->errors |= ERROR_STRING_NOT_NULL_TERMINATED;
+        return;
+    }
+
+    append_int32(event, size);
+    append_byte_array(event, (uint8_t*)buf, size);
+}
+
+static void start_field(struct stats_event* event, uint8_t typeId) {
+    event->lastFieldPos = event->size;
+    append_byte(event, typeId);
+    event->numElements++;
+}
+
+void stats_event_write_int32(struct stats_event* event, int32_t value) {
+    if (event->errors) return;
+
+    start_field(event, INT32_TYPE);
+    append_int32(event, value);
+}
+
+void stats_event_write_int64(struct stats_event* event, int64_t value) {
+    if (event->errors) return;
+
+    start_field(event, INT64_TYPE);
+    append_int64(event, value);
+}
+
+void stats_event_write_float(struct stats_event* event, float value) {
+    if (event->errors) return;
+
+    start_field(event, FLOAT_TYPE);
+    append_float(event, value);
+}
+
+void stats_event_write_bool(struct stats_event* event, bool value) {
+    if (event->errors) return;
+
+    start_field(event, BOOL_TYPE);
+    append_bool(event, value);
+}
+
+void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf, size_t numBytes) {
+    if (event->errors) return;
+
+    start_field(event, BYTE_ARRAY_TYPE);
+    append_int32(event, numBytes);
+    append_byte_array(event, buf, numBytes);
+}
+
+// Value is assumed to be encoded using UTF8
+void stats_event_write_string8(struct stats_event* event, const char* value) {
+    if (event->errors) return;
+
+    start_field(event, STRING_TYPE);
+    append_string(event, value);
+}
+
+// Tags are assumed to be encoded using UTF8
+void stats_event_write_attribution_chain(struct stats_event* event, const uint32_t* uids,
+                                         const char* const* tags, uint8_t numNodes) {
+    if (numNodes > MAX_BYTE_VALUE) event->errors |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
+    if (event->errors) return;
+
+    start_field(event, ATTRIBUTION_CHAIN_TYPE);
+    append_byte(event, numNodes);
+
+    for (uint8_t i = 0; i < numNodes; i++) {
+        append_int32(event, uids[i]);
+        append_string(event, tags[i]);
+    }
+}
+
+void stats_event_write_key_value_pairs(struct stats_event* event, struct key_value_pair* pairs,
+                                       uint8_t numPairs) {
+    if (numPairs > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_KEY_VALUE_PAIRS;
+    if (event->errors) return;
+
+    start_field(event, KEY_VALUE_PAIRS_TYPE);
+    append_byte(event, numPairs);
+
+    for (uint8_t i = 0; i < numPairs; i++) {
+        append_int32(event, pairs[i].key);
+        append_byte(event, pairs[i].valueType);
+        switch (pairs[i].valueType) {
+            case INT32_TYPE:
+                append_int32(event, pairs[i].int32Value);
+                break;
+            case INT64_TYPE:
+                append_int64(event, pairs[i].int64Value);
+                break;
+            case FLOAT_TYPE:
+                append_float(event, pairs[i].floatValue);
+                break;
+            case STRING_TYPE:
+                append_string(event, pairs[i].stringValue);
+                break;
+            default:
+                event->errors |= ERROR_INVALID_VALUE_TYPE;
+                return;
+        }
+    }
+}
+
+// Side-effect: modifies event->errors if field has too many annotations
+static void increment_annotation_count(struct stats_event* event) {
+    uint8_t fieldType = event->buf[event->lastFieldPos] & 0x0F;
+    uint32_t oldAnnotationCount = (event->buf[event->lastFieldPos] & 0xF0) >> 4;
+    uint32_t newAnnotationCount = oldAnnotationCount + 1;
+
+    if (newAnnotationCount > MAX_ANNOTATION_COUNT) {
+        event->errors |= ERROR_TOO_MANY_ANNOTATIONS;
+        return;
+    }
+
+    event->buf[event->lastFieldPos] = (((uint8_t)newAnnotationCount << 4) & 0xF0) | fieldType;
+}
+
+void stats_event_add_bool_annotation(struct stats_event* event, uint8_t annotationId, bool value) {
+    if (event->lastFieldPos == 0) event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
+    if (annotationId > MAX_BYTE_VALUE) event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
+    if (event->errors) return;
+
+    append_byte(event, annotationId);
+    append_byte(event, BOOL_TYPE);
+    append_bool(event, value);
+    increment_annotation_count(event);
+}
+
+void stats_event_add_int32_annotation(struct stats_event* event, uint8_t annotationId,
+                                      int32_t value) {
+    if (event->lastFieldPos == 0) event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
+    if (annotationId > MAX_BYTE_VALUE) event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
+    if (event->errors) return;
+
+    append_byte(event, annotationId);
+    append_byte(event, INT32_TYPE);
+    append_int32(event, value);
+    increment_annotation_count(event);
+}
+
+uint32_t stats_event_get_atom_id(struct stats_event* event) {
+    return event->atomId;
+}
+
+uint8_t* stats_event_get_buffer(struct stats_event* event, size_t* size) {
+    if (size) *size = event->size;
+    return event->buf;
+}
+
+uint32_t stats_event_get_errors(struct stats_event* event) {
+    return event->errors;
+}
+
+void stats_event_truncate_buffer(struct stats_event* event, bool truncate) {
+    event->truncate = truncate;
+}
+
+void stats_event_build(struct stats_event* event) {
+    if (event->built) return;
+
+    if (event->atomId == 0) event->errors |= ERROR_NO_ATOM_ID;
+
+    if (event->numElements > MAX_BYTE_VALUE) {
+        event->errors |= ERROR_TOO_MANY_FIELDS;
+    } else {
+        event->buf[POS_NUM_ELEMENTS] = event->numElements;
+    }
+
+    // If there are errors, rewrite buffer.
+    if (event->errors) {
+        event->buf[POS_NUM_ELEMENTS] = 3;
+        event->buf[POS_FIRST_FIELD] = ERROR_TYPE;
+        memcpy(&event->buf[POS_FIRST_FIELD + sizeof(uint8_t)], &event->errors,
+               sizeof(event->errors));
+        event->size = POS_FIRST_FIELD + sizeof(uint8_t) + sizeof(uint32_t);
+    }
+
+    // Truncate the buffer to the appropriate length in order to limit our
+    // memory usage.
+    if (event->truncate) event->buf = (uint8_t*)realloc(event->buf, event->size);
+
+    event->built = true;
+}
+
+int stats_event_write(struct stats_event* event) {
+    stats_event_build(event);
+    return write_buffer_to_statsd(&event->buf, event->size, event->atomId);
+}
+
+struct stats_event_api_table table = {
+        stats_event_obtain,
+        stats_event_build,
+        stats_event_write,
+        stats_event_release,
+        stats_event_set_atom_id,
+        stats_event_write_int32,
+        stats_event_write_int64,
+        stats_event_write_float,
+        stats_event_write_bool,
+        stats_event_write_byte_array,
+        stats_event_write_string8,
+        stats_event_write_attribution_chain,
+        stats_event_write_key_value_pairs,
+        stats_event_add_bool_annotation,
+        stats_event_add_int32_annotation,
+        stats_event_get_atom_id,
+        stats_event_get_buffer,
+        stats_event_get_errors,
+};
diff --git a/libstats/socket/stats_event_list.c b/libstats/socket/stats_event_list.c
new file mode 100644
index 0000000..661a223
--- /dev/null
+++ b/libstats/socket/stats_event_list.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/stats_event_list.h"
+
+#include <string.h>
+#include <sys/time.h>
+#include "stats_buffer_writer.h"
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+    uint32_t tag;
+    unsigned pos;                                    /* Read/write position into buffer */
+    unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements   */
+    unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1];  /* pos for list counter */
+    unsigned list_nest_depth;
+    unsigned len; /* Length or raw buffer. */
+    bool overflow;
+    bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+    enum {
+        kAndroidLoggerRead = 1,
+        kAndroidLoggerWrite = 2,
+    } read_write_flag;
+    uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+// Similar to create_android_logger(), but instead of allocation a new buffer,
+// this function resets the buffer for resuse.
+void reset_log_context(android_log_context ctx) {
+    if (!ctx) {
+        return;
+    }
+    android_log_context_internal* context = (android_log_context_internal*)(ctx);
+    uint32_t tag = context->tag;
+    memset(context, 0, sizeof(android_log_context_internal));
+
+    context->tag = tag;
+    context->read_write_flag = kAndroidLoggerWrite;
+    size_t needed = sizeof(uint8_t) + sizeof(uint8_t);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+    }
+    /* Everything is a list */
+    context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+    context->list[0] = context->pos + 1;
+    context->pos += needed;
+}
+
+int stats_write_list(android_log_context ctx) {
+    android_log_context_internal* context;
+    const char* msg;
+    ssize_t len;
+
+    context = (android_log_context_internal*)(ctx);
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+
+    if (context->list_nest_depth) {
+        return -EIO;
+    }
+
+    /* NB: if there was overflow, then log is truncated. Nothing reported */
+    context->storage[1] = context->count[0];
+    len = context->len = context->pos;
+    msg = (const char*)context->storage;
+    /* it's not a list */
+    if (context->count[0] <= 1) {
+        len -= sizeof(uint8_t) + sizeof(uint8_t);
+        if (len < 0) {
+            len = 0;
+        }
+        msg += sizeof(uint8_t) + sizeof(uint8_t);
+    }
+
+    return write_buffer_to_statsd((void*)msg, len, 0);
+}
+
+int write_to_logger(android_log_context ctx, log_id_t id) {
+    int retValue = 0;
+
+    if (WRITE_TO_LOGD) {
+        retValue = android_log_write_list(ctx, id);
+    }
+
+    if (WRITE_TO_STATSD) {
+        // log_event_list's cast operator is overloaded.
+        int ret = stats_write_list(ctx);
+        // In debugging phase, we may write to both logd and statsd. Prefer to
+        // return statsd socket write error code here.
+        if (ret < 0) {
+            retValue = ret;
+        }
+    }
+
+    return retValue;
+}
+
+static inline void copy4LE(uint8_t* buf, uint32_t val) {
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+}
+
+// Note: this function differs from android_log_write_string8_len in that the length passed in
+// should be treated as actual length and not max length.
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t actual_len) {
+    size_t needed;
+    ssize_t len = actual_len;
+    android_log_context_internal* context;
+
+    context = (android_log_context_internal*)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    if (!value) {
+        value = "";
+        len = 0;
+    }
+    needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        /* Truncate string for delivery */
+        len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+        if (len <= 0) {
+            context->overflow = true;
+            return -EIO;
+        }
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+    copy4LE(&context->storage[context->pos + 1], len);
+    if (len) {
+        memcpy(&context->storage[context->pos + 5], value, len);
+    }
+    context->pos += needed;
+    return len;
+}
diff --git a/libstats/statsd_writer.c b/libstats/socket/statsd_writer.c
similarity index 98%
copy from libstats/statsd_writer.c
copy to libstats/socket/statsd_writer.c
index 073b67f..04d3b46 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/socket/statsd_writer.c
@@ -101,7 +101,7 @@
             strcpy(un.sun_path, "/dev/socket/statsdw");
 
             if (TEMP_FAILURE_RETRY(
-                    connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
+                        connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
                 ret = -errno;
                 switch (ret) {
                     case -ENOTCONN:
diff --git a/libstats/statsd_writer.h b/libstats/socket/statsd_writer.h
similarity index 100%
rename from libstats/statsd_writer.h
rename to libstats/socket/statsd_writer.h
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
new file mode 100644
index 0000000..cf0592c
--- /dev/null
+++ b/libstats/socket/tests/stats_event_test.cpp
@@ -0,0 +1,344 @@
+/*
+ * 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 "stats_event.h"
+#include <gtest/gtest.h>
+#include <utils/SystemClock.h>
+
+using std::string;
+using std::vector;
+
+// Side-effect: this function moves the start of the buffer past the read value
+template <class T>
+T readNext(uint8_t** buffer) {
+    T value = *(T*)(*buffer);
+    *buffer += sizeof(T);
+    return value;
+}
+
+void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
+    uint8_t typeHeader = (numAnnotations << 4) | typeId;
+    EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
+}
+
+template <class T>
+void checkScalar(uint8_t** buffer, T expectedValue) {
+    EXPECT_EQ(readNext<T>(buffer), expectedValue);
+}
+
+void checkString(uint8_t** buffer, const string& expectedString) {
+    uint32_t size = readNext<uint32_t>(buffer);
+    string parsedString((char*)(*buffer), size);
+    EXPECT_EQ(parsedString, expectedString);
+    *buffer += size;  // move buffer past string we just read
+}
+
+void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
+    uint32_t size = readNext<uint32_t>(buffer);
+    vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
+    EXPECT_EQ(parsedByteArray, expectedByteArray);
+    *buffer += size;  // move buffer past byte array we just read
+}
+
+template <class T>
+void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
+    EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
+    EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
+    checkScalar<T>(buffer, annotationValue);
+}
+
+void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
+                   uint32_t atomId) {
+    // All events start with OBJECT_TYPE id.
+    checkTypeHeader(buffer, OBJECT_TYPE);
+
+    // We increment by 2 because the number of elements listed in the
+    // serialization accounts for the timestamp and atom id as well.
+    checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
+
+    // Check timestamp
+    checkTypeHeader(buffer, INT64_TYPE);
+    int64_t timestamp = readNext<int64_t>(buffer);
+    EXPECT_GE(timestamp, startTime);
+    EXPECT_LE(timestamp, endTime);
+
+    // Check atom id
+    checkTypeHeader(buffer, INT32_TYPE);
+    checkScalar(buffer, atomId);
+}
+
+TEST(StatsEventTest, TestScalars) {
+    uint32_t atomId = 100;
+    int32_t int32Value = -5;
+    int64_t int64Value = -2 * android::elapsedRealtimeNano();
+    float floatValue = 2.0;
+    bool boolValue = false;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_int32(event, int32Value);
+    stats_event_write_int64(event, int64Value);
+    stats_event_write_float(event, floatValue);
+    stats_event_write_bool(event, boolValue);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
+
+    // check int32 element
+    checkTypeHeader(&buffer, INT32_TYPE);
+    checkScalar(&buffer, int32Value);
+
+    // check int64 element
+    checkTypeHeader(&buffer, INT64_TYPE);
+    checkScalar(&buffer, int64Value);
+
+    // check float element
+    checkTypeHeader(&buffer, FLOAT_TYPE);
+    checkScalar(&buffer, floatValue);
+
+    // check bool element
+    checkTypeHeader(&buffer, BOOL_TYPE);
+    checkScalar(&buffer, boolValue);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestStrings) {
+    uint32_t atomId = 100;
+    string str = "test_string";
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_string8(event, str.c_str());
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, STRING_TYPE);
+    checkString(&buffer, str);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestByteArrays) {
+    uint32_t atomId = 100;
+    vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_byte_array(event, message.data(), message.size());
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
+    checkByteArray(&buffer, message);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestAttributionChains) {
+    uint32_t atomId = 100;
+
+    uint8_t numNodes = 50;
+    uint32_t uids[numNodes];
+    vector<string> tags(numNodes);  // storage that cTag elements point to
+    const char* cTags[numNodes];
+    for (int i = 0; i < (int)numNodes; i++) {
+        uids[i] = i;
+        tags.push_back("test" + std::to_string(i));
+        cTags[i] = tags[i].c_str();
+    }
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_attribution_chain(event, uids, cTags, numNodes);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
+    checkScalar(&buffer, numNodes);
+    for (int i = 0; i < numNodes; i++) {
+        checkScalar(&buffer, uids[i]);
+        checkString(&buffer, tags[i]);
+    }
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestKeyValuePairs) {
+    uint32_t atomId = 100;
+
+    uint8_t numPairs = 4;
+    struct key_value_pair pairs[numPairs];
+    pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = -1};
+    pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
+    pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 5.5};
+    string str = "test_key_value_pair_string";
+    pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_key_value_pairs(event, pairs, numPairs);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, KEY_VALUE_PAIRS_TYPE);
+    checkScalar(&buffer, numPairs);
+
+    // first pair
+    checkScalar(&buffer, pairs[0].key);
+    checkTypeHeader(&buffer, pairs[0].valueType);
+    checkScalar(&buffer, pairs[0].int32Value);
+
+    // second pair
+    checkScalar(&buffer, pairs[1].key);
+    checkTypeHeader(&buffer, pairs[1].valueType);
+    checkScalar(&buffer, pairs[1].int64Value);
+
+    // third pair
+    checkScalar(&buffer, pairs[2].key);
+    checkTypeHeader(&buffer, pairs[2].valueType);
+    checkScalar(&buffer, pairs[2].floatValue);
+
+    // fourth pair
+    checkScalar(&buffer, pairs[3].key);
+    checkTypeHeader(&buffer, pairs[3].valueType);
+    checkString(&buffer, str);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestAnnotations) {
+    uint32_t atomId = 100;
+
+    // first element information
+    bool boolValue = false;
+    uint8_t boolAnnotation1Id = 1;
+    uint8_t boolAnnotation2Id = 2;
+    bool boolAnnotation1Value = true;
+    int32_t boolAnnotation2Value = 3;
+
+    // second element information
+    float floatValue = -5.0;
+    uint8_t floatAnnotation1Id = 3;
+    uint8_t floatAnnotation2Id = 4;
+    int32_t floatAnnotation1Value = 8;
+    bool floatAnnotation2Value = false;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, 100);
+    stats_event_write_bool(event, boolValue);
+    stats_event_add_bool_annotation(event, boolAnnotation1Id, boolAnnotation1Value);
+    stats_event_add_int32_annotation(event, boolAnnotation2Id, boolAnnotation2Value);
+    stats_event_write_float(event, floatValue);
+    stats_event_add_int32_annotation(event, floatAnnotation1Id, floatAnnotation1Value);
+    stats_event_add_bool_annotation(event, floatAnnotation2Id, floatAnnotation2Value);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
+
+    // check first element
+    checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
+    checkScalar(&buffer, boolValue);
+    checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
+    checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
+
+    // check second element
+    checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
+    checkScalar(&buffer, floatValue);
+    checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
+    checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestNoAtomIdError) {
+    struct stats_event* event = stats_event_obtain();
+    // Don't set the atom id in order to trigger the error.
+    stats_event_build(event);
+
+    uint32_t errors = stats_event_get_errors(event);
+    EXPECT_NE(errors | ERROR_NO_ATOM_ID, 0);
+
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestOverflowError) {
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, 100);
+    // Add 1000 int32s to the event. Each int32 takes 5 bytes so this will
+    // overflow the 4068 byte buffer.
+    for (int i = 0; i < 1000; i++) {
+        stats_event_write_int32(event, 0);
+    }
+    stats_event_build(event);
+
+    uint32_t errors = stats_event_get_errors(event);
+    EXPECT_NE(errors | ERROR_OVERFLOW, 0);
+
+    stats_event_release(event);
+}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 9cd3d65..3695f72 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -240,6 +240,7 @@
         "tests/files/offline/debug_frame_load_bias_arm/*",
         "tests/files/offline/eh_frame_bias_x86/*",
         "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
+        "tests/files/offline/empty_arm64/*",
         "tests/files/offline/invalid_elf_offset_arm/*",
         "tests/files/offline/jit_debug_arm/*",
         "tests/files/offline/jit_debug_x86/*",
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 7676289..341275d 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -78,10 +78,31 @@
   CrcGenerateTable();
   Crc64GenerateTable();
 
-  std::vector<uint8_t> src(gnu_debugdata_size_);
-  if (!memory_->ReadFully(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
-    gnu_debugdata_offset_ = 0;
-    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
+  // Verify the request is not larger than the max size_t value.
+  if (gnu_debugdata_size_ > SIZE_MAX) {
+    return nullptr;
+  }
+  size_t initial_buffer_size;
+  if (__builtin_mul_overflow(5, gnu_debugdata_size_, &initial_buffer_size)) {
+    return nullptr;
+  }
+
+  size_t buffer_increment;
+  if (__builtin_mul_overflow(2, gnu_debugdata_size_, &buffer_increment)) {
+    return nullptr;
+  }
+
+  std::unique_ptr<uint8_t[]> src(new (std::nothrow) uint8_t[gnu_debugdata_size_]);
+  if (src.get() == nullptr) {
+    return nullptr;
+  }
+
+  std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
+  if (!dst->Resize(initial_buffer_size)) {
+    return nullptr;
+  }
+
+  if (!memory_->ReadFully(gnu_debugdata_offset_, src.get(), gnu_debugdata_size_)) {
     return nullptr;
   }
 
@@ -89,21 +110,23 @@
   CXzUnpacker state;
   alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
   alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
-
   XzUnpacker_Construct(&state, &alloc);
 
-  std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
   int return_val;
   size_t src_offset = 0;
   size_t dst_offset = 0;
   ECoderStatus status;
-  dst->Resize(5 * gnu_debugdata_size_);
   do {
-    size_t src_remaining = src.size() - src_offset;
+    size_t src_remaining = gnu_debugdata_size_ - src_offset;
     size_t dst_remaining = dst->Size() - dst_offset;
-    if (dst_remaining < 2 * gnu_debugdata_size_) {
-      dst->Resize(dst->Size() + 2 * gnu_debugdata_size_);
-      dst_remaining += 2 * gnu_debugdata_size_;
+    if (dst_remaining < buffer_increment) {
+      size_t new_size;
+      if (__builtin_add_overflow(dst->Size(), buffer_increment, &new_size) ||
+          !dst->Resize(new_size)) {
+        XzUnpacker_Free(&state);
+        return nullptr;
+      }
+      dst_remaining += buffer_increment;
     }
     return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
                                  &src_remaining, true, CODER_FINISH_ANY, &status);
@@ -112,13 +135,13 @@
   } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
   XzUnpacker_Free(&state);
   if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
-    gnu_debugdata_offset_ = 0;
-    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
     return nullptr;
   }
 
   // Shrink back down to the exact size.
-  dst->Resize(dst_offset);
+  if (!dst->Resize(dst_offset)) {
+    return nullptr;
+  }
 
   return dst.release();
 }
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index ec977e1..ee6c8a5 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -70,30 +70,28 @@
   // This also works:
   //   f0000-f2000 0 r-- /system/lib/libc.so
   //   f2000-f3000 2000 rw- /system/lib/libc.so
-  MapInfo* map_start = nullptr;
+  // It is also possible to see empty maps after the read-only like so:
+  //   f0000-f1000 0 r-- /system/lib/libc.so
+  //   f1000-f2000 0 ---
+  //   f2000-f3000 1000 r-x /system/lib/libc.so
+  //   f3000-f4000 2000 rw- /system/lib/libc.so
+  MapInfo* map_zero = nullptr;
   for (const auto& info : *maps) {
-    if (map_start != nullptr && map_start->name == info->name) {
-      if (info->offset != 0 &&
-          (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
-        Elf* elf = map_start->GetElf(memory_, arch());
-        uint64_t ptr;
-        if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
-          uint64_t offset_end = info->offset + info->end - info->start;
-          if (ptr >= info->offset && ptr < offset_end) {
-            ptr = info->start + ptr - info->offset;
-            if (ReadVariableData(ptr)) {
-              break;
-            }
+    if (info->offset != 0 && (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
+        map_zero != nullptr && Searchable(info->name) && info->name == map_zero->name) {
+      Elf* elf = map_zero->GetElf(memory_, arch());
+      uint64_t ptr;
+      if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
+        uint64_t offset_end = info->offset + info->end - info->start;
+        if (ptr >= info->offset && ptr < offset_end) {
+          ptr = info->start + ptr - info->offset;
+          if (ReadVariableData(ptr)) {
+            break;
           }
         }
-        map_start = nullptr;
       }
-    } else {
-      map_start = nullptr;
-    }
-    if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
-        Searchable(info->name)) {
-      map_start = info.get();
+    } else if (info->offset == 0 && !info->name.empty()) {
+      map_zero = info.get();
     }
   }
 }
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index f2dad84..31f3144 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -37,12 +37,12 @@
 bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
   // One last attempt, see if the previous map is read-only with the
   // same name and stretches across this map.
-  if (prev_map == nullptr || prev_map->flags != PROT_READ) {
+  if (prev_real_map == nullptr || prev_real_map->flags != PROT_READ) {
     return false;
   }
 
-  uint64_t map_size = end - prev_map->end;
-  if (!memory->Init(name, prev_map->offset, map_size)) {
+  uint64_t map_size = end - prev_real_map->end;
+  if (!memory->Init(name, prev_real_map->offset, map_size)) {
     return false;
   }
 
@@ -51,12 +51,12 @@
     return false;
   }
 
-  if (!memory->Init(name, prev_map->offset, max_size)) {
+  if (!memory->Init(name, prev_real_map->offset, max_size)) {
     return false;
   }
 
-  elf_offset = offset - prev_map->offset;
-  elf_start_offset = prev_map->offset;
+  elf_offset = offset - prev_real_map->offset;
+  elf_start_offset = prev_real_map->offset;
   return true;
 }
 
@@ -112,8 +112,8 @@
     // Need to check how to set the elf start offset. If this map is not
     // the r-x map of a r-- map, then use the real offset value. Otherwise,
     // use 0.
-    if (prev_map == nullptr || prev_map->offset != 0 || prev_map->flags != PROT_READ ||
-        prev_map->name != name) {
+    if (prev_real_map == nullptr || prev_real_map->offset != 0 ||
+        prev_real_map->flags != PROT_READ || prev_real_map->name != name) {
       elf_start_offset = offset;
     }
     return memory.release();
@@ -172,20 +172,20 @@
   // doesn't guarantee that this invariant will always be true. However,
   // if that changes, there is likely something else that will change and
   // break something.
-  if (offset == 0 || name.empty() || prev_map == nullptr || prev_map->name != name ||
-      prev_map->offset >= offset) {
+  if (offset == 0 || name.empty() || prev_real_map == nullptr || prev_real_map->name != name ||
+      prev_real_map->offset >= offset) {
     return nullptr;
   }
 
   // Make sure that relative pc values are corrected properly.
-  elf_offset = offset - prev_map->offset;
+  elf_offset = offset - prev_real_map->offset;
   // Use this as the elf start offset, otherwise, you always get offsets into
   // the r-x section, which is not quite the right information.
-  elf_start_offset = prev_map->offset;
+  elf_start_offset = prev_real_map->offset;
 
   MemoryRanges* ranges = new MemoryRanges;
-  ranges->Insert(
-      new MemoryRange(process_memory, prev_map->start, prev_map->end - prev_map->start, 0));
+  ranges->Insert(new MemoryRange(process_memory, prev_real_map->start,
+                                 prev_real_map->end - prev_real_map->start, 0));
   ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
 
   memory_backed_elf = true;
@@ -236,15 +236,15 @@
 
   if (!elf->valid()) {
     elf_start_offset = offset;
-  } else if (prev_map != nullptr && elf_start_offset != offset &&
-             prev_map->offset == elf_start_offset && prev_map->name == name) {
+  } else if (prev_real_map != nullptr && elf_start_offset != offset &&
+             prev_real_map->offset == elf_start_offset && prev_real_map->name == name) {
     // If there is a read-only map then a read-execute map that represents the
     // same elf object, make sure the previous map is using the same elf
     // object if it hasn't already been set.
-    std::lock_guard<std::mutex> guard(prev_map->mutex_);
-    if (prev_map->elf.get() == nullptr) {
-      prev_map->elf = elf;
-      prev_map->memory_backed_elf = memory_backed_elf;
+    std::lock_guard<std::mutex> guard(prev_real_map->mutex_);
+    if (prev_real_map->elf.get() == nullptr) {
+      prev_real_map->elf = elf;
+      prev_real_map->memory_backed_elf = memory_backed_elf;
     }
   }
   return elf.get();
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 0ab68db..8f49ad9 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -60,6 +60,8 @@
 }
 
 bool Maps::Parse() {
+  MapInfo* prev_map = nullptr;
+  MapInfo* prev_real_map = nullptr;
   return android::procinfo::ReadMapFile(
       GetMapsFile(),
       [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
@@ -67,17 +69,24 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.emplace_back(
-            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
-                        flags, name));
+        maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+        prev_map = maps_.back().get();
+        if (!prev_map->IsBlank()) {
+          prev_real_map = prev_map;
+        }
       });
 }
 
 void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
                const std::string& name, uint64_t load_bias) {
+  MapInfo* prev_map = maps_.empty() ? nullptr : maps_.back().get();
+  MapInfo* prev_real_map = prev_map;
+  while (prev_real_map != nullptr && prev_real_map->IsBlank()) {
+    prev_real_map = prev_real_map->prev_map;
+  }
+
   auto map_info =
-      std::make_unique<MapInfo>(maps_.empty() ? nullptr : maps_.back().get(), start, end, offset,
-                                flags, name);
+      std::make_unique<MapInfo>(prev_map, prev_real_map, start, end, offset, flags, name);
   map_info->load_bias = load_bias;
   maps_.emplace_back(std::move(map_info));
 }
@@ -89,14 +98,21 @@
 
   // Set the prev_map values on the info objects.
   MapInfo* prev_map = nullptr;
+  MapInfo* prev_real_map = nullptr;
   for (const auto& map_info : maps_) {
     map_info->prev_map = prev_map;
+    map_info->prev_real_map = prev_real_map;
     prev_map = map_info.get();
+    if (!prev_map->IsBlank()) {
+      prev_real_map = prev_map;
+    }
   }
 }
 
 bool BufferMaps::Parse() {
   std::string content(buffer_);
+  MapInfo* prev_map = nullptr;
+  MapInfo* prev_real_map = nullptr;
   return android::procinfo::ReadMapFileContent(
       &content[0],
       [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
@@ -104,9 +120,11 @@
         if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
           flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
         }
-        maps_.emplace_back(
-            new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
-                        flags, name));
+        maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+        prev_map = maps_.back().get();
+        if (!prev_map->IsBlank()) {
+          prev_real_map = prev_map;
+        }
       });
 }
 
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index a66cd5b..8de3d98 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -206,12 +206,12 @@
 }
 
 size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
-  if (addr >= raw_.size()) {
+  if (addr >= size_) {
     return 0;
   }
 
-  size_t bytes_left = raw_.size() - static_cast<size_t>(addr);
-  const unsigned char* actual_base = static_cast<const unsigned char*>(raw_.data()) + addr;
+  size_t bytes_left = size_ - static_cast<size_t>(addr);
+  const unsigned char* actual_base = static_cast<const unsigned char*>(raw_) + addr;
   size_t actual_len = std::min(bytes_left, size);
 
   memcpy(dst, actual_base, actual_len);
@@ -219,7 +219,7 @@
 }
 
 uint8_t* MemoryBuffer::GetPtr(size_t offset) {
-  if (offset < raw_.size()) {
+  if (offset < size_) {
     return &raw_[offset];
   }
   return nullptr;
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
index 3fe4bbb..a91e59f 100644
--- a/libunwindstack/MemoryBuffer.h
+++ b/libunwindstack/MemoryBuffer.h
@@ -29,18 +29,27 @@
 class MemoryBuffer : public Memory {
  public:
   MemoryBuffer() = default;
-  virtual ~MemoryBuffer() = default;
+  virtual ~MemoryBuffer() { free(raw_); }
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
 
   uint8_t* GetPtr(size_t offset);
 
-  void Resize(size_t size) { raw_.resize(size); }
+  bool Resize(size_t size) {
+    raw_ = reinterpret_cast<uint8_t*>(realloc(raw_, size));
+    if (raw_ == nullptr) {
+      size_ = 0;
+      return false;
+    }
+    size_ = size;
+    return true;
+  }
 
-  uint64_t Size() { return raw_.size(); }
+  uint64_t Size() { return size_; }
 
  private:
-  std::vector<uint8_t> raw_;
+  uint8_t* raw_ = nullptr;
+  size_t size_ = 0;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index 1df1dff..00b3367 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -100,8 +100,8 @@
   fn("x27", regs_[ARM64_REG_R27]);
   fn("x28", regs_[ARM64_REG_R28]);
   fn("x29", regs_[ARM64_REG_R29]);
-  fn("sp", regs_[ARM64_REG_SP]);
   fn("lr", regs_[ARM64_REG_LR]);
+  fn("sp", regs_[ARM64_REG_SP]);
   fn("pc", regs_[ARM64_REG_PC]);
   fn("pst", regs_[ARM64_REG_PSTATE]);
 }
@@ -110,10 +110,10 @@
   arm64_user_regs* user = reinterpret_cast<arm64_user_regs*>(remote_data);
 
   RegsArm64* regs = new RegsArm64();
-  memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
+  memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R30 + 1) * sizeof(uint64_t));
   uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
-  reg_data[ARM64_REG_PC] = user->pc;
   reg_data[ARM64_REG_SP] = user->sp;
+  reg_data[ARM64_REG_PC] = user->pc;
   reg_data[ARM64_REG_PSTATE] = user->pstate;
   return regs;
 }
diff --git a/libunwindstack/TEST_MAPPING b/libunwindstack/TEST_MAPPING
index 55771c0..909f897 100644
--- a/libunwindstack/TEST_MAPPING
+++ b/libunwindstack/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "libunwindstack_unit_test"
+    },
+    {
+      "name": "CtsSimpleperfTestCases"
     }
   ]
 }
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 8f0c516..052e79f 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -31,24 +31,26 @@
 class MemoryFileAtOffset;
 
 struct MapInfo {
-  MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
-          const char* name)
+  MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+          uint64_t flags, const char* name)
       : start(start),
         end(end),
         offset(offset),
         flags(flags),
         name(name),
-        prev_map(map_info),
+        prev_map(prev_map),
+        prev_real_map(prev_real_map),
         load_bias(INT64_MAX),
         build_id(0) {}
-  MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
-          const std::string& name)
+  MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+          uint64_t flags, const std::string& name)
       : start(start),
         end(end),
         offset(offset),
         flags(flags),
         name(name),
-        prev_map(map_info),
+        prev_map(prev_map),
+        prev_real_map(prev_real_map),
         load_bias(INT64_MAX),
         build_id(0) {}
   ~MapInfo();
@@ -71,6 +73,14 @@
   uint64_t elf_start_offset = 0;
 
   MapInfo* prev_map = nullptr;
+  // This is the previous map that is not empty with a 0 offset. For
+  // example, this set of maps:
+  //  1000-2000  r--p 000000 00:00 0 libc.so
+  //  2000-3000  ---p 000000 00:00 0 libc.so
+  //  3000-4000  r-xp 003000 00:00 0 libc.so
+  // The last map's prev_map would point to the 2000-3000 map, while the
+  // prev_real_map would point to the 1000-2000 map.
+  MapInfo* prev_real_map = nullptr;
 
   std::atomic_int64_t load_bias;
 
@@ -97,6 +107,8 @@
   // Returns the printable version of the build id (hex dump of raw data).
   std::string GetPrintableBuildID();
 
+  inline bool IsBlank() { return offset == 0 && flags == 0 && name.empty(); }
+
  private:
   MapInfo(const MapInfo&) = delete;
   void operator=(const MapInfo&) = delete;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 0149a42..1b54da6 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -105,7 +105,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
+  MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
   EXPECT_TRUE(DexFile::Create(0x500, &memory, &info) != nullptr);
 }
 
@@ -118,7 +118,7 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
   EXPECT_TRUE(DexFile::Create(0x600, &memory, &info) != nullptr);
 }
 
@@ -131,21 +131,21 @@
             static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
 
   MemoryFake memory;
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
   EXPECT_TRUE(DexFile::Create(0x400, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_empty_file) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
 TEST(DexFileTest, create_using_memory_file_does_not_exist) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
   EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
 }
 
@@ -158,7 +158,7 @@
 
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+  MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
   std::unique_ptr<DexFile> dex_file = DexFile::Create(0x4000, &memory, &info);
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -171,7 +171,7 @@
 TEST(DexFileTest, get_method) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
@@ -189,7 +189,7 @@
 TEST(DexFileTest, get_method_empty) {
   MemoryFake memory;
   memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
-  MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+  MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
   std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
   ASSERT_TRUE(dex_file != nullptr);
 
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 0dd3af6..477cf8e 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -64,7 +64,11 @@
                        "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
                        "100000-110000 rw-p 00f1000 00:00 0 /fake/elf3\n"
                        "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
-                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
+                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"
+                       "500000-501000 r--p 0000000 00:00 0 /fake/elf4\n"
+                       "501000-502000 ---p 0000000 00:00 0\n"
+                       "503000-510000 rw-p 0003000 00:00 0 /fake/elf4\n"
+                       "510000-520000 rw-p 0010000 00:00 0 /fake/elf4\n"));
     ASSERT_TRUE(maps_->Parse());
 
     // Global variable in a section that is not readable.
@@ -81,6 +85,11 @@
     map_info = maps_->Get(kMapGlobal);
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
+
+    // Global variable set in this map, but there is an empty map before rw map.
+    map_info = maps_->Get(kMapGlobalAfterEmpty);
+    ASSERT_TRUE(map_info != nullptr);
+    CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
   }
 
   void SetUp() override {
@@ -102,6 +111,8 @@
   static constexpr size_t kMapGlobalRw = 6;
   static constexpr size_t kMapDexFileEntries = 7;
   static constexpr size_t kMapDexFiles = 8;
+  static constexpr size_t kMapGlobalAfterEmpty = 9;
+  static constexpr size_t kMapDexFilesAfterEmpty = 12;
 
   std::shared_ptr<Memory> process_memory_;
   MemoryFake* memory_;
@@ -328,4 +339,18 @@
   EXPECT_EQ(0x123U, method_offset);
 }
 
+TEST_F(DexFilesTest, get_method_information_with_empty_map) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFilesAfterEmpty);
+
+  WriteDescriptor32(0x503800, 0x506000);
+  WriteEntry32(0x506000, 0, 0, 0x510000);
+  WriteDex(0x510000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x510100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 5735858..5f13546 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -78,8 +78,8 @@
 
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
-  MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
-  MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
 
   Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf1->valid());
@@ -119,17 +119,17 @@
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
   // Will have an elf at offset 0 in file.
-  MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
-  MapInfo info0_2(nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info0_1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+  MapInfo info0_2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
   // Will have an elf at offset 0x100 in file.
-  MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
-  MapInfo info100_2(nullptr, start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_1(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_2(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
   // Will have an elf at offset 0x200 in file.
-  MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
-  MapInfo info200_2(nullptr, start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_1(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_2(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
   // Will have an elf at offset 0 in file.
-  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
 
   Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf0_1->valid());
@@ -216,10 +216,10 @@
   uint64_t start = 0x1000;
   uint64_t end = 0x20000;
   // Multiple info sections at different offsets will have non-zero elf offsets.
-  MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
-  MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
-  MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
+  MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+  MapInfo info400_1(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
+  MapInfo info400_2(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
 
   Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
   ASSERT_TRUE(elf300_1->valid());
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index c33908d..fc90dab 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -105,6 +105,9 @@
   void FakeSetDynamicVaddrStart(uint64_t vaddr) { dynamic_vaddr_start_ = vaddr; }
   void FakeSetDynamicVaddrEnd(uint64_t vaddr) { dynamic_vaddr_end_ = vaddr; }
 
+  void FakeSetGnuDebugdataOffset(uint64_t offset) { gnu_debugdata_offset_ = offset; }
+  void FakeSetGnuDebugdataSize(uint64_t size) { gnu_debugdata_size_ = size; }
+
  private:
   std::unordered_map<std::string, uint64_t> globals_;
   std::string fake_build_id_;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index ea27e3e..3cf90fe 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -1944,4 +1944,23 @@
   CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, -0x4000);
 }
 
+TEST_F(ElfInterfaceTest, huge_gnu_debugdata_size) {
+  ElfInterfaceFake interface(nullptr);
+
+  interface.FakeSetGnuDebugdataOffset(0x1000);
+  interface.FakeSetGnuDebugdataSize(0xffffffffffffffffUL);
+  ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+
+  interface.FakeSetGnuDebugdataSize(0x4000000000000UL);
+  ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+
+  // This should exceed the size_t value of the first allocation.
+#if defined(__LP64__)
+  interface.FakeSetGnuDebugdataSize(0x3333333333333334ULL);
+#else
+  interface.FakeSetGnuDebugdataSize(0x33333334);
+#endif
+  ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 4866345..1f3ed81 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -275,7 +275,7 @@
   elf.FakeSetInterface(interface);
 
   elf.FakeSetValid(true);
-  MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
+  MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
 
   ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
 
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 6c1cfa2..6d8d58e 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -89,7 +89,7 @@
 };
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
-  MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x100, 0, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
@@ -108,7 +108,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -129,8 +129,9 @@
 
   // Now verify the elf start offset is set correctly based on the previous
   // info.
-  MapInfo prev_info(nullptr, 0, 0x100, 0x10, 0, "");
+  MapInfo prev_info(nullptr, nullptr, 0, 0x100, 0x10, 0, "");
   info.prev_map = &prev_info;
+  info.prev_real_map = &prev_info;
 
   // No preconditions met, change each one until it should set the elf start
   // offset to zero.
@@ -177,7 +178,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
+  MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -202,7 +203,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+  MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -220,7 +221,7 @@
 }
 
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
@@ -243,14 +244,14 @@
   // be returned if the file mapping fails, but the device check is incorrect.
   std::vector<uint8_t> buffer(1024);
   uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
-  MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
+  MapInfo info(nullptr, nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
 
   std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 }
 
 TEST_F(MapInfoCreateMemoryTest, process_memory) {
-  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr = {};
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
index 16451d1..6953e26 100644
--- a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -50,7 +50,8 @@
     elf_interface_ = new ElfInterfaceFake(memory_);
     elf_->FakeSetInterface(elf_interface_);
     elf_container_.reset(elf_);
-    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+    map_info_.reset(
+        new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
   }
 
   void MultipleThreadTest(std::string expected_build_id);
@@ -64,7 +65,7 @@
 };
 
 TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   EXPECT_EQ("", info.GetBuildID());
   EXPECT_EQ("", info.GetPrintableBuildID());
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index d60b8b1..7f97814 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -68,7 +68,7 @@
 };
 
 TEST_F(MapInfoGetElfTest, invalid) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   // The map is empty, but this should still create an invalid elf object.
   Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
@@ -77,7 +77,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, valid32) {
-  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -97,7 +97,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
-  MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
 
   Elf64_Ehdr ehdr;
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
@@ -111,7 +111,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
-  MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -123,7 +123,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
-  MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -139,7 +139,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
-  MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
 
   TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -155,7 +155,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, end_le_start) {
-  MapInfo info(nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -182,7 +182,7 @@
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x1000);
   memset(buffer.data(), 0, buffer.size());
@@ -211,7 +211,7 @@
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -241,7 +241,7 @@
 // embedded elf is bigger than the initial map, the new object is larger
 // than the original map size. Do this for a 32 bit elf and a 64 bit elf.
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
-  MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -269,7 +269,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
-  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
 
   std::vector<uint8_t> buffer(0x4000);
   memset(buffer.data(), 0, buffer.size());
@@ -297,7 +297,7 @@
 }
 
 TEST_F(MapInfoGetElfTest, check_device_maps) {
-  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
                "/dev/something");
 
   // Create valid elf data in process memory for this to verify that only
@@ -343,7 +343,7 @@
   wait = true;
   // Create all of the threads and have them do the GetElf at the same time
   // to make it likely that a race will occur.
-  MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
   for (size_t i = 0; i < kNumConcurrentThreads; i++) {
     std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
       while (wait)
@@ -373,8 +373,8 @@
 
 // Verify that previous maps don't automatically get the same elf object.
 TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
-  MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
-  MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+  MapInfo info1(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+  MapInfo info2(&info1, &info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -389,8 +389,25 @@
 // Verify that a read-only map followed by a read-execute map will result
 // in the same elf object in both maps.
 TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
-  MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
-  MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+  MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+  MapInfo rw_info(&r_info, &r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+  Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+
+  ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by an empty map, then followed by
+// a read-execute map will result in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_empty_then_read_exec_share_elf) {
+  MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+  MapInfo empty(&r_info, &r_info, 0x2000, 0x3000, 0, 0, "");
+  MapInfo rw_info(&empty, &r_info, 0x3000, 0x4000, 0x2000, PROT_READ | PROT_EXEC, elf_.path);
 
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index da3dbf2..971d452 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@
     process_memory_.reset(memory_);
     elf_ = new ElfFake(new MemoryFake);
     elf_container_.reset(elf_);
-    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+    map_info_.reset(new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
   }
 
   void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@
 };
 
 TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
-  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+  MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
 
   EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
 }
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
index ef76b1b..98edc0e 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -26,8 +26,8 @@
 namespace unwindstack {
 
 TEST(MapInfoTest, maps_constructor_const_char) {
-  MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
-  MapInfo map_info(&prev_map, 1, 2, 3, 4, "map");
+  MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
+  MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, "map");
 
   EXPECT_EQ(&prev_map, map_info.prev_map);
   EXPECT_EQ(1UL, map_info.start);
@@ -42,8 +42,8 @@
 
 TEST(MapInfoTest, maps_constructor_string) {
   std::string name("string_map");
-  MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
-  MapInfo map_info(&prev_map, 1, 2, 3, 4, name);
+  MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
+  MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, name);
 
   EXPECT_EQ(&prev_map, map_info.prev_map);
   EXPECT_EQ(1UL, map_info.start);
@@ -62,7 +62,7 @@
   elf->FakeSetInterface(interface);
   interface->FakePushFunctionData(FunctionData("function", 1000));
 
-  MapInfo map_info(nullptr, 1, 2, 3, 4, "");
+  MapInfo map_info(nullptr, nullptr, 1, 2, 3, 4, "");
   map_info.elf.reset(elf);
 
   std::string name;
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9e7a6ab..724eeb5 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -82,7 +82,7 @@
 }
 
 TEST(MapsTest, verify_parse_line) {
-  MapInfo info(nullptr, 0, 0, 0, 0, "");
+  MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
 
   VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
   EXPECT_EQ(1U, info.start);
@@ -155,7 +155,7 @@
 }
 
 TEST(MapsTest, verify_large_values) {
-  MapInfo info(nullptr, 0, 0, 0, 0, "");
+  MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
 #if defined(__LP64__)
   VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
   EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index bc95851..47e605a 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -111,8 +111,8 @@
   result.push_back({"x27", ARM64_REG_R27});
   result.push_back({"x28", ARM64_REG_R28});
   result.push_back({"x29", ARM64_REG_R29});
-  result.push_back({"sp", ARM64_REG_SP});
   result.push_back({"lr", ARM64_REG_LR});
+  result.push_back({"sp", ARM64_REG_SP});
   result.push_back({"pc", ARM64_REG_PC});
   result.push_back({"pst", ARM64_REG_PSTATE});
   return result;
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 472d1cf..0a33e2f 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
   RegsX86_64 regs_x86_64;
   RegsMips regs_mips;
   RegsMips64 regs_mips64;
-  MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
+  MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
   Elf* invalid_elf = new Elf(nullptr);
   map_info.elf.reset(invalid_elf);
 
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 364101a..c2bd836 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -169,15 +169,18 @@
 };
 
 std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
-    {"x0", ARM64_REG_R0},   {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},   {"x3", ARM64_REG_R3},
-    {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},   {"x6", ARM64_REG_R6},   {"x7", ARM64_REG_R7},
-    {"x8", ARM64_REG_R8},   {"x9", ARM64_REG_R9},   {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
-    {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14}, {"x15", ARM64_REG_R15},
-    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17}, {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19},
-    {"x20", ARM64_REG_R20}, {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
-    {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26}, {"x27", ARM64_REG_R27},
-    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29}, {"sp", ARM64_REG_SP},   {"lr", ARM64_REG_LR},
-    {"pc", ARM64_REG_PC},
+    {"x0", ARM64_REG_R0},      {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},
+    {"x3", ARM64_REG_R3},      {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},
+    {"x6", ARM64_REG_R6},      {"x7", ARM64_REG_R7},   {"x8", ARM64_REG_R8},
+    {"x9", ARM64_REG_R9},      {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+    {"x12", ARM64_REG_R12},    {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
+    {"x15", ARM64_REG_R15},    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
+    {"x18", ARM64_REG_R18},    {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
+    {"x21", ARM64_REG_R21},    {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+    {"x24", ARM64_REG_R24},    {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
+    {"x27", ARM64_REG_R27},    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
+    {"sp", ARM64_REG_SP},      {"lr", ARM64_REG_LR},   {"pc", ARM64_REG_PC},
+    {"pst", ARM64_REG_PSTATE},
 };
 
 std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
@@ -1697,4 +1700,40 @@
   EXPECT_EQ(0xffe67d10ULL, unwinder.frames()[16].sp);
 }
 
+TEST_F(UnwindOfflineTest, empty_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("empty_arm64/", ARCH_ARM64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00000000000963a4  libc.so (__ioctl+4)\n"
+      "  #01 pc 000000000005344c  libc.so (ioctl+140)\n"
+      "  #02 pc 0000000000050ce4  libbinder.so "
+      "(android::IPCThreadState::talkWithDriver(bool)+308)\n"
+      "  #03 pc 0000000000050e98  libbinder.so "
+      "(android::IPCThreadState::getAndExecuteCommand()+24)\n"
+      "  #04 pc 00000000000516ac  libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)\n"
+      "  #05 pc 00000000000443b0  netd (main+1056)\n"
+      "  #06 pc 0000000000045594  libc.so (__libc_init+108)\n",
+      frame_info);
+
+  EXPECT_EQ(0x72a02203a4U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x72a01dd44cU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x729f759ce4U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7ffb6c0c50U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x729f759e98U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7ffb6c0ce0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x729f75a6acU, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7ffb6c0d10U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x5d478af3b0U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7ffb6c0d40U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x72a01cf594U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7ffb6c0f30U, unwinder.frames()[6].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libbinder.so b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
new file mode 100644
index 0000000..f30384c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libc.so b/libunwindstack/tests/files/offline/empty_arm64/libc.so
new file mode 100644
index 0000000..b05dcaf
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/maps.txt b/libunwindstack/tests/files/offline/empty_arm64/maps.txt
new file mode 100644
index 0000000..edb83c6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/maps.txt
@@ -0,0 +1,9 @@
+5d4786b000-5d47893000 r--p 0 00:00 0   netd
+5d47893000-5d47894000 ---p 0 00:00 0
+5d47894000-5d47901000 --xp 29000 00:00 0   netd
+729f709000-729f750000 r--p 0 00:00 0   libbinder.so
+729f750000-729f751000 ---p 0 00:00 0
+729f751000-729f794000 --xp 48000 00:00 0   libbinder.so
+72a018a000-72a01c2000 r--p 0 00:00 0   libc.so
+72a01c2000-72a01c3000 ---p 0 00:00 0
+72a01c3000-72a023b000 --xp 39000 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/empty_arm64/netd b/libunwindstack/tests/files/offline/empty_arm64/netd
new file mode 100644
index 0000000..8a72e94
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/netd
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/regs.txt b/libunwindstack/tests/files/offline/empty_arm64/regs.txt
new file mode 100644
index 0000000..3d4279f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 1d
+x1: c0306201
+x2: 7ffb6c0c50
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 0
+x8: 1d
+x9: 7ffb6c0c00
+x10: 7ffb6c0c50
+x11: 7ffb6c0bd0
+x12: ffffff80ffffffd0
+x13: 0
+x14: 72a0240ce2
+x15: 20
+x16: 729f7a54e8
+x17: 72a01dd3c0
+x18: 72a0ac2000
+x19: 72a0666000
+x20: 719769b610
+x21: 719769b730
+x22: c0306201
+x23: fffffff7
+x24: 72a0666000
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7ffb6c0c30
+sp: 7ffb6c0b50
+lr: 72a01dd450
+pc: 72a02203a4
+pst: a0000000
diff --git a/libunwindstack/tests/files/offline/empty_arm64/stack.data b/libunwindstack/tests/files/offline/empty_arm64/stack.data
new file mode 100644
index 0000000..6d6108c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/stack.data
Binary files differ
diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
index 153cf96..7b2e37f 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/StrongPointer_test.cpp
@@ -56,3 +56,18 @@
     }
     ASSERT_TRUE(isDeleted) << "foo was leaked!";
 }
+
+TEST(StrongPointer, NullptrComparison) {
+    sp<SPFoo> foo;
+    ASSERT_EQ(foo, nullptr);
+    ASSERT_EQ(nullptr, foo);
+}
+
+TEST(StrongPointer, PointerComparison) {
+    bool isDeleted;
+    sp<SPFoo> foo = new SPFoo(&isDeleted);
+    ASSERT_EQ(foo.get(), foo);
+    ASSERT_EQ(foo, foo.get());
+    ASSERT_NE(nullptr, foo);
+    ASSERT_NE(foo, nullptr);
+}
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 953b859..17c5e10 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -52,7 +52,12 @@
 
     template<size_t N>
     static size_t align(void*& buffer) {
-        return align<N>( const_cast<void const*&>(buffer) );
+        static_assert(!(N & (N - 1)), "Can only align to a power of 2.");
+        void* b = buffer;
+        buffer = reinterpret_cast<void*>((uintptr_t(buffer) + (N-1)) & ~(N-1));
+        size_t delta = size_t(uintptr_t(buffer) - uintptr_t(b));
+        memset(b, 0, delta);
+        return delta;
     }
 
     static void advance(void*& buffer, size_t& size, size_t offset) {
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 42c6efb..89f048d 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -455,6 +455,7 @@
 };
 
 #undef COMPARE_WEAK
+#undef COMPARE_WEAK_FUNCTIONAL
 
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 07dd3f1..6f4fb47 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -27,43 +27,6 @@
 
 // ---------------------------------------------------------------------------
 
-// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
-// was created before the sp<>, and they point to different objects, they may
-// compare equal even if they are entirely unrelated. E.g. CameraService
-// currently performa such comparisons.
-
-#define COMPARE_STRONG(_op_)                                           \
-template<typename U>                                            \
-inline bool operator _op_ (const sp<U>& o) const {              \
-    return m_ptr _op_ o.m_ptr;                                  \
-}                                                               \
-template<typename U>                                            \
-inline bool operator _op_ (const U* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}                                                               \
-/* Needed to handle type inference for nullptr: */              \
-inline bool operator _op_ (const T* o) const {                  \
-    return m_ptr _op_ o;                                        \
-}
-
-template<template<typename C> class comparator, typename T, typename U>
-static inline bool _sp_compare_(T* a, U* b) {
-    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
-}
-
-// Use std::less and friends to avoid undefined behavior when ordering pointers
-// to different objects.
-#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_)               \
-template<typename U>                                             \
-inline bool operator _op_ (const sp<U>& o) const {               \
-    return _sp_compare_<_compare_>(m_ptr, o.m_ptr);              \
-}                                                                \
-template<typename U>                                             \
-inline bool operator _op_ (const U* o) const {                   \
-    return _sp_compare_<_compare_>(m_ptr, o);                    \
-}
-// ---------------------------------------------------------------------------
-
 template<typename T>
 class sp {
 public:
@@ -102,15 +65,6 @@
     inline T*       get() const            { return m_ptr; }
     inline explicit operator bool () const { return m_ptr != nullptr; }
 
-    // Operators
-
-    COMPARE_STRONG(==)
-    COMPARE_STRONG(!=)
-    COMPARE_STRONG_FUNCTIONAL(>, std::greater)
-    COMPARE_STRONG_FUNCTIONAL(<, std::less)
-    COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
-    COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
-
     // Punt these to the wp<> implementation.
     template<typename U>
     inline bool operator == (const wp<U>& o) const {
@@ -130,12 +84,69 @@
     T* m_ptr;
 };
 
+#define COMPARE_STRONG(_op_)                                           \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
+        return t.get() _op_ u.get();                                   \
+    }                                                                  \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const T* t, const sp<U>& u) {     \
+        return t _op_ u.get();                                         \
+    }                                                                  \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const sp<T>& t, const U* u) {     \
+        return t.get() _op_ u;                                         \
+    }                                                                  \
+    template <typename T>                                              \
+    static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
+        return t.get() _op_ nullptr;                                   \
+    }                                                                  \
+    template <typename T>                                              \
+    static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
+        return nullptr _op_ t.get();                                   \
+    }
+
+template <template <typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+    return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_)                     \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \
+        return _sp_compare_<_compare_>(t.get(), u.get());              \
+    }                                                                  \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const T* t, const sp<U>& u) {     \
+        return _sp_compare_<_compare_>(t, u.get());                    \
+    }                                                                  \
+    template <typename T, typename U>                                  \
+    static inline bool operator _op_(const sp<T>& t, const U* u) {     \
+        return _sp_compare_<_compare_>(t.get(), u);                    \
+    }                                                                  \
+    template <typename T>                                              \
+    static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \
+        return _sp_compare_<_compare_>(t.get(), nullptr);              \
+    }                                                                  \
+    template <typename T>                                              \
+    static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \
+        return _sp_compare_<_compare_>(nullptr, t.get());              \
+    }
+
+COMPARE_STRONG(==)
+COMPARE_STRONG(!=)
+COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+COMPARE_STRONG_FUNCTIONAL(<, std::less)
+COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
+
+#undef COMPARE_STRONG
+#undef COMPARE_STRONG_FUNCTIONAL
+
 // For code size reasons, we do not want these inlined or templated.
 void sp_report_race();
 void sp_report_stack_pointer();
 
-#undef COMPARE
-
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
 
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index f4544a1..b92c76c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -1,5 +1,3 @@
-subdirs = ["tests"]
-
 cc_library {
     name: "libvndksupport",
     native_bridge_supported: true,
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
index cf0f618..30b9c2e 100644
--- a/libvndksupport/linker.cpp
+++ b/libvndksupport/linker.cpp
@@ -26,9 +26,7 @@
 
 #include <initializer_list>
 
-__attribute__((weak)) extern "C" android_namespace_t* android_get_exported_namespace(const char*);
-__attribute__((weak)) extern "C" void* android_dlopen_ext(const char*, int,
-                                                          const android_dlextinfo*);
+extern "C" android_namespace_t* android_get_exported_namespace(const char*);
 
 namespace {
 
@@ -42,10 +40,8 @@
 static VendorNamespace get_vendor_namespace() {
     static VendorNamespace result = ([] {
         for (const char* name : {"sphal", "default"}) {
-            if (android_get_exported_namespace != nullptr) {
-                if (android_namespace_t* ns = android_get_exported_namespace(name)) {
-                    return VendorNamespace{ns, name};
-                }
+            if (android_namespace_t* ns = android_get_exported_namespace(name)) {
+                return VendorNamespace{ns, name};
             }
         }
         return VendorNamespace{};
@@ -59,10 +55,6 @@
     if (getpid() == 1) {
         return 0;
     }
-    if (android_get_exported_namespace == nullptr) {
-        ALOGD("android_get_exported_namespace() not available. Assuming system process.");
-        return 0;
-    }
 
     // In vendor process, 'vndk' namespace is not visible, whereas in system
     // process, it is.
@@ -76,10 +68,7 @@
                 .flags = ANDROID_DLEXT_USE_NAMESPACE,
                 .library_namespace = vendor_namespace.ptr,
         };
-        void* handle = nullptr;
-        if (android_dlopen_ext != nullptr) {
-            handle = android_dlopen_ext(name, flag, &dlextinfo);
-        }
+        void* handle = android_dlopen_ext(name, flag, &dlextinfo);
         if (!handle) {
             ALOGE("Could not load %s from %s namespace: %s.", name, vendor_namespace.name,
                   dlerror());
diff --git a/libziparchive/testdata/empty.zip b/libziparchive/testdata/empty.zip
new file mode 100644
index 0000000..15cb0ec
--- /dev/null
+++ b/libziparchive/testdata/empty.zip
Binary files differ
diff --git a/libziparchive/testdata/zero-size-cd.zip b/libziparchive/testdata/zero-size-cd.zip
new file mode 100644
index 0000000..b6c8cbe
--- /dev/null
+++ b/libziparchive/testdata/zero-size-cd.zip
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index ef29188..68837cc 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -265,14 +265,10 @@
   ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records,
         eocd->cd_size, eocd->cd_start_offset);
 
-  /*
-   * It all looks good.  Create a mapping for the CD, and set the fields
-   * in archive.
-   */
-
+  // It all looks good.  Create a mapping for the CD, and set the fields
+  // in archive.
   if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset),
                                            static_cast<size_t>(eocd->cd_size))) {
-    ALOGE("Zip: failed to intialize central directory.\n");
     return kMmapFailed;
   }
 
@@ -354,7 +350,7 @@
   if (archive->hash_table == nullptr) {
     ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
           archive->hash_table_size, sizeof(ZipStringOffset));
-    return -1;
+    return kAllocationFailed;
   }
 
   /*
@@ -365,24 +361,25 @@
   const uint8_t* ptr = cd_ptr;
   for (uint16_t i = 0; i < num_entries; i++) {
     if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
-      ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
+      ALOGW("Zip: ran off the end (item #%" PRIu16 ", %zu bytes of central directory)", i,
+            cd_length);
 #if defined(__ANDROID__)
       android_errorWriteLog(0x534e4554, "36392138");
 #endif
-      return -1;
+      return kInvalidFile;
     }
 
     const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
     if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
       ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
-      return -1;
+      return kInvalidFile;
     }
 
     const off64_t local_header_offset = cdr->local_file_header_offset;
     if (local_header_offset >= archive->directory_offset) {
       ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
             static_cast<int64_t>(local_header_offset), i);
-      return -1;
+      return kInvalidFile;
     }
 
     const uint16_t file_name_length = cdr->file_name_length;
@@ -394,12 +391,12 @@
       ALOGW("Zip: file name for entry %" PRIu16
             " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
             i, file_name_length, cd_length);
-      return -1;
+      return kInvalidEntryName;
     }
     // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
     if (!IsValidEntryName(file_name, file_name_length)) {
       ALOGW("Zip: invalid file name at entry %" PRIu16, i);
-      return -1;
+      return kInvalidEntryName;
     }
 
     // Add the CDE filename to the hash table.
@@ -414,7 +411,7 @@
     ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
     if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
       ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i);
-      return -1;
+      return kInvalidFile;
     }
   }
 
@@ -422,7 +419,7 @@
   if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&lfh_start_bytes),
                                         sizeof(uint32_t), 0)) {
     ALOGW("Zip: Unable to read header for entry at offset == 0.");
-    return -1;
+    return kInvalidFile;
   }
 
   if (lfh_start_bytes != LocalFileHeader::kSignature) {
@@ -430,7 +427,7 @@
 #if defined(__ANDROID__)
     android_errorWriteLog(0x534e4554, "64211847");
 #endif
-    return -1;
+    return kInvalidFile;
   }
 
   ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
@@ -439,16 +436,8 @@
 }
 
 static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
-  int32_t result = -1;
-  if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
-    return result;
-  }
-
-  if ((result = ParseZipArchive(archive))) {
-    return result;
-  }
-
-  return 0;
+  int32_t result = MapCentralDirectory(debug_file_name, archive);
+  return result != 0 ? result : ParseZipArchive(archive);
 }
 
 int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
@@ -1185,7 +1174,7 @@
     return result;
   } else {
     if (base_ptr_ == nullptr) {
-      ALOGE("Zip: invalid file map\n");
+      ALOGE("Zip: invalid file map");
       return -1;
     }
     return static_cast<off64_t>(data_length_);
@@ -1196,12 +1185,12 @@
 bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
   if (has_fd_) {
     if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
-      ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
+      ALOGE("Zip: failed to read at offset %" PRId64, off);
       return false;
     }
   } else {
     if (off < 0 || off > static_cast<off64_t>(data_length_)) {
-      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", off, data_length_);
+      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
       return false;
     }
     memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
@@ -1219,13 +1208,17 @@
   if (mapped_zip.HasFd()) {
     directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
                                                       cd_start_offset, cd_size, PROT_READ);
-    if (!directory_map) return false;
+    if (!directory_map) {
+      ALOGE("Zip: failed to map central directory (offset %" PRId64 ", size %zu): %s",
+            cd_start_offset, cd_size, strerror(errno));
+      return false;
+    }
 
     CHECK_EQ(directory_map->size(), cd_size);
     central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
   } else {
     if (mapped_zip.GetBasePtr() == nullptr) {
-      ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
+      ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer");
       return false;
     }
     if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 60fdec0..1d05fc7 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -42,6 +42,7 @@
     "Invalid entry name",
     "I/O error",
     "File mapping failed",
+    "Allocation failed",
 };
 
 enum ErrorCodes : int32_t {
@@ -87,7 +88,10 @@
   // We were not able to mmap the central directory or entry contents.
   kMmapFailed = -12,
 
-  kLastErrorCode = kMmapFailed,
+  // An allocation failed.
+  kAllocationFailed = -13,
+
+  kLastErrorCode = kAllocationFailed,
 };
 
 class MappedZipFile {
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 8781ab7..0916304 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -36,13 +36,9 @@
 
 static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
 
-static const std::string kMissingZip = "missing.zip";
 static const std::string kValidZip = "valid.zip";
 static const std::string kLargeZip = "large.zip";
 static const std::string kBadCrcZip = "bad_crc.zip";
-static const std::string kCrashApk = "crash.apk";
-static const std::string kBadFilenameZip = "bad_filename.zip";
-static const std::string kUpdateZip = "dummy-update.zip";
 
 static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
                                                 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
@@ -52,13 +48,6 @@
 
 static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
 
-static const std::string kATxtName("a.txt");
-static const std::string kBTxtName("b.txt");
-static const std::string kNonexistentTxtName("nonexistent.txt");
-static const std::string kEmptyTxtName("empty.txt");
-static const std::string kLargeCompressTxtName("compress.txt");
-static const std::string kLargeUncompressTxtName("uncompress.txt");
-
 static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
   const std::string abs_path = test_data_dir + "/" + name;
   return OpenArchive(abs_path.c_str(), handle);
@@ -69,19 +58,31 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
   CloseArchive(handle);
 
-  ASSERT_EQ(-1, OpenArchiveWrapper(kBadFilenameZip, &handle));
+  ASSERT_EQ(kInvalidEntryName, OpenArchiveWrapper("bad_filename.zip", &handle));
   CloseArchive(handle);
 }
 
 TEST(ziparchive, OutOfBound) {
   ZipArchiveHandle handle;
-  ASSERT_EQ(-8, OpenArchiveWrapper(kCrashApk, &handle));
+  ASSERT_EQ(kInvalidOffset, OpenArchiveWrapper("crash.apk", &handle));
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, EmptyArchive) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(kEmptyArchive, OpenArchiveWrapper("empty.zip", &handle));
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, ZeroSizeCentralDirectory) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(kInvalidFile, OpenArchiveWrapper("zero-size-cd.zip", &handle));
   CloseArchive(handle);
 }
 
 TEST(ziparchive, OpenMissing) {
   ZipArchiveHandle handle;
-  ASSERT_NE(0, OpenArchiveWrapper(kMissingZip, &handle));
+  ASSERT_NE(0, OpenArchiveWrapper("missing.zip", &handle));
 
   // Confirm the file descriptor is not going to be mistaken for a valid one.
   ASSERT_EQ(-1, GetFileDescriptor(handle));
@@ -200,7 +201,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
+  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
 
   // Known facts about a.txt, from zipinfo -v.
   ASSERT_EQ(63, data.offset);
@@ -211,7 +212,7 @@
   ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
 
   // An entry that doesn't exist. Should be a negative return code.
-  ASSERT_LT(FindEntry(handle, kNonexistentTxtName, &data), 0);
+  ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
 
   CloseArchive(handle);
 }
@@ -259,7 +260,7 @@
 
   // An entry that's deflated.
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
+  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
   const uint32_t a_size = data.uncompressed_length;
   ASSERT_EQ(a_size, kATxtContents.size());
   uint8_t* buffer = new uint8_t[a_size];
@@ -268,7 +269,7 @@
   delete[] buffer;
 
   // An entry that's stored.
-  ASSERT_EQ(0, FindEntry(handle, kBTxtName, &data));
+  ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
   const uint32_t b_size = data.uncompressed_length;
   ASSERT_EQ(b_size, kBTxtContents.size());
   buffer = new uint8_t[b_size];
@@ -323,7 +324,7 @@
   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
 
   ZipEntry entry;
-  ASSERT_EQ(0, FindEntry(handle, kEmptyTxtName, &entry));
+  ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
   uint8_t buffer[1];
   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
@@ -403,7 +404,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry entry;
-  ASSERT_EQ(0, FindEntry(handle, kATxtName, &entry));
+  ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
 
   // Assert that the first 8 bytes of the file haven't been clobbered.
@@ -425,7 +426,7 @@
 
 #if !defined(_WIN32)
 TEST(ziparchive, OpenFromMemory) {
-  const std::string zip_path = test_data_dir + "/" + kUpdateZip;
+  const std::string zip_path = test_data_dir + "/dummy-update.zip";
   android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
   ASSERT_NE(-1, fd);
   struct stat sb;
@@ -510,27 +511,27 @@
 }
 
 TEST(ziparchive, StreamCompressed) {
-  ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContents, false);
+  ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
 }
 
 TEST(ziparchive, StreamUncompressed) {
-  ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, false);
+  ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
 }
 
 TEST(ziparchive, StreamRawCompressed) {
-  ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContentsCompressed, true);
+  ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
 }
 
 TEST(ziparchive, StreamRawUncompressed) {
-  ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, true);
+  ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
 }
 
 TEST(ziparchive, StreamLargeCompressed) {
-  ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeCompressTxtName);
+  ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
 }
 
 TEST(ziparchive, StreamLargeUncompressed) {
-  ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeUncompressTxtName);
+  ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
 }
 
 TEST(ziparchive, StreamCompressedBadCrc) {
@@ -539,7 +540,7 @@
 
   ZipEntry entry;
   std::vector<uint8_t> read_data;
-  ZipArchiveStreamTest(handle, kATxtName, false, false, &entry, &read_data);
+  ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
 
   CloseArchive(handle);
 }
@@ -550,7 +551,7 @@
 
   ZipEntry entry;
   std::vector<uint8_t> read_data;
-  ZipArchiveStreamTest(handle, kBTxtName, false, false, &entry, &read_data);
+  ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
 
   CloseArchive(handle);
 }
@@ -647,7 +648,8 @@
 
   // Out of bounds.
   ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
-  ASSERT_STREQ("Unknown return code", ErrorCodeString(-13));
+  ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
+  ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
 
   ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
 }
@@ -698,7 +700,7 @@
   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
                                         kZipFileWithBrokenLfhSignature.size()));
   ZipArchiveHandle handle;
-  ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
+  ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
 }
 
 class VectorReader : public zip_archive::Reader {
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index b26ad4d..1c3acb8 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -304,10 +304,13 @@
     bool cmdlineValid;             // cmdline has been cached
     bool updated;                  // cleared before monitoring pass.
     bool killed;                   // sent a kill to this thread, next panic...
+    bool frozen;                   // process is in frozen cgroup.
 
     void setComm(const char* _comm) { strncpy(comm + 1, _comm, sizeof(comm) - 2); }
 
-    proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state)
+    void setFrozen(bool _frozen) { frozen = _frozen; }
+
+    proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state, bool frozen)
         : tid(tid),
           schedUpdate(0),
           nrSwitches(0),
@@ -327,7 +330,8 @@
           exeMissingValid(false),
           cmdlineValid(false),
           updated(true),
-          killed(!llkTestWithKill) {
+          killed(!llkTestWithKill),
+          frozen(frozen) {
         memset(comm, '\0', sizeof(comm));
         setComm(_comm);
     }
@@ -373,6 +377,8 @@
         return uid;
     }
 
+    bool isFrozen() { return frozen; }
+
     void reset(void) {  // reset cache, if we detected pid rollover
         uid = -1;
         state = '?';
@@ -592,8 +598,9 @@
     tids.erase(tid);
 }
 
-proc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state) {
-    auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state)));
+proc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state,
+                  bool frozen) {
+    auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state, frozen)));
     return &it.first->second;
 }
 
@@ -1039,12 +1046,18 @@
                 continue;
             }
 
+            // Get the process cgroup
+            auto cgroup = ReadFile(piddir + "/cgroup");
+            auto frozen = cgroup.find(":freezer:/frozen") != std::string::npos;
+
             auto procp = llkTidLookup(tid);
             if (procp == nullptr) {
-                procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state);
+                procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state, frozen);
             } else {
                 // comm can change ...
                 procp->setComm(pdir);
+                // frozen can change, too...
+                procp->setFrozen(frozen);
                 procp->updated = true;
                 // pid/ppid/tid wrap?
                 if (((procp->update != prevUpdate) && (procp->update != llkUpdate)) ||
@@ -1084,6 +1097,9 @@
             if ((tid == myTid) || llkSkipPid(tid)) {
                 continue;
             }
+            if (procp->isFrozen()) {
+                break;
+            }
             if (llkSkipPpid(ppid)) {
                 break;
             }
@@ -1101,7 +1117,7 @@
 
             auto pprocp = llkTidLookup(ppid);
             if (pprocp == nullptr) {
-                pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
+                pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?', false);
             }
             if (pprocp) {
                 if (llkSkipPproc(pprocp, procp)) break;
diff --git a/llkd/llkd-debuggable.rc b/llkd/llkd-debuggable.rc
index 724cb5e..4b11b1c 100644
--- a/llkd/llkd-debuggable.rc
+++ b/llkd/llkd-debuggable.rc
@@ -13,7 +13,7 @@
     disabled
     user llkd
     group llkd readproc
-    capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE
+    capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE SYS_ADMIN
     file /dev/kmsg w
     file /proc/sysrq-trigger w
     writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
index 96079cc..475512c 100644
--- a/llkd/tests/llkd_test.cpp
+++ b/llkd/tests/llkd_test.cpp
@@ -89,7 +89,8 @@
         rest();
         std::string setprop("setprop ");
         // Manually check that SyS_openat is _added_ to the list when restarted
-        execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat").c_str());
+        // 4.19+ kernels report __arm64_sys_openat b/147486902
+        execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat,__arm64_sys_openat").c_str());
         rest();
         execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " false").c_str());
         rest();
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 7b18438..08e3d22 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -110,7 +110,7 @@
 #endif
 
 static int openLogFile(const char* pathname, size_t sizeKB) {
-    int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+    int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP);
     if (fd < 0) {
         return fd;
     }
diff --git a/logcat/logcatd b/logcat/logcatd
index 622e567..5a1415d 100755
--- a/logcat/logcatd
+++ b/logcat/logcatd
@@ -4,6 +4,10 @@
 # first reads the 'last' logcat to persistent storage with `-L` then run logcat again without
 # `-L` to read the current logcat buffers to persistent storage.
 
+# init sets the umask to 077 for forked processes. logpersist needs to create files that are group
+# readable. So relax the umask to only disallow group wx and world rwx.
+umask 037
+
 has_last="false"
 for arg in "$@"; do
   if [ "$arg" == "-L" -o "$arg" == "--last" ]; then
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index e986184..64d5500 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -30,8 +30,8 @@
     setprop logd.logpersistd.enable true
 
 on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
-    # all exec/services are called with umask(077), so no gain beyond 0700
-    mkdir /data/misc/logd 0700 logd log
+    # log group should be able to read persisted logs
+    mkdir /data/misc/logd 0750 logd log
     start logcatd
 
 # stop logcatd service and clear data
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
index 46aa8c1..248a78c 100644
--- a/logd/logtagd.rc
+++ b/logd/logtagd.rc
@@ -2,7 +2,7 @@
 # logtagd event log tag service (debug only)
 #
 on post-fs-data
-    mkdir /data/misc/logd 0700 logd log
+    mkdir /data/misc/logd 0750 logd log
     write /data/misc/logd/event-log-tags ""
     chown logd log /data/misc/logd/event-log-tags
     chmod 0600 /data/misc/logd/event-log-tags
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
index 9a18edb..a25dc44 100644
--- a/logd/tests/AndroidTest.xml
+++ b/logd/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
diff --git a/property_service/Android.bp b/property_service/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/property_service/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index ac802b5..108d15a 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -2,6 +2,7 @@
     name: "libpropertyinfoparser",
     host_supported: true,
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     srcs: ["property_info_parser.cpp"],
diff --git a/property_service/property_info_checker/Android.bp b/property_service/property_info_checker/Android.bp
index 7d66199..65e660a 100644
--- a/property_service/property_info_checker/Android.bp
+++ b/property_service/property_info_checker/Android.bp
@@ -7,6 +7,7 @@
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
         "libbase",
+        "liblog",
         "libsepol",
     ],
     srcs: ["property_info_checker.cpp"],
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 2dbdb60..a9d0ed0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -67,6 +67,11 @@
   EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
 endif
 
+EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
+ifeq ($(CLANG_COVERAGE),true)
+  EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%p-%m.profraw
+endif
+
 # Put it here instead of in init.rc module definition,
 # because init.rc is conditionally included.
 #
@@ -147,6 +152,7 @@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
 
 # Append PLATFORM_VNDK_VERSION to base name.
@@ -156,132 +162,6 @@
 )
 endef
 
-
-#######################################
-# ld.config.txt selection variables
-#
-_enforce_vndk_at_runtime := false
-ifdef BOARD_VNDK_VERSION
-  ifneq ($(BOARD_VNDK_RUNTIME_DISABLE),true)
-    _enforce_vndk_at_runtime := true
-  endif
-endif
-
-_enforce_vndk_lite_at_runtime := false
-ifeq ($(_enforce_vndk_at_runtime),false)
-  ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
-    _enforce_vndk_lite_at_runtime := true
-  endif
-endif
-
-#######################################
-# ld.config.txt
-#
-# For VNDK enforced devices that have defined BOARD_VNDK_VERSION, use
-# "ld.config.txt" as a source file. This configuration includes strict VNDK
-# run-time restrictions for vendor process.
-#
-# Other treblized devices, that have not defined BOARD_VNDK_VERSION or that
-# have set BOARD_VNDK_RUNTIME_DISABLE to true, use "ld.config.vndk_lite.txt"
-# as a source file. This configuration does not have strict VNDK run-time
-# restrictions.
-#
-# If the device is not treblized, use "ld.config.legacy.txt" for legacy
-# namespace configuration.
-#
-include $(CLEAR_VARS)
-LOCAL_MODULE := ld.config.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-
-# Start of i18n and ART APEX compatibility.
-#
-# Meta-comment:
-# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
-# entries need to be associated with something that goes into /system.
-# ld.config.txt qualifies but it could be anything else in /system until soong
-# supports creation of symlinks. http://b/123333111
-#
-# Keeping the appearance of files/dirs having old locations for apps that have
-# come to rely on them.
-
-# http://b/121248172 - create a link from /system/usr/icu to
-# /apex/com.android.i18n/etc/icu so that apps can find the ICU .dat file.
-# A symlink can't overwrite a directory and the /system/usr/icu directory once
-# existed so the required structure must be created whatever we find.
-LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
-LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.i18n/etc/icu $(TARGET_OUT)/usr/icu
-
-# TODO(b/124106384): Clean up compat symlinks for ART binaries.
-ART_BINARIES := dalvikvm dex2oat
-LOCAL_POST_INSTALL_CMD += && mkdir -p $(TARGET_OUT)/bin
-$(foreach b,$(ART_BINARIES), \
-  $(eval LOCAL_POST_INSTALL_CMD += \
-    && ln -sf /apex/com.android.art/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
-)
-
-# End of i18n and ART APEX compatibilty.
-
-ifeq ($(_enforce_vndk_at_runtime),true)
-
-# for VNDK enforced devices
-# This file will be replaced with dynamically generated one from system/linkerconfig
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
-LOCAL_SRC_FILES := etc/ld.config.txt
-include $(BUILD_PREBUILT)
-
-else ifeq ($(_enforce_vndk_lite_at_runtime),true)
-
-# for treblized but VNDK lightly enforced devices
-LOCAL_MODULE_STEM := ld.config.vndk_lite.txt
-include $(BUILD_SYSTEM)/base_rules.mk
-ld_config_template := $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt
-vndk_version := $(PLATFORM_VNDK_VERSION)
-libz_is_llndk := true
-include $(LOCAL_PATH)/update_and_install_ld_config.mk
-
-else
-
-# for legacy non-treblized devices
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
-LOCAL_SRC_FILES := etc/ld.config.legacy.txt
-include $(BUILD_PREBUILT)
-
-endif  # ifeq ($(_enforce_vndk_at_runtime),true)
-
-#######################################
-# ld.config.vndk_lite.txt
-#
-# This module is only for GSI.
-#
-ifeq ($(_enforce_vndk_lite_at_runtime),false)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := ld.config.vndk_lite.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
-include $(BUILD_SYSTEM)/base_rules.mk
-ld_config_template := $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt
-vndk_version := $(PLATFORM_VNDK_VERSION)
-libz_is_llndk := true
-include $(LOCAL_PATH)/update_and_install_ld_config.mk
-
-endif  # ifeq ($(_enforce_vndk_lite_at_runtime),false)
-
-_enforce_vndk_at_runtime :=
-_enforce_vndk_lite_at_runtime :=
-
-#######################################
-# ld.config.txt for recovery
-include $(CLEAR_VARS)
-LOCAL_MODULE := ld.config.recovery.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := etc/ld.config.recovery.txt
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc
-LOCAL_MODULE_STEM := ld.config.txt
-include $(BUILD_PREBUILT)
-
 #######################################
 # sanitizer.libraries.txt
 include $(CLEAR_VARS)
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index a99756a..5c87843 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -1,198 +1,3 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Bionic loader config file.
-# This gives the exactly the same namespace setup in pre-O.
-#
-
-# All binaries gets the same configuration 'legacy'
-dir.legacy = /system
-dir.legacy = /product
-dir.legacy = /vendor
-dir.legacy = /odm
-dir.legacy = /sbin
-
-# Except for /postinstall, where only /system and /product are searched
-dir.postinstall = /postinstall
-
-# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
-# This must be last.
-dir.legacy = /data
-
-[legacy]
-namespace.default.isolated = false
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.default.visible = true
-
-namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
-namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /odm/${LIB}
-
-namespace.default.asan.search.paths  = /data/asan/system/${LIB}
-namespace.default.asan.search.paths +=           /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.default.asan.search.paths +=           /vendor/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}
-namespace.default.asan.search.paths +=           /odm/${LIB}
-
-###############################################################################
-# APEX related namespaces.
-###############################################################################
-
-additional.namespaces = art,conscrypt,media,neuralnetworks,resolv
-
-# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-# If a shared library or an executable requests a shared library that
-# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the art namespace. And then, if the
-# shared library cannot be loaded from the art namespace either, the
-# dynamic linker tries to load the shared library from the resolv namespace.
-# Finally, if all attempts fail, the dynamic linker returns an error.
-namespace.default.links = art,resolv,neuralnetworks
-namespace.default.asan.links = art,resolv,neuralnetworks
-namespace.default.link.art.shared_libs  = libandroidicu.so
-namespace.default.link.art.shared_libs += libdexfile_external.so
-namespace.default.link.art.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.art.shared_libs += libicui18n.so
-namespace.default.link.art.shared_libs += libicuuc.so
-namespace.default.link.art.shared_libs += libnativebridge.so
-namespace.default.link.art.shared_libs += libnativehelper.so
-namespace.default.link.art.shared_libs += libnativeloader.so
-
-# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.art.shared_libs += libpac.so
-
-# When libnetd_resolv.so can't be found in the default namespace, search for it
-# in the resolv namespace. Don't allow any other libraries from the resolv namespace
-# to be loaded in the default namespace.
-namespace.default.link.resolv.shared_libs = libnetd_resolv.so
-
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "art" APEX namespace
-#
-# This namespace exposes externally accessible libraries from the ART APEX.
-# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.art.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.art.visible = true
-
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = default,neuralnetworks
-# Need allow_all_shared_libs because libart.so can dlopen oat files in
-# /system/framework and /data.
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
-namespace.art.link.default.allow_all_shared_libs = true
-namespace.art.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "media" APEX namespace
-#
-# This namespace is for libraries within the media APEX.
-###############################################################################
-namespace.media.isolated = true
-namespace.media.visible = true
-
-namespace.media.search.paths = /apex/com.android.media/${LIB}
-namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
-
-namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
-
-namespace.media.links = default
-namespace.media.link.default.shared_libs  = libbinder_ndk.so
-namespace.media.link.default.shared_libs += libc.so
-namespace.media.link.default.shared_libs += libcgrouprc.so
-namespace.media.link.default.shared_libs += libdl.so
-namespace.media.link.default.shared_libs += liblog.so
-namespace.media.link.default.shared_libs += libmediametrics.so
-namespace.media.link.default.shared_libs += libmediandk.so
-namespace.media.link.default.shared_libs += libm.so
-namespace.media.link.default.shared_libs += libvndksupport.so
-
-namespace.media.link.default.shared_libs += libclang_rt.asan-aarch64-android.so
-namespace.media.link.default.shared_libs += libclang_rt.asan-arm-android.so
-namespace.media.link.default.shared_libs += libclang_rt.asan-i686-android.so
-namespace.media.link.default.shared_libs += libclang_rt.asan-x86_64-android.so
-namespace.media.link.default.shared_libs += libclang_rt.hwasan-aarch64-android.so
-
-###############################################################################
-# "conscrypt" APEX namespace
-#
-# This namespace is for libraries within the conscrypt APEX.
-# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.conscrypt.isolated = true
-namespace.conscrypt.visible = true
-
-namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = art,default
-namespace.conscrypt.link.art.shared_libs = libandroidio.so
-namespace.conscrypt.link.default.shared_libs  = libc.so
-namespace.conscrypt.link.default.shared_libs += libm.so
-namespace.conscrypt.link.default.shared_libs += libdl.so
-namespace.conscrypt.link.default.shared_libs += liblog.so
-
-###############################################################################
-# "resolv" APEX namespace
-#
-# This namespace is for libraries within the resolv APEX.
-###############################################################################
-namespace.resolv.isolated = true
-namespace.resolv.visible = true
-
-namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
-namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
-namespace.resolv.links = default
-namespace.resolv.link.default.shared_libs  = libc.so
-namespace.resolv.link.default.shared_libs += libcgrouprc.so
-namespace.resolv.link.default.shared_libs += libm.so
-namespace.resolv.link.default.shared_libs += libdl.so
-namespace.resolv.link.default.shared_libs += libbinder_ndk.so
-namespace.resolv.link.default.shared_libs += liblog.so
-namespace.resolv.link.default.shared_libs += libvndksupport.so
-
-###############################################################################
-# "neuralnetworks" APEX namespace
-#
-# This namespace is for libraries within the NNAPI APEX.
-###############################################################################
-namespace.neuralnetworks.isolated = true
-namespace.neuralnetworks.visible = true
-
-namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs  = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-
-
-###############################################################################
-# Namespace config for binaries under /postinstall.
-# Only one default namespace is defined and it has no directories other than
-# /system/lib and /product/lib in the search paths. This is because linker
-# calls realpath on the search paths and this causes selinux denial if the
-# paths (/vendor, /odm) are not allowed to the poinstall binaries.
-# There is no reason to allow the binaries to access the paths.
-###############################################################################
-[postinstall]
-namespace.default.isolated = false
-namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+# This file is no longer in use.
+# Please update linker configuration generator instead.
+# You can find the code from /system/linkerconfig
\ No newline at end of file
diff --git a/rootdir/etc/ld.config.recovery.txt b/rootdir/etc/ld.config.recovery.txt
index 5d6c01a..5c87843 100644
--- a/rootdir/etc/ld.config.recovery.txt
+++ b/rootdir/etc/ld.config.recovery.txt
@@ -1,9 +1,3 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Bionic loader config file for recovery mode
-#
-
-dir.recovery = /system/bin
-
-[recovery]
-namespace.default.search.paths = /system/${LIB}
+# This file is no longer in use.
+# Please update linker configuration generator instead.
+# You can find the code from /system/linkerconfig
\ No newline at end of file
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 9c9f4a9..5c87843 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -1,609 +1,3 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Bionic loader config file.
-#
-
-# Don't change the order here. The first pattern that matches with the
-# absolute path of an executable is selected.
-dir.system = /system/bin/
-dir.system = /system/xbin/
-dir.system = /%SYSTEM_EXT%/bin/
-dir.system = /%PRODUCT%/bin/
-
-dir.vendor = /odm/bin/
-dir.vendor = /vendor/bin/
-dir.vendor = /data/nativetest/odm
-dir.vendor = /data/nativetest64/odm
-dir.vendor = /data/benchmarktest/odm
-dir.vendor = /data/benchmarktest64/odm
-dir.vendor = /data/nativetest/vendor
-dir.vendor = /data/nativetest64/vendor
-dir.vendor = /data/benchmarktest/vendor
-dir.vendor = /data/benchmarktest64/vendor
-
-dir.unrestricted = /data/nativetest/unrestricted
-dir.unrestricted = /data/nativetest64/unrestricted
-
-# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
-# necessary) the unrestricted subdirs above. Then clean this up.
-dir.unrestricted = /data/local/tmp
-
-dir.postinstall = /postinstall
-
-# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
-# This must be last.
-dir.system = /data
-
-[system]
-additional.namespaces = art,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
-
-###############################################################################
-# "default" namespace
-#
-# Framework-side code runs in this namespace. However, libs from other
-# partitions are also allowed temporarily.
-###############################################################################
-namespace.default.isolated = false
-# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
-# libart.
-namespace.default.visible = true
-
-namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /odm/${LIB}
-namespace.default.search.paths += /vendor/${LIB}
-
-namespace.default.asan.search.paths  = /data/asan/system/${LIB}
-namespace.default.asan.search.paths +=           /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
-namespace.default.asan.search.paths +=           /%SYSTEM_EXT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
-namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}
-namespace.default.asan.search.paths +=           /odm/${LIB}
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.default.asan.search.paths +=           /vendor/${LIB}
-
-# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-# If a shared library or an executable requests a shared library that
-# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the art namespace. And then, if the
-# shared library cannot be loaded from the art namespace either, the
-# dynamic linker tries to load the shared library from the resolv namespace.
-# Finally, if all attempts fail, the dynamic linker returns an error.
-namespace.default.links = art,resolv,neuralnetworks
-namespace.default.link.art.shared_libs  = libandroidicu.so
-namespace.default.link.art.shared_libs += libdexfile_external.so
-namespace.default.link.art.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.art.shared_libs += libicui18n.so
-namespace.default.link.art.shared_libs += libicuuc.so
-namespace.default.link.art.shared_libs += libnativebridge.so
-namespace.default.link.art.shared_libs += libnativehelper.so
-namespace.default.link.art.shared_libs += libnativeloader.so
-
-# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.art.shared_libs += libpac.so
-
-# When libnetd_resolv.so can't be found in the default namespace, search for it
-# in the resolv namespace. Don't allow any other libraries from the resolv namespace
-# to be loaded in the default namespace.
-namespace.default.link.resolv.shared_libs = libnetd_resolv.so
-
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "art" APEX namespace
-#
-# This namespace pulls in externally accessible libs from the ART APEX.
-# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.art.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.art.visible = true
-
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = default,neuralnetworks
-# Need allow_all_shared_libs because libart.so can dlopen oat files in
-# /system/framework and /data.
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
-namespace.art.link.default.allow_all_shared_libs = true
-namespace.art.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "media" APEX namespace
-#
-# This namespace is for libraries within the media APEX.
-###############################################################################
-namespace.media.isolated = true
-namespace.media.visible = true
-
-namespace.media.search.paths = /apex/com.android.media/${LIB}
-namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
-
-namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
-
-namespace.media.links = default,neuralnetworks
-namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.media.link.default.shared_libs += libbinder_ndk.so
-namespace.media.link.default.shared_libs += libmediametrics.so
-namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-# LLNDK library moved into apex
-namespace.media.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "conscrypt" APEX namespace
-#
-# This namespace is for libraries within the conscrypt APEX.
-# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.conscrypt.isolated = true
-namespace.conscrypt.visible = true
-
-namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = art,default
-namespace.conscrypt.link.art.shared_libs = libandroidio.so
-namespace.conscrypt.link.default.shared_libs  = libc.so
-namespace.conscrypt.link.default.shared_libs += libm.so
-namespace.conscrypt.link.default.shared_libs += libdl.so
-namespace.conscrypt.link.default.shared_libs += liblog.so
-namespace.conscrypt.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# "resolv" APEX namespace
-#
-# This namespace is for libraries within the resolv APEX.
-###############################################################################
-namespace.resolv.isolated = true
-namespace.resolv.visible = true
-
-namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
-namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
-namespace.resolv.links = default
-namespace.resolv.link.default.shared_libs  = libc.so
-namespace.resolv.link.default.shared_libs += libcgrouprc.so
-namespace.resolv.link.default.shared_libs += libm.so
-namespace.resolv.link.default.shared_libs += libdl.so
-namespace.resolv.link.default.shared_libs += libbinder_ndk.so
-namespace.resolv.link.default.shared_libs += liblog.so
-namespace.resolv.link.default.shared_libs += libvndksupport.so
-namespace.resolv.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# "sphal" namespace
-#
-# SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
-# loaded inside system processes. libEGL_<chipset>.so, libGLESv2_<chipset>.so,
-# android.hardware.graphics.mapper@2.0-impl.so, etc are SP-HALs.
-#
-# This namespace is exclusivly for SP-HALs. When the framework tries to dynami-
-# cally load SP-HALs, android_dlopen_ext() is used to explicitly specifying
-# that they should be searched and loaded from this namespace.
-#
-# Note that there is no link from the default namespace to this namespace.
-###############################################################################
-namespace.sphal.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.sphal.visible = true
-
-namespace.sphal.search.paths  = /odm/${LIB}
-namespace.sphal.search.paths += /vendor/${LIB}
-namespace.sphal.search.paths += /vendor/${LIB}/hw
-
-namespace.sphal.permitted.paths  = /odm/${LIB}
-namespace.sphal.permitted.paths += /vendor/${LIB}
-namespace.sphal.permitted.paths += /system/vendor/${LIB}
-
-namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
-namespace.sphal.asan.search.paths +=           /odm/${LIB}
-namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.sphal.asan.search.paths +=           /vendor/${LIB}
-
-namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
-namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
-namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
-namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
-
-# Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used. Order is important here as the namespaces are
-# tried in this order. rs should be before vndk because both are capable
-# of loading libRS_internal.so
-namespace.sphal.links = rs,default,vndk,neuralnetworks
-
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
-
-namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-
-# LLNDK library moved into apex
-namespace.sphal.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "rs" namespace
-#
-# This namespace is exclusively for Renderscript internal libraries.
-# This namespace has slightly looser restriction than the vndk namespace because
-# of the genuine characteristics of Renderscript; /data is in the permitted path
-# to load the compiled *.so file and libmediandk.so can be used here.
-###############################################################################
-namespace.rs.isolated = true
-namespace.rs.visible = true
-
-namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
-namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
-namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.rs.search.paths += /odm/${LIB}
-namespace.rs.search.paths += /vendor/${LIB}
-
-namespace.rs.permitted.paths  = /odm/${LIB}
-namespace.rs.permitted.paths += /vendor/${LIB}
-namespace.rs.permitted.paths += /system/vendor/${LIB}
-namespace.rs.permitted.paths += /data
-
-namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
-namespace.rs.asan.search.paths +=           /odm/${LIB}/vndk-sp
-namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
-namespace.rs.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
-namespace.rs.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
-namespace.rs.asan.search.paths +=           /odm/${LIB}
-namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.rs.asan.search.paths +=           /vendor/${LIB}
-
-namespace.rs.asan.permitted.paths  = /data/asan/odm/${LIB}
-namespace.rs.asan.permitted.paths +=           /odm/${LIB}
-namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
-namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
-namespace.rs.asan.permitted.paths += /data
-
-namespace.rs.links = default,neuralnetworks
-
-namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
-# namespace because RS framework libs are using them.
-namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
-
-# LLNDK library moved into apex
-namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "vndk" namespace
-#
-# This namespace is exclusively for vndk-sp libs.
-###############################################################################
-namespace.vndk.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.vndk.visible = true
-
-namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
-namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
-namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
-
-namespace.vndk.permitted.paths  = /odm/${LIB}/hw
-namespace.vndk.permitted.paths += /odm/${LIB}/egl
-namespace.vndk.permitted.paths += /vendor/${LIB}/hw
-namespace.vndk.permitted.paths += /vendor/${LIB}/egl
-namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
-# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
-namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
-
-namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
-namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
-
-namespace.vndk.asan.permitted.paths  = /data/asan/odm/${LIB}/hw
-namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/hw
-namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
-namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/egl
-namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
-namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/hw
-namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
-namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
-
-namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
-namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
-
-# When these NDK libs are required inside this namespace, then it is redirected
-# to the default namespace. This is possible since their ABI is stable across
-# Android releases.
-namespace.vndk.links = default,neuralnetworks
-
-namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-namespace.vndk.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "neuralnetworks" APEX namespace
-#
-# This namespace is for libraries within the NNAPI APEX.
-###############################################################################
-namespace.neuralnetworks.isolated = true
-namespace.neuralnetworks.visible = true
-
-namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs  = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# Namespace config for vendor processes. In O, no restriction is enforced for
-# them. However, in O-MR1, access to /system/${LIB} will not be allowed to
-# the default namespace. 'system' namespace will be added to give limited
-# (LL-NDK only) access.
-###############################################################################
-[vendor]
-additional.namespaces = art,neuralnetworks
-
-namespace.default.isolated = false
-
-namespace.default.search.paths  = /odm/${LIB}
-namespace.default.search.paths += /odm/${LIB}/vndk
-namespace.default.search.paths += /odm/${LIB}/vndk-sp
-namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /vendor/${LIB}/vndk
-namespace.default.search.paths += /vendor/${LIB}/vndk-sp
-
-# Access to system libraries is allowed
-namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.default.search.paths += /system/${LIB}
-namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
-# Put /system/lib/vndk at the last search order in vndk_lite for GSI
-namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
-
-namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
-namespace.default.asan.search.paths +=           /odm/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk
-namespace.default.asan.search.paths +=           /odm/${LIB}/vndk
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
-namespace.default.asan.search.paths +=           /odm/${LIB}/vndk-sp
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.default.asan.search.paths +=           /vendor/${LIB}
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk
-namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
-namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
-namespace.default.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.default.asan.search.paths += /data/asan/system/${LIB}
-namespace.default.asan.search.paths +=           /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
-namespace.default.asan.search.paths +=           /%SYSTEM_EXT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
-namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
-namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
-
-namespace.default.links = art,neuralnetworks
-namespace.default.link.art.shared_libs  = libdexfile_external.so
-namespace.default.link.art.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.art.shared_libs += libicui18n.so
-namespace.default.link.art.shared_libs += libicuuc.so
-namespace.default.link.art.shared_libs += libnativebridge.so
-namespace.default.link.art.shared_libs += libnativehelper.so
-namespace.default.link.art.shared_libs += libnativeloader.so
-# Workaround for b/124772622
-namespace.default.link.art.shared_libs += libandroidicu.so
-
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "art" APEX namespace
-#
-# This namespace exposes externally accessible libraries from the ART APEX.
-# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.art.isolated = true
-
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = default
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
-namespace.art.link.default.allow_all_shared_libs = true
-
-###############################################################################
-# "neuralnetworks" APEX namespace
-#
-# This namespace is for libraries within the NNAPI APEX.
-###############################################################################
-namespace.neuralnetworks.isolated = true
-namespace.neuralnetworks.visible = true
-
-namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs  = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# Namespace config for native tests that need access to both system and vendor
-# libraries. This replicates the default linker config (done by
-# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
-# includes the requisite namespace setup for APEXes.
-###############################################################################
-[unrestricted]
-additional.namespaces = art,media,conscrypt,resolv,neuralnetworks
-
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.default.visible = true
-
-namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /odm/${LIB}
-namespace.default.search.paths += /vendor/${LIB}
-
-namespace.default.asan.search.paths  = /data/asan/system/${LIB}
-namespace.default.asan.search.paths +=           /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}
-namespace.default.asan.search.paths +=           /odm/${LIB}
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.default.asan.search.paths +=           /vendor/${LIB}
-
-# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-namespace.default.links = art,resolv,neuralnetworks
-namespace.default.link.art.shared_libs  = libandroidicu.so
-namespace.default.link.art.shared_libs += libdexfile_external.so
-namespace.default.link.art.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.art.shared_libs += libicui18n.so
-namespace.default.link.art.shared_libs += libicuuc.so
-namespace.default.link.art.shared_libs += libnativebridge.so
-namespace.default.link.art.shared_libs += libnativehelper.so
-namespace.default.link.art.shared_libs += libnativeloader.so
-
-# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.art.shared_libs += libpac.so
-
-namespace.default.link.resolv.shared_libs = libnetd_resolv.so
-
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "art" APEX namespace
-#
-# This namespace exposes externally accessible libraries from the ART APEX.
-# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.art.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.art.visible = true
-
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = default
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
-namespace.runtime.link.default.allow_all_shared_libs = true
-
-###############################################################################
-# "media" APEX namespace
-#
-# This namespace is for libraries within the media APEX.
-###############################################################################
-namespace.media.isolated = true
-namespace.media.visible = true
-
-namespace.media.search.paths = /apex/com.android.media/${LIB}
-namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
-
-namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
-
-namespace.media.links = default,neuralnetworks
-namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
-namespace.media.link.default.shared_libs += libbinder_ndk.so
-namespace.media.link.default.shared_libs += libmediametrics.so
-namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-# LLNDK library moved into apex
-namespace.media.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "conscrypt" APEX namespace
-#
-# This namespace is for libraries within the conscrypt APEX.
-# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.conscrypt.isolated = true
-namespace.conscrypt.visible = true
-
-namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = art,default
-namespace.conscrypt.link.art.shared_libs = libandroidio.so
-namespace.conscrypt.link.default.shared_libs  = libc.so
-namespace.conscrypt.link.default.shared_libs += libm.so
-namespace.conscrypt.link.default.shared_libs += libdl.so
-namespace.conscrypt.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# "resolv" APEX namespace
-#
-# This namespace is for libraries within the resolv APEX.
-###############################################################################
-namespace.resolv.isolated = true
-namespace.resolv.visible = true
-
-namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
-namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
-namespace.resolv.links = default
-namespace.resolv.link.default.shared_libs  = libc.so
-namespace.resolv.link.default.shared_libs += libcgrouprc.so
-namespace.resolv.link.default.shared_libs += libm.so
-namespace.resolv.link.default.shared_libs += libdl.so
-namespace.resolv.link.default.shared_libs += libbinder_ndk.so
-namespace.resolv.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# "neuralnetworks" APEX namespace
-#
-# This namespace is for libraries within the NNAPI APEX.
-###############################################################################
-namespace.neuralnetworks.isolated = true
-namespace.neuralnetworks.visible = true
-
-namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs  = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# Namespace config for binaries under /postinstall.
-# Only default namespace is defined and default has no directories
-# other than /system/lib in the search paths. This is because linker calls
-# realpath on the search paths and this causes selinux denial if the paths
-# (/vendor, /odm) are not allowed to the postinstall binaries. There is no
-# reason to allow the binaries to access the paths.
-###############################################################################
-[postinstall]
-namespace.default.isolated = false
-namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
+# This file is no longer in use.
+# Please update linker configuration generator instead.
+# You can find the code from /system/linkerconfig
\ No newline at end of file
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 50005d9..fdaaf1a 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -15,4 +15,5 @@
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
     %EXPORT_GLOBAL_GCOV_OPTIONS%
+    %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
     %EXPORT_GLOBAL_HWASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b89c45e..7f4f025 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -38,9 +38,22 @@
     # Allow up to 32K FDs per process
     setrlimit nofile 32768 32768
 
+    # Set up linker config subdirectories based on mount namespaces
+    mkdir /linkerconfig/bootstrap 0755
+    mkdir /linkerconfig/default 0755
+
+    # Disable dm-verity hash prefetching, since it doesn't help performance
+    # Read more in b/136247322
+    write /sys/module/dm_verity/parameters/prefetch_cluster 0
+
     # Generate ld.config.txt for early executed processes
-    exec -- /system/bin/linkerconfig --target /linkerconfig/ld.config.txt
-    chmod 444 /linkerconfig/ld.config.txt
+    exec -- /system/bin/linkerconfig --target /linkerconfig/bootstrap
+    chmod 644 /linkerconfig/bootstrap/ld.config.txt
+    copy /linkerconfig/bootstrap/ld.config.txt /linkerconfig/default/ld.config.txt
+    chmod 644 /linkerconfig/default/ld.config.txt
+
+    # Mount bootstrap linker configuration as current
+    mount none /linkerconfig/bootstrap /linkerconfig bind rec
 
     start ueventd
 
@@ -49,6 +62,9 @@
     # the libraries are available to the processes started after this statement.
     exec_start apexd-bootstrap
 
+    # Generate linker config based on apex mounted in bootstrap namespace
+    update_linker_config
+
     # These must already exist by the time boringssl_self_test32 / boringssl_self_test64 run.
     mkdir /dev/boringssl 0755 root root
     mkdir /dev/boringssl/selftest 0755 root root
@@ -149,7 +165,7 @@
     mkdir /mnt/secure/asec 0700 root root
     mkdir /mnt/asec 0755 root system
     mkdir /mnt/obb 0755 root system
-    mkdir /mnt/media_rw 0750 root media_rw
+    mkdir /mnt/media_rw 0750 root external_storage
     mkdir /mnt/user 0755 root root
     mkdir /mnt/user/0 0755 root root
     mkdir /mnt/user/0/self 0755 root root
@@ -157,7 +173,7 @@
     mkdir /mnt/user/0/emulated/0 0755 root root
 
     # Prepare directories for pass through processes
-    mkdir /mnt/pass_through 0755 root root
+    mkdir /mnt/pass_through 0700 root root
     mkdir /mnt/pass_through/0 0755 root root
     mkdir /mnt/pass_through/0/self 0755 root root
     mkdir /mnt/pass_through/0/emulated 0755 root root
@@ -535,6 +551,7 @@
     chown bluetooth bluetooth /data/misc/bluedroid/bt_config.conf
     mkdir /data/misc/bluetooth 0770 bluetooth bluetooth
     mkdir /data/misc/bluetooth/logs 0770 bluetooth bluetooth
+    mkdir /data/misc/credstore 0700 credstore credstore
     mkdir /data/misc/keystore 0700 keystore keystore
     mkdir /data/misc/gatekeeper 0700 system system
     mkdir /data/misc/keychain 0771 system system
@@ -572,11 +589,11 @@
     # profile file layout
     mkdir /data/misc/profiles 0771 system system
     mkdir /data/misc/profiles/cur 0771 system system
-    mkdir /data/misc/profiles/ref 0771 system system
+    mkdir /data/misc/profiles/ref 0770 system system
     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/apexdata 0711 root root
     mkdir /data/misc/apexrollback 0700 root root
 
     mkdir /data/preloads 0775 system system encryption=None
@@ -679,6 +696,10 @@
     mount none /data/user /data_mirror/data_ce/null bind rec
     mount none /data/user_de /data_mirror/data_de/null bind rec
 
+    # Create mirror directory for jit profiles
+    mkdir /data_mirror/cur_profiles 0700 root root
+    mount none /data/misc/profiles/cur /data_mirror/cur_profiles 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
@@ -688,6 +709,9 @@
     mkdir /data/rollback 0700 system system encryption=DeleteIfNecessary
     mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
 
+    # Create root dir for Incremental Service
+    mkdir /data/incremental 0771 system system encryption=Require
+
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
     perform_apex_config
@@ -921,14 +945,33 @@
 on property:sys.sysctl.tcp_def_init_rwnd=*
     write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
 
-on property:security.perf_harden=0
+# perf_event_open syscall security:
+# Newer kernels have the ability to control the use of the syscall via SELinux
+# hooks. init tests for this, and sets sys_init.perf_lsm_hooks to 1 if the
+# kernel has the hooks. In this case, the system-wide perf_event_paranoid
+# sysctl is set to -1 (unrestricted use), and the SELinux policy is used for
+# controlling access. On older kernels, the paranoid value is the only means of
+# controlling access. It is normally 3 (allow only root), but the shell user
+# can lower it to 1 (allowing thread-scoped pofiling) via security.perf_harden.
+on property:sys.init.perf_lsm_hooks=1
+    write /proc/sys/kernel/perf_event_paranoid -1
+on property:security.perf_harden=0 && property:sys.init.perf_lsm_hooks=""
     write /proc/sys/kernel/perf_event_paranoid 1
+on property:security.perf_harden=1 && property:sys.init.perf_lsm_hooks=""
+    write /proc/sys/kernel/perf_event_paranoid 3
+
+# Additionally, simpleperf profiler uses debug.* and security.perf_harden
+# sysprops to be able to indirectly set these sysctls.
+on property:security.perf_harden=0
     write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}
     write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}
     write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}
-
+# Default values.
 on property:security.perf_harden=1
-    write /proc/sys/kernel/perf_event_paranoid 3
+    write /proc/sys/kernel/perf_event_max_sample_rate 100000
+    write /proc/sys/kernel/perf_cpu_time_max_percent 25
+    write /proc/sys/kernel/perf_event_mlock_kb 516
+
 
 # on shutdown
 # In device's init.rc, this trigger can be used to do device-specific actions
@@ -963,9 +1006,13 @@
 
 on userspace-reboot-requested
   # TODO(b/135984674): reset all necessary properties here.
-  setprop sys.boot_completed 0
-  setprop sys.init.updatable_crashing 0
+  setprop sys.boot_completed ""
+  setprop sys.init.updatable_crashing ""
+  setprop sys.init.updatable_crashing_process_name ""
   setprop apexd.status ""
+  setprop sys.user.0.ce_available ""
+  setprop sys.shutdown.requested ""
+  setprop service.bootanim.exit ""
 
 on userspace-reboot-fs-remount
   # Make sure that vold is running.
@@ -975,6 +1022,7 @@
   exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
   exec - system system -- /system/bin/vdc checkpoint markBootAttempt
   remount_userdata
+  start bootanim
 
 on userspace-reboot-resume
   trigger userspace-reboot-fs-remount
diff --git a/rootdir/ld_config_backward_compatibility_check.py b/rootdir/ld_config_backward_compatibility_check.py
deleted file mode 100755
index 1a27578..0000000
--- a/rootdir/ld_config_backward_compatibility_check.py
+++ /dev/null
@@ -1,177 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-#
-
-import glob
-import os.path
-import re
-import sys
-
-PREBUILTS_VNDK_DIR = "prebuilts/vndk"
-VENDOR_DIRECTORIES = ('/vendor', '/odm')
-
-def find_latest_vndk_snapshot_version():
-  """Returns latest vndk snapshot version in current source tree.
-  It will skip the test if the snapshot directories are not found.
-
-  Returns:
-    latest_version: string
-  """
-  vndk_dir_list = glob.glob(PREBUILTS_VNDK_DIR + "/v*")
-  if not vndk_dir_list:
-    """Exit without error because we may have source trees that do not include
-    VNDK snapshot directories in it.
-    """
-    sys.exit(0)
-  vndk_ver_list = [re.match(r".*/v(\d+)", vndk_dir).group(1)
-                                          for vndk_dir in vndk_dir_list]
-  latest_version = max(vndk_ver_list)
-  if latest_version == '27':
-    """Exit without error because VNDK v27 is not using ld.config.txt template
-    """
-    sys.exit(0)
-  return latest_version
-
-def get_vendor_configuration(ld_config_file):
-  """Reads the ld.config.txt file to parse the namespace configurations.
-  It finds the configurations that include vendor directories.
-
-  Args:
-    ld_config_file: string, path (relative to build top) of the ld.config.txt
-                    file.
-  Returns:
-    configs: dict{string:[string]}, dictionary of namespace configurations.
-             it has 'section + property' names as keys and the directory list
-             as values.
-  """
-  try:
-    conf_file = open(ld_config_file)
-  except IOError:
-    print("error: could not read %s" % ld_config_file)
-    sys.exit(1)
-
-  configs = dict()
-  current_section = None
-
-  with conf_file:
-    for line in conf_file:
-      # ignore comments
-      found = line.find('#')
-      if found != -1:
-        line = line[:found]
-      line = line.strip()
-      if not line:
-        continue
-
-      if line[0] == '[' and line[-1] == ']':
-        # new section started
-        current_section = line[1:-1]
-        continue
-
-      if current_section == None:
-        continue
-
-      found = line.find('+=')
-      opr_len = 2
-      if found == -1:
-        found = line.find('=')
-        opr_len = 1
-      if found == -1:
-        continue
-
-      namespace = line[:found].strip()
-      if not namespace.endswith(".paths"):
-        # check ".paths" only
-        continue
-      namespace = '[' + current_section + ']' + namespace
-      values = line[found + opr_len:].strip()
-      directories = values.split(':')
-
-      for directory in directories:
-        if any(vendor_dir in directory for vendor_dir in VENDOR_DIRECTORIES):
-          if namespace in configs:
-            configs[namespace].append(directory)
-          else:
-            configs[namespace] = [directory]
-
-  return configs
-
-def get_snapshot_config(version):
-  """Finds the ld.config.{version}.txt file from the VNDK snapshot directory.
-  In the vndk prebuilt directory (prebuilts/vndk/v{version}), it searches
-  {arch}/configs/ld.config.{version}.txt file, where {arch} is one of ('arm64',
-  'arm', 'x86_64', 'x86').
-
-  Args:
-    version: string, the VNDK snapshot version to search.
-  Returns:
-    ld_config_file: string, relative path to ld.config.{version}.txt
-  """
-  arch_list = ('arm64', 'arm', 'x86_64', 'x86')
-  for arch in arch_list:
-    ld_config_file = (PREBUILTS_VNDK_DIR
-                + "/v{0}/{1}/configs/ld.config.{0}.txt".format(version, arch))
-    if os.path.isfile(ld_config_file):
-      return ld_config_file
-  print("error: cannot find ld.config.{0}.txt file in snapshot v{0}"
-                                                        .format(version))
-  sys.exit(1)
-
-def check_backward_compatibility(ld_config, vndk_snapshot_version):
-  """Checks backward compatibility for current ld.config.txt file with the
-  old ld.config.txt file. If any of the vendor directories in the old namespace
-  configurations are missing, the test will fail. It is allowed to have new
-  vendor directories in current ld.config.txt file.
-
-  Args:
-    ld_config: string, relative path to current ld.config.txt file.
-    vndk_snapshot_version: string, the VNDK snapshot version that has an old
-                           ld.config.txt file to compare.
-  Returns:
-    result: bool, True if the current configuration is backward compatible.
-  """
-  current_config = get_vendor_configuration(ld_config)
-  old_config = get_vendor_configuration(
-                                get_snapshot_config(vndk_snapshot_version))
-  for namespace in old_config:
-    if namespace not in current_config:
-      print("error: cannot find %s which was provided in ld.config.%s.txt"
-                                        % (namespace, vndk_snapshot_version))
-      return False
-    for path in old_config[namespace]:
-      if not path in current_config[namespace]:
-        print("error: %s for %s in ld.config.%s.txt are missing in %s"
-                % (path, namespace, vndk_snapshot_version, ld_config))
-        return False
-  return True
-
-def main():
-  if len(sys.argv) != 2:
-    print ("Usage: %s target_ld_config_txt_file_name" % sys.argv[0])
-    sys.exit(1)
-
-  latest_vndk_snapshot_version = find_latest_vndk_snapshot_version()
-  if not check_backward_compatibility(sys.argv[1],
-                                          latest_vndk_snapshot_version):
-    print("error: %s has backward incompatible changes to old "
-          "vendor partition." % sys.argv[1])
-    sys.exit(1)
-
-  # Current ld.config.txt file is backward compatible
-  sys.exit(0)
-
-if __name__ == '__main__':
-  main()
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
deleted file mode 100644
index 44f7b65..0000000
--- a/rootdir/update_and_install_ld_config.mk
+++ /dev/null
@@ -1,207 +0,0 @@
-#####################################################################
-# Builds linker config file, ld.config.txt, from the specified template
-# under $(LOCAL_PATH)/etc/*.
-#
-# Inputs:
-#   (expected to follow an include of $(BUILD_SYSTEM)/base_rules.mk)
-#   ld_config_template: template linker config file to use,
-#                       e.g. $(LOCAL_PATH)/etc/ld.config.txt
-#   vndk_version: version of the VNDK library lists used to update the
-#                 template linker config file, e.g. 28
-#   lib_list_from_prebuilts: should be set to 'true' if the VNDK library
-#                            lists should be read from /prebuilts/vndk/*
-#   libz_is_llndk: should be set to 'true' if libz must be included in
-#                  llndk and not in vndk-sp
-# Outputs:
-#   Builds and installs ld.config.$VER.txt or ld.config.vndk_lite.txt
-#####################################################################
-
-# Read inputs
-ld_config_template := $(strip $(ld_config_template))
-check_backward_compatibility := $(strip $(check_backward_compatibility))
-vndk_version := $(strip $(vndk_version))
-lib_list_from_prebuilts := $(strip $(lib_list_from_prebuilts))
-libz_is_llndk := $(strip $(libz_is_llndk))
-
-my_vndk_use_core_variant := $(TARGET_VNDK_USE_CORE_VARIANT)
-ifeq ($(lib_list_from_prebuilts),true)
-my_vndk_use_core_variant := false
-endif
-
-compatibility_check_script := \
-  $(LOCAL_PATH)/ld_config_backward_compatibility_check.py
-intermediates_dir := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE))
-library_lists_dir := $(intermediates_dir)
-ifeq ($(lib_list_from_prebuilts),true)
-  library_lists_dir := prebuilts/vndk/v$(vndk_version)/$(TARGET_ARCH)/configs
-endif
-
-llndk_libraries_file := $(library_lists_dir)/llndk.libraries.$(vndk_version).txt
-vndksp_libraries_file := $(library_lists_dir)/vndksp.libraries.$(vndk_version).txt
-vndkcore_libraries_file := $(library_lists_dir)/vndkcore.libraries.$(vndk_version).txt
-vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.$(vndk_version).txt
-llndk_moved_to_apex_libraries_file := $(library_lists_dir)/llndkinapex.libraries.txt
-ifeq ($(my_vndk_use_core_variant),true)
-vndk_using_core_variant_libraries_file := $(library_lists_dir)/vndk_using_core_variant.libraries.$(vndk_version).txt
-endif
-
-sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
-  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
-  $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
-  $(UBSAN_RUNTIME_LIBRARY) \
-  $(TSAN_RUNTIME_LIBRARY) \
-  $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
-  $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
-  $(2ND_UBSAN_RUNTIME_LIBRARY) \
-  $(2ND_TSAN_RUNTIME_LIBRARY)))
-# If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.
-vndk_version_suffix := $(if $(vndk_version),-$(vndk_version))
-
-ifneq ($(lib_list_from_prebuilts),true)
-ifeq ($(libz_is_llndk),true)
-  llndk_libraries_list := $(LLNDK_LIBRARIES) libz
-  vndksp_libraries_list := $(filter-out libz,$(VNDK_SAMEPROCESS_LIBRARIES))
-else
-  llndk_libraries_list := $(LLNDK_LIBRARIES)
-  vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)
-endif
-
-# LLNDK libraries that has been moved to an apex package and no longer are present on
-# /system image.
-llndk_libraries_moved_to_apex_list:=$(LLNDK_MOVED_TO_APEX_LIBRARIES)
-
-# Returns the unique installed basenames of a module, or module.so if there are
-# none.  The guess is to handle cases like libc, where the module itself is
-# marked uninstallable but a symlink is installed with the name libc.so.
-# $(1): list of libraries
-# $(2): suffix to to add to each library (not used for guess)
-define module-installed-files-or-guess
-$(foreach lib,$(1),$(or $(strip $(sort $(notdir $(call module-installed-files,$(lib)$(2))))),$(lib).so))
-endef
-
-# $(1): list of libraries
-# $(2): suffix to add to each library
-# $(3): output file to write the list of libraries to
-define write-libs-to-file
-$(3): PRIVATE_LIBRARIES := $(1)
-$(3): PRIVATE_SUFFIX := $(2)
-$(3):
-	echo -n > $$@ && $$(foreach so,$$(call module-installed-files-or-guess,$$(PRIVATE_LIBRARIES),$$(PRIVATE_SUFFIX)),echo $$(so) >> $$@;)
-endef
-$(eval $(call write-libs-to-file,$(llndk_libraries_list),,$(llndk_libraries_file)))
-$(eval $(call write-libs-to-file,$(vndksp_libraries_list),.vendor,$(vndksp_libraries_file)))
-$(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),.vendor,$(vndkcore_libraries_file)))
-$(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),.vendor,$(vndkprivate_libraries_file)))
-ifeq ($(my_vndk_use_core_variant),true)
-$(eval $(call write-libs-to-file,$(VNDK_USING_CORE_VARIANT_LIBRARIES),,$(vndk_using_core_variant_libraries_file)))
-endif
-endif # ifneq ($(lib_list_from_prebuilts),true)
-
-# Given a file with a list of libs, filter-out the VNDK private libraries
-# and write resulting list to a new file in "a:b:c" format
-#
-# $(1): libs file from which to filter-out VNDK private libraries
-# $(2): output file with the filtered list of lib names
-$(LOCAL_BUILT_MODULE): private-filter-out-private-libs = \
-  paste -sd ":" $(1) > $(2) && \
-  while read -r privatelib; do sed -i.bak "s/$$privatelib//" $(2) ; done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
-  sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(2) && \
-  rm -f $(2).bak
-
-# # Given a file with a list of libs in "a:b:c" format, filter-out the LLNDK libraries migrated into apex file
-# # and write resulting list to a new file in "a:b:c" format
- $(LOCAL_BUILT_MODULE): private-filter-out-llndk-in-apex-libs = \
-   for lib in $(PRIVATE_LLNDK_LIBRARIES_MOVED_TO_APEX_LIST); do sed -i.bak s/$$lib.so// $(1); done && \
-   sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(1) && \
-   rm -f $(1).bak
-
-$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_FILE := $(llndk_libraries_file)
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SP_LIBRARIES_FILE := $(vndksp_libraries_file)
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES_FILE := $(vndkcore_libraries_file)
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE := $(vndkprivate_libraries_file)
-$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_SUFFIX := $(vndk_version_suffix)
-$(LOCAL_BUILT_MODULE): PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
-$(LOCAL_BUILT_MODULE): PRIVATE_COMP_CHECK_SCRIPT := $(compatibility_check_script)
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_TAG := \#VNDK$(vndk_version)\#
-$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_MOVED_TO_APEX_LIST := $(llndk_libraries_moved_to_apex_list)
-deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
-  $(vndkprivate_libraries_file)
-ifeq ($(check_backward_compatibility),true)
-deps += $(compatibility_check_script) $(wildcard prebuilts/vndk/*/*/configs/ld.config.*.txt)
-endif
-ifeq ($(my_vndk_use_core_variant),true)
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE := $(vndk_using_core_variant_libraries_file)
-deps += $(vndk_using_core_variant_libraries_file)
-endif
-
-$(LOCAL_BUILT_MODULE): $(ld_config_template) $(deps)
-	@echo "Generate: $< -> $@"
-ifeq ($(check_backward_compatibility),true)
-	@echo "Checking backward compatibility..."
-	$(hide) $(PRIVATE_COMP_CHECK_SCRIPT) $<
-endif
-	@mkdir -p $(dir $@)
-	$(call private-filter-out-private-libs,$(PRIVATE_LLNDK_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
-	$(call private-filter-out-llndk-in-apex-libs,$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
-	$(hide) sed -e "s?%LLNDK_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)?g" $< >$@
-	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_SP_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)
-	$(hide) sed -i.bak -e "s?%VNDK_SAMEPROCESS_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)?g" $@
-	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_CORE_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)
-	$(hide) sed -i.bak -e "s?%VNDK_CORE_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)?g" $@
-
-ifeq ($(my_vndk_use_core_variant),true)
-	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)
-	$(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%?,vndk_in_system?g" $@
-	$(hide) sed -i.bak -e "s?%VNDK_USING_CORE_VARIANT_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)?g" $@
-else
-	$(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%??g" $@
-	# Unlike LLNDK or VNDK-SP, VNDK_USING_CORE_VARIANT_LIBRARIES can be nothing
-	# if TARGET_VNDK_USE_CORE_VARIANT is not set.  In this case, we need to remove
-	# the entire line in the linker config so that we are not left with a line
-	# like:
-	#   namespace.vndk.link.vndk_in_system.shared_libs =
-	$(hide) sed -i.bak -e 's?^.*= %VNDK_USING_CORE_VARIANT_LIBRARIES%$$??' $@
-endif
-
-	$(hide) echo -n > $(PRIVATE_INTERMEDIATES_DIR)/private_llndk && \
-	while read -r privatelib; \
-	do (grep $$privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk ; \
-	done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
-	paste -sd ":" $(PRIVATE_INTERMEDIATES_DIR)/private_llndk | \
-	sed -i.bak -e "s?%PRIVATE_LLNDK_LIBRARIES%?$$(cat -)?g" $@
-
-	$(hide) sed -i.bak -e "s?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g" $@
-	$(hide) sed -i.bak -e "s?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g" $@
-	$(hide) sed -i.bak -e "s?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g" $@
-	$(hide) sed -i.bak -e "s?%SYSTEM_EXT%?$(TARGET_COPY_OUT_SYSTEM_EXT)?g" $@
-	$(hide) sed -i.bak -e "s?^$(PRIVATE_VNDK_VERSION_TAG)??g" $@
-	$(hide) sed -i.bak "/^\#VNDK[0-9]\{2\}\#.*$$/d" $@
-	$(hide) rm -f $@.bak
-
-ld_config_template :=
-check_backward_compatibility :=
-vndk_version :=
-lib_list_from_prebuilts :=
-libz_is_llndk :=
-compatibility_check_script :=
-intermediates_dir :=
-library_lists_dir :=
-llndk_libraries_file :=
-llndk_moved_to_apex_libraries_file :=
-vndksp_libraries_file :=
-vndkcore_libraries_file :=
-vndkprivate_libraries_file :=
-deps :=
-sanitizer_runtime_libraries :=
-vndk_version_suffix :=
-llndk_libraries_list :=
-vndksp_libraries_list :=
-write-libs-to-file :=
-
-ifeq ($(my_vndk_use_core_variant),true)
-vndk_using_core_variant_libraries_file :=
-vndk_using_core_variant_libraries_list :=
-endif
-
-my_vndk_use_core_variant :=
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index d391cc1..3bee875 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -218,25 +218,28 @@
 
 bzip2: bzcat bzip2 bunzip2
 
+gavinhoward/bc: bc
+
 one-true-awk: awk
 
 toolbox: getevent getprop setprop start stop
 
-toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
-chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
-diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
-false fgrep file find flock fmt free freeramdisk fsfreeze getconf
-getenforce getfattr grep groups gunzip gzip head help hostname hwclock
-i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
-install ionice iorenice iotop kill killall ln load\_policy log logname
-losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+toybox: acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+chown chroot chrt cksum clear cmp comm cp cpio cut date dd devmem
+df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze fsync getconf
+getenforce getfattr getopt grep groups gunzip gzip head help hostname
+hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd
+insmod install ionice iorenice iotop kill killall ln load\_policy log
+logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
 mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
 mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
 paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
-printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
-rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
-setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
-stat strings stty swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout top touch tr traceroute traceroute6 true truncate tty tunctl
-ulimit umount uname uniq unix2dos unlink unshare uptime usleep uudecode
-uuencode uuidgen vconfig vmstat watch wc which whoami xargs xxd yes zcat
+printf prlimit ps pwd pwdx readelf readlink realpath renice restorecon
+rev rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort
+split stat strings stty swapoff swapon sync sysctl tac tail tar taskset
+tee time timeout top touch tr traceroute traceroute6 true truncate
+tty tunctl ulimit umount uname uniq unix2dos unlink unshare uptime
+usleep uudecode uuencode uuidgen vconfig vi vmstat watch wc which
+whoami xargs xxd yes zcat
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 3817fb5..e35bd6f 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -51,7 +51,7 @@
     storaged_sp->init();
     storaged_sp->report_storage_info();
 
-    LOG_TO(SYSTEM, INFO) << "storaged: Start";
+    LOG(INFO) << "storaged: Start";
 
     for (;;) {
         storaged_sp->event_checked();
@@ -76,6 +76,8 @@
     bool flag_dump_perf = false;
     int opt;
 
+    android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+
     for (;;) {
         int opt_idx = 0;
         static struct option long_options[] = {
@@ -124,13 +126,13 @@
         pthread_t storaged_main_thread;
         errno = pthread_create(&storaged_main_thread, NULL, storaged_main, NULL);
         if (errno != 0) {
-            PLOG_TO(SYSTEM, ERROR) << "Failed to create main thread";
+            PLOG(ERROR) << "Failed to create main thread";
             return -1;
         }
 
         if (StoragedService::start() != android::OK ||
             StoragedPrivateService::start() != android::OK) {
-            PLOG_TO(SYSTEM, ERROR) << "Failed to start storaged service";
+            PLOG(ERROR) << "Failed to start storaged service";
             return -1;
         }
 
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 1d934a2..573b8c5 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -97,25 +97,23 @@
 
     health = get_health_service();
     if (health == NULL) {
-        LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
+        LOG(WARNING) << "health: failed to find IHealth service";
         return;
     }
 
     BatteryStatus status = BatteryStatus::UNKNOWN;
     auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
         if (r != Result::SUCCESS) {
-            LOG_TO(SYSTEM, WARNING)
-                << "health: cannot get battery status " << toString(r);
+            LOG(WARNING) << "health: cannot get battery status " << toString(r);
             return;
         }
         if (v == BatteryStatus::UNKNOWN) {
-            LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
+            LOG(WARNING) << "health: invalid battery status";
         }
         status = v;
     });
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
-            << ret.description();
+        LOG(WARNING) << "health: get charge status transaction error " << ret.description();
     }
 
     mUidm.init(is_charger_on(status));
@@ -126,11 +124,11 @@
 
 void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
     if (health != NULL && interfacesEqual(health, who.promote())) {
-        LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
+        LOG(ERROR) << "health service died, exiting";
         android::hardware::IPCThreadState::self()->stopProcess();
         exit(1);
     } else {
-        LOG_TO(SYSTEM, ERROR) << "unknown service died";
+        LOG(ERROR) << "unknown service died";
     }
 }
 
@@ -192,7 +190,7 @@
         reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
         uid_io_usage.ByteSize());
     if (proto.crc() != computed_crc) {
-        LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
+        LOG(WARNING) << "CRC mismatch in " << proto_file;
         return;
     }
 
@@ -228,8 +226,7 @@
     char* data = nullptr;
     if (posix_memalign(reinterpret_cast<void**>(&data),
                        pagesize, proto->ByteSize())) {
-        PLOG_TO(SYSTEM, ERROR) << "Faied to alloc aligned buffer (size: "
-                               << proto->ByteSize() << ")";
+        PLOG(ERROR) << "Faied to alloc aligned buffer (size: " << proto->ByteSize() << ")";
         return data;
     }
 
@@ -246,7 +243,7 @@
                     (user_id == USER_SYSTEM ? O_DIRECT : 0),
                  S_IRUSR | S_IWUSR)));
     if (fd == -1) {
-        PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
+        PLOG(ERROR) << "Faied to open tmp file: " << tmp_file;
         return;
     }
 
@@ -261,7 +258,7 @@
             start = steady_clock::now();
             ret = write(fd, data, MIN(benchmark_unit_size, size));
             if (ret <= 0) {
-                PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+                PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
                 return;
             }
             end = steady_clock::now();
@@ -284,7 +281,7 @@
         }
     } else {
         if (!WriteFully(fd, data, size)) {
-            PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+            PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
             return;
         }
     }
@@ -343,22 +340,21 @@
     if (mConfig.event_time_check_usec &&
         clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
         check_time = false;
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        PLOG(ERROR) << "clock_gettime() failed";
     }
 
     event();
 
     if (mConfig.event_time_check_usec && check_time) {
         if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
-            PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+            PLOG(ERROR) << "clock_gettime() failed";
             return;
         }
         int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
                        (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
         if (cost > mConfig.event_time_check_usec) {
-            LOG_TO(SYSTEM, ERROR)
-                << "event loop spent " << cost << " usec, threshold "
-                << mConfig.event_time_check_usec << " usec";
+            LOG(ERROR) << "event loop spent " << cost << " usec, threshold "
+                       << mConfig.event_time_check_usec << " usec";
         }
     }
 }
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 8b5001d..52bd4e0 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -41,8 +41,8 @@
     // skip if the input structure are all zeros
     if (perf == NULL || perf->is_zero()) return;
 
-    LOG_TO(SYSTEM, INFO) << "disk_perf " << type
-              << " rd: " << perf->read_perf << " kbps, " << perf->read_ios << " iops"
+    LOG(INFO) << "disk_perf " << type << " rd: " << perf->read_perf << " kbps, " << perf->read_ios
+              << " iops"
               << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
               << " q: " << perf->queue;
 }
@@ -71,7 +71,7 @@
     // when system is running.
     int ret = clock_gettime(CLOCK_MONOTONIC, ts);
     if (ret < 0) {
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        PLOG(ERROR) << "clock_gettime() failed";
         return false;
     }
     return true;
@@ -93,7 +93,7 @@
 
     std::string buffer;
     if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
-        PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+        PLOG(ERROR) << disk_stats_path << ": ReadFileToString failed.";
         return false;
     }
 
@@ -130,12 +130,12 @@
     bool success = false;
     auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
         if (result == Result::NOT_SUPPORTED) {
-            LOG_TO(SYSTEM, DEBUG) << "getDiskStats is not supported on health HAL.";
+            LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
             return;
         }
         if (result != Result::SUCCESS || halStats.size() == 0) {
-            LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
-                                  << " and size " << halStats.size();
+            LOG(ERROR) << "getDiskStats failed with result " << toString(result) << " and size "
+                       << halStats.size();
             return;
         }
 
@@ -144,7 +144,7 @@
     });
 
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with " << ret.description();
+        LOG(ERROR) << "getDiskStats failed with " << ret.description();
         return false;
     }
 
@@ -199,9 +199,9 @@
 void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
 {
     if (dst->end_time != 0 && dst->end_time != src->start_time) {
-        LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
-            << " are added. dst end with " << dst->end_time
-            << ", src start with " << src->start_time;
+        LOG(WARNING) << "Two dis-continuous periods of diskstats"
+                     << " are added. dst end with " << dst->end_time << ", src start with "
+                     << src->start_time;
     }
 
     *dst += *src;
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 6668cf3..bb21829 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -76,7 +76,7 @@
     if (!perf_history.has_day_start_sec() ||
         perf_history.daily_perf_size() > (int)daily_perf.size() ||
         perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
-        LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto";
+        LOG(ERROR) << "Invalid IOPerfHistory proto";
         return;
     }
 
@@ -114,7 +114,7 @@
 {
     struct statvfs buf;
     if (statvfs(userdata_path.c_str(), &buf) != 0) {
-        PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
+        PLOG(WARNING) << "Failed to get userdata info";
         return;
     }
 
@@ -328,12 +328,12 @@
 void health_storage_info_t::report() {
     auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
         if (result == Result::NOT_SUPPORTED) {
-            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo is not supported on health HAL.";
+            LOG(DEBUG) << "getStorageInfo is not supported on health HAL.";
             return;
         }
         if (result != Result::SUCCESS || halInfos.size() == 0) {
-            LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with result " << toString(result)
-                                  << " and size " << halInfos.size();
+            LOG(ERROR) << "getStorageInfo failed with result " << toString(result) << " and size "
+                       << halInfos.size();
             return;
         }
         set_values_from_hal_storage_info(halInfos[0]);
@@ -341,7 +341,7 @@
     });
 
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with " << ret.description();
+        LOG(ERROR) << "getStorageInfo failed with " << ret.description();
     }
 }
 
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 55380ba..f47bf72 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -71,8 +71,7 @@
         !ParseUint(fields[8],  &io[BACKGROUND].write_bytes) ||
         !ParseUint(fields[9],  &io[FOREGROUND].fsync) ||
         !ParseUint(fields[10], &io[BACKGROUND].fsync)) {
-        LOG_TO(SYSTEM, WARNING) << "Invalid uid I/O stats: \""
-                                << s << "\"";
+        LOG(WARNING) << "Invalid uid I/O stats: \"" << s << "\"";
         return false;
     }
     return true;
@@ -95,8 +94,7 @@
         !ParseUint(fields[size - 3], &io[BACKGROUND].write_bytes) ||
         !ParseUint(fields[size - 2], &io[FOREGROUND].fsync) ||
         !ParseUint(fields[size - 1], &io[BACKGROUND].fsync)) {
-        LOG_TO(SYSTEM, WARNING) << "Invalid task I/O stats: \""
-                                << s << "\"";
+        LOG(WARNING) << "Invalid task I/O stats: \"" << s << "\"";
         return false;
     }
     comm = Join(std::vector<std::string>(
@@ -123,13 +121,13 @@
 {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) {
-        LOG_TO(SYSTEM, ERROR) << "defaultServiceManager failed";
+        LOG(ERROR) << "defaultServiceManager failed";
         return;
     }
 
     sp<IBinder> binder = sm->getService(String16("package_native"));
     if (binder == NULL) {
-        LOG_TO(SYSTEM, ERROR) << "getService package_native failed";
+        LOG(ERROR) << "getService package_native failed";
         return;
     }
 
@@ -137,8 +135,7 @@
     std::vector<std::string> names;
     binder::Status status = package_mgr->getNamesForUids(uids, &names);
     if (!status.isOk()) {
-        LOG_TO(SYSTEM, ERROR) << "package_native::getNamesForUids failed: "
-                              << status.exceptionMessage();
+        LOG(ERROR) << "package_native::getNamesForUids failed: " << status.exceptionMessage();
         return;
     }
 
@@ -158,7 +155,7 @@
     std::unordered_map<uint32_t, uid_info> uid_io_stats;
     std::string buffer;
     if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
-        PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
+        PLOG(ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
         return uid_io_stats;
     }
 
diff --git a/trusty/Android.bp b/trusty/Android.bp
deleted file mode 100644
index 2fb2e19..0000000
--- a/trusty/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-subdirs = [
-    "gatekeeper",
-    "keymaster",
-    "libtrusty",
-    "storage/*",
-]
diff --git a/trusty/confirmationui/.clang-format b/trusty/confirmationui/.clang-format
new file mode 100644
index 0000000..b0dc94c
--- /dev/null
+++ b/trusty/confirmationui/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/trusty/confirmationui/Android.bp b/trusty/confirmationui/Android.bp
new file mode 100644
index 0000000..60e0e71
--- /dev/null
+++ b/trusty/confirmationui/Android.bp
@@ -0,0 +1,95 @@
+// Copyright (C) 2020 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK.  Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+cc_binary {
+    name: "android.hardware.confirmationui@1.0-service.trusty",
+    relative_install_path: "hw",
+    vendor: true,
+    shared_libs: [
+        "android.hardware.confirmationui@1.0",
+        "android.hardware.confirmationui.not-so-secure-input",
+        "android.hardware.confirmationui@1.0-lib.trusty",
+        "libbase",
+        "libhidlbase",
+        "libutils",
+    ],
+
+    init_rc: ["android.hardware.confirmationui@1.0-service.trusty.rc"],
+
+    vintf_fragments: ["android.hardware.confirmationui@1.0-service.trusty.xml"],
+
+    srcs: [
+        "service.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DTEEUI_USE_STD_VECTOR",
+    ],
+}
+
+cc_library {
+    name: "android.hardware.confirmationui@1.0-lib.trusty",
+    vendor: true,
+    shared_libs: [
+        "android.hardware.confirmationui@1.0",
+        "android.hardware.keymaster@4.0",
+        "libbase",
+        "libhidlbase",
+        "libteeui_hal_support",
+        "libtrusty",
+        "libutils",
+    ],
+
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "TrustyApp.cpp",
+        "TrustyConfirmationUI.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DTEEUI_USE_STD_VECTOR",
+    ],
+}
+
+cc_library {
+    name: "android.hardware.confirmationui.not-so-secure-input",
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+        "libteeui_hal_support",
+    ],
+
+    srcs: [
+        "NotSoSecureInput.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DTEEUI_USE_STD_VECTOR",
+    ],
+}
\ No newline at end of file
diff --git a/trusty/confirmationui/NotSoSecureInput.cpp b/trusty/confirmationui/NotSoSecureInput.cpp
new file mode 100644
index 0000000..3d9a2d6
--- /dev/null
+++ b/trusty/confirmationui/NotSoSecureInput.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <endian.h>
+#include <memory>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+#include <secure_input/evdev.h>
+#include <secure_input/secure_input_device.h>
+#include <teeui/utils.h>
+
+#include <initializer_list>
+
+using namespace secure_input;
+
+using teeui::AuthTokenKey;
+using teeui::ByteBufferProxy;
+using teeui::Hmac;
+using teeui::optional;
+using teeui::ResponseCode;
+using teeui::TestKeyBits;
+
+constexpr const auto kTestKey = AuthTokenKey::fill(static_cast<uint8_t>(TestKeyBits::BYTE));
+
+class SecureInputHMacer {
+  public:
+    static optional<Hmac> hmac256(const AuthTokenKey& key,
+                                  std::initializer_list<ByteBufferProxy> buffers) {
+        HMAC_CTX hmacCtx;
+        HMAC_CTX_init(&hmacCtx);
+        if (!HMAC_Init_ex(&hmacCtx, key.data(), key.size(), EVP_sha256(), nullptr)) {
+            return {};
+        }
+        for (auto& buffer : buffers) {
+            if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
+                return {};
+            }
+        }
+        Hmac result;
+        if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
+            return {};
+        }
+        return result;
+    }
+};
+
+using HMac = teeui::HMac<SecureInputHMacer>;
+
+Nonce generateNonce() {
+    /*
+     * Completely random nonce.
+     * Running the secure input protocol from the HAL service is not secure
+     * because we don't trust the non-secure world (i.e., HLOS/Android/Linux). So
+     * using a constant "nonce" here does not weaken security. If this code runs
+     * on a truly trustworthy source of input events this function needs to return
+     * hight entropy nonces.
+     * As of this writing the call to RAND_bytes is commented, because the
+     * emulator this HAL service runs on does not have a good source of entropy.
+     * It would block the call to RAND_bytes indefinitely.
+     */
+    Nonce result{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
+    // RAND_bytes(result.data(), result.size());
+    return result;
+}
+
+/**
+ * This is an implementation of the SecureInput protocol in unserspace. This is
+ * just an example and should not be used as is. The protocol implemented her
+ * should be used by a trusted input device that can assert user events with
+ * high assurance even if the HLOS kernel is compromised. A confirmationui HAL
+ * that links directly against this implementation is not secure and shal not be
+ * used on a production device.
+ */
+class NotSoSecureInput : public SecureInput {
+  public:
+    NotSoSecureInput(HsBeginCb hsBeginCb, HsFinalizeCb hsFinalizeCb, DeliverEventCb deliverEventCb,
+                     InputResultCb inputResultCb)
+        : hsBeginCb_{hsBeginCb}, hsFinalizeCb_{hsFinalizeCb}, deliverEventCb_{deliverEventCb},
+          inputResultCb_{inputResultCb}, discardEvents_{true} {}
+
+    operator bool() const override { return true; }
+
+    void handleEvent(const EventDev& evdev) override {
+        bool gotEvent;
+        input_event evt;
+        std::tie(gotEvent, evt) = evdev.readEvent();
+        while (gotEvent) {
+            if (!(discardEvents_) && evt.type == EV_KEY &&
+                (evt.code == KEY_POWER || evt.code == KEY_VOLUMEDOWN || evt.code == KEY_VOLUMEUP) &&
+                evt.value == 1) {
+                DTupKeyEvent event = DTupKeyEvent::RESERVED;
+
+                // Translate the event code into DTupKeyEvent which the TA understands.
+                switch (evt.code) {
+                case KEY_POWER:
+                    event = DTupKeyEvent::PWR;
+                    break;
+                case KEY_VOLUMEDOWN:
+                    event = DTupKeyEvent::VOL_DOWN;
+                    break;
+                case KEY_VOLUMEUP:
+                    event = DTupKeyEvent::VOL_UP;
+                    break;
+                }
+
+                // The event goes into the HMAC in network byte order.
+                uint32_t keyEventBE = htobe32(static_cast<uint32_t>(event));
+                auto signature = HMac::hmac256(kTestKey, kConfirmationUIEventLabel,
+                                               teeui::bytesCast(keyEventBE), nCi_);
+
+                teeui::ResponseCode rc;
+                InputResponse ir;
+                auto response = std::tie(rc, ir);
+                if (event != DTupKeyEvent::RESERVED) {
+                    response = deliverEventCb_(event, *signature);
+                    if (rc != ResponseCode::OK) {
+                        LOG(ERROR) << "DeliverInputEvent returned with " << uint32_t(rc);
+                        inputResultCb_(rc);
+                    } else {
+                        switch (ir) {
+                        case InputResponse::OK:
+                            inputResultCb_(rc);
+                            break;
+                        case InputResponse::PENDING_MORE:
+                            rc = performDTUPHandshake();
+                            if (rc != ResponseCode::OK) {
+                                inputResultCb_(rc);
+                            }
+                            break;
+                        case InputResponse::TIMED_OUT:
+                            inputResultCb_(rc);
+                            break;
+                        }
+                    }
+                }
+            }
+            std::tie(gotEvent, evt) = evdev.readEvent();
+        }
+    }
+
+    void start() override {
+        auto rc = performDTUPHandshake();
+        if (rc != ResponseCode::OK) {
+            inputResultCb_(rc);
+        }
+        discardEvents_ = false;
+    };
+
+  private:
+    teeui::ResponseCode performDTUPHandshake() {
+        ResponseCode rc;
+        LOG(INFO) << "Start handshake";
+        Nonce nCo;
+        std::tie(rc, nCo) = hsBeginCb_();
+        if (rc != ResponseCode::OK) {
+            LOG(ERROR) << "Failed to begin secure input handshake (" << uint32_t(rc) << ")";
+            return rc;
+        }
+
+        nCi_ = generateNonce();
+        rc =
+            hsFinalizeCb_(*HMac::hmac256(kTestKey, kConfirmationUIHandshakeLabel, nCo, nCi_), nCi_);
+
+        if (rc != ResponseCode::OK) {
+            LOG(ERROR) << "Failed to finalize secure input handshake (" << uint32_t(rc) << ")";
+            return rc;
+        }
+        return ResponseCode::OK;
+    }
+
+    HsBeginCb hsBeginCb_;
+    HsFinalizeCb hsFinalizeCb_;
+    DeliverEventCb deliverEventCb_;
+    InputResultCb inputResultCb_;
+
+    std::atomic_bool discardEvents_;
+    Nonce nCi_;
+};
+
+namespace secure_input {
+
+std::shared_ptr<SecureInput> createSecureInput(SecureInput::HsBeginCb hsBeginCb,
+                                               SecureInput::HsFinalizeCb hsFinalizeCb,
+                                               SecureInput::DeliverEventCb deliverEventCb,
+                                               SecureInput::InputResultCb inputResultCb) {
+    return std::make_shared<NotSoSecureInput>(hsBeginCb, hsFinalizeCb, deliverEventCb,
+                                              inputResultCb);
+}
+
+}  // namespace secure_input
diff --git a/trusty/confirmationui/README b/trusty/confirmationui/README
new file mode 100644
index 0000000..45d4e76
--- /dev/null
+++ b/trusty/confirmationui/README
@@ -0,0 +1,20 @@
+## Secure UI Architecture
+
+To implement confirmationui a secure UI architecture is required. This entails a way
+to display the confirmation dialog driven by a reduced trusted computing base, typically
+a trusted execution environment (TEE), without having to rely on Linux and the Android
+system for integrity and authenticity of input events. This implementation provides
+neither. But it provides most of the functionlity required to run a full Android Protected
+Confirmation feature when integrated into a secure UI architecture.
+
+## Secure input (NotSoSecureInput)
+
+This implementation does not provide any security guaranties.
+The input method (NotSoSecureInput) runs a cryptographic protocols that is
+sufficiently secure IFF the end point is implemented on a trustworthy
+secure input device. But since the endpoint is currently in the HAL
+service itself this implementation is not secure.
+
+NOTE that a secure input device end point needs a good source of entropy
+for generating nonces. The current implementation (NotSoSecureInput.cpp#generateNonce)
+uses a constant nonce.
\ No newline at end of file
diff --git a/trusty/confirmationui/TrustyApp.cpp b/trusty/confirmationui/TrustyApp.cpp
new file mode 100644
index 0000000..e4c68f9
--- /dev/null
+++ b/trusty/confirmationui/TrustyApp.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2020, 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 "TrustyApp.h"
+
+#include <android-base/logging.h>
+#include <sys/uio.h>
+#include <trusty/tipc.h>
+
+namespace android {
+namespace trusty {
+
+// 0x1000 is the message buffer size but we need to leave some space for a protocol header.
+// This assures that packets can always be read/written in one read/write operation.
+static constexpr const uint32_t kPacketSize = 0x1000 - 32;
+
+enum class PacketType : uint32_t {
+    SND,
+    RCV,
+    ACK,
+};
+
+struct PacketHeader {
+    PacketType type;
+    uint32_t remaining;
+};
+
+const char* toString(PacketType t) {
+    switch (t) {
+    case PacketType::SND:
+        return "SND";
+    case PacketType::RCV:
+        return "RCV";
+    case PacketType::ACK:
+        return "ACK";
+    default:
+        return "UNKNOWN";
+    }
+}
+
+static constexpr const uint32_t kHeaderSize = sizeof(PacketHeader);
+static constexpr const uint32_t kPayloadSize = kPacketSize - kHeaderSize;
+
+ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+                  uint8_t* iend) {
+    while (obegin != oend) {
+        PacketHeader header = {
+            .type = PacketType::SND,
+            .remaining = uint32_t(oend - obegin),
+        };
+        uint32_t body_size = std::min(kPayloadSize, header.remaining);
+        iovec iov[] = {
+            {
+                .iov_base = &header,
+                .iov_len = kHeaderSize,
+            },
+            {
+                .iov_base = const_cast<uint8_t*>(obegin),
+                .iov_len = body_size,
+            },
+        };
+        int rc = writev(handle, iov, 2);
+        if (!rc) {
+            PLOG(ERROR) << "Error sending SND message. " << rc;
+            return rc;
+        }
+
+        obegin += body_size;
+
+        rc = read(handle, &header, kHeaderSize);
+        if (!rc) {
+            PLOG(ERROR) << "Error reading ACK. " << rc;
+            return rc;
+        }
+
+        if (header.type != PacketType::ACK || header.remaining != oend - obegin) {
+            LOG(ERROR) << "malformed ACK";
+            return -1;
+        }
+    }
+
+    ssize_t remaining = 0;
+    auto begin = ibegin;
+    do {
+        PacketHeader header = {
+            .type = PacketType::RCV,
+            .remaining = 0,
+        };
+
+        iovec iov[] = {
+            {
+                .iov_base = &header,
+                .iov_len = kHeaderSize,
+            },
+            {
+                .iov_base = begin,
+                .iov_len = uint32_t(iend - begin),
+            },
+        };
+
+        ssize_t rc = writev(handle, iov, 1);
+        if (!rc) {
+            PLOG(ERROR) << "Error sending RCV message. " << rc;
+            return rc;
+        }
+
+        rc = readv(handle, iov, 2);
+        if (rc < 0) {
+            PLOG(ERROR) << "Error reading response. " << rc;
+            return rc;
+        }
+
+        uint32_t body_size = std::min(kPayloadSize, header.remaining);
+        if (body_size != rc - kHeaderSize) {
+            LOG(ERROR) << "Unexpected amount of data: " << rc;
+            return -1;
+        }
+
+        remaining = header.remaining - body_size;
+        begin += body_size;
+    } while (remaining);
+
+    return begin - ibegin;
+}
+
+TrustyApp::TrustyApp(const std::string& path, const std::string& appname)
+    : handle_(kInvalidHandle) {
+    handle_ = tipc_connect(path.c_str(), appname.c_str());
+    if (handle_ == kInvalidHandle) {
+        LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:"
+                   << "\"" << path << "\"";
+    }
+    LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\"";
+}
+TrustyApp::~TrustyApp() {
+    if (handle_ != kInvalidHandle) {
+        tipc_close(handle_);
+    }
+    LOG(INFO) << "Done shutting down TrustyApp";
+}
+
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/confirmationui/TrustyApp.h b/trusty/confirmationui/TrustyApp.h
new file mode 100644
index 0000000..05a25f6
--- /dev/null
+++ b/trusty/confirmationui/TrustyApp.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <teeui/msg_formatting.h>
+#include <trusty/tipc.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <functional>
+#include <future>
+#include <iostream>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#define AT __FILE__ ":" << __LINE__ << ": "
+
+namespace android {
+namespace trusty {
+
+using ::teeui::Message;
+using ::teeui::msg2tuple_t;
+using ::teeui::ReadStream;
+using ::teeui::WriteStream;
+
+#ifndef TEEUI_USE_STD_VECTOR
+/*
+ * TEEUI_USE_STD_VECTOR makes certain wire types like teeui::MsgString and
+ * teeui::MsgVector be aliases for std::vector. This is required for thread safe
+ * message serialization. Always compile this with -DTEEUI_USE_STD_VECTOR set in
+ * CFLAGS of the HAL service.
+ */
+#error "Must be compiled with -DTEEUI_USE_STD_VECTOR."
+#endif
+
+enum class TrustyAppError : int32_t {
+    OK,
+    ERROR = -1,
+    MSG_TOO_LONG = -2,
+};
+
+/*
+ * There is a hard limitation of 0x1800 bytes for the to-be-signed message size. The protocol
+ * overhead is limited, so that 0x2000 is a buffer size that will be sufficient in any benign
+ * mode of operation.
+ */
+static constexpr const size_t kSendBufferSize = 0x2000;
+
+ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+                  uint8_t* iend);
+
+class TrustyApp {
+  private:
+    int handle_;
+    static constexpr const int kInvalidHandle = -1;
+    /*
+     * This mutex serializes communication with the trusted app, not handle_.
+     * Calling issueCmd during construction or deletion is undefined behavior.
+     */
+    std::mutex mutex_;
+
+  public:
+    TrustyApp(const std::string& path, const std::string& appname);
+    ~TrustyApp();
+
+    template <typename Request, typename Response, typename... T>
+    std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) {
+        std::lock_guard<std::mutex> lock(mutex_);
+
+        if (handle_ == kInvalidHandle) {
+            LOG(ERROR) << "TrustyApp not connected";
+            return {TrustyAppError::ERROR, {}};
+        }
+
+        uint8_t buffer[kSendBufferSize];
+        WriteStream out(buffer);
+
+        out = write(Request(), out, args...);
+        if (!out) {
+            LOG(ERROR) << AT << "send command failed: message formatting";
+            return {TrustyAppError::MSG_TOO_LONG, {}};
+        }
+
+        auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+                            &buffer[kSendBufferSize]);
+        if (rc < 0) return {TrustyAppError::ERROR, {}};
+
+        ReadStream in(&buffer[0], rc);
+        auto result = read(Response(), in);
+        if (!std::get<0>(result)) {
+            LOG(ERROR) << "send command failed: message parsing";
+            return {TrustyAppError::ERROR, {}};
+        }
+
+        return {std::get<0>(result) ? TrustyAppError::OK : TrustyAppError::ERROR,
+                tuple_tail(std::move(result))};
+    }
+
+    template <typename Request, typename... T> TrustyAppError issueCmd(const T&... args) {
+        std::lock_guard<std::mutex> lock(mutex_);
+
+        if (handle_ == kInvalidHandle) {
+            LOG(ERROR) << "TrustyApp not connected";
+            return TrustyAppError::ERROR;
+        }
+
+        uint8_t buffer[kSendBufferSize];
+        WriteStream out(buffer);
+
+        out = write(Request(), out, args...);
+        if (!out) {
+            LOG(ERROR) << AT << "send command failed: message formatting";
+            return TrustyAppError::MSG_TOO_LONG;
+        }
+
+        auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+                            &buffer[kSendBufferSize]);
+        if (rc < 0) {
+            LOG(ERROR) << "send command failed: " << strerror(errno) << " (" << errno << ")";
+            return TrustyAppError::ERROR;
+        }
+
+        if (rc > 0) {
+            LOG(ERROR) << "Unexpected non zero length response";
+            return TrustyAppError::ERROR;
+        }
+        return TrustyAppError::OK;
+    }
+
+    operator bool() const { return handle_ != kInvalidHandle; }
+};
+
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/confirmationui/TrustyConfirmationUI.cpp b/trusty/confirmationui/TrustyConfirmationUI.cpp
new file mode 100644
index 0000000..6b25893
--- /dev/null
+++ b/trusty/confirmationui/TrustyConfirmationUI.cpp
@@ -0,0 +1,513 @@
+/*
+ *
+ * Copyright 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 "TrustyConfirmationUI.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <poll.h>
+#include <pthread.h>
+#include <secure_input/evdev.h>
+#include <secure_input/secure_input_device.h>
+#include <secure_input/secure_input_proto.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <teeui/msg_formatting.h>
+#include <teeui/utils.h>
+#include <time.h>
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using namespace secure_input;
+
+using ::android::trusty::TrustyAppError;
+
+using ::teeui::AbortMsg;
+using ::teeui::DeliverTestCommandMessage;
+using ::teeui::DeliverTestCommandResponse;
+using ::teeui::FetchConfirmationResult;
+using ::teeui::MsgString;
+using ::teeui::MsgVector;
+using ::teeui::PromptUserConfirmationMsg;
+using ::teeui::PromptUserConfirmationResponse;
+using ::teeui::ResultMsg;
+
+using ::secure_input::createSecureInput;
+
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+
+using ::std::tie;
+
+using TeeuiRc = ::teeui::ResponseCode;
+
+constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
+constexpr const char kConfirmationuiAppName[] = "com.android.trusty.confirmationui";
+
+namespace {
+
+class Finalize {
+  private:
+    std::function<void()> f_;
+
+  public:
+    Finalize(std::function<void()> f) : f_(f) {}
+    ~Finalize() {
+        if (f_) f_();
+    }
+    void release() { f_ = {}; }
+};
+
+ResponseCode convertRc(TeeuiRc trc) {
+    static_assert(
+        uint32_t(TeeuiRc::OK) == uint32_t(ResponseCode::OK) &&
+            uint32_t(TeeuiRc::Canceled) == uint32_t(ResponseCode::Canceled) &&
+            uint32_t(TeeuiRc::Aborted) == uint32_t(ResponseCode::Aborted) &&
+            uint32_t(TeeuiRc::OperationPending) == uint32_t(ResponseCode::OperationPending) &&
+            uint32_t(TeeuiRc::Ignored) == uint32_t(ResponseCode::Ignored) &&
+            uint32_t(TeeuiRc::SystemError) == uint32_t(ResponseCode::SystemError) &&
+            uint32_t(TeeuiRc::Unimplemented) == uint32_t(ResponseCode::Unimplemented) &&
+            uint32_t(TeeuiRc::Unexpected) == uint32_t(ResponseCode::Unexpected) &&
+            uint32_t(TeeuiRc::UIError) == uint32_t(ResponseCode::UIError) &&
+            uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(ResponseCode::UIErrorMissingGlyph) &&
+            uint32_t(TeeuiRc::UIErrorMessageTooLong) ==
+                uint32_t(ResponseCode::UIErrorMessageTooLong) &&
+            uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==
+                uint32_t(ResponseCode::UIErrorMalformedUTF8Encoding),
+        "teeui::ResponseCode and "
+        "::android::hardware::confirmationui::V1_0::Responsecude are out of "
+        "sync");
+    return ResponseCode(trc);
+}
+
+teeui::UIOption convertUIOption(UIOption uio) {
+    static_assert(uint32_t(UIOption::AccessibilityInverted) ==
+                          uint32_t(teeui::UIOption::AccessibilityInverted) &&
+                      uint32_t(UIOption::AccessibilityMagnified) ==
+                          uint32_t(teeui::UIOption::AccessibilityMagnified),
+                  "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
+                  "anre out of sync");
+    return teeui::UIOption(uio);
+}
+
+inline MsgString hidl2MsgString(const hidl_string& s) {
+    return {s.c_str(), s.c_str() + s.size()};
+}
+template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) {
+    return {v};
+}
+
+inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) {
+    MsgVector<teeui::UIOption> result(v.size());
+    for (unsigned int i = 0; i < v.size(); ++i) {
+        result[i] = convertUIOption(v[i]);
+    }
+    return result;
+}
+
+}  // namespace
+
+TrustyConfirmationUI::TrustyConfirmationUI()
+    : listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored) {}
+
+TrustyConfirmationUI::~TrustyConfirmationUI() {
+    ListenerState state = listener_state_;
+    if (state == ListenerState::SetupDone || state == ListenerState::Interactive) {
+        abort();
+    }
+    if (state != ListenerState::None) {
+        callback_thread_.join();
+    }
+}
+
+std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>>
+TrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText,
+                                              const MsgVector<uint8_t>& extraData,
+                                              const MsgString& locale,
+                                              const MsgVector<teeui::UIOption>& uiOptions) {
+    std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+    /*
+     * This is the main listener thread function. The listener thread life cycle
+     * is equivalent to the life cycle of a single confirmation request. The life
+     * cycle is devided in four phases.
+     *  * The starting phase:
+     *    * The Trusted App gets loaded and/or the connection to it gets established.
+     *    * A connection to the secure input device is established.
+     *    * The prompt is initiated. This sends all information required by the
+     *      confirmation dialog to the TA. The dialog is not yet displayed.
+     *    * An event loop is created.
+     *      * The event loop listens for user input events, fetches them from the
+     *        secure input device, and delivers them to the TA.
+     *    * All evdev devices are grabbed to give confirmationui exclusive access
+     *      to user input.
+     *
+     * Note: During the starting phase the hwbinder service thread is blocked and
+     * waiting for possible Errors. If the setup phase concludes sucessfully, the
+     * hwbinder service thread gets unblocked and returns successfully. Errors
+     * that occur after the first phase are delivered by callback interface.
+     *
+     *  * The 2nd phase - non interactive phase
+     *    * The event loop thread is started.
+     *    * After a grace period:
+     *      * A handshake between the secure input device SecureInput and the TA
+     *        is performed.
+     *      * The input event handler are armed to process user input events.
+     *
+     *  * The 3rd phase - interactive phase
+     *    * We wait to any external event
+     *      * Abort
+     *      * Secure user input asserted
+     *      * Secure input delivered (for non interactive VTS testing)
+     *    * The result is fetched from the TA.
+     *
+     *  * The 4th phase - cleanup
+     *    The cleanup phase is given by the scope of automatic variables created
+     *    in this function. The cleanup commences in reverse order of their creation.
+     *    Here is a list of more complex items in the order in which they go out of
+     *    scope
+     *    * finalizeSecureTouch - signals and joins the secure touch thread.
+     *    * eventloop - signals and joins the event loop thread. The event
+     *      handlers also own all EventDev instances which ungrab the event devices.
+     *      When the eventloop goes out of scope the EventDevs get destroyed
+     *      relinquishing the exclusive hold on the event devices.
+     *    * finalizeConfirmationPrompt - calls abort on the TA, making sure a
+     *      pending operation gets canceled. If the prompt concluded successfully this
+     *      is a spurious call but semantically a no op.
+     *    * secureInput - shuts down the connection to the secure input device
+     *      SecureInput.
+     *    * app - disconnects the TA. Since app is a shared pointer this may not
+     *      unload the app here. It is possible that more instances of the shared
+     *      pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and
+     *      TrustyConfirmationUI::abort. But these instances are extremely short lived
+     *      and it is safe if they are destroyed by either.
+     *    * stateLock - unlocks the listener_state_lock_ if it happens to be held
+     *      at the time of return.
+     */
+
+    std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> result;
+    TeeuiRc& rc = std::get<TeeuiRc>(result);
+    rc = TeeuiRc::SystemError;
+
+    listener_state_ = ListenerState::Starting;
+
+    auto app = std::make_shared<TrustyApp>(kTrustyDeviceName, kConfirmationuiAppName);
+    if (!app) return result;  // TeeuiRc::SystemError
+
+    app_ = app;
+
+    auto hsBegin = [&]() -> std::tuple<TeeuiRc, Nonce> {
+        auto [error, result] =
+            app->issueCmd<secure_input::InputHandshake, secure_input::InputHandshakeResponse>();
+        auto& [rc, nCo] = result;
+
+        if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
+            LOG(ERROR) << "Failed to begin secure input handshake (" << int32_t(error) << "/"
+                       << uint32_t(rc) << ")";
+            rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
+        }
+        return result;
+    };
+
+    auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc {
+        auto [error, finalizeResponse] =
+            app->issueCmd<FinalizeInputSessionHandshake, FinalizeInputSessionHandshakeResponse>(
+                nCi, sig);
+        auto& [rc] = finalizeResponse;
+        if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
+            LOG(ERROR) << "Failed to finalize secure input handshake (" << int32_t(error) << "/"
+                       << uint32_t(rc) << ")";
+            rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
+        }
+        return rc;
+    };
+
+    auto deliverInput = [&](DTupKeyEvent event,
+                            const Signature& sig) -> std::tuple<TeeuiRc, InputResponse> {
+        auto [error, result] =
+            app->issueCmd<DeliverInputEvent, DeliverInputEventResponse>(event, sig);
+        auto& [rc, ir] = result;
+        if (error != TrustyAppError::OK) {
+            LOG(ERROR) << "Failed to deliver input command";
+            rc = TeeuiRc::SystemError;
+        }
+        return result;
+    };
+
+    std::atomic<TeeuiRc> eventRC = TeeuiRc::OperationPending;
+    auto inputResult = [&](TeeuiRc rc) {
+        TeeuiRc expected = TeeuiRc::OperationPending;
+        if (eventRC.compare_exchange_strong(expected, rc)) {
+            listener_state_condv_.notify_all();
+        }
+    };
+
+    // create Secure Input device.
+    auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult);
+    if (!secureInput || !(*secureInput)) {
+        LOG(ERROR) << "Failed to open secure input device";
+        return result;  // TeeuiRc::SystemError;
+    }
+
+    Finalize finalizeConfirmationPrompt([app] {
+        LOG(INFO) << "Calling abort for cleanup";
+        app->issueCmd<AbortMsg>();
+    });
+
+    // initiate prompt
+    LOG(INFO) << "Initiating prompt";
+    TrustyAppError error;
+    auto initResponse = std::tie(rc);
+    std::tie(error, initResponse) =
+        app->issueCmd<PromptUserConfirmationMsg, PromptUserConfirmationResponse>(
+            promptText, extraData, locale, uiOptions);
+    if (error == TrustyAppError::MSG_TOO_LONG) {
+        LOG(ERROR) << "PromptUserConfirmationMsg failed: message too long";
+        rc = TeeuiRc::UIErrorMessageTooLong;
+        return result;
+    } else if (error != TrustyAppError::OK) {
+        LOG(ERROR) << "PromptUserConfirmationMsg failed: " << int32_t(error);
+        return result;  // TeeuiRc::SystemError;
+    }
+    if (rc != TeeuiRc::OK) {
+        LOG(ERROR) << "PromptUserConfirmationMsg failed: " << uint32_t(rc);
+        return result;
+    }
+
+    LOG(INFO) << "Grabbing event devices";
+    EventLoop eventloop;
+    bool grabbed =
+        grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) {
+            if (!(flags & POLLIN)) return;
+            secureInput->handleEvent(evDev);
+        });
+
+    if (!grabbed) {
+        rc = TeeuiRc::SystemError;
+        return result;
+    }
+
+    abort_called_ = false;
+    secureInputDelivered_ = false;
+
+    //  ############################## Start 2nd Phase #############################################
+    listener_state_ = ListenerState::SetupDone;
+    stateLock.unlock();
+    listener_state_condv_.notify_all();
+
+    if (!eventloop.start()) {
+        rc = TeeuiRc::SystemError;
+        return result;
+    }
+
+    stateLock.lock();
+
+    LOG(INFO) << "going to sleep for the grace period";
+    auto then = std::chrono::system_clock::now() +
+                std::chrono::milliseconds(kUserPreInputGracePeriodMillis) +
+                std::chrono::microseconds(50);
+    listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; });
+    LOG(INFO) << "waking up";
+
+    if (abort_called_) {
+        LOG(ERROR) << "Abort called";
+        result = {TeeuiRc::Aborted, {}, {}};
+        return result;
+    }
+
+    LOG(INFO) << "Arming event poller";
+    // tell the event poller to act on received input events from now on.
+    secureInput->start();
+
+    //  ############################## Start 3rd Phase - interactive phase #########################
+    LOG(INFO) << "Transition to Interactive";
+    listener_state_ = ListenerState::Interactive;
+    stateLock.unlock();
+    listener_state_condv_.notify_all();
+
+    stateLock.lock();
+    listener_state_condv_.wait(stateLock, [&]() {
+        return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_;
+    });
+    LOG(INFO) << "Listener waking up";
+    if (abort_called_) {
+        LOG(ERROR) << "Abort called";
+        result = {TeeuiRc::Aborted, {}, {}};
+        return result;
+    }
+
+    if (!secureInputDelivered_) {
+        if (eventRC != TeeuiRc::OK) {
+            LOG(ERROR) << "Bad input response";
+            result = {eventRC, {}, {}};
+            return result;
+        }
+    }
+
+    stateLock.unlock();
+
+    LOG(INFO) << "Fetching Result";
+    std::tie(error, result) = app->issueCmd<FetchConfirmationResult, ResultMsg>();
+    LOG(INFO) << "Result yields " << int32_t(error) << "/" << uint32_t(rc);
+    if (error != TrustyAppError::OK) {
+        result = {TeeuiRc::SystemError, {}, {}};
+    }
+    return result;
+
+    //  ############################## Start 4th Phase - cleanup ##################################
+}
+
+// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+// follow.
+Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation(
+    const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
+    const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
+    const hidl_vec<UIOption>& uiOptions) {
+    std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
+    if (!stateLock.try_lock()) {
+        return ResponseCode::OperationPending;
+    }
+    switch (listener_state_) {
+    case ListenerState::None:
+        break;
+    case ListenerState::Starting:
+    case ListenerState::SetupDone:
+    case ListenerState::Interactive:
+        return ResponseCode::OperationPending;
+    case ListenerState::Terminating:
+        callback_thread_.join();
+        listener_state_ = ListenerState::None;
+        break;
+    default:
+        return ResponseCode::Unexpected;
+    }
+
+    assert(listener_state_ == ListenerState::None);
+
+    callback_thread_ = std::thread(
+        [this](sp<IConfirmationResultCallback> resultCB, hidl_string promptText,
+               hidl_vec<uint8_t> extraData, hidl_string locale, hidl_vec<UIOption> uiOptions) {
+            auto [trc, msg, token] =
+                promptUserConfirmation_(hidl2MsgString(promptText), hidl2MsgVector(extraData),
+                                        hidl2MsgString(locale), hidl2MsgVector(uiOptions));
+            bool do_callback = (listener_state_ == ListenerState::Interactive ||
+                                listener_state_ == ListenerState::SetupDone) &&
+                               resultCB;
+            prompt_result_ = convertRc(trc);
+            listener_state_ = ListenerState::Terminating;
+            if (do_callback) {
+                auto error = resultCB->result(prompt_result_, msg, token);
+                if (!error.isOk()) {
+                    LOG(ERROR) << "Result callback failed " << error.description();
+                }
+            } else {
+                listener_state_condv_.notify_all();
+            }
+        },
+        resultCB, promptText, extraData, locale, uiOptions);
+
+    listener_state_condv_.wait(stateLock, [this] {
+        return listener_state_ == ListenerState::SetupDone ||
+               listener_state_ == ListenerState::Interactive ||
+               listener_state_ == ListenerState::Terminating;
+    });
+    if (listener_state_ == ListenerState::Terminating) {
+        callback_thread_.join();
+        listener_state_ = ListenerState::None;
+        return prompt_result_;
+    }
+    return ResponseCode::OK;
+}
+
+Return<ResponseCode>
+TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
+    ResponseCode rc = ResponseCode::Ignored;
+    {
+        /*
+         * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
+         * implementation responds with a mock confirmation token signed with a test key. The
+         * problem is that the non interactive grace period was not formalized in the HAL spec,
+         * so that the VTS test does not account for the grace period. (It probably should.)
+         * This means we can only pass the VTS test if we block until the grace period is over
+         * (SetupDone -> Interactive) before we deliver the input event.
+         *
+         * The true secure input is delivered by a different mechanism and gets ignored -
+         * not queued - until the grace period is over.
+         *
+         */
+        std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+        listener_state_condv_.wait(stateLock,
+                                   [this] { return listener_state_ != ListenerState::SetupDone; });
+
+        if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored;
+        auto sapp = app_.lock();
+        if (!sapp) return ResponseCode::Ignored;
+        auto [error, response] =
+            sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(
+                static_cast<teeui::TestModeCommands>(secureInputToken.challenge));
+        if (error != TrustyAppError::OK) return ResponseCode::SystemError;
+        auto& [trc] = response;
+        if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;
+        rc = convertRc(trc);
+    }
+    if (secureInputDelivered_) listener_state_condv_.notify_all();
+    // VTS test expect an OK response if the event was successfully delivered.
+    // But since the TA returns the callback response now, we have to translate
+    // Canceled into OK. Canceled is only returned if the delivered event canceled
+    // the operation, which means that the event was successfully delivered. Thus
+    // we return OK.
+    if (rc == ResponseCode::Canceled) return ResponseCode::OK;
+    return rc;
+}
+
+Return<void> TrustyConfirmationUI::abort() {
+    {
+        std::unique_lock<std::mutex> stateLock(listener_state_lock_);
+        if (listener_state_ == ListenerState::SetupDone ||
+            listener_state_ == ListenerState::Interactive) {
+            auto sapp = app_.lock();
+            if (sapp) sapp->issueCmd<AbortMsg>();
+            abort_called_ = true;
+        }
+    }
+    listener_state_condv_.notify_all();
+    return Void();
+}
+
+android::sp<IConfirmationUI> createTrustyConfirmationUI() {
+    return new TrustyConfirmationUI();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace confirmationui
+}  // namespace hardware
+}  // namespace android
diff --git a/trusty/confirmationui/TrustyConfirmationUI.h b/trusty/confirmationui/TrustyConfirmationUI.h
new file mode 100644
index 0000000..3a7c7ef
--- /dev/null
+++ b/trusty/confirmationui/TrustyConfirmationUI.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020, 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 ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
+#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
+
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <hidl/Status.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <teeui/generic_messages.h>
+#include <thread>
+
+#include "TrustyApp.h"
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::android::trusty::TrustyApp;
+
+class TrustyConfirmationUI : public IConfirmationUI {
+  public:
+    TrustyConfirmationUI();
+    virtual ~TrustyConfirmationUI();
+    // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+    // follow.
+    Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
+                                                const hidl_string& promptText,
+                                                const hidl_vec<uint8_t>& extraData,
+                                                const hidl_string& locale,
+                                                const hidl_vec<UIOption>& uiOptions) override;
+    Return<ResponseCode> deliverSecureInputEvent(
+        const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
+    Return<void> abort() override;
+
+  private:
+    std::weak_ptr<TrustyApp> app_;
+    std::thread callback_thread_;
+
+    enum class ListenerState : uint32_t {
+        None,
+        Starting,
+        SetupDone,
+        Interactive,
+        Terminating,
+    };
+
+    /*
+     * listener_state is protected by listener_state_lock. It makes transitions between phases
+     * of the confirmation operation atomic.
+     * (See TrustyConfirmationUI.cpp#promptUserConfirmation_ for details about operation phases)
+     */
+    ListenerState listener_state_;
+    /*
+     * abort_called_ is also protected by listener_state_lock_ and indicates that the HAL user
+     * called abort.
+     */
+    bool abort_called_;
+    std::mutex listener_state_lock_;
+    std::condition_variable listener_state_condv_;
+    ResponseCode prompt_result_;
+    bool secureInputDelivered_;
+
+    std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>>
+    promptUserConfirmation_(const teeui::MsgString& promptText,
+                            const teeui::MsgVector<uint8_t>& extraData,
+                            const teeui::MsgString& locale,
+                            const teeui::MsgVector<teeui::UIOption>& uiOptions);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace confirmationui
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
new file mode 100644
index 0000000..dc7a03b
--- /dev/null
+++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty
+    class hal
+    user nobody
+    group drmrpc input
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
new file mode 100644
index 0000000..9008b87
--- /dev/null
+++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.confirmationui</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+        <name>IConfirmationUI</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/trusty/confirmationui/include/TrustyConfirmationuiHal.h b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
new file mode 100644
index 0000000..2ab9389
--- /dev/null
+++ b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+android::sp<IConfirmationUI> createTrustyConfirmationUI();
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace confirmationui
+}  // namespace hardware
+}  // namespace android
diff --git a/trusty/confirmationui/service.cpp b/trusty/confirmationui/service.cpp
new file mode 100644
index 0000000..dd7e84b
--- /dev/null
+++ b/trusty/confirmationui/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <TrustyConfirmationuiHal.h>
+
+using android::sp;
+using android::hardware::confirmationui::V1_0::implementation::createTrustyConfirmationUI;
+
+int main() {
+    ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
+    auto service = createTrustyConfirmationUI();
+    auto status = service->registerAsService();
+    if (status != android::OK) {
+        LOG(FATAL) << "Could not register service for ConfirmationUI 1.0 (" << status << ")";
+        return -1;
+    }
+    ::android::hardware::joinRpcThreadpool();
+    return -1;
+}
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index f6e9bee..8dba78d 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -12,10 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-subdirs = [
-    "tipc-test",
-]
-
 cc_library {
     name: "libtrusty",
     vendor: true,
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 5f56408..e230941 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -48,6 +48,8 @@
         return VIRT_RPMB;
     } else if (!strcmp(dev_type_name, "sock")) {
         return SOCK_RPMB;
+    } else if (!strcmp(dev_type_name, "ufs")) {
+        return UFS_RPMB;
     } else {
         return UNKNOWN_RPMB;
     }
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 0bd9e68..7dfd0d0 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -16,6 +16,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <scsi/sg.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -51,6 +52,50 @@
 
 #define MMC_BLOCK_SIZE 512
 
+/*
+ * There should be no timeout for security protocol ioctl call, so we choose a
+ * large number for timeout.
+ * 20000 millisecs == 20 seconds
+ */
+#define TIMEOUT 20000
+
+/*
+ * The sg device driver that supports new interface has a major version number of "3".
+ * SG_GET_VERSION_NUM ioctl() will yield a number greater than or 30000.
+ */
+#define RPMB_MIN_SG_VERSION_NUM 30000
+
+/*
+ * CDB format of SECURITY PROTOCOL IN/OUT commands
+ * (JEDEC Standard No. 220D, Page 264)
+ */
+struct sec_proto_cdb {
+    /*
+     * OPERATION CODE = A2h for SECURITY PROTOCOL IN command,
+     * OPERATION CODE = B5h for SECURITY PROTOCOL OUT command.
+     */
+    uint8_t opcode;
+    /* SECURITY PROTOCOL = ECh (JEDEC Universal Flash Storage) */
+    uint8_t sec_proto;
+    /*
+     * The SECURITY PROTOCOL SPECIFIC field specifies the RPMB Protocol ID.
+     * CDB Byte 2 = 00h and CDB Byte 3 = 01h for RPMB Region 0.
+     */
+    uint8_t cdb_byte_2;
+    uint8_t cdb_byte_3;
+    /*
+     * Byte 4 and 5 are reserved.
+     */
+    uint8_t cdb_byte_4;
+    uint8_t cdb_byte_5;
+    /* ALLOCATION/TRANSFER LENGTH in big-endian */
+    uint32_t length;
+    /* Byte 9 is reserved. */
+    uint8_t cdb_byte_10;
+    /* CONTROL = 00h. */
+    uint8_t ctrl;
+} __packed;
+
 static int rpmb_fd = -1;
 static uint8_t read_buf[4096];
 static enum dev_type dev_type = UNKNOWN_RPMB;
@@ -71,6 +116,21 @@
 
 #endif
 
+static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned char cmd_len,
+                          unsigned char mx_sb_len, unsigned int dxfer_len, void* dxferp,
+                          unsigned char* cmdp, void* sbp) {
+    memset(io_hdrp, 0, sizeof(sg_io_hdr_t));
+    io_hdrp->interface_id = 'S';
+    io_hdrp->dxfer_direction = dxfer_direction;
+    io_hdrp->cmd_len = cmd_len;
+    io_hdrp->mx_sb_len = mx_sb_len;
+    io_hdrp->dxfer_len = dxfer_len;
+    io_hdrp->dxferp = dxferp;
+    io_hdrp->cmdp = cmdp;
+    io_hdrp->sbp = sbp;
+    io_hdrp->timeout = TIMEOUT;
+}
+
 static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
     struct {
         struct mmc_ioc_multi_cmd multi;
@@ -132,6 +192,57 @@
     return rc;
 }
 
+static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) {
+    int rc;
+    const uint8_t* write_buf = req->payload;
+    /*
+     * Meaning of member values are stated on the definition of struct sec_proto_cdb.
+     */
+    struct sec_proto_cdb in_cdb = {0xA2, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
+    struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
+    unsigned char sense_buffer[32];
+
+    if (req->reliable_write_size) {
+        /* Prepare SECURITY PROTOCOL OUT command. */
+        out_cdb.length = __builtin_bswap32(req->reliable_write_size);
+        sg_io_hdr_t io_hdr;
+        set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
+                      req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,
+                      sense_buffer);
+        rc = ioctl(sg_fd, SG_IO, &io_hdr);
+        if (rc < 0) {
+            ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+        }
+        write_buf += req->reliable_write_size;
+    }
+
+    if (req->write_size) {
+        /* Prepare SECURITY PROTOCOL OUT command. */
+        out_cdb.length = __builtin_bswap32(req->write_size);
+        sg_io_hdr_t io_hdr;
+        set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
+                      req->write_size, (void*)write_buf, (unsigned char*)&out_cdb, sense_buffer);
+        rc = ioctl(sg_fd, SG_IO, &io_hdr);
+        if (rc < 0) {
+            ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+        }
+        write_buf += req->write_size;
+    }
+
+    if (req->read_size) {
+        /* Prepare SECURITY PROTOCOL IN command. */
+        out_cdb.length = __builtin_bswap32(req->read_size);
+        sg_io_hdr_t io_hdr;
+        set_sg_io_hdr(&io_hdr, SG_DXFER_FROM_DEV, sizeof(in_cdb), sizeof(sense_buffer),
+                      req->read_size, read_buf, (unsigned char*)&in_cdb, sense_buffer);
+        rc = ioctl(sg_fd, SG_IO, &io_hdr);
+        if (rc < 0) {
+            ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+        }
+    }
+    return rc;
+}
+
 static int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,
                               size_t payload_size) {
     int rc;
@@ -194,6 +305,13 @@
             msg->result = STORAGE_ERR_GENERIC;
             goto err_response;
         }
+    } else if (dev_type == UFS_RPMB) {
+        rc = send_ufs_rpmb_req(rpmb_fd, req);
+        if (rc < 0) {
+            ALOGE("send_ufs_rpmb_req failed: %d, %s\n", rc, strerror(errno));
+            msg->result = STORAGE_ERR_GENERIC;
+            goto err_response;
+        }
     } 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);
@@ -233,7 +351,7 @@
 }
 
 int rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {
-    int rc;
+    int rc, sg_version_num;
     dev_type = open_dev_type;
 
     if (dev_type != SOCK_RPMB) {
@@ -243,6 +361,15 @@
             return rc;
         }
         rpmb_fd = rc;
+
+        /* For UFS, it is prudent to check we have a sg device by calling an ioctl */
+        if (dev_type == UFS_RPMB) {
+            if ((ioctl(rpmb_fd, SG_GET_VERSION_NUM, &sg_version_num) < 0) ||
+                (sg_version_num < RPMB_MIN_SG_VERSION_NUM)) {
+                ALOGE("%s is not a sg device, or old sg driver\n", rpmb_devname);
+                return -1;
+            }
+        }
     } else {
         struct sockaddr_un unaddr;
         struct sockaddr *addr = (struct sockaddr *)&unaddr;
@@ -263,6 +390,7 @@
             return rc;
         }
     }
+
     return 0;
 }
 
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 09af3c5..f4e1b51 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, SOCK_RPMB };
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB, UFS_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);