Merge "init: actually remount /sys when changing network namespaces"
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 3d10030..3aa03a7 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -83,6 +83,14 @@
     DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
 };
 
+class SilentStandardStreamsCallbackInterface : public StandardStreamsCallbackInterface {
+  public:
+    SilentStandardStreamsCallbackInterface() = default;
+    void OnStdout(const char*, int) override final {}
+    void OnStderr(const char*, int) override final {}
+    int Done(int status) override final { return status; }
+};
+
 // Singleton.
 extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
 
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 1275641..a438dbb 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -44,6 +44,8 @@
 #include "sysdeps/errno.h"
 #include "sysdeps/stat.h"
 
+#include "client/commandline.h"
+
 #include <android-base/file.h>
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
@@ -202,12 +204,11 @@
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
         std::string error;
-        FeatureSet features;
-        if (!adb_get_feature_set(&features, &error)) {
+        if (!adb_get_feature_set(&features_, &error)) {
             fd = -1;
             Error("failed to get feature set: %s", error.c_str());
         } else {
-            have_stat_v2_ = CanUseFeature(features, kFeatureStat2);
+            have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
             fd = adb_connect("sync:", &error);
             if (fd < 0) {
                 Error("connect failed: %s", error.c_str());
@@ -232,6 +233,8 @@
         line_printer_.KeepInfoLine();
     }
 
+    const FeatureSet& Features() const { return features_; }
+
     bool IsValid() { return fd >= 0; }
 
     bool ReceivedError(const char* from, const char* to) {
@@ -576,6 +579,7 @@
 
   private:
     bool expect_done_;
+    FeatureSet features_;
     bool have_stat_v2_;
 
     TransferLedger global_ledger_;
@@ -805,7 +809,7 @@
 }
 
 static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
-                             const std::string& lpath,
+                             std::vector<std::string>* directory_list, const std::string& lpath,
                              const std::string& rpath) {
     std::vector<copyinfo> dirlist;
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
@@ -848,21 +852,9 @@
     // Close this directory and recurse.
     dir.reset();
 
-    // Add the current directory to the list if it was empty, to ensure that
-    // it gets created.
-    if (empty_dir) {
-        // TODO(b/25566053): Make pushing empty directories work.
-        // TODO(b/25457350): We don't preserve permissions on directories.
-        sc.Warning("skipping empty directory '%s'", lpath.c_str());
-        copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
-                    android::base::Basename(lpath), S_IFDIR);
-        ci.skip = true;
-        file_list->push_back(ci);
-        return true;
-    }
-
     for (const copyinfo& ci : dirlist) {
-        local_build_list(sc, file_list, ci.lpath, ci.rpath);
+        directory_list->push_back(ci.rpath);
+        local_build_list(sc, file_list, directory_list, ci.lpath, ci.rpath);
     }
 
     return true;
@@ -879,11 +871,54 @@
 
     // Recursively build the list of files to copy.
     std::vector<copyinfo> file_list;
+    std::vector<std::string> directory_list;
+
+    for (std::string dirpath = rpath; dirpath != "/"; dirpath = android::base::Dirname(dirpath)) {
+        directory_list.push_back(dirpath);
+    }
+    std::reverse(directory_list.begin(), directory_list.end());
+
     int skipped = 0;
-    if (!local_build_list(sc, &file_list, lpath, rpath)) {
+    if (!local_build_list(sc, &file_list, &directory_list, lpath, rpath)) {
         return false;
     }
 
+    // b/110953234:
+    // P shipped with a bug that causes directory creation as a side-effect of a push to fail.
+    // Work around this by explicitly doing a mkdir via shell.
+    //
+    // Devices that don't support shell_v2 are unhappy if we try to send a too-long packet to them,
+    // but they're not affected by this bug, so only apply the workaround if we have shell_v2.
+    //
+    // TODO(b/25457350): We don't preserve permissions on directories.
+    // TODO: Find all of the leaves and `mkdir -p` them instead?
+    if (CanUseFeature(sc.Features(), kFeatureShell2)) {
+        SilentStandardStreamsCallbackInterface cb;
+        std::string cmd = "mkdir";
+        for (const auto& dir : directory_list) {
+            std::string escaped_path = escape_arg(dir);
+            if (escaped_path.size() > 16384) {
+                // Somewhat arbitrarily limit that probably won't ever happen.
+                sc.Error("path too long: %s", escaped_path.c_str());
+                return false;
+            }
+
+            // The maximum should be 64kiB, but that's not including other stuff that gets tacked
+            // onto the command line, so let's be a bit conservative.
+            if (cmd.size() + escaped_path.size() > 32768) {
+                // Dispatch the command, ignoring failure (since the directory might already exist).
+                send_shell_command(cmd, false, &cb);
+                cmd = "mkdir";
+            }
+            cmd += " ";
+            cmd += escaped_path;
+        }
+
+        if (cmd != "mkdir") {
+            send_shell_command(cmd, false, &cb);
+        }
+    }
+
     if (check_timestamps) {
         for (const copyinfo& ci : file_list) {
             if (!sc.SendLstat(ci.rpath.c_str())) {
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f1197d7..f2911e0 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -314,7 +314,6 @@
 #else /* !_WIN32 a.k.a. Unix */
 
 #include <cutils/sockets.h>
-#include <cutils/threads.h>
 #include <fcntl.h>
 #include <poll.h>
 #include <signal.h>
diff --git a/adb/test_device.py b/adb/test_device.py
index 5aa2684..4abe7a7 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -750,7 +750,6 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
-    @unittest.expectedFailure # b/25566053
     def test_push_empty(self):
         """Push a directory containing an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
diff --git a/base/file.cpp b/base/file.cpp
index 2f697a1..d6fe753 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -199,17 +199,23 @@
 bool RemoveFileIfExists(const std::string& path, std::string* err) {
   struct stat st;
 #if defined(_WIN32)
-  //TODO: Windows version can't handle symbol link correctly.
+  // TODO: Windows version can't handle symbolic links correctly.
   int result = stat(path.c_str(), &st);
   bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
 #else
   int result = lstat(path.c_str(), &st);
   bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
 #endif
+  if (result == -1) {
+    if (errno == ENOENT || errno == ENOTDIR) return true;
+    if (err != nullptr) *err = strerror(errno);
+    return false;
+  }
+
   if (result == 0) {
     if (!file_type_removable) {
       if (err != nullptr) {
-        *err = "is not a regular or symbol link file";
+        *err = "is not a regular file or symbolic link";
       }
       return false;
     }
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 02b431d..6794652 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -26,6 +26,10 @@
 
 #include "android-base/test_utils.h"
 
+#if !defined(_WIN32)
+#include <pwd.h>
+#endif
+
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
   errno = 0;
@@ -115,7 +119,7 @@
   ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
 }
 
-TEST(file, RemoveFileIfExist) {
+TEST(file, RemoveFileIfExists) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   close(tf.fd);
@@ -126,9 +130,43 @@
   TemporaryDir td;
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
-  ASSERT_EQ("is not a regular or symbol link file", err);
+  ASSERT_EQ("is not a regular file or symbolic link", err);
 }
 
+TEST(file, RemoveFileIfExists_ENOTDIR) {
+  TemporaryFile tf;
+  close(tf.fd);
+  tf.fd = -1;
+  std::string err{"xxx"};
+  ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
+  ASSERT_EQ("xxx", err);
+}
+
+#if !defined(_WIN32)
+TEST(file, RemoveFileIfExists_EACCES) {
+  // EACCES -- one of the directories in the path has no search permission
+  // root can bypass permission restrictions, so drop root.
+  if (getuid() == 0) {
+    passwd* shell = getpwnam("shell");
+    setgid(shell->pw_gid);
+    setuid(shell->pw_uid);
+  }
+
+  TemporaryDir td;
+  TemporaryFile tf(td.path);
+  close(tf.fd);
+  tf.fd = -1;
+  std::string err{"xxx"};
+  // Remove dir's search permission.
+  ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
+  ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
+  ASSERT_EQ("Permission denied", err);
+  // Set dir's search permission again.
+  ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
+  ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
+}
+#endif
+
 TEST(file, Readlink) {
 #if !defined(_WIN32)
   // Linux doesn't allow empty symbolic links.
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
index f4ba809..dba1fc6 100644
--- a/base/include/android-base/threads.h
+++ b/base/include/android-base/threads.h
@@ -23,3 +23,8 @@
 uint64_t GetThreadId();
 }
 }  // namespace android
+
+#if defined(__GLIBC__)
+// bionic has this Linux-specifix call, but glibc doesn't.
+extern "C" int tgkill(int tgid, int tid, int sig);
+#endif
diff --git a/base/threads.cpp b/base/threads.cpp
index a71382b..48f6197 100644
--- a/base/threads.cpp
+++ b/base/threads.cpp
@@ -46,3 +46,9 @@
 
 }  // namespace base
 }  // namespace android
+
+#if defined(__GLIBC__)
+int tgkill(int tgid, int tid, int sig) {
+  return syscall(__NR_tgkill, tgid, tid, sig);
+}
+#endif
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index f59fa84..1050cf5 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -46,6 +46,9 @@
 cc_test {
     name: "liblp_test",
     defaults: ["fs_mgr_defaults"],
+    cppflags: [
+        "-Wno-unused-parameter",
+    ],
     static_libs: [
         "libbase",
         "liblog",
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 720590d..d15fa8c 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -27,8 +27,8 @@
 #include <android-base/unique_fd.h>
 #include <uuid/uuid.h>
 
-#include "liblp/metadata_format.h"
-#include "liblp/reader.h"
+#include "liblp/liblp.h"
+#include "reader.h"
 #include "utility.h"
 
 namespace android {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 3cd95ae..8bde313 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -23,7 +23,7 @@
 #include <map>
 #include <memory>
 
-#include "metadata_format.h"
+#include "liblp.h"
 
 namespace android {
 namespace fs_mgr {
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
new file mode 100644
index 0000000..469ef9e
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -0,0 +1,75 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef LIBLP_LIBLP_H
+#define LIBLP_LIBLP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+    LpMetadataGeometry geometry;
+    LpMetadataHeader header;
+    std::vector<LpMetadataPartition> partitions;
+    std::vector<LpMetadataExtent> extents;
+};
+
+// Place an initial partition table on the device. This will overwrite the
+// existing geometry, and should not be used for normal partition table
+// updates. False can be returned if the geometry is incompatible with the
+// block device or an I/O error occurs.
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                         uint32_t slot_number);
+
+// Update the partition table for a given metadata slot number. False is
+// returned if an error occurs, which can include:
+//  - Invalid slot number.
+//  - I/O error.
+//  - Corrupt or missing metadata geometry on disk.
+//  - Incompatible geometry.
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number);
+
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+
+// Read/Write logical partition metadata to an image file, for diagnostics or
+// flashing.
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGuid(const LpMetadataPartition& partition);
+
+// Helper to return a slot number for a slot suffix.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_LIBLP_H
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 27602ac..b5202f0 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -67,7 +67,7 @@
  *     | Geometry Backup    |
  *     +--------------------+
  */
-#define LP_METADATA_PARTITION_NAME "android"
+#define LP_METADATA_PARTITION_NAME "super"
 
 /* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
 #define LP_SECTOR_SIZE 512
@@ -262,28 +262,4 @@
 } /* extern "C" */
 #endif
 
-#ifdef __cplusplus
-namespace android {
-namespace fs_mgr {
-
-// Helper structure for easily interpreting deserialized metadata, or
-// re-serializing metadata.
-struct LpMetadata {
-    LpMetadataGeometry geometry;
-    LpMetadataHeader header;
-    std::vector<LpMetadataPartition> partitions;
-    std::vector<LpMetadataExtent> extents;
-};
-
-// Helper to extract safe C++ strings from partition info.
-std::string GetPartitionName(const LpMetadataPartition& partition);
-std::string GetPartitionGuid(const LpMetadataPartition& partition);
-
-// Helper to return a slot number for a slot suffix.
-uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
-
-}  // namespace fs_mgr
-}  // namespace android
-#endif
-
 #endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/include/liblp/writer.h b/fs_mgr/liblp/include/liblp/writer.h
deleted file mode 100644
index efa409d..0000000
--- a/fs_mgr/liblp/include/liblp/writer.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIBLP_WRITER_H
-#define LIBLP_WRITER_H
-
-#include "metadata_format.h"
-
-namespace android {
-namespace fs_mgr {
-
-// When flashing the initial logical partition layout, we also write geometry
-// information at the start and end of the big physical partition. This helps
-// locate metadata and backup metadata in the case of corruption or a failed
-// update. For normal changes to the metadata, we never modify the geometry.
-enum class SyncMode {
-    // Write geometry information.
-    Flash,
-    // Normal update of a single slot.
-    Update
-};
-
-// Write the given partition table to the given block device, writing only
-// copies according to the given sync mode.
-//
-// This will perform some verification, such that the device has enough space
-// to store the metadata as well as all of its extents.
-//
-// The slot number indicates which metadata slot to use.
-bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number);
-bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number);
-
-// Helper function to serialize geometry and metadata to a normal file, for
-// flashing or debugging.
-bool WriteToImageFile(const char* file, const LpMetadata& metadata);
-bool WriteToImageFile(int fd, const LpMetadata& metadata);
-
-}  // namespace fs_mgr
-}  // namespace android
-
-#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 2595654..e91cc3e 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -23,10 +23,10 @@
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
-#include <liblp/reader.h>
-#include <liblp/writer.h>
 
+#include "reader.h"
 #include "utility.h"
+#include "writer.h"
 
 using namespace std;
 using namespace android::fs_mgr;
@@ -102,7 +102,7 @@
     if (!exported) {
         return {};
     }
-    if (!WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0)) {
+    if (!FlashPartitionTable(fd, *exported.get(), 0)) {
         return {};
     }
     return fd;
@@ -131,7 +131,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    EXPECT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get(), 0));
 }
 
 // Test the basics of flashing a partition and reading it back.
@@ -146,7 +146,7 @@
     // Export and flash.
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
 
     // Read back. Note that some fields are only filled in during
     // serialization, so exported and imported will not be identical. For
@@ -195,7 +195,7 @@
 
     // Change the name before writing to the next slot.
     strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
-    ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     // Read back the original slot, make sure it hasn't changed.
     imported = ReadMetadata(fd, 0);
@@ -231,7 +231,7 @@
     unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
     ASSERT_NE(metadata, nullptr);
     for (uint32_t i = 1; i < kMetadataSlots; i++) {
-        ASSERT_TRUE(WritePartitionTable(fd, *metadata.get(), SyncMode::Update, i));
+        ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
     }
 
     // Verify that we can't read unavailable slots.
@@ -246,25 +246,25 @@
 
     unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.metadata_slot_count++;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.first_logical_sector++;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.last_logical_sector--;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 }
 
 // Test that changing one bit of metadata is enough to break the checksum.
@@ -353,8 +353,8 @@
     ASSERT_GE(fd, 0);
 
     // Check that we are able to write our table.
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *exported.get(), 1));
 
     // Check that adding one more partition overflows the metadata allotment.
     partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
@@ -364,7 +364,7 @@
     ASSERT_NE(exported, nullptr);
 
     // The new table should be too large to be written.
-    ASSERT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
 
     // Check that the first and last logical sectors weren't touched when we
     // wrote this almost-full metadata.
@@ -393,3 +393,130 @@
     unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);
     ASSERT_NE(imported, nullptr);
 }
+
+class BadWriter {
+  public:
+    // When requested, write garbage instead of the requested bytes, then
+    // return false.
+    bool operator()(int fd, const std::string& blob) {
+        write_count_++;
+        if (write_count_ == fail_on_write_) {
+            std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size());
+            memset(new_data.get(), 0xe5, blob.size());
+            EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size()));
+            return false;
+        } else {
+            if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+                return false;
+            }
+            return fail_after_write_ != write_count_;
+        }
+    }
+    void Reset() {
+        fail_on_write_ = 0;
+        fail_after_write_ = 0;
+        write_count_ = 0;
+    }
+    void FailOnWrite(int number) {
+        Reset();
+        fail_on_write_ = number;
+    }
+    void FailAfterWrite(int number) {
+        Reset();
+        fail_after_write_ = number;
+    }
+
+  private:
+    int fail_on_write_ = 0;
+    int fail_after_write_ = 0;
+    int write_count_ = 0;
+};
+
+// Test that an interrupted flash operation on the "primary" copy of metadata
+// is not fatal.
+TEST(liblp, UpdatePrimaryMetadataFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(1);
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+
+    // We should still be able to read the backup copy.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Flash again, this time fail the backup copy. We should still be able
+    // to read the primary.
+    writer.FailOnWrite(3);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that an interrupted flash operation on the "backup" copy of metadata
+// is not fatal.
+TEST(liblp, UpdateBackupMetadataFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(2);
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+
+    // We should still be able to read the primary copy.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Flash again, this time fail the primary copy. We should still be able
+    // to read the primary.
+    writer.FailOnWrite(2);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that an interrupted write *in between* writing metadata will read
+// the correct metadata copy. The primary is always considered newer than
+// the backup.
+TEST(liblp, UpdateMetadataCleanFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Change the name of the existing partition.
+    unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+    ASSERT_NE(new_table, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    new_table->partitions[0].name[0]++;
+
+    // Flash it, but fail to write the backup copy.
+    writer.FailAfterWrite(2);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+
+    // When we read back, we should get the updated primary copy.
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+
+    // Flash again. After, the backup and primary copy should be coherent.
+    // Note that the sync step should have used the primary to sync, not
+    // the backup.
+    writer.Reset();
+    ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index a0eeec9..985cf09 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "liblp/reader.h"
+#include "reader.h"
 
 #include <stddef.h>
 #include <stdlib.h>
@@ -111,16 +111,6 @@
     return ParseGeometry(buffer.get(), geometry);
 }
 
-// Helper function to read geometry from a device without an open descriptor.
-bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) {
-    android::base::unique_fd fd(open(block_device, O_RDONLY));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
-        return false;
-    }
-    return ReadLogicalPartitionGeometry(fd, geometry);
-}
-
 static bool ValidateTableBounds(const LpMetadataHeader& header,
                                 const LpMetadataTableDescriptor& table) {
     if (table.offset > header.tables_size) {
@@ -175,8 +165,6 @@
     return true;
 }
 
-using ReadMetadataFn = std::function<bool(void* buffer, size_t num_bytes)>;
-
 // Parse and validate all metadata at the current position in the given file
 // descriptor.
 static std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
@@ -243,6 +231,26 @@
     return metadata;
 }
 
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number) {
+    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number) {
+    int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
 std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
     LpMetadataGeometry geometry;
     if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
@@ -254,24 +262,11 @@
         return nullptr;
     }
 
-    // First try the primary copy.
-    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
-    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
-        return nullptr;
-    }
-    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
-
-    // If the primary copy failed, try the backup copy.
+    // Read the priamry copy, and if that fails, try the backup.
+    std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
     if (!metadata) {
-        offset = GetBackupMetadataOffset(geometry, slot_number);
-        if (SeekFile64(fd, offset, SEEK_END) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
-            return nullptr;
-        }
-        metadata = ParseMetadata(fd);
+        metadata = ReadBackupMetadata(fd, geometry, slot_number);
     }
-
     if (metadata) {
         metadata->geometry = geometry;
     }
diff --git a/fs_mgr/liblp/include/liblp/reader.h b/fs_mgr/liblp/reader.h
similarity index 63%
rename from fs_mgr/liblp/include/liblp/reader.h
rename to fs_mgr/liblp/reader.h
index 982fe65..c4cac8fc 100644
--- a/fs_mgr/liblp/include/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -21,23 +21,22 @@
 
 #include <memory>
 
-#include "metadata_format.h"
+#include <liblp/liblp.h>
 
 namespace android {
 namespace fs_mgr {
 
-// Read logical partition metadata from its predetermined location on a block
-// device. If readback fails, we also attempt to load from a backup copy.
-std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
 std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
 
-// Read and validate the logical partition geometry from a block device.
-bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry);
+// Helper functions for manually reading geometry and metadata.
 bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
 
-// Read logical partition metadata from an image file that was created with
-// WriteToImageFile().
-std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+// These functions assume a valid geometry and slot number.
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number);
+
 std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index dcc569e..2143e13 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-#include "utility.h"
 #include <gtest/gtest.h>
+#include <liblp/liblp.h>
+
+#include "utility.h"
 
 using namespace android;
 using namespace android::fs_mgr;
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 89cbabd..b85e4ad 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "writer.h"
+
 #include <inttypes.h>
 #include <unistd.h>
 
@@ -22,8 +24,7 @@
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 
-#include "liblp/reader.h"
-#include "liblp/writer.h"
+#include "reader.h"
 #include "utility.h"
 
 namespace android {
@@ -73,8 +74,14 @@
 
 // Perform sanity checks so we don't accidentally overwrite valid metadata
 // with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blockdevice_size,
-                                        uint64_t metadata_size) {
+static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
+    uint64_t blockdevice_size;
+    if (!GetDescriptorSize(fd, &blockdevice_size)) {
+        return false;
+    }
+
+    *blob = SerializeMetadata(metadata);
+
     const LpMetadataHeader& header = metadata.header;
     const LpMetadataGeometry& geometry = metadata.geometry;
     // Validate the usable sector range.
@@ -83,7 +90,7 @@
         return false;
     }
     // Make sure we're writing within the space reserved.
-    if (metadata_size > geometry.metadata_max_size) {
+    if (blob->size() > geometry.metadata_max_size) {
         LERROR << "Logical partition metadata is too large.";
         return false;
     }
@@ -124,75 +131,24 @@
     return true;
 }
 
-bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number) {
-    uint64_t size;
-    if (!GetDescriptorSize(fd, &size)) {
-        return false;
-    }
-
-    const LpMetadataGeometry& geometry = metadata.geometry;
-    if (sync_mode != SyncMode::Flash) {
-        // Verify that the old geometry is identical. If it's not, then we've
-        // based this new metadata on invalid assumptions.
-        LpMetadataGeometry old_geometry;
-        if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
-            return false;
-        }
-        if (!CompareGeometry(geometry, old_geometry)) {
-            LERROR << "Incompatible geometry in new logical partition metadata";
-            return false;
-        }
-    }
-
-    // Make sure we're writing to a valid metadata slot.
-    if (slot_number >= geometry.metadata_slot_count) {
-        LERROR << "Invalid logical partition metadata slot number.";
-        return false;
-    }
-
-    // Before writing geometry and/or logical partition tables, perform some
-    // basic checks that the geometry and tables are coherent, and will fit
-    // on the given block device.
-    std::string blob = SerializeMetadata(metadata);
-    if (!ValidateGeometryAndMetadata(metadata, size, blob.size())) {
-        return false;
-    }
-
-    // First write geometry if this is a flash operation. It gets written to
-    // the first and last 4096-byte regions of the device.
-    if (sync_mode == SyncMode::Flash) {
-        std::string blob = SerializeGeometry(metadata.geometry);
-        if (SeekFile64(fd, 0, SEEK_SET) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
-            return false;
-        }
-        if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-            PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
-            return false;
-        }
-        if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
-            return false;
-        }
-        if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-            PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
-            return false;
-        }
-    }
-
-    // Write the primary copy of the metadata.
+static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                                 const std::string& blob,
+                                 const std::function<bool(int, const std::string&)>& writer) {
     int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
     if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
         return false;
     }
-    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+    if (!writer(fd, blob)) {
         PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
         return false;
     }
+    return true;
+}
 
-    // Write the backup copy of the metadata.
+static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                                const std::string& blob,
+                                const std::function<bool(int, const std::string&)>& writer) {
     int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
     int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
     if (abs_offset == (int64_t)-1) {
@@ -204,21 +160,157 @@
                << " is within logical partition bounds, sector " << geometry.last_logical_sector;
         return false;
     }
-    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+    if (!writer(fd, blob)) {
         PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
         return false;
     }
     return true;
 }
 
-bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                          const std::string& blob,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    // Make sure we're writing to a valid metadata slot.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+    if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
+        return false;
+    }
+    if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
+        return false;
+    }
+    return true;
+}
+
+static bool DefaultWriter(int fd, const std::string& blob) {
+    return android::base::WriteFully(fd, blob.data(), blob.size());
+}
+
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string metadata_blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
+        return false;
+    }
+
+    // Write geometry to the first and last 4096 bytes of the device.
+    std::string blob = SerializeGeometry(metadata.geometry);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        return false;
+    }
+    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+
+    // Write metadata to the correct slot, now that geometry is in place.
+    return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter);
+}
+
+static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
+    return !memcmp(a.header.header_checksum, b.header.header_checksum,
+                   sizeof(a.header.header_checksum));
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
+        return false;
+    }
+
+    // Verify that the old geometry is identical. If it's not, then we might be
+    // writing a table that was built for a different device, so we must reject
+    // it.
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    LpMetadataGeometry old_geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+        return false;
+    }
+    if (!CompareGeometry(geometry, old_geometry)) {
+        LERROR << "Incompatible geometry in new logical partition metadata";
+        return false;
+    }
+
+    // Validate the slot number now, before we call Read*Metadata.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+
+    // Try to read both existing copies of the metadata, if any.
+    std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number);
+    std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number);
+
+    if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) {
+        // If the backup copy does not match the primary copy, we first
+        // synchronize the backup copy. This guarantees that a partial write
+        // still leaves one copy intact.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            return false;
+        }
+    } else if (backup && !primary) {
+        // The backup copy is coherent, and the primary is not. Sync it for
+        // safety.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            return false;
+        }
+    }
+
+    // Both copies should now be in sync, so we can continue the update.
+    return WriteMetadata(fd, geometry, slot_number, blob, writer);
+}
+
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
                          uint32_t slot_number) {
-    android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
         return false;
     }
-    return WritePartitionTable(fd, metadata, sync_mode, slot_number);
+    return FlashPartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return UpdatePartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+    return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter);
 }
 
 bool WriteToImageFile(int fd, const LpMetadata& input) {
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
new file mode 100644
index 0000000..94c1d31
--- /dev/null
+++ b/fs_mgr/liblp/writer.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include <functional>
+#include <string>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+// These variants are for testing only. The path-based functions should be used
+// for actual operation, so that open() is called with the correct flags.
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer);
+
+// Helper function to serialize geometry and metadata to a normal file, for
+// flashing or debugging.
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+bool WriteToImageFile(int fd, const LpMetadata& metadata);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index b4bf35f..a10e636 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -42,7 +42,6 @@
     "Backtrace.cpp",
     "BacktraceCurrent.cpp",
     "BacktracePtrace.cpp",
-    "thread_utils.c",
     "ThreadEntry.cpp",
     "UnwindStack.cpp",
     "UnwindStackMap.cpp",
@@ -94,7 +93,6 @@
             ],
 
             static_libs: [
-                "libcutils",
                 "libprocinfo",
             ],
 
@@ -145,7 +143,6 @@
         "backtrace_offline_test.cpp",
         "backtrace_test.cpp",
         "GetPss.cpp",
-        "thread_utils.c",
     ],
 
     cflags: [
@@ -159,7 +156,6 @@
         "libbacktrace",
         "libdexfile",
         "libbase",
-        "libcutils",
         "liblog",
         "libunwindstack",
     ],
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 6445a7c..6bec63c 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -23,6 +23,7 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
+#include <android-base/threads.h>
 
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
@@ -31,7 +32,6 @@
 
 #include "BacktraceLog.h"
 #include "UnwindStack.h"
-#include "thread_utils.h"
 
 using android::base::StringPrintf;
 
@@ -124,7 +124,7 @@
   if (pid == BACKTRACE_CURRENT_PROCESS) {
     pid = getpid();
     if (tid == BACKTRACE_CURRENT_THREAD) {
-      tid = gettid();
+      tid = android::base::GetThreadId();
     }
   } else if (tid == BACKTRACE_CURRENT_THREAD) {
     tid = pid;
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index f6f4423..39cb995 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -28,13 +28,13 @@
 
 #include <string>
 
+#include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
 #include "BacktraceAsyncSafeLog.h"
 #include "BacktraceCurrent.h"
 #include "ThreadEntry.h"
-#include "thread_utils.h"
 
 bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
   if (!VerifyReadWordArgs(ptr, out_value)) {
@@ -76,7 +76,7 @@
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
 
-  if (Tid() != gettid()) {
+  if (Tid() != android::base::GetThreadId()) {
     return UnwindThread(num_ignore_frames);
   }
 
@@ -114,16 +114,17 @@
 static void SignalLogOnly(int, siginfo_t*, void*) {
   ErrnoRestorer restore;
 
-  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(),
-                       THREAD_SIGNAL);
+  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(),
+                       static_cast<int>(android::base::GetThreadId()), THREAD_SIGNAL);
 }
 
 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
   ErrnoRestorer restore;
 
-  ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
+  ThreadEntry* entry = ThreadEntry::Get(getpid(), android::base::GetThreadId(), false);
   if (!entry) {
-    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(),
+                         static_cast<int>(android::base::GetThreadId()));
     return;
   }
 
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 399721d..6a967f7 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -32,8 +32,6 @@
 #include <procinfo/process_map.h>
 #endif
 
-#include "thread_utils.h"
-
 using android::base::StringPrintf;
 
 std::string backtrace_map_t::Name() const {
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
index bf6b16f..9da457d 100644
--- a/libbacktrace/BacktracePtrace.cpp
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -28,7 +28,6 @@
 
 #include "BacktraceLog.h"
 #include "BacktracePtrace.h"
-#include "thread_utils.h"
 
 #if !defined(__APPLE__)
 static bool PtraceRead(pid_t tid, uint64_t addr, word_t* out_value) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index e087b2e..4e7f761 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -23,10 +23,6 @@
 #include <set>
 #include <string>
 
-#if !defined(__ANDROID__)
-#include <cutils/threads.h>
-#endif
-
 #include <backtrace/Backtrace.h>
 #include <demangle.h>
 #include <unwindstack/Elf.h>
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
index a23e3b4..099ac60 100644
--- a/libbacktrace/backtrace_benchmarks.cpp
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -27,6 +27,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/threads.h>
 
 #include <benchmark/benchmark.h>
 
@@ -154,7 +155,7 @@
 
 static void CreateBacktrace(benchmark::State& state, BacktraceMap* map, BacktraceCreateFn fn) {
   while (state.KeepRunning()) {
-    std::unique_ptr<Backtrace> backtrace(fn(getpid(), gettid(), map));
+    std::unique_ptr<Backtrace> backtrace(fn(getpid(), android::base::GetThreadId(), map));
     backtrace->Unwind(0);
   }
 }
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 9877f29..7d1027e 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -31,9 +31,9 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
 
 #include <gtest/gtest.h>
 
@@ -99,7 +99,7 @@
 
 static void* OfflineThreadFunc(void* arg) {
   OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
-  fn_arg->tid = gettid();
+  fn_arg->tid = android::base::GetThreadId();
   test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
   return nullptr;
 }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index f78a31f..06a32c7 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -47,16 +47,15 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/test_utils.h>
+#include <android-base/threads.h>
 #include <android-base/unique_fd.h>
 #include <cutils/atomic.h>
-#include <cutils/threads.h>
 
 #include <gtest/gtest.h>
 
 // For the THREAD_SIGNAL definition.
 #include "BacktraceCurrent.h"
 #include "backtrace_testlib.h"
-#include "thread_utils.h"
 
 // Number of microseconds per milliseconds.
 #define US_PER_MSEC             1000
@@ -525,7 +524,7 @@
 }
 
 void VerifyLevelThread(void*) {
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
   VERIFY_NO_ERROR(backtrace->GetError().error_code);
@@ -538,7 +537,7 @@
 }
 
 static void VerifyMaxThread(void*) {
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
   ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
@@ -553,7 +552,7 @@
 static void* ThreadLevelRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
+  thread->tid = android::base::GetThreadId();
   EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
   return nullptr;
 }
@@ -644,7 +643,7 @@
 static void* ThreadMaxRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
+  thread->tid = android::base::GetThreadId();
   EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
   return nullptr;
 }
@@ -994,7 +993,7 @@
 static void* ThreadReadTest(void* data) {
   thread_t* thread_data = reinterpret_cast<thread_t*>(data);
 
-  thread_data->tid = gettid();
+  thread_data->tid = android::base::GetThreadId();
 
   // Create two map pages.
   // Mark the second page as not-readable.
@@ -1816,7 +1815,8 @@
 
 static void TestFrameSkipNumbering(create_func_t create_func, map_create_func_t map_create_func) {
   std::unique_ptr<BacktraceMap> map(map_create_func(getpid(), false));
-  std::unique_ptr<Backtrace> backtrace(create_func(getpid(), gettid(), map.get()));
+  std::unique_ptr<Backtrace> backtrace(
+      create_func(getpid(), android::base::GetThreadId(), map.get()));
   backtrace->Unwind(1);
   ASSERT_NE(0U, backtrace->NumFrames());
   ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c
deleted file mode 100644
index e75f56e..0000000
--- a/libbacktrace/thread_utils.c
+++ /dev/null
@@ -1,29 +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 "thread_utils.h"
-
-#if !defined(__BIONIC__)
-
-// glibc doesn't implement or export tgkill.
-#include <unistd.h>
-#include <sys/syscall.h>
-
-int tgkill(int tgid, int tid, int sig) {
-  return syscall(__NR_tgkill, tgid, tid, sig);
-}
-
-#endif
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h
deleted file mode 100644
index 9590657..0000000
--- a/libbacktrace/thread_utils.h
+++ /dev/null
@@ -1,32 +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.
- */
-
-#ifndef _LIBBACKTRACE_THREAD_UTILS_H
-#define _LIBBACKTRACE_THREAD_UTILS_H
-
-#include <unistd.h>
-
-#if !defined(__ANDROID__)
-#include <cutils/threads.h>
-#endif
-
-__BEGIN_DECLS
-
-int tgkill(int tgid, int tid, int sig);
-
-__END_DECLS
-
-#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index cdbb65f..58e59d6 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -65,7 +65,6 @@
         "iosched_policy.cpp",
         "load_file.cpp",
         "native_handle.cpp",
-        "open_memstream.c",
         "record_stream.cpp",
         "sched_policy.cpp",
         "sockets.cpp",
diff --git a/libcutils/hashmap.cpp b/libcutils/hashmap.cpp
index 10e3b25..2a4a52e 100644
--- a/libcutils/hashmap.cpp
+++ b/libcutils/hashmap.cpp
@@ -36,7 +36,7 @@
     size_t bucketCount;
     int (*hash)(void* key);
     bool (*equals)(void* keyA, void* keyB);
-    mutex_t lock; 
+    mutex_t lock;
     size_t size;
 };
 
@@ -44,18 +44,18 @@
         int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
     assert(hash != NULL);
     assert(equals != NULL);
-    
+
     Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
     if (map == NULL) {
         return NULL;
     }
-    
+
     // 0.75 load factor.
     size_t minimumBucketCount = initialCapacity * 4 / 3;
     map->bucketCount = 1;
     while (map->bucketCount <= minimumBucketCount) {
         // Bucket count must be power of 2.
-        map->bucketCount <<= 1; 
+        map->bucketCount <<= 1;
     }
 
     map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
@@ -63,14 +63,14 @@
         free(map);
         return NULL;
     }
-    
+
     map->size = 0;
 
     map->hash = hash;
     map->equals = equals;
-    
+
     mutex_init(&map->lock);
-    
+
     return map;
 }
 
@@ -89,12 +89,8 @@
     h ^= (((unsigned int) h) >> 14);
     h += (h << 4);
     h ^= (((unsigned int) h) >> 10);
-       
-    return h;
-}
 
-size_t hashmapSize(Hashmap* map) {
-    return map->size;
+    return h;
 }
 
 static inline size_t calculateIndex(size_t bucketCount, int hash) {
@@ -111,7 +107,7 @@
             // Abort expansion.
             return;
         }
-        
+
         // Move over existing entries.
         size_t i;
         for (i = 0; i < map->bucketCount; i++) {
@@ -240,54 +236,6 @@
     return NULL;
 }
 
-bool hashmapContainsKey(Hashmap* map, void* key) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry* entry = map->buckets[index];
-    while (entry != NULL) {
-        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
-            return true;
-        }
-        entry = entry->next;
-    }
-
-    return false;
-}
-
-void* hashmapMemoize(Hashmap* map, void* key, 
-        void* (*initialValue)(void* key, void* context), void* context) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry** p = &(map->buckets[index]);
-    while (true) {
-        Entry* current = *p;
-
-        // Add a new entry.
-        if (current == NULL) {
-            *p = createEntry(key, hash, NULL);
-            if (*p == NULL) {
-                errno = ENOMEM;
-                return NULL;
-            }
-            void* value = initialValue(key, context);
-            (*p)->value = value;
-            map->size++;
-            expandIfNecessary(map);
-            return value;
-        }
-
-        // Return existing value.
-        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
-            return current->value;
-        }
-
-        // Move to next entry.
-        p = &current->next;
-    }
-}
-
 void* hashmapRemove(Hashmap* map, void* key) {
     int hash = hashKey(map, key);
     size_t index = calculateIndex(map->bucketCount, hash);
@@ -310,9 +258,8 @@
     return NULL;
 }
 
-void hashmapForEach(Hashmap* map, 
-        bool (*callback)(void* key, void* value, void* context),
-        void* context) {
+void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
+                    void* context) {
     size_t i;
     for (i = 0; i < map->bucketCount; i++) {
         Entry* entry = map->buckets[i];
@@ -325,34 +272,3 @@
         }
     }
 }
-
-size_t hashmapCurrentCapacity(Hashmap* map) {
-    size_t bucketCount = map->bucketCount;
-    return bucketCount * 3 / 4;
-}
-
-size_t hashmapCountCollisions(Hashmap* map) {
-    size_t collisions = 0;
-    size_t i;
-    for (i = 0; i < map->bucketCount; i++) {
-        Entry* entry = map->buckets[i];
-        while (entry != NULL) {
-            if (entry->next != NULL) {
-                collisions++;
-            }
-            entry = entry->next;
-        }
-    }
-    return collisions;
-}
-
-int hashmapIntHash(void* key) {
-    // Return the key value itself.
-    return *((int*) key);
-}
-
-bool hashmapIntEquals(void* keyA, void* keyB) {
-    int a = *((int*) keyA);
-    int b = *((int*) keyB);
-    return a == b;
-}
diff --git a/libcutils/include/cutils/hashmap.h b/libcutils/include/cutils/hashmap.h
index 5cb344c..9cfd669 100644
--- a/libcutils/include/cutils/hashmap.h
+++ b/libcutils/include/cutils/hashmap.h
@@ -16,6 +16,9 @@
 
 /**
  * Hash map.
+ *
+ * Use std::map or std::unordered_map instead.
+ * https://en.cppreference.com/w/cpp/container
  */
 
 #ifndef __HASHMAP_H
@@ -68,38 +71,17 @@
 void* hashmapGet(Hashmap* map, void* key);
 
 /**
- * Returns true if the map contains an entry for the given key.
- */
-bool hashmapContainsKey(Hashmap* map, void* key);
-
-/**
- * Gets the value for a key. If a value is not found, this function gets a 
- * value and creates an entry using the given callback.
- *
- * If memory allocation fails, the callback is not called, this function
- * returns NULL, and errno is set to ENOMEM.
- */
-void* hashmapMemoize(Hashmap* map, void* key, 
-        void* (*initialValue)(void* key, void* context), void* context);
-
-/**
  * Removes an entry from the map. Returns the removed value or NULL if no
  * entry was present.
  */
 void* hashmapRemove(Hashmap* map, void* key);
 
 /**
- * Gets the number of entries in this map.
- */
-size_t hashmapSize(Hashmap* map);
-
-/**
  * Invokes the given callback on each entry in the map. Stops iterating if
  * the callback returns false.
  */
-void hashmapForEach(Hashmap* map, 
-        bool (*callback)(void* key, void* value, void* context),
-        void* context);
+void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
+                    void* context);
 
 /**
  * Concurrency support.
@@ -115,36 +97,8 @@
  */
 void hashmapUnlock(Hashmap* map);
 
-/**
- * Key utilities.
- */
-
-/**
- * Hashes int keys. 'key' is a pointer to int.
- */
-int hashmapIntHash(void* key);
-
-/**
- * Compares two int keys for equality.
- */
-bool hashmapIntEquals(void* keyA, void* keyB);
-
-/**
- * For debugging.
- */
-
-/**
- * Gets current capacity.
- */
-size_t hashmapCurrentCapacity(Hashmap* map);
-
-/**
- * Counts the number of entry collisions.
- */
-size_t hashmapCountCollisions(Hashmap* map);
-
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* __HASHMAP_H */ 
+#endif /* __HASHMAP_H */
diff --git a/libcutils/include/cutils/open_memstream.h b/libcutils/include/cutils/open_memstream.h
deleted file mode 100644
index c1a81eb..0000000
--- a/libcutils/include/cutils/open_memstream.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CUTILS_OPEN_MEMSTREAM_H__
-#define __CUTILS_OPEN_MEMSTREAM_H__
-
-#include <stdio.h>
-
-#if defined(__APPLE__)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-FILE* open_memstream(char** bufp, size_t* sizep);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __APPLE__ */
-
-#endif /*__CUTILS_OPEN_MEMSTREAM_H__*/
diff --git a/libcutils/include_vndk/cutils/open_memstream.h b/libcutils/include_vndk/cutils/open_memstream.h
deleted file mode 120000
index c894084..0000000
--- a/libcutils/include_vndk/cutils/open_memstream.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/cutils/open_memstream.h
\ No newline at end of file
diff --git a/libcutils/open_memstream.c b/libcutils/open_memstream.c
deleted file mode 100644
index 9183266..0000000
--- a/libcutils/open_memstream.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#if defined(__APPLE__)
-
-/*
- * Implementation of the POSIX open_memstream() function, which Linux has
- * but BSD lacks.
- *
- * Summary:
- * - Works like a file-backed FILE* opened with fopen(name, "w"), but the
- *   backing is a chunk of memory rather than a file.
- * - The buffer expands as you write more data.  Seeking past the end
- *   of the file and then writing to it zero-fills the gap.
- * - The values at "*bufp" and "*sizep" should be considered read-only,
- *   and are only valid immediately after an fflush() or fclose().
- * - A '\0' is maintained just past the end of the file. This is not included
- *   in "*sizep".  (The behavior w.r.t. fseek() is not clearly defined.
- *   The spec says the null byte is written when a write() advances EOF,
- *   but it looks like glibc ensures the null byte is always found at EOF,
- *   even if you just seeked backwards.  The example on the opengroup.org
- *   page suggests that this is the expected behavior.  The null must be
- *   present after a no-op fflush(), which we can't see, so we have to save
- *   and restore it.  Annoying, but allows file truncation.)
- * - After fclose(), the caller must eventually free(*bufp).
- *
- * This is built out of funopen(), which BSD has but Linux lacks.  There is
- * no flush() operator, so we need to keep the user pointers up to date
- * after each operation.
- *
- * I don't think Windows has any of the above, but we don't need to use
- * them there, so we just supply a stub.
- */
-#include <cutils/open_memstream.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#if 0
-# define DBUG(x) printf x
-#else
-# define DBUG(x) ((void)0)
-#endif
-
-/*
- * Definition of a seekable, write-only memory stream.
- */
-typedef struct {
-    char**      bufp;       /* pointer to buffer pointer */
-    size_t*     sizep;      /* pointer to eof */
-
-    size_t      allocSize;  /* size of buffer */
-    size_t      eof;        /* furthest point we've written to */
-    size_t      offset;     /* current write offset */
-    char        saved;      /* required by NUL handling */
-} MemStream;
-
-#define kInitialSize    1024
-
-/*
- * Ensure that we have enough storage to write "size" bytes at the
- * current offset.  We also have to take into account the extra '\0'
- * that we maintain just past EOF.
- *
- * Returns 0 on success.
- */
-static int ensureCapacity(MemStream* stream, int writeSize)
-{
-    DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize));
-
-    size_t neededSize = stream->offset + writeSize + 1;
-    if (neededSize <= stream->allocSize)
-        return 0;
-
-    size_t newSize;
-
-    if (stream->allocSize == 0) {
-        newSize = kInitialSize;
-    } else {
-        newSize = stream->allocSize;
-        newSize += newSize / 2;             /* expand by 3/2 */
-    }
-
-    if (newSize < neededSize)
-        newSize = neededSize;
-    DBUG(("+++ realloc %p->%p to size=%d\n",
-        stream->bufp, *stream->bufp, newSize));
-    char* newBuf = (char*) realloc(*stream->bufp, newSize);
-    if (newBuf == NULL)
-        return -1;
-
-    *stream->bufp = newBuf;
-    stream->allocSize = newSize;
-    return 0;
-}
-
-/*
- * Write data to a memstream, expanding the buffer if necessary.
- *
- * If we previously seeked beyond EOF, zero-fill the gap.
- *
- * Returns the number of bytes written.
- */
-static int write_memstream(void* cookie, const char* buf, int size)
-{
-    MemStream* stream = (MemStream*) cookie;
-
-    if (ensureCapacity(stream, size) < 0)
-        return -1;
-
-    /* seeked past EOF earlier? */
-    if (stream->eof < stream->offset) {
-        DBUG(("+++ zero-fill gap from %d to %d\n",
-            stream->eof, stream->offset-1));
-        memset(*stream->bufp + stream->eof, '\0',
-            stream->offset - stream->eof);
-    }
-
-    /* copy data, advance write pointer */
-    memcpy(*stream->bufp + stream->offset, buf, size);
-    stream->offset += size;
-
-    if (stream->offset > stream->eof) {
-        /* EOF has advanced, update it and append null byte */
-        DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset));
-        assert(stream->offset < stream->allocSize);
-        stream->eof = stream->offset;
-    } else {
-        /* within previously-written area; save char we're about to stomp */
-        DBUG(("+++ within written area, saving '%c' at %d\n",
-            *(*stream->bufp + stream->offset), stream->offset));
-        stream->saved = *(*stream->bufp + stream->offset);
-    }
-    *(*stream->bufp + stream->offset) = '\0';
-    *stream->sizep = stream->offset;
-
-    return size;
-}
-
-/*
- * Seek within a memstream.
- *
- * Returns the new offset, or -1 on failure.
- */
-static fpos_t seek_memstream(void* cookie, fpos_t offset, int whence)
-{
-    MemStream* stream = (MemStream*) cookie;
-    off_t newPosn = (off_t) offset;
-
-    if (whence == SEEK_CUR) {
-        newPosn += stream->offset;
-    } else if (whence == SEEK_END) {
-        newPosn += stream->eof;
-    }
-
-    if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) {
-        /* bad offset - negative or huge */
-        DBUG(("+++ bogus seek offset %ld\n", (long) newPosn));
-        errno = EINVAL;
-        return (fpos_t) -1;
-    }
-
-    if (stream->offset < stream->eof) {
-        /*
-         * We were pointing to an area we'd already written to, which means
-         * we stomped on a character and must now restore it.
-         */
-        DBUG(("+++ restoring char '%c' at %d\n",
-            stream->saved, stream->offset));
-        *(*stream->bufp + stream->offset) = stream->saved;
-    }
-
-    stream->offset = (size_t) newPosn;
-
-    if (stream->offset < stream->eof) {
-        /*
-         * We're seeked backward into the stream.  Preserve the character
-         * at EOF and stomp it with a NUL.
-         */
-        stream->saved = *(*stream->bufp + stream->offset);
-        *(*stream->bufp + stream->offset) = '\0';
-        *stream->sizep = stream->offset;
-    } else {
-        /*
-         * We're positioned at, or possibly beyond, the EOF.  We want to
-         * publish the current EOF, not the current position.
-         */
-        *stream->sizep = stream->eof;
-    }
-
-    return newPosn;
-}
-
-/*
- * Close the memstream.  We free everything but the data buffer.
- */
-static int close_memstream(void* cookie)
-{
-    free(cookie);
-    return 0;
-}
-
-/*
- * Prepare a memstream.
- */
-FILE* open_memstream(char** bufp, size_t* sizep)
-{
-    FILE* fp;
-    MemStream* stream;
-
-    if (bufp == NULL || sizep == NULL) {
-        errno = EINVAL;
-        return NULL;
-    }
-
-    stream = (MemStream*) calloc(1, sizeof(MemStream));
-    if (stream == NULL)
-        return NULL;
-
-    fp = funopen(stream,
-        NULL, write_memstream, seek_memstream, close_memstream);
-    if (fp == NULL) {
-        free(stream);
-        return NULL;
-    }
-
-    *sizep = 0;
-    *bufp = NULL;
-    stream->bufp = bufp;
-    stream->sizep = sizep;
-
-    return fp;
-}
-
-
-
-
-#if 0
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/*
- * Simple regression test.
- *
- * To test on desktop Linux with valgrind, it's possible to make a simple
- * change to open_memstream() to use fopencookie instead:
- *
- *  cookie_io_functions_t iofuncs =
- *      { NULL, write_memstream, seek_memstream, close_memstream };
- *  fp = fopencookie(stream, "w", iofuncs);
- *
- * (Some tweaks to seek_memstream are also required, as that takes a
- * pointer to an offset rather than an offset, and returns 0 or -1.)
- */
-int testMemStream(void)
-{
-    FILE *stream;
-    char *buf;
-    size_t len;
-    off_t eob;
-
-    printf("Test1\n");
-
-    /* std example */
-    stream = open_memstream(&buf, &len);
-    fprintf(stream, "hello my world");
-    fflush(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    eob = ftello(stream);
-    fseeko(stream, 0, SEEK_SET);
-    fprintf(stream, "good-bye");
-    fseeko(stream, eob, SEEK_SET);
-    fclose(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    free(buf);
-
-    printf("Test2\n");
-
-    /* std example without final seek-to-end */
-    stream = open_memstream(&buf, &len);
-    fprintf(stream, "hello my world");
-    fflush(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    eob = ftello(stream);
-    fseeko(stream, 0, SEEK_SET);
-    fprintf(stream, "good-bye");
-    //fseeko(stream, eob, SEEK_SET);
-    fclose(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    free(buf);
-
-    printf("Test3\n");
-
-    /* fancy example; should expand buffer with writes */
-    static const int kCmpLen = 1024 + 128;
-    char* cmp = malloc(kCmpLen);
-    memset(cmp, 0, 1024);
-    memset(cmp+1024, 0xff, kCmpLen-1024);
-    sprintf(cmp, "This-is-a-tes1234");
-    sprintf(cmp + 1022, "abcdef");
-
-    stream = open_memstream (&buf, &len);
-    setvbuf(stream, NULL, _IONBF, 0);   /* note: crashes in glibc with this */
-    fprintf(stream, "This-is-a-test");
-    fseek(stream, -1, SEEK_CUR);    /* broken in glibc; can use {13,SEEK_SET} */
-    fprintf(stream, "1234");
-    fseek(stream, 1022, SEEK_SET);
-    fputc('a', stream);
-    fputc('b', stream);
-    fputc('c', stream);
-    fputc('d', stream);
-    fputc('e', stream);
-    fputc('f', stream);
-    fflush(stream);
-
-    if (memcmp(buf, cmp, len+1) != 0) {
-        printf("mismatch\n");
-    } else {
-        printf("match\n");
-    }
-
-    printf("Test4\n");
-    stream = open_memstream (&buf, &len);
-    fseek(stream, 5000, SEEK_SET);
-    fseek(stream, 4096, SEEK_SET);
-    fseek(stream, -1, SEEK_SET);        /* should have no effect */
-    fputc('x', stream);
-    if (ftell(stream) == 4097)
-        printf("good\n");
-    else
-        printf("BAD: offset is %ld\n", ftell(stream));
-
-    printf("DONE\n");
-
-    return 0;
-}
-
-/* expected output:
-Test1
-buf=hello my world, len=14
-buf=good-bye world, len=14
-Test2
-buf=hello my world, len=14
-buf=good-bye, len=8
-Test3
-match
-Test4
-good
-DONE
-*/
-
-#endif
-
-#endif /* __APPLE__ */
diff --git a/libcutils/str_parms.cpp b/libcutils/str_parms.cpp
index f5a52a7..d818c51 100644
--- a/libcutils/str_parms.cpp
+++ b/libcutils/str_parms.cpp
@@ -354,12 +354,8 @@
 char *str_parms_to_str(struct str_parms *str_parms)
 {
     char *str = NULL;
-
-    if (hashmapSize(str_parms->map) > 0)
-        hashmapForEach(str_parms->map, combine_strings, &str);
-    else
-        str = strdup("");
-    return str;
+    hashmapForEach(str_parms->map, combine_strings, &str);
+    return (str != NULL) ? str : strdup("");
 }
 
 static bool dump_entry(void* key, void* value, void* /*context*/) {
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 4fbf729..933d65a 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -32,6 +32,8 @@
 #include "ScopedDisableMalloc.h"
 #include "ScopedPipe.h"
 
+#include <android-base/threads.h>
+
 using namespace std::chrono_literals;
 
 namespace android {
@@ -260,7 +262,7 @@
 
       ThreadCapture thread_capture(ret, heap);
       thread_capture.InjectTestFunc([&](pid_t tid) {
-        syscall(SYS_tgkill, ret, tid, SIGKILL);
+        tgkill(ret, tid, SIGKILL);
         usleep(10000);
       });
       auto list_tids = allocator::vector<pid_t>(heap);
@@ -319,7 +321,7 @@
 
           ThreadCapture thread_capture(child, heap);
           thread_capture.InjectTestFunc([&](pid_t tid) {
-            syscall(SYS_tgkill, child, tid, sig);
+            tgkill(child, tid, sig);
             usleep(10000);
           });
           auto list_tids = allocator::vector<pid_t>(heap);
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 3563fc1..19a1783 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -53,8 +53,21 @@
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
 // there is no namespace associated with the class_loader.
+// TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
 __attribute__((visibility("default")))
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
+class NativeLoaderNamespace;
+__attribute__((visibility("default")))
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(
+    JNIEnv* env, jobject class_loader);
+// Load library.  Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does
+// not require access to JNIEnv either.
+__attribute__((visibility("default")))
+void* OpenNativeLibrary(NativeLoaderNamespace* ns,
+                        const char* path,
+                        bool* needs_native_bridge,
+                        std::string* error_msg);
 #endif
 
 __attribute__((visibility("default")))
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 7fef106..67c1c10 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -29,6 +29,7 @@
 #include "nativebridge/native_bridge.h"
 
 #include <algorithm>
+#include <list>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -150,15 +151,14 @@
  public:
   LibraryNamespaces() : initialized_(false) { }
 
-  bool Create(JNIEnv* env,
-              uint32_t target_sdk_version,
-              jobject class_loader,
-              bool is_shared,
-              bool is_for_vendor,
-              jstring java_library_path,
-              jstring java_permitted_path,
-              NativeLoaderNamespace* ns,
-              std::string* error_msg) {
+  NativeLoaderNamespace* Create(JNIEnv* env,
+                                uint32_t target_sdk_version,
+                                jobject class_loader,
+                                bool is_shared,
+                                bool is_for_vendor,
+                                jstring java_library_path,
+                                jstring java_permitted_path,
+                                std::string* error_msg) {
     std::string library_path; // empty string by default.
 
     if (java_library_path != nullptr) {
@@ -182,10 +182,10 @@
     }
 
     if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
-      return false;
+      return nullptr;
     }
 
-    bool found = FindNamespaceByClassLoader(env, class_loader, nullptr);
+    bool found = FindNamespaceByClassLoader(env, class_loader);
 
     LOG_ALWAYS_FATAL_IF(found,
                         "There is already a namespace associated with this classloader");
@@ -199,13 +199,12 @@
       namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
     }
 
-    NativeLoaderNamespace parent_ns;
-    bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
+    NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
 
     bool is_native_bridge = false;
 
-    if (found_parent_namespace) {
-      is_native_bridge = !parent_ns.is_android_namespace();
+    if (parent_ns != nullptr) {
+      is_native_bridge = !parent_ns->is_android_namespace();
     } else if (!library_path.empty()) {
       is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
     }
@@ -251,15 +250,17 @@
 
     NativeLoaderNamespace native_loader_ns;
     if (!is_native_bridge) {
+      android_namespace_t* android_parent_ns =
+          parent_ns == nullptr ? nullptr : parent_ns->get_android_ns();
       android_namespace_t* ns = android_create_namespace(namespace_name,
                                                          nullptr,
                                                          library_path.c_str(),
                                                          namespace_type,
                                                          permitted_path.c_str(),
-                                                         parent_ns.get_android_ns());
+                                                         android_parent_ns);
       if (ns == nullptr) {
         *error_msg = dlerror();
-        return false;
+        return nullptr;
       }
 
       // Note that when vendor_ns is not configured this function will return nullptr
@@ -269,49 +270,50 @@
 
       if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = dlerror();
-        return false;
+        return nullptr;
       }
 
       if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
         // vendor apks are allowed to use VNDK-SP libraries.
         if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
           *error_msg = dlerror();
-          return false;
+          return nullptr;
         }
       }
 
       if (!vendor_public_libraries_.empty()) {
         if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = dlerror();
-          return false;
+          return nullptr;
         }
       }
 
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
+      native_bridge_namespace_t* native_bridge_parent_namespace =
+          parent_ns == nullptr ? nullptr : parent_ns->get_native_bridge_ns();
       native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
                                                                   nullptr,
                                                                   library_path.c_str(),
                                                                   namespace_type,
                                                                   permitted_path.c_str(),
-                                                                  parent_ns.get_native_bridge_ns());
-
+                                                                  native_bridge_parent_namespace);
       if (ns == nullptr) {
         *error_msg = NativeBridgeGetError();
-        return false;
+        return nullptr;
       }
 
       native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
 
       if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = NativeBridgeGetError();
-        return false;
+        return nullptr;
       }
 
       if (!vendor_public_libraries_.empty()) {
         if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = NativeBridgeGetError();
-          return false;
+          return nullptr;
         }
       }
 
@@ -320,24 +322,19 @@
 
     namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
 
-    *ns = native_loader_ns;
-    return true;
+    return &(namespaces_.back().second);
   }
 
-  bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
+  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
     auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
                 [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
                   return env->IsSameObject(value.first, class_loader);
                 });
     if (it != namespaces_.end()) {
-      if (ns != nullptr) {
-        *ns = it->second;
-      }
-
-      return true;
+      return &it->second;
     }
 
-    return false;
+    return nullptr;
   }
 
   void Initialize() {
@@ -557,24 +554,23 @@
     return env->CallObjectMethod(class_loader, get_parent);
   }
 
-  bool FindParentNamespaceByClassLoader(JNIEnv* env,
-                                        jobject class_loader,
-                                        NativeLoaderNamespace* ns) {
+  NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
     jobject parent_class_loader = GetParentClassLoader(env, class_loader);
 
     while (parent_class_loader != nullptr) {
-      if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) {
-        return true;
+      NativeLoaderNamespace* ns;
+      if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+        return ns;
       }
 
       parent_class_loader = GetParentClassLoader(env, parent_class_loader);
     }
 
-    return false;
+    return nullptr;
   }
 
   bool initialized_;
-  std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
   std::string system_public_libraries_;
   std::string vendor_public_libraries_;
   std::string oem_public_libraries_;
@@ -614,7 +610,6 @@
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 
   std::string error_msg;
-  NativeLoaderNamespace ns;
   bool success = g_namespaces->Create(env,
                                       target_sdk_version,
                                       class_loader,
@@ -622,8 +617,7 @@
                                       is_for_vendor,
                                       library_path,
                                       permitted_path,
-                                      &ns,
-                                      &error_msg);
+                                      &error_msg) != nullptr;
   if (!success) {
     return env->NewStringUTF(error_msg.c_str());
   }
@@ -649,43 +643,24 @@
   }
 
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  NativeLoaderNamespace ns;
+  NativeLoaderNamespace* ns;
 
-  if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
+  if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
     // This is the case where the classloader was not created by ApplicationLoaders
     // In this case we create an isolated not-shared namespace for it.
-    if (!g_namespaces->Create(env,
-                              target_sdk_version,
-                              class_loader,
-                              false /* is_shared */,
-                              false /* is_for_vendor */,
-                              library_path,
-                              nullptr,
-                              &ns,
-                              error_msg)) {
+    if ((ns = g_namespaces->Create(env,
+                                   target_sdk_version,
+                                   class_loader,
+                                   false /* is_shared */,
+                                   false /* is_for_vendor */,
+                                   library_path,
+                                   nullptr,
+                                   error_msg)) == nullptr) {
       return nullptr;
     }
   }
 
-  if (ns.is_android_namespace()) {
-    android_dlextinfo extinfo;
-    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
-    extinfo.library_namespace = ns.get_android_ns();
-
-    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
-    if (handle == nullptr) {
-      *error_msg = dlerror();
-    }
-    *needs_native_bridge = false;
-    return handle;
-  } else {
-    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
-    if (handle == nullptr) {
-      *error_msg = NativeBridgeGetError();
-    }
-    *needs_native_bridge = true;
-    return handle;
-  }
+  return OpenNativeLibrary(ns, path, needs_native_bridge, error_msg);
 #else
   UNUSED(env, target_sdk_version, class_loader);
 
@@ -741,18 +716,45 @@
 }
 
 #if defined(__ANDROID__)
+void* OpenNativeLibrary(NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
+                        std::string* error_msg) {
+  if (ns->is_android_namespace()) {
+    android_dlextinfo extinfo;
+    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+    extinfo.library_namespace = ns->get_android_ns();
+
+    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
+    if (handle == nullptr) {
+      *error_msg = dlerror();
+    }
+    *needs_native_bridge = false;
+    return handle;
+  } else {
+    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
+    if (handle == nullptr) {
+      *error_msg = NativeBridgeGetError();
+    }
+    *needs_native_bridge = true;
+    return handle;
+  }
+}
+
 // native_bridge_namespaces are not supported for callers of this function.
 // This function will return nullptr in the case when application is running
 // on native bridge.
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  NativeLoaderNamespace ns;
-  if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
-    return ns.is_android_namespace() ? ns.get_android_ns() : nullptr;
+  NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+  if (ns != nullptr) {
+    return ns->is_android_namespace() ? ns->get_android_ns() : nullptr;
   }
 
   return nullptr;
 }
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
 #endif
 
 }; //  android namespace
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 915cddb..2c00456 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -204,49 +204,19 @@
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
-    if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+    if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
       last_error_.code = ERROR_MEMORY_INVALID;
-      last_error_.address =
-          offset + reinterpret_cast<uintptr_t>(&phdr.p_type) - reinterpret_cast<uintptr_t>(&phdr);
+      last_error_.address = offset;
       return false;
     }
 
-    if (HandleType(offset, phdr.p_type)) {
-      continue;
-    }
-
     switch (phdr.p_type) {
     case PT_LOAD:
     {
-      // Get the flags first, if this isn't an executable header, ignore it.
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_flags) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       if ((phdr.p_flags & PF_X) == 0) {
         continue;
       }
 
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
                                           static_cast<size_t>(phdr.p_memsz)};
       if (phdr.p_offset == 0) {
@@ -256,46 +226,20 @@
     }
 
     case PT_GNU_EH_FRAME:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       // This is really the pointer to the .eh_frame_hdr section.
       eh_frame_hdr_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       eh_frame_hdr_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_offset) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_vaddr) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_vaddr_ = phdr.p_vaddr;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        last_error_.code = ERROR_MEMORY_INVALID;
-        last_error_.address = offset + reinterpret_cast<uintptr_t>(&phdr.p_memsz) -
-                              reinterpret_cast<uintptr_t>(&phdr);
-        return false;
-      }
       dynamic_size_ = phdr.p_memsz;
       break;
+
+    default:
+      HandleUnknownType(phdr.p_type, phdr.p_offset, phdr.p_filesz);
+      break;
     }
   }
   return true;
@@ -313,8 +257,7 @@
   ShdrType shdr;
   if (ehdr.e_shstrndx < ehdr.e_shnum) {
     uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
-    if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-        memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+    if (memory_->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
       sec_offset = shdr.sh_offset;
       sec_size = shdr.sh_size;
     }
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index a3244e8..3dd5d54 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -87,23 +87,17 @@
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
-bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
+void ElfInterfaceArm::HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) {
   if (type != PT_ARM_EXIDX) {
-    return false;
-  }
-
-  Elf32_Phdr phdr;
-  if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
-    return true;
+    return;
   }
 
   // The offset already takes into account the load bias.
-  start_offset_ = phdr.p_offset;
+  start_offset_ = ph_offset;
 
   // Always use filesz instead of memsz. In most cases they are the same,
   // but some shared libraries wind up setting one correctly and not the other.
-  total_entries_ = phdr.p_filesz / 8;
-  return true;
+  total_entries_ = ph_filesz / 8;
 }
 
 bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 3bee9cf..4c3a0c3 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -70,7 +70,7 @@
 
   bool FindEntry(uint32_t pc, uint64_t* entry_offset);
 
-  bool HandleType(uint64_t offset, uint32_t type) override;
+  void HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) override;
 
   bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
 
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 0c588da..5c1210d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -118,7 +118,7 @@
   template <typename SymType>
   bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
 
-  virtual bool HandleType(uint64_t, uint32_t) { return false; }
+  virtual void HandleUnknownType(uint32_t, uint64_t, uint64_t) {}
 
   template <typename EhdrType>
   static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index c0c07f4..dee5e98 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -41,18 +41,6 @@
 
   bool ReadFully(uint64_t addr, void* dst, size_t size);
 
-  inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
-    if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
-      return false;
-    }
-    uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
-    if (__builtin_add_overflow(addr, offset, &offset)) {
-      return false;
-    }
-    // The read will check if offset + size overflows.
-    return ReadFully(offset, field, size);
-  }
-
   inline bool Read32(uint64_t addr, uint32_t* dst) {
     return ReadFully(addr, dst, sizeof(uint32_t));
   }
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index a8bb4aa..43c6a97 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -242,44 +242,21 @@
   ASSERT_EQ(0xa020U, entries[4]);
 }
 
-TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
+TEST_F(ElfInterfaceArmTest, HandleUnknownType_arm_exidx) {
   ElfInterfaceArmFake interface(&memory_);
 
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK));
-}
-
-TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
-  ElfInterfaceArmFake interface(&memory_);
-
-  Elf32_Phdr phdr = {};
   interface.FakeSetStartOffset(0x1000);
   interface.FakeSetTotalEntries(100);
-  phdr.p_offset = 0x2000;
-  phdr.p_filesz = 0xa00;
 
-  // Verify that if reads fail, we don't set the values but still get true.
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  // Verify that if the type is not the one we want, we don't set the values.
+  interface.HandleUnknownType(0x70000000, 0x2000, 320);
   ASSERT_EQ(0x1000U, interface.start_offset());
   ASSERT_EQ(100U, interface.total_entries());
 
   // Everything is correct and present.
-  memory_.SetMemory(0x1000, &phdr, sizeof(phdr));
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  interface.HandleUnknownType(0x70000001, 0x2000, 320);
   ASSERT_EQ(0x2000U, interface.start_offset());
-  ASSERT_EQ(320U, interface.total_entries());
+  ASSERT_EQ(40U, interface.total_entries());
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx) {
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 4a9ed9f..3655984 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -51,40 +51,6 @@
   uint64_t four;
 };
 
-TEST(MemoryTest, read_field) {
-  MemoryFakeAlwaysReadZero memory;
-
-  FakeStruct data;
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.one, sizeof(data.one)));
-  ASSERT_EQ(0, data.one);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.two, sizeof(data.two)));
-  ASSERT_FALSE(data.two);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.three, sizeof(data.three)));
-  ASSERT_EQ(0U, data.three);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.four, sizeof(data.four)));
-  ASSERT_EQ(0U, data.four);
-}
-
-TEST(MemoryTest, read_field_fails) {
-  MemoryFakeAlwaysReadZero memory;
-
-  FakeStruct data;
-  memset(&data, 0xff, sizeof(data));
-
-  ASSERT_FALSE(memory.ReadField(UINT64_MAX, &data, &data.three, sizeof(data.three)));
-
-  // Field and start reversed, should fail.
-  ASSERT_FALSE(memory.ReadField(100, &data.two, &data, sizeof(data.two)));
-  ASSERT_FALSE(memory.ReadField(0, &data.two, &data, sizeof(data.two)));
-}
-
 TEST(MemoryTest, read_string) {
   std::string name("string_in_memory");
 
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 83695bb..ea992c7 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -32,6 +32,7 @@
 #include <vector>
 
 #include <android-base/stringprintf.h>
+#include <android-base/threads.h>
 
 #include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
@@ -231,8 +232,7 @@
     usleep(1000);
   }
   ASSERT_NE(0, tid.load());
-  // Portable tgkill method.
-  ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
+  ASSERT_EQ(0, tgkill(getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
 
   // Wait for context data.
   void* ucontext;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c1ae932..d3f038e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -318,8 +318,8 @@
     start vndservicemanager
 
     # Once everything is setup, no need to modify /.
-    # The bind+ro combination avoids modifying any other mount flags.
-    mount rootfs rootfs / remount bind ro
+    # 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