Merge "base: support optional flags in android::base::Pipe."
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/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/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/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/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 3369fb9..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",
diff --git a/init/builtins.cpp b/init/builtins.cpp
index e1a86e4..169edbe 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1098,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();
@@ -1216,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}}},
@@ -1225,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/selinux.cpp b/init/selinux.cpp
index e4da52c..c0fc3ce 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -434,12 +434,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/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/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/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/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: [