Merge "Add a version script for liblog for the platform"
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index eda4b77..9a25d10 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -144,53 +144,51 @@
     }
 
     std::string reason;
-    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
-    if (fd < 0) {
+    unique_fd fd;
+    if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
         *error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
                                              __adb_server_socket_spec, reason.c_str());
         return -2;
     }
 
-    if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
+    if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd.get(), error)) {
         return -1;
     }
 
-    if (!SendProtocolString(fd, service)) {
+    if (!SendProtocolString(fd.get(), service)) {
         *error = perror_str("write failure during connection");
-        adb_close(fd);
         return -1;
     }
 
-    if (!adb_status(fd, error)) {
-        adb_close(fd);
+    if (!adb_status(fd.get(), error)) {
         return -1;
     }
 
-    D("_adb_connect: return fd %d", fd);
-    return fd;
+    D("_adb_connect: return fd %d", fd.get());
+    return fd.release();
 }
 
 bool adb_kill_server() {
     D("adb_kill_server");
     std::string reason;
-    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
-    if (fd < 0) {
+    unique_fd fd;
+    if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
         fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
                 reason.c_str());
         return true;
     }
 
-    if (!SendProtocolString(fd, "host:kill")) {
+    if (!SendProtocolString(fd.get(), "host:kill")) {
         fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
         return false;
     }
 
     // The server might send OKAY, so consume that.
     char buf[4];
-    ReadFdExactly(fd, buf, 4);
+    ReadFdExactly(fd.get(), buf, 4);
     // Now that no more data is expected, wait for socket orderly shutdown or error, indicating
     // server death.
-    ReadOrderlyShutdown(fd);
+    ReadOrderlyShutdown(fd.get());
     return true;
 }
 
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 9bc42e1..e963e3d 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -762,6 +762,17 @@
 }
 
 static int adb_abb(int argc, const char** argv) {
+    FeatureSet features;
+    std::string error_message;
+    if (!adb_get_feature_set(&features, &error_message)) {
+        fprintf(stderr, "error: %s\n", error_message.c_str());
+        return 1;
+    }
+
+    if (!CanUseFeature(features, kFeatureAbb)) {
+        error_exit("abb is not supported by the device");
+    }
+
     // Defaults.
     constexpr char escape_char = '~';  // -e
     constexpr bool use_shell_protocol = true;
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 400b12f..1ba0de0 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -22,6 +22,9 @@
     wrapper: "deployagent/deployagent.sh",
     proto: {
         type: "lite",
+    },
+    dex_preopt: {
+        enabled: false,
     }
 }
 
diff --git a/adb/services.cpp b/adb/services.cpp
index 8636657..73fed09 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -72,24 +72,23 @@
 }
 
 int service_to_fd(std::string_view name, atransport* transport) {
-    int ret = -1;
+    unique_fd ret;
 
     if (is_socket_spec(name)) {
         std::string error;
-        ret = socket_spec_connect(name, &error);
-        if (ret < 0) {
+        if (!socket_spec_connect(&ret, name, nullptr, nullptr, &error)) {
             LOG(ERROR) << "failed to connect to socket '" << name << "': " << error;
         }
     } else {
 #if !ADB_HOST
-        ret = daemon_service_to_fd(name, transport).release();
+        ret = daemon_service_to_fd(name, transport);
 #endif
     }
 
     if (ret >= 0) {
         close_on_exec(ret);
     }
-    return ret;
+    return ret.release();
 }
 
 #if ADB_HOST
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 4cddc84..cc67b6b 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -67,7 +67,7 @@
 });
 
 bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
-                           std::string* error) {
+                           std::string* serial, std::string* error) {
     if (!spec.starts_with("tcp:")) {
         *error = "specification is not tcp: ";
         *error += spec;
@@ -92,7 +92,7 @@
 
         // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
         //        on an address that isn't 'localhost' is unsupported.
-        if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, nullptr, error)) {
+        if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, serial, error)) {
             return false;
         }
 
@@ -139,63 +139,68 @@
 
     std::string error;
     std::string hostname;
-    if (!parse_tcp_socket_spec(spec, &hostname, nullptr, &error)) {
+    if (!parse_tcp_socket_spec(spec, &hostname, nullptr, nullptr, &error)) {
         return false;
     }
     return tcp_host_is_local(hostname);
 }
 
-int socket_spec_connect(std::string_view spec, std::string* error) {
-    if (spec.starts_with("tcp:")) {
+bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std::string* serial,
+                         std::string* error) {
+    if (address.starts_with("tcp:")) {
         std::string hostname;
-        int port;
-        if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
-            return -1;
+        int port_value = port ? *port : 0;
+        if (!parse_tcp_socket_spec(address, &hostname, &port_value, serial, error)) {
+            return false;
         }
 
-        int result;
         if (tcp_host_is_local(hostname)) {
-            result = network_loopback_client(port, SOCK_STREAM, error);
+            fd->reset(network_loopback_client(port_value, SOCK_STREAM, error));
         } else {
 #if ADB_HOST
-            result = network_connect(hostname, port, SOCK_STREAM, 0, error);
+            fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
 #else
             // Disallow arbitrary connections in adbd.
             *error = "adbd does not support arbitrary tcp connections";
-            return -1;
+            return false;
 #endif
         }
 
-        if (result >= 0) {
-            disable_tcp_nagle(result);
+        if (fd->get() > 0) {
+            disable_tcp_nagle(fd->get());
+            if (port) {
+                *port = port_value;
+            }
+            return true;
         }
-        return result;
+        return false;
     }
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (spec.starts_with(prefix)) {
+        if (address.starts_with(prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("socket type %s is unavailable on this platform",
                                       it.first.c_str());
-                return -1;
+                return false;
             }
 
-            return network_local_client(&spec[prefix.length()], it.second.socket_namespace,
-                                        SOCK_STREAM, error);
+            fd->reset(network_local_client(&address[prefix.length()], it.second.socket_namespace,
+                                           SOCK_STREAM, error));
+            return true;
         }
     }
 
     *error = "unknown socket specification: ";
-    *error += spec;
-    return -1;
+    *error += address;
+    return false;
 }
 
 int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port) {
     if (spec.starts_with("tcp:")) {
         std::string hostname;
         int port;
-        if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
+        if (!parse_tcp_socket_spec(spec, &hostname, &port, nullptr, error)) {
             return -1;
         }
 
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 5b06973..687d751 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -17,14 +17,18 @@
 #pragma once
 
 #include <string>
+#include <tuple>
+
+#include "adb_unique_fd.h"
 
 // Returns true if the argument starts with a plausible socket prefix.
 bool is_socket_spec(std::string_view spec);
 bool is_local_socket_spec(std::string_view spec);
 
-int socket_spec_connect(std::string_view spec, std::string* error);
+bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std::string* serial,
+                         std::string* error);
 int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
 
 // Exposed for testing.
 bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
-                           std::string* error);
+                           std::string* serial, std::string* error);
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index 40ce21c..f5ec0f1 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -21,34 +21,37 @@
 #include <gtest/gtest.h>
 
 TEST(socket_spec, parse_tcp_socket_spec) {
-    std::string hostname, error;
+    std::string hostname, error, serial;
     int port;
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &serial, &error));
     EXPECT_EQ("", hostname);
     EXPECT_EQ(5037, port);
+    EXPECT_EQ("", serial);
 
     // Bad ports:
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &serial, &error));
 
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("localhost", hostname);
     EXPECT_EQ(1234, port);
+    EXPECT_EQ("localhost:1234", serial);
 
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &serial, &error));
 
     // IPv6:
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &error));
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("::1", hostname);
     EXPECT_EQ(1234, port);
+    EXPECT_EQ("[::1]:1234", serial);
 
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
+    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &serial, &error));
 }
diff --git a/adb/transport.cpp b/adb/transport.cpp
index f59a135..ae53597 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -68,6 +68,7 @@
 const char* const kFeaturePushSync = "push_sync";
 const char* const kFeatureApex = "apex";
 const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
+const char* const kFeatureAbb = "abb";
 
 namespace {
 
@@ -1013,7 +1014,8 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-            kFeatureShell2, kFeatureCmd, kFeatureStat2, kFeatureFixedPushMkdir, kFeatureApex
+            kFeatureShell2,         kFeatureCmd,  kFeatureStat2,
+            kFeatureFixedPushMkdir, kFeatureApex, kFeatureAbb,
             // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
             // to know about. Otherwise, the client can be stuck running an old
             // version of the server even after upgrading their copy of adb.
diff --git a/adb/transport.h b/adb/transport.h
index 790004f..6f53e6ec 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -64,6 +64,8 @@
 extern const char* const kFeatureApex;
 // adbd has b/110953234 fixed.
 extern const char* const kFeatureFixedPushMkdir;
+// adbd supports android binder bridge (abb).
+extern const char* const kFeatureAbb;
 
 TransportId NextTransportId();
 
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index dc87ac7..b384085 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -45,6 +45,7 @@
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "socket_spec.h"
 #include "sysdeps/chrono.h"
 
 #if ADB_HOST
@@ -70,32 +71,17 @@
 
 std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
                                                     std::string* response) {
-    std::string serial;
-    std::string host;
+    unique_fd fd;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
-        D("failed to parse address: '%s'", address.c_str());
-        return std::make_tuple(unique_fd(), port, serial);
-    }
-
-    std::string error;
-    unique_fd fd(network_connect(host.c_str(), port, SOCK_STREAM, 10, &error));
-    if (fd == -1) {
-        *response = android::base::StringPrintf("unable to connect to %s: %s",
-                                                serial.c_str(), error.c_str());
+    std::string serial;
+    if (socket_spec_connect(&fd, "tcp:" + address, &port, &serial, response)) {
+        close_on_exec(fd);
+        if (!set_tcp_keepalive(fd, 1)) {
+            D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
+        }
         return std::make_tuple(std::move(fd), port, serial);
     }
-
-    D("client: connected %s remote on fd %d", serial.c_str(), fd.get());
-    close_on_exec(fd);
-    disable_tcp_nagle(fd);
-
-    // Send a TCP keepalive ping to the device every second so we can detect disconnects.
-    if (!set_tcp_keepalive(fd, 1)) {
-        D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
-    }
-
-    return std::make_tuple(std::move(fd), port, serial);
+    return std::make_tuple(unique_fd(), 0, "");
 }
 
 void connect_device(const std::string& address, std::string* response) {
@@ -251,8 +237,9 @@
     adb_thread_setname("server socket");
     D("transport: server_socket_thread() starting");
     while (serverfd == -1) {
+        std::string spec = android::base::StringPrintf("tcp:%d", port);
         std::string error;
-        serverfd.reset(network_inaddr_any_server(port, SOCK_STREAM, &error));
+        serverfd.reset(socket_spec_listen(spec, &error));
         if (serverfd < 0) {
             D("server: cannot bind socket yet: %s", error.c_str());
             std::this_thread::sleep_for(1s);
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index c8d12cf..2c890b4 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -161,22 +161,35 @@
 
 // Inline functions, so that they can be used header-only.
 template <typename Closer>
-inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write) {
+inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
+                 int flags = O_CLOEXEC) {
   int pipefd[2];
 
 #if defined(__linux__)
-  if (pipe2(pipefd, O_CLOEXEC) != 0) {
+  if (pipe2(pipefd, flags) != 0) {
     return false;
   }
 #else  // defined(__APPLE__)
+  if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
+    return false;
+  }
   if (pipe(pipefd) != 0) {
     return false;
   }
 
-  if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
-    close(pipefd[0]);
-    close(pipefd[1]);
-    return false;
+  if (flags & O_CLOEXEC) {
+    if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+      close(pipefd[0]);
+      close(pipefd[1]);
+      return false;
+    }
+  }
+  if (flags & O_NONBLOCK) {
+    if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+      close(pipefd[0]);
+      close(pipefd[1]);
+      return false;
+    }
   }
 #endif
 
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index ead2105..716fe95 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -132,6 +132,7 @@
         "libext2_uuid",
         "libext4_utils",
         "libfs_mgr",
+        "libgsi",
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 81f0560..8a72627 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -33,6 +33,7 @@
 #define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
 #define FB_CMD_UPDATE_SUPER "update-super"
 #define FB_CMD_OEM "oem"
+#define FB_CMD_GSI "gsi"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index e91598d..a2336bf 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -28,6 +28,7 @@
 #include <cutils/android_reboot.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr.h>
+#include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 #include <uuid/uuid.h>
@@ -460,3 +461,22 @@
     bool wipe = (args.size() >= 3 && args[2] == "wipe");
     return UpdateSuper(device, args[1], wipe);
 }
+
+bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (!android::gsi::IsGsiInstalled()) {
+        return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
+    }
+    if (args.size() != 2) {
+        return device->WriteFail("Invalid arguments");
+    }
+    if (args[1] == "wipe") {
+        if (!android::gsi::UninstallGsi()) {
+            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
+        }
+    } else if (args[1] == "disable") {
+        if (!android::gsi::DisableGsi()) {
+            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
+        }
+    }
+    return device->WriteStatus(FastbootResult::OKAY, "Success");
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index bb1f988..afd6d08 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -48,3 +48,4 @@
 bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 7be721a..56fafab 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -53,6 +53,7 @@
               {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
               {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
               {FB_CMD_OEM, OemCmdHandler},
+              {FB_CMD_GSI, GsiHandler},
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0b8d9b2..d753f0f 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -387,6 +387,7 @@
             "                            Format a flash partition.\n"
             " set_active SLOT            Set the active slot.\n"
             " oem [COMMAND...]           Execute OEM-specific command.\n"
+            " gsi wipe|disable           Wipe or disable a GSI installation (fastbootd only).\n"
             "\n"
             "boot image:\n"
             " boot KERNEL [RAMDISK [SECOND]]\n"
@@ -1926,6 +1927,16 @@
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
             fb->ResizePartition(partition, size);
+        } else if (command == "gsi") {
+            if (args.empty()) {
+                syntax_error("missing 'wipe' or 'disable' argument");
+            } else if (args.size() == 1 && args[0] == "wipe") {
+                fb->RawCommand("gsi:wipe", "wiping GSI");
+            } else if (args.size() == 1 && args[0] == "disable") {
+                fb->RawCommand("gsi:disable", "disabling GSI");
+            } else {
+                syntax_error("expected 'wipe' or 'disable'");
+            }
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index d9f2837..974e13e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -59,6 +59,7 @@
         "libfs_avb",
         "libfstab",
         "libdm",
+        "libgsi",
     ],
     export_static_lib_headers: [
         "libfs_avb",
@@ -105,5 +106,8 @@
         },
     },
     export_include_dirs: ["include_fstab"],
-    header_libs: ["libbase_headers"],
+    header_libs: [
+        "libbase_headers",
+        "libgsi_headers",
+    ],
 }
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index 817a0b8..cbbd3bc 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,2 +1,3 @@
 bowgotsai@google.com
+dvander@google.com
 tomcherry@google.com
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 19445c8..ded3678 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -79,6 +79,9 @@
 
 #define ZRAM_CONF_DEV   "/sys/block/zram0/disksize"
 #define ZRAM_CONF_MCS   "/sys/block/zram0/max_comp_streams"
+#define ZRAM_BACK_DEV   "/sys/block/zram0/backing_dev"
+
+#define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
@@ -109,6 +112,7 @@
     FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
     FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
     FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
+    FS_STAT_ENABLE_VERITY_FAILED = 0x80000,
 };
 
 // TODO: switch to inotify()
@@ -439,6 +443,43 @@
     }
 }
 
+// Enable fs-verity if needed.
+static void tune_verity(const std::string& blk_device, const FstabEntry& entry,
+                        const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_verity = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_VERITY)) != 0;
+    bool want_verity = entry.fs_mgr_flags.fs_verity;
+
+    if (has_verity || !want_verity) {
+        return;
+    }
+
+    std::string verity_support;
+    if (!android::base::ReadFileToString(SYSFS_EXT4_VERITY, &verity_support)) {
+        LERROR << "Failed to open " << SYSFS_EXT4_VERITY;
+        return;
+    }
+
+    if (!(android::base::Trim(verity_support) == "supported")) {
+        LERROR << "Current ext4 verity not supported by kernel";
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable ext4 verity on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    LINFO << "Enabling ext4 verity on " << blk_device;
+
+    const char* argv[] = {TUNE2FS_BIN, "-O", "verity", blk_device.c_str()};
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 verity on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_VERITY_FAILED;
+    }
+}
+
 // Read the primary superblock from an f2fs filesystem.  On failure return
 // false.  If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
 #define F2FS_BLKSIZE 4096
@@ -510,12 +551,14 @@
     }
 
     if (is_extfs(entry.fs_type) &&
-        (entry.fs_mgr_flags.reserved_size || entry.fs_mgr_flags.file_encryption)) {
+        (entry.fs_mgr_flags.reserved_size || entry.fs_mgr_flags.file_encryption ||
+         entry.fs_mgr_flags.fs_verity)) {
         struct ext4_super_block sb;
 
         if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
             tune_reserved_size(blk_device, entry, &sb, &fs_stat);
             tune_encrypt(blk_device, entry, &sb, &fs_stat);
+            tune_verity(blk_device, entry, &sb, &fs_stat);
         }
     }
 
@@ -1373,6 +1416,70 @@
     return 0;
 }
 
+static bool InstallZramDevice(const std::string& device) {
+    if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) {
+        PERROR << "Cannot write " << device << " in: " << ZRAM_BACK_DEV;
+        return false;
+    }
+    LINFO << "Success to set " << device << " to " << ZRAM_BACK_DEV;
+    return true;
+}
+
+static bool PrepareZramDevice(const std::string& loop, off64_t size, const std::string& bdev) {
+    if (loop.empty() && bdev.empty()) return true;
+
+    if (bdev.length()) {
+        return InstallZramDevice(bdev);
+    }
+
+    // Get free loopback
+    unique_fd loop_fd(TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
+    if (loop_fd.get() == -1) {
+        PERROR << "Cannot open loop-control";
+        return false;
+    }
+
+    int num = ioctl(loop_fd.get(), LOOP_CTL_GET_FREE);
+    if (num == -1) {
+        PERROR << "Cannot get free loop slot";
+        return false;
+    }
+
+    // Prepare target path
+    unique_fd target_fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0664)));
+    if (target_fd.get() == -1) {
+        PERROR << "Cannot open target path: " << loop;
+        return false;
+    }
+    if (fallocate(target_fd.get(), 0, 0, size) < 0) {
+        PERROR << "Cannot truncate target path: " << loop;
+        return false;
+    }
+
+    // Connect loopback (device_fd) to target path (target_fd)
+    std::string device = android::base::StringPrintf("/dev/block/loop%d", num);
+    unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
+    if (device_fd.get() == -1) {
+        PERROR << "Cannot open /dev/block/loop" << num;
+        return false;
+    }
+
+    if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get())) {
+        PERROR << "Cannot set loopback to target path";
+        return false;
+    }
+
+    // set block size & direct IO
+    if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096)) {
+        PWARNING << "Cannot set 4KB blocksize to /dev/block/loop" << num;
+    }
+    if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1)) {
+        PWARNING << "Cannot set direct_io to /dev/block/loop" << num;
+    }
+
+    return InstallZramDevice(device);
+}
+
 bool fs_mgr_swapon_all(const Fstab& fstab) {
     bool ret = true;
     for (const auto& entry : fstab) {
@@ -1381,6 +1488,10 @@
             continue;
         }
 
+        if (!PrepareZramDevice(entry.zram_loopback_path, entry.zram_loopback_size, entry.zram_backing_dev_path)) {
+            LERROR << "Skipping losetup for '" << entry.blk_device << "'";
+        }
+
         if (entry.zram_size > 0) {
             // A zram_size was specified, so we need to configure the
             // device.  There is no point in having multiple zram devices
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 53b47be..9d4f280 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -28,8 +28,10 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <libgsi/libgsi.h>
 
 #include "fs_mgr_priv.h"
 
@@ -43,6 +45,9 @@
     std::string key_dir;
     std::string verity_loc;
     std::string sysfs_path;
+    std::string zram_loopback_path;
+    uint64_t zram_loopback_size = 512 * 1024 * 1024; // 512MB by default
+    std::string zram_backing_dev_path;
     off64_t part_length = 0;
     std::string label;
     int partnum = -1;
@@ -117,6 +122,10 @@
         {"checkpoint=block", MF_CHECKPOINT_BLK},
         {"checkpoint=fs", MF_CHECKPOINT_FS},
         {"slotselect_other", MF_SLOTSELECT_OTHER},
+        {"zram_loopback_path=", MF_ZRAM_LOOPBACK_PATH},
+        {"zram_loopback_size=", MF_ZRAM_LOOPBACK_SIZE},
+        {"zram_backing_dev_path=", MF_ZRAM_BACKING_DEV_PATH},
+        {"fsverity", MF_FS_VERITY},
         {0, 0},
 };
 
@@ -344,6 +353,16 @@
                 } else if (flag == MF_SYSFS) {
                     /* The path to trigger device gc by idle-maint of vold. */
                     flag_vals->sysfs_path = arg;
+                } else if (flag == MF_ZRAM_LOOPBACK_PATH) {
+                    /* The path to use loopback for zram. */
+                    flag_vals->zram_loopback_path = arg;
+                } else if (flag == MF_ZRAM_LOOPBACK_SIZE) {
+                    if (!android::base::ParseByteCount(arg, &flag_vals->zram_loopback_size)) {
+                        LERROR << "Warning: zram_loopback_size = flag malformed";
+                    }
+                } else if (flag == MF_ZRAM_BACKING_DEV_PATH) {
+                    /* The path to use loopback for zram. */
+                    flag_vals->zram_backing_dev_path = arg;
                 }
                 break;
             }
@@ -569,6 +588,9 @@
         entry.logical_blk_size = flag_vals.logical_blk_size;
         entry.sysfs_path = std::move(flag_vals.sysfs_path);
         entry.vbmeta_partition = std::move(flag_vals.vbmeta_partition);
+        entry.zram_loopback_path = std::move(flag_vals.zram_loopback_path);
+        entry.zram_loopback_size = std::move(flag_vals.zram_loopback_size);
+        entry.zram_backing_dev_path = std::move(flag_vals.zram_backing_dev_path);
         if (entry.fs_mgr_flags.logical) {
             entry.logical_partition_name = entry.blk_device;
         }
@@ -638,6 +660,35 @@
     return boot_devices;
 }
 
+static void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+    auto iter = std::remove_if(fstab->begin(), fstab->end(),
+                               [&](const auto& entry) { return entry.mount_point == mount_point; });
+    fstab->erase(iter, fstab->end());
+}
+
+static void TransformFstabForGsi(Fstab* fstab) {
+    EraseFstabEntry(fstab, "/system");
+    EraseFstabEntry(fstab, "/data");
+
+    fstab->emplace_back(BuildGsiSystemFstabEntry());
+
+    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
+
+    FstabEntry userdata = {
+            .blk_device = "userdata_gsi",
+            .mount_point = "/data",
+            .fs_type = "ext4",
+            .flags = kFlags,
+            .reserved_size = 128 * 1024 * 1024,
+    };
+    userdata.fs_mgr_flags.wait = true;
+    userdata.fs_mgr_flags.check = true;
+    userdata.fs_mgr_flags.logical = true;
+    userdata.fs_mgr_flags.quota = true;
+    userdata.fs_mgr_flags.late_mount = true;
+    fstab->emplace_back(userdata);
+}
+
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
     auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (!fstab_file) {
@@ -645,10 +696,15 @@
         return false;
     }
 
-    if (!fs_mgr_read_fstab_file(fstab_file.get(), path == "/proc/mounts", fstab)) {
+    bool is_proc_mounts = path == "/proc/mounts";
+
+    if (!fs_mgr_read_fstab_file(fstab_file.get(), is_proc_mounts, fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
+    if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+        TransformFstabForGsi(fstab);
+    }
 
     return true;
 }
@@ -776,6 +832,8 @@
         free(fstab->recs[i].key_dir);
         free(fstab->recs[i].label);
         free(fstab->recs[i].sysfs_path);
+        free(fstab->recs[i].zram_loopback_path);
+        free(fstab->recs[i].zram_backing_dev_path);
     }
 
     /* Free the fstab_recs array created by calloc(3) */
@@ -873,6 +931,9 @@
     entry.erase_blk_size = fstab_rec->erase_blk_size;
     entry.logical_blk_size = fstab_rec->logical_blk_size;
     entry.sysfs_path = fstab_rec->sysfs_path;
+    entry.zram_loopback_path = fstab_rec->zram_loopback_path;
+    entry.zram_loopback_size = fstab_rec->zram_loopback_size;
+    entry.zram_backing_dev_path = fstab_rec->zram_backing_dev_path;
 
     return entry;
 }
@@ -916,6 +977,9 @@
         legacy_fstab->recs[i].erase_blk_size = fstab[i].erase_blk_size;
         legacy_fstab->recs[i].logical_blk_size = fstab[i].logical_blk_size;
         legacy_fstab->recs[i].sysfs_path = strdup(fstab[i].sysfs_path.c_str());
+        legacy_fstab->recs[i].zram_loopback_path = strdup(fstab[i].zram_loopback_path.c_str());
+        legacy_fstab->recs[i].zram_loopback_size = fstab[i].zram_loopback_size;
+        legacy_fstab->recs[i].zram_backing_dev_path = strdup(fstab[i].zram_backing_dev_path.c_str());
     }
     return legacy_fstab;
 }
@@ -1023,3 +1087,21 @@
 int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_CHECKPOINT_BLK;
 }
+
+int fs_mgr_is_fs_verity(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_FS_VERITY;
+}
+
+FstabEntry BuildGsiSystemFstabEntry() {
+    FstabEntry system = {
+            .blk_device = "system_gsi",
+            .mount_point = "/system",
+            .fs_type = "ext4",
+            .flags = MS_RDONLY,
+            .fs_options = "barrier=1",
+    };
+    system.fs_mgr_flags.wait = true;
+    system.fs_mgr_flags.logical = true;
+    system.fs_mgr_flags.first_stage_mount = true;
+    return system;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 6364ca9..0f89467 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -48,6 +48,7 @@
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
+#include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 
@@ -802,8 +803,9 @@
 bool fs_mgr_overlayfs_invalid() {
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
 
-    // in recovery or fastbootd mode, not allowed!
+    // in recovery, fastbootd, or gsi mode, not allowed!
     if (fs_mgr_access("/system/bin/recovery")) return true;
+    if (android::gsi::IsGsiRunning()) return true;
 
     return false;
 }
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7842ca2..3b9ddee 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -122,6 +122,14 @@
                           0x80000000
 #define MF_SLOTSELECT_OTHER  \
                          0x100000000
+#define MF_ZRAM_LOOPBACK_PATH    \
+                         0x200000000
+#define MF_ZRAM_LOOPBACK_SIZE    \
+                         0x400000000
+#define MF_ZRAM_BACKING_DEV_PATH \
+                         0x800000000
+#define MF_FS_VERITY  \
+                         0x1000000000
 // clang-format on
 
 #define DM_BUF_SIZE 4096
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 0997254..4a05949 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -59,6 +59,9 @@
     off64_t erase_blk_size;
     off64_t logical_blk_size;
     char* sysfs_path;
+    char* zram_loopback_path;
+    uint64_t zram_loopback_size;
+    char* zram_backing_dev_path;
 };
 
 struct fstab* fs_mgr_read_fstab_default();
@@ -92,6 +95,7 @@
 int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab);
 int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab);
 int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
+int fs_mgr_is_fs_verity(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
 std::set<std::string> fs_mgr_get_boot_devices();
@@ -119,6 +123,9 @@
     off64_t logical_blk_size = 0;
     std::string sysfs_path;
     std::string vbmeta_partition;
+    std::string zram_loopback_path;
+    uint64_t zram_loopback_size;
+    std::string zram_backing_dev_path;
 
     // TODO: Remove this union once fstab_rec is deprecated. It only serves as a
     // convenient way to convert between fstab_rec::fs_mgr_flags and these bools.
@@ -168,6 +175,10 @@
 
             // bit 32
             bool slot_select_other : 1;
+            bool zram_loopback_path : 1;
+            bool zram_loopback_size : 1;
+            bool zram_backing_dev_path : 1;
+            bool fs_verity : 1;
         };
     } fs_mgr_flags;
 
@@ -187,3 +198,6 @@
 FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec);
 Fstab LegacyFstabToFstab(const struct fstab* legacy_fstab);
 fstab* FstabToLegacyFstab(const Fstab& fstab);
+
+// Helper method to build a GSI fstab entry for mounting /system.
+FstabEntry BuildGsiSystemFstabEntry();
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 4a0d4b5..71b39a9 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -304,7 +304,6 @@
     return true;
 }
 
-#if 0
 static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
     if (fs_type == EXT4_SUPER_MAGIC) {
         // No pinning necessary for ext4. The blocks, once allocated, are expected
@@ -345,7 +344,6 @@
     }
     return moved_blocks_nr == 0;
 }
-#endif
 
 static void LogExtent(uint32_t num, const struct fiemap_extent& ext) {
     LOG(INFO) << "Extent #" << num;
@@ -477,13 +475,15 @@
 
     if (create) {
         if (!AllocateFile(file_fd, abs_path, blocksz, file_size)) {
+            LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
+                       << " bytes";
             cleanup(abs_path, create);
             return nullptr;
         }
     }
 
     // f2fs may move the file blocks around.
-    if (!PinFile(file_fd, file_path, fs_type)) {
+    if (!PinFile(file_fd, abs_path, fs_type)) {
         cleanup(abs_path, create);
         LOG(ERROR) << "Failed to pin the file in storage";
         return nullptr;
@@ -538,13 +538,11 @@
         return false;
     }
 
-#if 0
-    // TODO(b/122138114): check why this fails.
     if (!IsFilePinned(file_fd_, file_path_, fs_type_)) {
         LOG(ERROR) << "Failed write: file " << file_path_ << " is not pinned";
         return false;
     }
-#endif
+
     // find extents that must be written to and then write one at a time.
     uint32_t num_extent = 1;
     uint32_t buffer_offset = 0;
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index b99ff8f..110d56e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -269,6 +269,11 @@
 }
 
 static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+    if (device_info.logical_block_size == 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " logical block size must not be zero.";
+        return false;
+    }
     if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
         LERROR << "Block device " << device_info.partition_name
                << " logical block size must be a multiple of 512.";
@@ -335,7 +340,7 @@
         out.alignment = device_info.alignment;
         out.alignment_offset = device_info.alignment_offset;
         out.size = device_info.size;
-        if (device_info.partition_name.size() >= sizeof(out.partition_name)) {
+        if (device_info.partition_name.size() > sizeof(out.partition_name)) {
             LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
             return false;
         }
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 9c5ec5c..8934aaf 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -127,7 +127,7 @@
  * num_entries, and the result must not overflow a 32-bit signed integer.
  */
 typedef struct LpMetadataTableDescriptor {
-    /*  0: Location of the table, relative to the metadata header. */
+    /*  0: Location of the table, relative to end of the metadata header. */
     uint32_t offset;
     /*  4: Number of entries in the table. */
     uint32_t num_entries;
@@ -272,7 +272,7 @@
 
     /* 40: Maximum size in bytes. If 0, the group has no maximum size. */
     uint64_t maximum_size;
-} LpMetadataPartitionGroup;
+} __attribute__((packed)) LpMetadataPartitionGroup;
 
 /* This flag is only intended to be used with super_empty.img and super.img on
  * retrofit devices. If set, the group needs a slot suffix to be interpreted
@@ -323,7 +323,7 @@
 
     /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
     uint32_t flags;
-} LpMetadataBlockDevice;
+} __attribute__((packed)) LpMetadataBlockDevice;
 
 /* This flag is only intended to be used with super_empty.img and super.img on
  * retrofit devices. On these devices there are A and B super partitions, and
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 24c6b2c..dcee6d2 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -256,6 +256,10 @@
             LERROR << "Logical partition has invalid attribute set.";
             return nullptr;
         }
+        if (partition.first_extent_index + partition.num_extents < partition.first_extent_index) {
+            LERROR << "Logical partition first_extent_index + num_extents overflowed.";
+            return nullptr;
+        }
         if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
             LERROR << "Logical partition has invalid extent list.";
             return nullptr;
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 54a1883..bffcb7e 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -373,11 +373,11 @@
         // safety.
         std::string old_blob;
         if (!ValidateAndSerializeMetadata(opener, *backup.get(), slot_suffix, &old_blob)) {
-            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            LERROR << "Error serializing backup metadata to repair corrupted primary";
             return false;
         }
         if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
-            LERROR << "Error writing primary metadata to repair corrupted backup";
+            LERROR << "Error writing backup metadata to repair corrupted primary";
             return false;
         }
     }
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 825109f..0765f04 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -164,12 +164,26 @@
 
 Returns: true if device in root state" ]
 adb_root() {
+  [ `adb_sh echo '${USER}'` != root ] || return 0
   adb root >/dev/null </dev/null 2>/dev/null
   sleep 2
   adb_wait 2m &&
     [ `adb_sh echo '${USER}'` = root ]
 }
 
+[ "USAGE: adb_unroot
+
+NB: This can be flakey on devices due to USB state
+
+Returns: true if device in un root state" ]
+adb_unroot() {
+  [ `adb_sh echo '${USER}'` = root ] || return 0
+  adb unroot >/dev/null </dev/null 2>/dev/null
+  sleep 2
+  adb_wait 2m &&
+    [ `adb_sh echo '${USER}'` != root ]
+}
+
 [ "USAGE: fastboot_getvar var expected
 
 Returns: true if var output matches expected" ]
@@ -194,6 +208,15 @@
   echo ${O} >&2
 }
 
+[ "USAGE: cleanup
+
+Do nothing: should be redefined when necessary
+
+Returns: cleans up any latent resources, reverses configurations" ]
+cleanup () {
+  :
+}
+
 [ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
 
 If -d, or -t <epoch> argument is supplied, dump logcat.
@@ -212,6 +235,7 @@
     shift 2
   fi
   echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
+  cleanup
   exit 1
 }
 
@@ -558,6 +582,7 @@
 echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
 # Only root can read vendor if sepolicy permissions are as expected
 if ${enforcing}; then
+  adb_unroot
   B="`adb_cat /vendor/hello`" &&
     die "re-read /vendor/hello after reboot w/o root"
   check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
diff --git a/init/Android.bp b/init/Android.bp
index c920dc2..9f5d17d 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -19,7 +19,6 @@
     cpp_std: "experimental",
     sanitize: {
         misc_undefined: ["signed-integer-overflow"],
-        address: false,  // TODO(b/120561310): Fix ASAN to work without /proc mounted and re-enable.
     },
     cflags: [
         "-DLOG_UEVENTS=0",
@@ -77,6 +76,7 @@
         "libext4_utils",
         "libfs_mgr",
         "libfscrypt",
+        "libgsi",
         "libhidl-gen-utils",
         "libkeyutils",
         "liblog",
diff --git a/init/Android.mk b/init/Android.mk
index bdd0301..69c63e1 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -91,6 +91,7 @@
     libz \
     libselinux \
     libcap \
+    libgsi \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 43a520f..169edbe 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -54,6 +54,7 @@
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
 #include <fscrypt/fscrypt_init_extensions.h>
+#include <libgsi/libgsi.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -520,6 +521,9 @@
         return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
+        if (android::gsi::IsGsiRunning()) {
+            return Error() << "cannot wipe within GSI";
+        }
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
         const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
         return reboot_into_recovery(options);
@@ -1022,7 +1026,8 @@
     }
     service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
-            if (fscrypt_is_native()) {
+            // TODO (b/122850122): support this in gsi
+            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
                 LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
                 if (auto result = reboot_into_recovery(
                             {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
@@ -1093,86 +1098,6 @@
     }
 }
 
-static Result<Success> bind_mount_file(const char* source, const char* mount_point,
-                                       bool remount_private) {
-    if (remount_private && mount(nullptr, mount_point, nullptr, MS_PRIVATE, nullptr) == -1) {
-        return ErrnoError() << "Could not change " << mount_point << " to a private mount point";
-    }
-    if (mount(source, mount_point, nullptr, MS_BIND, nullptr) == -1) {
-        return ErrnoError() << "Could not bind-mount " << source << " to " << mount_point;
-    }
-    return Success();
-}
-
-static Result<Success> bind_mount_bionic(const char* linker_source, const char* lib_dir_source,
-                                         const char* linker_mount_point, const char* lib_mount_dir,
-                                         bool remount_private) {
-    if (access(linker_source, F_OK) != 0) {
-        return Success();
-    }
-    if (auto result = bind_mount_file(linker_source, linker_mount_point, remount_private);
-        !result) {
-        return result;
-    }
-    for (auto libname : kBionicLibFileNames) {
-        std::string mount_point = lib_mount_dir + libname;
-        std::string source = lib_dir_source + libname;
-        if (auto result = bind_mount_file(source.c_str(), mount_point.c_str(), remount_private);
-            !result) {
-            return result;
-        }
-    }
-    return Success();
-}
-
-// The bootstrap bionic libs and the bootstrap linker are bind-mounted to
-// the mount points for pre-apexd processes.
-static Result<Success> do_prepare_bootstrap_bionic(const BuiltinArguments& args) {
-    static bool prepare_bootstrap_bionic_done = false;
-    if (prepare_bootstrap_bionic_done) {
-        return Error() << "prepare_bootstrap_bionic was already executed. Cannot be executed again";
-    }
-    if (auto result = bind_mount_bionic(kBootstrapLinkerPath, kBootstrapBionicLibsDir,
-                                        kLinkerMountPoint, kBionicLibsMountPointDir, false);
-        !result) {
-        return result;
-    }
-    if (auto result = bind_mount_bionic(kBootstrapLinkerPath64, kBootstrapBionicLibsDir64,
-                                        kLinkerMountPoint64, kBionicLibsMountPointDir64, false);
-        !result) {
-        return result;
-    }
-
-    LOG(INFO) << "prepare_bootstrap_bionic done";
-    prepare_bootstrap_bionic_done = true;
-    return Success();
-}
-
-// The bionic libs and the dynamic linker from the runtime APEX are bind-mounted
-// to the mount points. As a result, the previous mounts done by
-// prepare_bootstrap_bionic become hidden.
-static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
-    static bool setup_runtime_bionic_done = false;
-    if (setup_runtime_bionic_done) {
-        return Error() << "setup_runtime_bionic was already executed. Cannot be executed again";
-    }
-    if (auto result = bind_mount_bionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir,
-                                        kLinkerMountPoint, kBionicLibsMountPointDir, true);
-        !result) {
-        return result;
-    }
-    if (auto result = bind_mount_bionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64,
-                                        kLinkerMountPoint64, kBionicLibsMountPointDir64, true);
-        !result) {
-        return result;
-    }
-
-    ServiceList::GetInstance().MarkRuntimeAvailable();
-    LOG(INFO) << "setup_runtime_bionic done";
-    setup_runtime_bionic_done = true;
-    return Success();
-}
-
 // Builtin-function-map start
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1211,7 +1136,6 @@
         {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
         {"mount",                   {3,     kMax, {false,  do_mount}}},
         {"parse_apex_configs",      {0,     0,    {false,  do_parse_apex_configs}}},
-        {"prepare_bootstrap_bionic",{0,     0,    {false,  do_prepare_bootstrap_bionic}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
         {"restart",                 {1,     1,    {false,  do_restart}}},
@@ -1220,7 +1144,6 @@
         {"rm",                      {1,     1,    {true,   do_rm}}},
         {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
         {"setprop",                 {2,     2,    {true,   do_setprop}}},
-        {"setup_runtime_bionic",    {0,     0,    {false,  do_setup_runtime_bionic}}},
         {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
         {"start",                   {1,     1,    {false,  do_start}}},
         {"stop",                    {1,     1,    {false,  do_stop}}},
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 71fe401..affa39e 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -34,6 +34,7 @@
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
+#include <libgsi/libgsi.h>
 #include <liblp/liblp.h>
 
 #include "devices.h"
@@ -79,6 +80,7 @@
     bool IsDmLinearEnabled();
     bool GetDmLinearMetadataDevice();
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
+    void UseGsiIfPresent();
 
     ListenerAction UeventCallback(const Uevent& uevent);
 
@@ -207,6 +209,8 @@
     }
 
     required_devices_partition_names_.emplace(super_partition_name_);
+    // When booting from live GSI images, userdata is the super device.
+    required_devices_partition_names_.emplace("userdata");
     return true;
 }
 
@@ -410,6 +414,16 @@
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
 bool FirstStageMount::TrySwitchSystemAsRoot() {
+    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/metadata";
+    });
+    if (metadata_partition != fstab_.end()) {
+        if (MountPartition(&(*metadata_partition))) {
+            fstab_.erase(metadata_partition);
+            UseGsiIfPresent();
+        }
+    }
+
     auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
         return entry.mount_point == "/system";
     });
@@ -513,6 +527,40 @@
     return true;
 }
 
+void FirstStageMount::UseGsiIfPresent() {
+    std::string metadata_file, error;
+
+    if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) {
+        LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
+        return;
+    }
+
+    auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str());
+    if (!metadata) {
+        LOG(ERROR) << "GSI partition layout could not be read";
+        return;
+    }
+
+    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), "/dev/block/by-name/userdata")) {
+        LOG(ERROR) << "GSI partition layout could not be instantiated";
+        return;
+    }
+
+    if (!android::gsi::MarkSystemAsGsi()) {
+        PLOG(ERROR) << "GSI indicator file could not be written";
+        return;
+    }
+
+    // Replace the existing system fstab entry.
+    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/system";
+    });
+    if (system_partition != fstab_.end()) {
+        fstab_.erase(system_partition);
+    }
+    fstab_.emplace_back(BuildGsiSystemFstabEntry());
+}
+
 bool FirstStageMountVBootV1::GetDmVerityDevices() {
     std::string verity_loc_device;
     need_dm_verity_ = false;
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 008a868..5b90969 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,9 +20,11 @@
 #include <fcntl.h>
 #include <linux/fs.h>
 #include <mntent.h>
+#include <linux/loop.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
+#include <sys/swap.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -55,9 +57,11 @@
 #include "service.h"
 #include "sigchld_handler.h"
 
+using android::base::GetBoolProperty;
 using android::base::Split;
 using android::base::StringPrintf;
 using android::base::Timer;
+using android::base::unique_fd;
 
 namespace android {
 namespace init {
@@ -193,7 +197,7 @@
     return true;
 }
 
-static void DumpUmountDebuggingInfo(bool dump_all) {
+static void DumpUmountDebuggingInfo() {
     int status;
     if (!security_getenforce()) {
         LOG(INFO) << "Run lsof";
@@ -202,10 +206,9 @@
                                 true, nullptr, nullptr, 0);
     }
     FindPartitionsToUmount(nullptr, nullptr, true);
-    if (dump_all) {
-        // dump current tasks, this log can be lengthy, so only dump with dump_all
-        android::base::WriteStringToFile("t", "/proc/sysrq-trigger");
-    }
+    // dump current CPU stack traces and uninterruptible tasks
+    android::base::WriteStringToFile("l", "/proc/sysrq-trigger");
+    android::base::WriteStringToFile("w", "/proc/sysrq-trigger");
 }
 
 static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
@@ -268,11 +271,11 @@
     UmountStat stat = UmountPartitions(timeout - t.duration());
     if (stat != UMOUNT_STAT_SUCCESS) {
         LOG(INFO) << "umount timeout, last resort, kill all and try";
-        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
+        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
         KillAllProcesses();
         // even if it succeeds, still it is timeout and do not run fsck with all processes killed
         UmountStat st = UmountPartitions(0ms);
-        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
+        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
     }
 
     if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
@@ -285,6 +288,48 @@
     return stat;
 }
 
+// zram is able to use backing device on top of a loopback device.
+// In order to unmount /data successfully, we have to kill the loopback device first
+#define ZRAM_DEVICE   "/dev/block/zram0"
+#define ZRAM_RESET    "/sys/block/zram0/reset"
+#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
+static void KillZramBackingDevice() {
+    std::string backing_dev;
+    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return;
+
+    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return;
+
+    // cut the last "\n"
+    backing_dev.erase(backing_dev.length() - 1);
+
+    // shutdown zram handle
+    Timer swap_timer;
+    LOG(INFO) << "swapoff() start...";
+    if (swapoff(ZRAM_DEVICE) == -1) {
+        LOG(ERROR) << "zram_backing_dev: swapoff (" << backing_dev << ")" << " failed";
+        return;
+    }
+    LOG(INFO) << "swapoff() took " << swap_timer;;
+
+    if (!android::base::WriteStringToFile("1", ZRAM_RESET)) {
+        LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
+        return;
+    }
+
+    // clear loopback device
+    unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
+    if (loop.get() < 0) {
+        LOG(ERROR) << "zram_backing_dev: open(" << backing_dev << ")" << " failed";
+        return;
+    }
+
+    if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) {
+        LOG(ERROR) << "zram_backing_dev: loop_clear (" << backing_dev << ")" << " failed";
+        return;
+    }
+    LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully.";
+}
+
 //* Reboot / shutdown the system.
 // cmd ANDROID_RB_* as defined in android_reboot.h
 // reason Reason string like "reboot", "shutdown,userrequested"
@@ -354,9 +399,31 @@
     Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
     Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
     if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
-        // will not check animation class separately
+        bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
+
+        if (do_shutdown_animation) {
+            property_set("service.bootanim.exit", "0");
+            // Could be in the middle of animation. Stop and start so that it can pick
+            // up the right mode.
+            bootAnim->Stop();
+        }
+
         for (const auto& service : ServiceList::GetInstance()) {
-            if (service->classnames().count("animation")) service->SetShutdownCritical();
+            if (service->classnames().count("animation") == 0) {
+                continue;
+            }
+
+            // start all animation classes if stopped.
+            if (do_shutdown_animation) {
+                service->Start().IgnoreError();
+            }
+            service->SetShutdownCritical();  // will not check animation class separately
+        }
+
+        if (do_shutdown_animation) {
+            bootAnim->Start().IgnoreError();
+            surfaceFlinger->SetShutdownCritical();
+            bootAnim->SetShutdownCritical();
         }
     }
 
@@ -427,6 +494,9 @@
         sync();
         LOG(INFO) << "sync() before umount took" << sync_timer;
     }
+    // 5. drop caches and disable zram backing device, if exist
+    KillZramBackingDevice();
+
     UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
     // Follow what linux shutdown is doing: one more sync with little bit delay
     {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index e4da52c..d93e9ec 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -304,13 +304,18 @@
     if (!GetVendorMappingVersion(&vend_plat_vers)) {
         return false;
     }
-    std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+    std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
 
     std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
     if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
         product_policy_cil_file.clear();
     }
 
+    std::string product_mapping_file("/product/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+    if (access(product_mapping_file.c_str(), F_OK) == -1) {
+        product_mapping_file.clear();
+    }
+
     // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
     // nonplat_sepolicy.cil.
     std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
@@ -340,7 +345,7 @@
         "-m", "-M", "true", "-G", "-N",
         // Target the highest policy language version supported by the kernel
         "-c", version_as_string.c_str(),
-        mapping_file.c_str(),
+        plat_mapping_file.c_str(),
         "-o", compiled_sepolicy,
         // We don't care about file_contexts output by the compiler
         "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
@@ -350,6 +355,9 @@
     if (!product_policy_cil_file.empty()) {
         compile_args.push_back(product_policy_cil_file.c_str());
     }
+    if (!product_mapping_file.empty()) {
+        compile_args.push_back(product_mapping_file.c_str());
+    }
     if (!plat_pub_versioned_cil_file.empty()) {
         compile_args.push_back(plat_pub_versioned_cil_file.c_str());
     }
@@ -434,12 +442,6 @@
 
     selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
     selinux_android_restorecon("/dev/device-mapper", 0);
-
-    selinux_android_restorecon("/sbin/mke2fs_static", 0);
-    selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
-
-    selinux_android_restorecon("/sbin/mkfs.f2fs", 0);
-    selinux_android_restorecon("/sbin/sload.f2fs", 0);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
diff --git a/init/service.cpp b/init/service.cpp
index f3eafe4..272809f 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -140,43 +140,6 @@
     return Success();
 }
 
-Result<Success> Service::SetUpPreApexdMounts() const {
-    // If a pre-apexd service is 're' launched after the runtime APEX is
-    // available, unmount the linker and bionic libs which are currently
-    // bind mounted to the files in the runtime APEX. This will reveal
-    // the hidden mount points (targetting the bootstrap ones in the
-    // system partition) which were setup before the runtime APEX was
-    // started. Note that these unmounts are done in a separate mount namespace
-    // for the process. It does not affect other processes including the init.
-    if (pre_apexd_ && ServiceList::GetInstance().IsRuntimeAvailable()) {
-        if (access(kLinkerMountPoint, F_OK) == 0) {
-            if (umount(kLinkerMountPoint) == -1) {
-                return ErrnoError() << "Could not umount " << kLinkerMountPoint;
-            }
-            for (const auto& libname : kBionicLibFileNames) {
-                std::string mount_point = kBionicLibsMountPointDir + libname;
-                if (umount(mount_point.c_str()) == -1) {
-                    return ErrnoError() << "Could not umount " << mount_point;
-                }
-            }
-        }
-
-        if (access(kLinkerMountPoint64, F_OK) == 0) {
-            if (umount(kLinkerMountPoint64) == -1) {
-                return ErrnoError() << "Could not umount " << kLinkerMountPoint64;
-            }
-            for (const auto& libname : kBionicLibFileNames) {
-                std::string mount_point = kBionicLibsMountPointDir64 + libname;
-                std::string source = kBootstrapBionicLibsDir64 + libname;
-                if (umount(mount_point.c_str()) == -1) {
-                    return ErrnoError() << "Could not umount " << mount_point;
-                }
-            }
-        }
-    }
-    return Success();
-}
-
 Result<Success> Service::SetUpPidNamespace() const {
     if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
         return ErrnoError() << "Could not set name";
@@ -406,7 +369,7 @@
 
     // If we crash > 4 times in 4 minutes, reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
-    if (((flags_ & SVC_CRITICAL) || !pre_apexd_) && !(flags_ & SVC_RESTART)) {
+    if (((flags_ & SVC_CRITICAL) || classnames_.count("updatable")) && !(flags_ & SVC_RESTART)) {
         if (now < time_crashed_ + 4min) {
             if (++crash_count_ > 4) {
                 if (flags_ & SVC_CRITICAL) {
@@ -966,14 +929,6 @@
         scon = *result;
     }
 
-    if (!ServiceList::GetInstance().IsRuntimeAvailable() && !pre_apexd_) {
-        // If this service is started before the runtime APEX gets available,
-        // mark it as pre-apexd one. Note that this marking is permanent. So
-        // for example, if the service is re-launched (e.g., due to crash),
-        // it is still recognized as pre-apexd... for consistency.
-        pre_apexd_ = true;
-    }
-
     LOG(INFO) << "starting service '" << name_ << "'...";
 
     pid_t pid = -1;
@@ -990,37 +945,6 @@
             LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
         }
 
-        // b/122559956: mount namespace is not cloned for the devices that don't support
-        // the update of bionic libraries via APEX. In that case, because the bionic
-        // libraries in the runtime APEX and the bootstrap bionic libraries are
-        // identical, it doesn't matter which libs are used. This is also to avoid the
-        // bug in sdcardfs which is triggered when we have multiple mount namespaces
-        // across vold and the others. BIONIC_UPDATABLE shall be true only for the
-        // devices where kernel has the fix for the sdcardfs bug (see the commit message
-        // for the fix).
-        static bool bionic_updatable =
-                android::base::GetBoolProperty("ro.apex.bionic_updatable", false);
-
-        if (bionic_updatable && pre_apexd_) {
-            // pre-apexd process gets a private copy of the mount namespace.
-            // However, this does not mean that mount/unmount events are not
-            // shared across pre-apexd processes and post-apexd processes.
-            // *Most* of the events are still shared because the propagation
-            // type of / is set to 'shared'. (see `mount rootfs rootfs /shared
-            // rec` in init.rc)
-            //
-            // This unsharing is required to not propagate the mount events
-            // under /system/lib/{libc|libdl|libm}.so and /system/bin/linker(64)
-            // whose propagation type is set to private. With this,
-            // bind-mounting the bionic libs and the dynamic linker from the
-            // runtime APEX to the mount points does not affect pre-apexd
-            // processes which should use the bootstrap ones.
-            if (unshare(CLONE_NEWNS) != 0) {
-                LOG(FATAL) << "Creating a new mount namespace for service"
-                           << " '" << name_ << "' failed: " << strerror(errno);
-            }
-        }
-
         if (namespace_flags_ & CLONE_NEWNS) {
             if (auto result = SetUpMountNamespace(); !result) {
                 LOG(FATAL) << "Service '" << name_
@@ -1028,14 +952,6 @@
             }
         }
 
-        // b/122559956: same as above
-        if (bionic_updatable && pre_apexd_ && ServiceList::GetInstance().IsRuntimeAvailable()) {
-            if (auto result = SetUpPreApexdMounts(); !result) {
-                LOG(FATAL) << "Pre-apexd service '" << name_
-                           << "' could not setup the mount points: " << result.error();
-            }
-        }
-
         if (namespace_flags_ & CLONE_NEWPID) {
             // This will fork again to run an init process inside the PID
             // namespace.
@@ -1408,10 +1324,6 @@
     delayed_service_names_.clear();
 }
 
-void ServiceList::MarkRuntimeAvailable() {
-    runtime_available_ = true;
-}
-
 void ServiceList::DelayService(const Service& service) {
     if (services_update_finished_) {
         LOG(ERROR) << "Cannot delay the start of service '" << service.name()
diff --git a/init/service.h b/init/service.h
index 676111f..56e75b0 100644
--- a/init/service.h
+++ b/init/service.h
@@ -62,24 +62,6 @@
 namespace android {
 namespace init {
 
-static constexpr const char* kLinkerMountPoint = "/system/bin/linker";
-static constexpr const char* kBootstrapLinkerPath = "/system/bin/linker";
-static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker";
-
-static constexpr const char* kBionicLibsMountPointDir = "/system/lib/";
-static constexpr const char* kBootstrapBionicLibsDir = "/system/lib/";
-static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/";
-
-static constexpr const char* kLinkerMountPoint64 = "/system/bin/linker64";
-static constexpr const char* kBootstrapLinkerPath64 = "/system/bin/linker64";
-static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64";
-
-static constexpr const char* kBionicLibsMountPointDir64 = "/system/lib64/";
-static constexpr const char* kBootstrapBionicLibsDir64 = "/system/lib64/";
-static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/";
-
-static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"};
-
 class Service {
   public:
     Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
@@ -142,7 +124,6 @@
     std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
     const std::vector<std::string>& args() const { return args_; }
     bool is_updatable() const { return updatable_; }
-    bool is_pre_apexd() const { return pre_apexd_; }
 
   private:
     using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
@@ -151,7 +132,6 @@
     Result<Success> SetUpMountNamespace() const;
     Result<Success> SetUpPidNamespace() const;
     Result<Success> EnterNamespaces() const;
-    Result<Success> SetUpPreApexdMounts() const;
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void ZapStdio() const;
@@ -262,8 +242,6 @@
     std::vector<std::string> args_;
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
-
-    bool pre_apexd_ = false;
 };
 
 class ServiceList {
@@ -306,16 +284,13 @@
     const std::vector<Service*> services_in_shutdown_order() const;
 
     void MarkServicesUpdate();
-    void MarkRuntimeAvailable();
     bool IsServicesUpdated() const { return services_update_finished_; }
-    bool IsRuntimeAvailable() const { return runtime_available_; }
     void DelayService(const Service& service);
 
   private:
     std::vector<std::unique_ptr<Service>> services_;
 
     bool services_update_finished_ = false;
-    bool runtime_available_ = false;
     std::vector<std::string> delayed_service_names_;
 };
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 0723612..03edfb5 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -190,7 +190,8 @@
  */
 #define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
 
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
+/* use the ranges below to determine whether a process is isolated */
+#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */
 #define AID_ISOLATED_END 99999   /* end of uids for fully isolated sandboxed processes */
 
 #define AID_USER 100000        /* TODO: switch users over to AID_USER_OFFSET */
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index c852bbb..24054c6 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 cc_binary {
-    name: "librank2",
+    name: "librank",
     cflags: [
         "-Wall",
         "-Werror",
@@ -27,7 +27,7 @@
 }
 
 cc_binary {
-    name: "procmem2",
+    name: "procmem",
     cflags: [
         "-Wall",
         "-Werror",
@@ -41,7 +41,7 @@
 }
 
 cc_binary {
-    name: "procrank2",
+    name: "procrank",
     cflags: [
         "-Wall",
         "-Werror",
@@ -55,7 +55,7 @@
 }
 
 cc_binary {
-    name: "showmap2",
+    name: "showmap",
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/libmemtrack/.clang-format b/libmemtrack/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmemtrack/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 0955633..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -28,10 +28,11 @@
 
 cc_binary {
     name: "memtrack_test",
-    srcs: ["memtrack_test.c"],
+    srcs: ["memtrack_test.cpp"],
+    static_libs: ["libc++fs"],
     shared_libs: [
+        "libbase",
         "libmemtrack",
-        "libpagemap",
     ],
     cflags: [
         "-Wall",
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
deleted file mode 100644
index 77c935e..0000000
--- a/libmemtrack/memtrack_test.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <memtrack/memtrack.h>
-
-#include <pagemap/pagemap.h>
-
-#define DIV_ROUND_UP(x,y) (((x) + (y) - 1) / (y))
-
-static int getprocname(pid_t pid, char *buf, int len) {
-    char *filename;
-    FILE *f;
-    int rc = 0;
-    static const char* unknown_cmdline = "<unknown>";
-
-    if (len <= 0) {
-        return -1;
-    }
-
-    if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) {
-        rc = 1;
-        goto exit;
-    }
-
-    f = fopen(filename, "r");
-    if (f == NULL) {
-        rc = 2;
-        goto releasefilename;
-    }
-
-    if (fgets(buf, len, f) == NULL) {
-        rc = 3;
-        goto closefile;
-    }
-
-closefile:
-    (void) fclose(f);
-releasefilename:
-    free(filename);
-exit:
-    if (rc != 0) {
-        /*
-         * The process went away before we could read its process name. Try
-         * to give the user "<unknown>" here, but otherwise they get to look
-         * at a blank.
-         */
-        if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
-            rc = 4;
-        }
-    }
-
-    return rc;
-}
-
-int main(int argc, char *argv[])
-{
-    int ret;
-    pm_kernel_t *ker;
-    size_t num_procs;
-    pid_t *pids;
-    struct memtrack_proc *p;
-    size_t i;
-
-    (void)argc;
-    (void)argv;
-
-    ret = pm_kernel_create(&ker);
-    if (ret) {
-        fprintf(stderr, "Error creating kernel interface -- "
-                        "does this kernel have pagemap?\n");
-        exit(EXIT_FAILURE);
-    }
-
-    ret = pm_kernel_pids(ker, &pids, &num_procs);
-    if (ret) {
-        fprintf(stderr, "Error listing processes.\n");
-        exit(EXIT_FAILURE);
-    }
-
-    p = memtrack_proc_new();
-    if (ret) {
-        fprintf(stderr, "failed to create memtrack process handle\n");
-        exit(EXIT_FAILURE);
-    }
-
-    for (i = 0; i < num_procs; i++) {
-        pid_t pid = pids[i];
-        char cmdline[256];
-        size_t v1;
-        size_t v2;
-        size_t v3;
-        size_t v4;
-        size_t v5;
-        size_t v6;
-
-        getprocname(pid, cmdline, (int)sizeof(cmdline));
-
-        ret = memtrack_proc_get(p, pid);
-        if (ret) {
-            fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n",
-                    pid, strerror(-ret), ret);
-            continue;
-        }
-
-        v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
-        v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
-        v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
-        v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
-        v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
-        v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
-
-        if (v1 | v2 | v3 | v4 | v5 | v6) {
-            printf("%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid,
-                   v1, v2, v3, v4, v5, v6, cmdline);
-        }
-    }
-
-    memtrack_proc_destroy(p);
-
-    return 0;
-}
diff --git a/libmemtrack/memtrack_test.cpp b/libmemtrack/memtrack_test.cpp
new file mode 100644
index 0000000..aeeaf24
--- /dev/null
+++ b/libmemtrack/memtrack_test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <filesystem>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <memtrack/memtrack.h>
+
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+
+static void getprocname(pid_t pid, std::string* name) {
+    std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+    if (!::android::base::ReadFileToString(fname, name)) {
+        fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+        *name = "<unknown>";
+    }
+}
+
+int main(int /* argc */, char** /* argv */) {
+    int ret;
+    struct memtrack_proc* p;
+    std::vector<pid_t> pids;
+
+    p = memtrack_proc_new();
+    if (p == nullptr) {
+        fprintf(stderr, "failed to create memtrack process handle\n");
+        exit(EXIT_FAILURE);
+    }
+
+    for (auto& de : std::filesystem::directory_iterator("/proc")) {
+        if (!std::filesystem::is_directory(de.status())) {
+            continue;
+        }
+
+        pid_t pid;
+        if (!::android::base::ParseInt(de.path().filename().string(), &pid)) {
+            continue;
+        }
+        pids.emplace_back(pid);
+    }
+
+    for (auto& pid : pids) {
+        size_t v1;
+        size_t v2;
+        size_t v3;
+        size_t v4;
+        size_t v5;
+        size_t v6;
+        std::string cmdline;
+
+        getprocname(pid, &cmdline);
+
+        ret = memtrack_proc_get(p, pid);
+        if (ret) {
+            fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n", pid, strerror(-ret),
+                    ret);
+            continue;
+        }
+
+        v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
+        v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
+        v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
+        v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
+        v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
+        v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
+
+        if (v1 | v2 | v3 | v4 | v5 | v6) {
+            fprintf(stdout, "%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid, v1, v2, v3, v4, v5, v6,
+                    cmdline.c_str());
+        }
+    }
+
+    memtrack_proc_destroy(p);
+
+    return ret;
+}
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index ca026b3..2d6ce85 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -109,19 +109,6 @@
                                     struct android_namespace_t* to,
                                     const char* shared_libs_sonames);
 
-/*
- * Get the default library search path.
- * The path will be copied into buffer, which must have space for at least
- * buffer_size chars. Elements are separated with ':', and the path will always
- * be null-terminated.
- *
- * If buffer_size is too small to hold the entire default search path and the
- * null terminator, this function will abort. There is currently no way to find
- * out what the required buffer size is. At the time of this writing, PATH_MAX
- * is sufficient and used by all callers of this function.
- */
-extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
-
 extern struct android_namespace_t* android_get_exported_namespace(const char* name);
 
 __END_DECLS
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index af53dc5..260f655 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -47,7 +47,7 @@
 
 __attribute__((visibility("default"))) void* OpenNativeLibrary(
     JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
-    jstring library_path, bool* needs_native_bridge, char** error_msg);
+    const char* caller_location, jstring library_path, bool* needs_native_bridge, char** error_msg);
 
 __attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle,
                                                                const bool needs_native_bridge,
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index af7df72..ab17b29 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -43,6 +43,10 @@
 #include <android-base/properties.h>
 #endif
 
+extern "C" {
+struct android_namespace_t* android_get_exported_namespace(const char*);
+}
+
 #define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
                                              "%s:%d: %s CHECK '" #predicate "' failed.",\
                                              __FILE__, __LINE__, __FUNCTION__)
@@ -119,6 +123,8 @@
 // This list includes all directories app is allowed to access this way.
 static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
 
+static constexpr const char* kApexPath = "/apex/";
+
 static bool is_debuggable() {
   char debuggable[PROP_VALUE_MAX];
   property_get("ro.debuggable", debuggable, "0");
@@ -623,13 +629,51 @@
   return nullptr;
 }
 
+#if defined(__ANDROID__)
+static android_namespace_t* FindExportedNamespace(const char* caller_location) {
+  std::string location = caller_location;
+  // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
+  // /apex/com.android...modulename/...
+  //
+  // And we extract from it 'modulename', which is the name of the linker namespace.
+  if (android::base::StartsWith(location, kApexPath)) {
+    size_t slash_index = location.find_first_of('/', strlen(kApexPath));
+    LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
+                        "Error finding namespace of apex: no slash in path %s", caller_location);
+    size_t dot_index = location.find_last_of('.', slash_index);
+    LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
+                        "Error finding namespace of apex: no dot in apex name %s", caller_location);
+    std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+    android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
+    LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
+                        "Error finding namespace of apex: no namespace called %s", name.c_str());
+    return boot_namespace;
+  }
+  return nullptr;
+}
+#endif
+
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, jstring library_path, bool* needs_native_bridge,
-                        char** error_msg) {
+                        jobject class_loader, const char* caller_location, jstring library_path,
+                        bool* needs_native_bridge, char** error_msg) {
 #if defined(__ANDROID__)
   UNUSED(target_sdk_version);
   if (class_loader == nullptr) {
     *needs_native_bridge = false;
+    if (caller_location != nullptr) {
+      android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
+      if (boot_namespace != nullptr) {
+        const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE,
+            .library_namespace = boot_namespace,
+        };
+        void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
+        if (handle == nullptr) {
+          *error_msg = strdup(dlerror());
+        }
+        return handle;
+      }
+    }
     void* handle = dlopen(path, RTLD_NOW);
     if (handle == nullptr) {
       *error_msg = strdup(dlerror());
@@ -654,7 +698,7 @@
 
   return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
 #else
-  UNUSED(env, target_sdk_version, class_loader);
+  UNUSED(env, target_sdk_version, class_loader, caller_location);
 
   // Do some best effort to emulate library-path support. It will not
   // work for dependencies.
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 73dc921..d1b8271 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -191,6 +191,7 @@
         "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoCreateMemoryTest.cpp",
+        "tests/MapInfoGetBuildIDTest.cpp",
         "tests/MapInfoGetElfTest.cpp",
         "tests/MapInfoGetLoadBiasTest.cpp",
         "tests/MapInfoTest.cpp",
@@ -343,6 +344,7 @@
     ],
 
     shared_libs: [
+        "libbase",
         "libunwindstack",
     ],
 }
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 5b586a2..2f5eed9 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -140,8 +140,11 @@
   return true;
 }
 
-bool Elf::GetBuildID(std::string* build_id) {
-  return valid_ && interface_->GetBuildID(build_id);
+std::string Elf::GetBuildID() {
+  if (!valid_) {
+    return "";
+  }
+  return interface_->GetBuildID();
 }
 
 void Elf::GetLastError(ErrorData* data) {
@@ -384,4 +387,22 @@
   return false;
 }
 
+std::string Elf::GetBuildID(Memory* memory) {
+  if (!IsValidElf(memory)) {
+    return "";
+  }
+
+  uint8_t class_type;
+  if (!memory->Read(EI_CLASS, &class_type, 1)) {
+    return "";
+  }
+
+  if (class_type == ELFCLASS32) {
+    return ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(memory);
+  } else if (class_type == ELFCLASS64) {
+    return ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(memory);
+  }
+  return "";
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 709e6c9..c1b98d9 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -238,31 +238,31 @@
 }
 
 template <typename NhdrType>
-bool ElfInterface::ReadBuildID(std::string* build_id) {
+std::string ElfInterface::ReadBuildID() {
   // Ensure there is no overflow in any of the calulations below.
   uint64_t tmp;
   if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
-    return false;
+    return "";
   }
 
   uint64_t offset = 0;
   while (offset < gnu_build_id_size_) {
     if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
-      return false;
+      return "";
     }
     NhdrType hdr;
     if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
-      return false;
+      return "";
     }
     offset += sizeof(hdr);
 
     if (gnu_build_id_size_ - offset < hdr.n_namesz) {
-      return false;
+      return "";
     }
     if (hdr.n_namesz > 0) {
       std::string name(hdr.n_namesz, '\0');
       if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
-        return false;
+        return "";
       }
 
       // Trim trailing \0 as GNU is stored as a C string in the ELF file.
@@ -273,18 +273,20 @@
       offset += (hdr.n_namesz + 3) & ~3;
 
       if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
-        if (gnu_build_id_size_ - offset < hdr.n_descsz) {
-          return false;
+        if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+          return "";
         }
-        build_id->resize(hdr.n_descsz);
-        return memory_->ReadFully(gnu_build_id_offset_ + offset, &(*build_id)[0],
-                                  hdr.n_descsz);
+        std::string build_id(hdr.n_descsz - 1, '\0');
+        if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
+          return build_id;
+        }
+        return "";
       }
     }
     // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
     offset += (hdr.n_descsz + 3) & ~3;
   }
-  return false;
+  return "";
 }
 
 template <typename EhdrType, typename ShdrType>
@@ -536,6 +538,103 @@
   *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
 }
 
+template <typename EhdrType, typename ShdrType>
+bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
+  EhdrType ehdr;
+  if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+    return false;
+  }
+
+  uint64_t offset = ehdr.e_shoff;
+  uint64_t sec_offset;
+  uint64_t sec_size;
+  ShdrType shdr;
+  if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+    return false;
+  }
+
+  uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
+  if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
+    return false;
+  }
+  sec_offset = shdr.sh_offset;
+  sec_size = shdr.sh_size;
+
+  // Skip the first header, it's always going to be NULL.
+  offset += ehdr.e_shentsize;
+  for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
+    if (!memory->ReadFully(offset, &shdr, sizeof(shdr))) {
+      return false;
+    }
+    std::string name;
+    if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
+        memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+      *build_id_offset = shdr.sh_offset;
+      *build_id_size = shdr.sh_size;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+template <typename EhdrType, typename ShdrType, typename NhdrType>
+std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
+  uint64_t note_offset;
+  uint64_t note_size;
+  if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, &note_offset, &note_size)) {
+    return "";
+  }
+
+  // Ensure there is no overflow in any of the calculations below.
+  uint64_t tmp;
+  if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
+    return "";
+  }
+
+  uint64_t offset = 0;
+  while (offset < note_size) {
+    if (note_size - offset < sizeof(NhdrType)) {
+      return "";
+    }
+    NhdrType hdr;
+    if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
+      return "";
+    }
+    offset += sizeof(hdr);
+
+    if (note_size - offset < hdr.n_namesz) {
+      return "";
+    }
+    if (hdr.n_namesz > 0) {
+      std::string name(hdr.n_namesz, '\0');
+      if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
+        return "";
+      }
+
+      // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+      if (name.back() == '\0') name.resize(name.size() - 1);
+
+      // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+      offset += (hdr.n_namesz + 3) & ~3;
+
+      if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+        if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+          return "";
+        }
+        std::string build_id(hdr.n_descsz - 1, '\0');
+        if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
+          return build_id;
+        }
+        return "";
+      }
+    }
+    // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+    offset += (hdr.n_descsz + 3) & ~3;
+  }
+  return "";
+}
+
 // Instantiate all of the needed template functions.
 template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
 template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
@@ -551,8 +650,8 @@
 template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
 template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
 
-template bool ElfInterface::ReadBuildID<Elf32_Nhdr>(std::string*);
-template bool ElfInterface::ReadBuildID<Elf64_Nhdr>(std::string*);
+template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
+template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
 
 template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
 template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
@@ -571,4 +670,9 @@
 template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
 template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
 
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
+    Memory*);
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
+    Memory*);
+
 }  // namespace unwindstack
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index b9d8b3d..f319971 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -146,6 +146,10 @@
     }
   }
 
+  if (process_memory == nullptr) {
+    return nullptr;
+  }
+
   // Need to verify that this elf is valid. It's possible that
   // only part of the elf file to be mapped into memory is in the executable
   // map. In this case, there will be another read-only map that includes the
@@ -263,4 +267,48 @@
   return cur_load_bias;
 }
 
+MapInfo::~MapInfo() {
+  uintptr_t id = build_id.load();
+  if (id != 0) {
+    delete reinterpret_cast<std::string*>(id);
+  }
+}
+
+std::string MapInfo::GetBuildID() {
+  uintptr_t id = build_id.load();
+  if (build_id != 0) {
+    return *reinterpret_cast<std::string*>(id);
+  }
+
+  // No need to lock, at worst if multiple threads do this at the same
+  // time it should be detected and only one thread should win and
+  // save the data.
+  std::unique_ptr<std::string> cur_build_id(new std::string);
+
+  // Now need to see if the elf object exists.
+  // Make sure no other thread is trying to add the elf to this map.
+  mutex_.lock();
+  Elf* elf_obj = elf.get();
+  mutex_.unlock();
+  if (elf_obj != nullptr) {
+    *cur_build_id = elf_obj->GetBuildID();
+  } else {
+    // This will only work if we can get the file associated with this memory.
+    // If this is only available in memory, then the section name information
+    // is not present and we will not be able to find the build id info.
+    std::unique_ptr<Memory> memory(GetFileMemory());
+    if (memory != nullptr) {
+      *cur_build_id = Elf::GetBuildID(memory.get());
+    }
+  }
+
+  id = reinterpret_cast<uintptr_t>(cur_build_id.get());
+  uintptr_t expected_id = 0;
+  if (build_id.compare_exchange_weak(expected_id, id)) {
+    // Value saved, so make sure the memory is not freed.
+    cur_build_id.release();
+  }
+  return *reinterpret_cast<std::string*>(id);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 57207de..0dd95cf 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -144,7 +144,6 @@
 
   bool return_address_attempt = false;
   bool adjust_pc = false;
-  std::unique_ptr<JitDebug> jit_debug;
   for (; frames_.size() < max_frames_;) {
     uint64_t cur_pc = regs_->pc();
     uint64_t cur_sp = regs_->sp();
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
index db0fb54..8caecc7 100644
--- a/libunwindstack/benchmarks/unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -20,6 +20,9 @@
 
 #include <benchmark/benchmark.h>
 
+#include <android-base/strings.h>
+
+#include <unwindstack/Elf.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -80,4 +83,63 @@
 }
 BENCHMARK(BM_cached_unwind);
 
+static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
+                       unwindstack::MapInfo** build_id_map_info) {
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+    return;
+  }
+
+  // Find the libc.so share library and use that for benchmark purposes.
+  *build_id_map_info = nullptr;
+  for (unwindstack::MapInfo* map_info : maps) {
+    if (map_info->offset == 0 && map_info->GetBuildID() != "") {
+      *build_id_map_info = map_info;
+      break;
+    }
+  }
+
+  if (*build_id_map_info == nullptr) {
+    state.SkipWithError("Failed to find a map with a BuildID.");
+  }
+}
+
+static void BM_get_build_id_from_elf(benchmark::State& state) {
+  unwindstack::LocalMaps maps;
+  unwindstack::MapInfo* build_id_map_info;
+  Initialize(state, maps, &build_id_map_info);
+
+  unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
+                                                    unwindstack::Regs::CurrentArch());
+  if (!elf->valid()) {
+    state.SkipWithError("Cannot get valid elf from map.");
+  }
+
+  for (auto _ : state) {
+    uintptr_t id = build_id_map_info->build_id;
+    if (id != 0) {
+      delete reinterpret_cast<std::string*>(id);
+      build_id_map_info->build_id = 0;
+    }
+    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+  }
+}
+BENCHMARK(BM_get_build_id_from_elf);
+
+static void BM_get_build_id_from_file(benchmark::State& state) {
+  unwindstack::LocalMaps maps;
+  unwindstack::MapInfo* build_id_map_info;
+  Initialize(state, maps, &build_id_map_info);
+
+  for (auto _ : state) {
+    uintptr_t id = build_id_map_info->build_id;
+    if (id != 0) {
+      delete reinterpret_cast<std::string*>(id);
+      build_id_map_info->build_id = 0;
+    }
+    benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+  }
+}
+BENCHMARK(BM_get_build_id_from_file);
+
 BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 27f7201..00a249f 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -65,8 +65,6 @@
 
   bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
 
-  bool GetBuildID(std::string* build_id);
-
   uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
 
   bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
@@ -74,6 +72,8 @@
 
   ElfInterface* CreateInterfaceFromMemory(Memory* memory);
 
+  std::string GetBuildID();
+
   uint64_t GetLoadBias() { return load_bias_; }
 
   bool IsValidPc(uint64_t pc);
@@ -102,6 +102,8 @@
 
   static uint64_t GetLoadBias(Memory* memory);
 
+  static std::string GetBuildID(Memory* memory);
+
   static void SetCachingEnabled(bool enable);
   static bool CachingEnabled() { return cache_enabled_; }
 
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 52992d9..d41bb13 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -62,7 +62,7 @@
 
   virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
 
-  virtual bool GetBuildID(std::string* build_id) = 0;
+  virtual std::string GetBuildID() = 0;
 
   virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
 
@@ -100,6 +100,9 @@
   template <typename EhdrType, typename PhdrType>
   static uint64_t GetLoadBias(Memory* memory);
 
+  template <typename EhdrType, typename ShdrType, typename NhdrType>
+  static std::string ReadBuildIDFromMemory(Memory* memory);
+
  protected:
   template <typename AddressType>
   void InitHeadersWithTemplate(uint64_t load_bias);
@@ -128,7 +131,7 @@
   static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
 
   template <typename NhdrType>
-  bool ReadBuildID(std::string* build_id);
+  std::string ReadBuildID();
 
   Memory* memory_;
   std::unordered_map<uint64_t, LoadInfo> pt_loads_;
@@ -192,9 +195,7 @@
     return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
   }
 
-  bool GetBuildID(std::string* build_id) {
-    return ElfInterface::ReadBuildID<Elf32_Nhdr>(build_id);
-  }
+  std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf32_Nhdr>(); }
 
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
@@ -226,9 +227,7 @@
     return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
   }
 
-  bool GetBuildID(std::string* build_id) {
-    return ElfInterface::ReadBuildID<Elf64_Nhdr>(build_id);
-  }
+  std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf64_Nhdr>(); }
 
   static void GetMaxSize(Memory* memory, uint64_t* size) {
     GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index a9febd1..5143ff1 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -38,7 +38,8 @@
         flags(flags),
         name(name),
         prev_map(map_info),
-        load_bias(static_cast<uint64_t>(-1)) {}
+        load_bias(static_cast<uint64_t>(-1)),
+        build_id(0) {}
   MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
           const std::string& name)
       : start(start),
@@ -47,8 +48,9 @@
         flags(flags),
         name(name),
         prev_map(map_info),
-        load_bias(static_cast<uint64_t>(-1)) {}
-  ~MapInfo() = default;
+        load_bias(static_cast<uint64_t>(-1)),
+        build_id(0) {}
+  ~MapInfo();
 
   uint64_t start = 0;
   uint64_t end = 0;
@@ -68,6 +70,11 @@
 
   std::atomic_uint64_t load_bias;
 
+  // This is a pointer to a new'd std::string.
+  // Using an atomic value means that we don't need to lock and will
+  // make it easier to move to a fine grained lock in the future.
+  std::atomic_uintptr_t build_id;
+
   // This function guarantees it will never return nullptr.
   Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
 
@@ -77,6 +84,8 @@
 
   bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
 
+  std::string GetBuildID();
+
  private:
   MapInfo(const MapInfo&) = delete;
   void operator=(const MapInfo&) = delete;
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index c2bd0f6..946bc3c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -72,9 +72,7 @@
 
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
   bool GetGlobalVariable(const std::string&, uint64_t*) override;
-  bool GetBuildID(std::string*) override {
-    return false;
-  }
+  std::string GetBuildID() override { return fake_build_id_; }
 
   bool Step(uint64_t, Regs*, Memory*, bool*) override;
 
@@ -82,6 +80,9 @@
     globals_[global] = offset;
   }
 
+  void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
+  void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
+
   static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
   static void FakePushStepData(const StepData data) { steps_.push_back(data); }
 
@@ -96,6 +97,7 @@
 
  private:
   std::unordered_map<std::string, uint64_t> globals_;
+  std::string fake_build_id_;
 
   static std::deque<FunctionData> functions_;
   static std::deque<StepData> steps_;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 6023dc4..7239749 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -1228,9 +1228,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_TRUE(elf->GetBuildID(&build_id));
-  EXPECT_STREQ(build_id.c_str(), "BUILDID");
+  ASSERT_EQ("BUILDID", elf->GetBuildID());
 }
 
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
@@ -1292,9 +1290,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_TRUE(elf->GetBuildID(&build_id));
-  EXPECT_STREQ(build_id.c_str(), "BUILDID");
+  ASSERT_EQ("BUILDID", elf->GetBuildID());
 }
 
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
@@ -1346,8 +1342,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_FALSE(elf->GetBuildID(&build_id));
+  ASSERT_EQ("", elf->GetBuildID());
 }
 
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
@@ -1399,8 +1394,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_FALSE(elf->GetBuildID(&build_id));
+  ASSERT_EQ("", elf->GetBuildID());
 }
 
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
@@ -1452,8 +1446,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  std::string build_id;
-  ASSERT_FALSE(elf->GetBuildID(&build_id));
+  ASSERT_EQ("", elf->GetBuildID());
 }
 
 TEST_F(ElfInterfaceTest, build_id32) {
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index f7689ce..1ff2306 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -311,7 +311,7 @@
   void InitHeaders(uint64_t) override {}
   bool GetSoname(std::string*) override { return false; }
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
-  bool GetBuildID(std::string*) override { return false; }
+  std::string GetBuildID() override { return ""; }
 
   MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
   MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
new file mode 100644
index 0000000..3b89c59
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/test_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetBuildIDTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    tf_.reset(new TemporaryFile);
+
+    memory_ = new MemoryFake;
+    elf_ = new ElfFake(new MemoryFake);
+    elf_interface_ = new ElfInterfaceFake(memory_);
+    elf_->FakeSetInterface(elf_interface_);
+    elf_container_.reset(elf_);
+    map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+  }
+
+  void MultipleThreadTest(std::string expected_build_id);
+
+  MemoryFake* memory_;
+  ElfFake* elf_;
+  ElfInterfaceFake* elf_interface_;
+  std::unique_ptr<ElfFake> elf_container_;
+  std::unique_ptr<MapInfo> map_info_;
+  std::unique_ptr<TemporaryFile> tf_;
+};
+
+TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
+  MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+
+  EXPECT_EQ("", info.GetBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_elf) {
+  map_info_->elf.reset(elf_container_.release());
+  elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+  EXPECT_EQ("FAKE_BUILD_ID", map_info_->GetBuildID());
+}
+
+void MapInfoGetBuildIDTest::MultipleThreadTest(std::string expected_build_id) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  std::string build_id_values[kNumConcurrentThreads];
+  std::vector<std::thread*> threads;
+
+  std::atomic_bool wait;
+  wait = true;
+  // Create all of the threads and have them do the GetLoadBias at the same time
+  // to make it likely that a race will occur.
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    std::thread* thread = new std::thread([i, this, &wait, &build_id_values]() {
+      while (wait)
+        ;
+      build_id_values[i] = map_info_->GetBuildID();
+    });
+    threads.push_back(thread);
+  }
+
+  // Set them all going and wait for the threads to finish.
+  wait = false;
+  for (auto thread : threads) {
+    thread->join();
+    delete thread;
+  }
+
+  // Now verify that all of the elf files are exactly the same and valid.
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    EXPECT_EQ(expected_build_id, build_id_values[i]) << "Thread " << i << " mismatched.";
+  }
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists) {
+  map_info_->elf.reset(elf_container_.release());
+  elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+  MultipleThreadTest("FAKE_BUILD_ID");
+}
+
+static void InitElfData(int fd) {
+  Elf32_Ehdr ehdr;
+  TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shnum = 3;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr);
+  ehdr.e_shstrndx = 2;
+  off_t offset = 0;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(ehdr)), write(fd, &ehdr, sizeof(ehdr)));
+
+  char note_section[128];
+  Elf32_Nhdr note_header = {};
+  note_header.n_namesz = 4;   // "GNU"
+  note_header.n_descsz = 12;  // "ELF_BUILDID"
+  note_header.n_type = NT_GNU_BUILD_ID;
+  memcpy(&note_section, &note_header, sizeof(note_header));
+  size_t note_offset = sizeof(note_header);
+  memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+  note_offset += sizeof("GNU");
+  memcpy(&note_section[note_offset], "ELF_BUILDID", sizeof("ELF_BUILDID"));
+  note_offset += sizeof("ELF_BUILDID");
+
+  Elf32_Shdr shdr = {};
+  shdr.sh_type = SHT_NOTE;
+  shdr.sh_name = 0x500;
+  shdr.sh_offset = 0xb000;
+  shdr.sh_size = sizeof(note_section);
+  offset += ehdr.e_shoff + sizeof(shdr);
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+  // The string data for section header names.
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 0x20000;
+  shdr.sh_offset = 0xf000;
+  shdr.sh_size = 0x1000;
+  offset += sizeof(shdr);
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+  offset = 0xf500;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(".note.gnu.build-id")),
+            write(fd, ".note.gnu.build-id", sizeof(".note.gnu.build-id")));
+
+  offset = 0xb000;
+  ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(note_section)),
+            write(fd, note_section, sizeof(note_section)));
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_memory) {
+  InitElfData(tf_->fd);
+
+  EXPECT_EQ("ELF_BUILDID", map_info_->GetBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists_in_memory) {
+  InitElfData(tf_->fd);
+
+  MultipleThreadTest("ELF_BUILDID");
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 99f8fa3..d7b8485 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -88,6 +88,12 @@
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
+
+  // Now verify that an empty process memory returns an invalid elf object.
+  info.elf.reset();
+  elf = info.GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 3f2dfb0..3f5b88b 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -123,8 +123,8 @@
     printf("Soname: %s\n", soname.c_str());
   }
 
-  std::string build_id;
-  if (elf.GetBuildID(&build_id)) {
+  std::string build_id = elf.GetBuildID();
+  if (!build_id.empty()) {
     printf("Build ID: ");
     for (size_t i = 0; i < build_id.size(); ++i) {
       printf("%02hhx", build_id[i]);
diff --git a/logd/Android.mk b/logd/Android.mk
index b3ce560..aafa28d 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -5,7 +5,6 @@
 LOCAL_MODULE := logtagd.rc
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
 
 include $(BUILD_PREBUILT)
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 6637deb..5c57d69 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -13,5 +13,6 @@
     ],
     stl: "none",
     system_shared_libs: [],
+    header_libs: ["libc_headers"],
     export_include_dirs: ["include"],
 }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index f88f6b9..85c7e9e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -22,7 +22,6 @@
 LOCAL_MODULE := init-debug.rc
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
 
 include $(BUILD_PREBUILT)
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 2dda648..3804c86 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -28,7 +28,7 @@
 dir.postinstall = /postinstall
 
 [system]
-additional.namespaces = runtime,sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -129,6 +129,36 @@
 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.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.conscrypt.link.default.allow_all_shared_libs = true
+
+###############################################################################
 # "sphal" namespace
 #
 # SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 33b4698..2ce25b5 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -28,7 +28,7 @@
 dir.postinstall = /postinstall
 
 [system]
-additional.namespaces = runtime,sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -76,6 +76,36 @@
 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.links = default
+namespace.media.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = default
+# TODO(b/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.conscrypt.link.default.allow_all_shared_libs = true
+
+###############################################################################
 # "sphal" namespace
 #
 # SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9b20964..537bf86 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -12,12 +12,6 @@
 import /init.${ro.zygote}.rc
 
 on early-init
-    # Mount shared so changes propagate into child namespaces
-    # Do this before other processes are started from init. Otherwise,
-    # processes launched while the propagation type of / is 'private'
-    # won't get mount events from others.
-    mount rootfs rootfs / shared rec
-
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000
 
@@ -46,8 +40,6 @@
     # cgroup for system_server and surfaceflinger
     mkdir /dev/memcg/system 0550 system system
 
-    prepare_bootstrap_bionic
-
     start ueventd
 
 on init
@@ -117,6 +109,8 @@
     mkdir /mnt/runtime/read/self 0755 root root
     mkdir /mnt/runtime/write 0755 root root
     mkdir /mnt/runtime/write/self 0755 root root
+    mkdir /mnt/runtime/full 0755 root root
+    mkdir /mnt/runtime/full/self 0755 root root
 
     # Symlink to keep legacy apps working in multi-user world
     symlink /storage/self/primary /sdcard
@@ -352,6 +346,8 @@
     # Once everything is setup, no need to modify /.
     # The bind+remount combination allows this to work in containers.
     mount rootfs rootfs / remount bind ro nodev
+    # Mount shared so changes propagate into child namespaces
+    mount rootfs rootfs / shared rec
     # Mount default storage into root namespace
     mount none /mnt/runtime/default /storage bind rec
     mount none none /storage slave rec
@@ -587,14 +583,6 @@
     # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
 
-    # Wait for apexd to finish activating APEXes before starting more processes.
-    # This certainly reduces the parallelism but is required to make as many processes
-    # as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
-    # so the impact on the booting time is not significant.
-    wait_for_prop apexd.status ready
-    setup_runtime_bionic
-    parse_apex_configs
-
     # If there is no post-fs-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
     # won't work.
@@ -816,3 +804,6 @@
 service flash_recovery /system/bin/install-recovery.sh
     class main
     oneshot
+
+on property:apexd.status=ready
+    parse_apex_configs
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index ac87979..2e95687 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -4,6 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index a535846..1cfc3d6 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -4,6 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -19,5 +20,6 @@
     user root
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
+    updatable
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 6fc810b..8ab012d 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -4,6 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 7ddd52e..5abf149 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -4,6 +4,7 @@
     user root
     group root readproc reserved_disk
     socket zygote stream 660 root system
+    updatable
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
@@ -19,5 +20,6 @@
     user root
     group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
+    updatable
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index dc36596..e1de130 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -160,6 +160,7 @@
     std::string dest_path_default = "/mnt/runtime/default/" + label;
     std::string dest_path_read = "/mnt/runtime/read/" + label;
     std::string dest_path_write = "/mnt/runtime/write/" + label;
+    std::string dest_path_full = "/mnt/runtime/full/" + label;
 
     umask(0);
     if (multi_user) {
@@ -172,7 +173,10 @@
                                       default_normal, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
-                                      derive_gid, default_normal, use_esdfs)) {
+                                      derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
+                                      default_normal, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -186,7 +190,10 @@
                                       derive_gid, default_normal, use_esdfs) ||
             !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
                                       multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
-                                      derive_gid, default_normal, use_esdfs)) {
+                                      derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
+                                      default_normal, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 2d4a26f..cb5c5cb 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -18,6 +18,7 @@
         "newfs_msdos",
         "reboot",
         "sh",
+        "simpleperf",
         "tcpdump",
         "toolbox",
         "toybox",
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 32499e3..9676b79 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -17,12 +17,10 @@
     vendor: true,
 
     srcs: ["tipc_test.c"],
-    static_libs: [
-        "libtrusty",
-    ],
     shared_libs: [
         "libc",
         "liblog",
+        "libtrusty",
     ],
     gtest: false,
     cflags: [