Merge "adb: change mdns tls service names (RFC 6763)."
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 0ad0465..31c938c 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -416,14 +416,18 @@
     return android::base::StringPrintf("%s:%s", prefix, command);
 }
 
-const FeatureSet& adb_get_feature_set() {
-    static const android::base::NoDestructor<FeatureSet> features([] {
-        std::string result;
-        if (!adb_query(format_host_command("features"), &result, &result)) {
-            fprintf(stderr, "failed to get feature set: %s\n", result.c_str());
-            return FeatureSet{};
-        }
-        return StringToFeatureSet(result);
-    }());
-    return *features;
+const std::optional<FeatureSet>& adb_get_feature_set(std::string* error) {
+    static std::string cached_error [[clang::no_destroy]];
+    static const std::optional<FeatureSet> features
+            [[clang::no_destroy]] ([]() -> std::optional<FeatureSet> {
+                std::string result;
+                if (adb_query(format_host_command("features"), &result, &cached_error)) {
+                    return StringToFeatureSet(result);
+                }
+                return std::nullopt;
+            }());
+    if (!features && error) {
+        *error = cached_error;
+    }
+    return features;
 }
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index bf0be40..27be28f 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -76,7 +76,7 @@
 std::string format_host_command(const char* _Nonnull command);
 
 // Get the feature set of the current preferred transport.
-const FeatureSet& adb_get_feature_set();
+const std::optional<FeatureSet>& adb_get_feature_set(std::string* _Nullable error);
 
 #if defined(__linux__)
 // Get the path of a file containing the path to the server executable, if the socket spec set via
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 3810cc8..59c8563 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -57,11 +57,12 @@
 }
 
 static bool can_use_feature(const char* feature) {
-    auto&& features = adb_get_feature_set();
-    if (features.empty()) {
+    // We ignore errors here, if the device is missing, we'll notice when we try to push install.
+    auto&& features = adb_get_feature_set(nullptr);
+    if (!features) {
         return false;
     }
-    return CanUseFeature(features, feature);
+    return CanUseFeature(*features, feature);
 }
 
 static InstallMode best_install_mode() {
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 6a7493f..29f9dc1 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -672,16 +672,17 @@
 }
 
 static int adb_shell(int argc, const char** argv) {
-    auto&& features = adb_get_feature_set();
-    if (features.empty()) {
-        return 1;
+    std::string error;
+    auto&& features = adb_get_feature_set(&error);
+    if (!features) {
+        error_exit("%s", error.c_str());
     }
 
     enum PtyAllocationMode { kPtyAuto, kPtyNo, kPtyYes, kPtyDefinitely };
 
     // Defaults.
-    char escape_char = '~'; // -e
-    bool use_shell_protocol = CanUseFeature(features, kFeatureShell2); // -x
+    char escape_char = '~';                                                 // -e
+    bool use_shell_protocol = CanUseFeature(*features, kFeatureShell2);     // -x
     PtyAllocationMode tty = use_shell_protocol ? kPtyAuto : kPtyDefinitely; // -t/-T
 
     // Parse shell-specific command-line options.
@@ -757,7 +758,7 @@
     if (!use_shell_protocol) {
         if (shell_type_arg != kShellServiceArgPty) {
             fprintf(stderr, "error: %s only supports allocating a pty\n",
-                    !CanUseFeature(features, kFeatureShell2) ? "device" : "-x");
+                    !CanUseFeature(*features, kFeatureShell2) ? "device" : "-x");
             return 1;
         } else {
             // If we're not using the shell protocol, the type argument must be empty.
@@ -777,11 +778,13 @@
 }
 
 static int adb_abb(int argc, const char** argv) {
-    auto&& features = adb_get_feature_set();
-    if (features.empty()) {
+    std::string error;
+    auto&& features = adb_get_feature_set(&error);
+    if (!features) {
+        error_exit("%s", error.c_str());
         return 1;
     }
-    if (!CanUseFeature(features, kFeatureAbb)) {
+    if (!CanUseFeature(*features, kFeatureAbb)) {
         error_exit("abb is not supported by the device");
     }
 
@@ -1159,9 +1162,9 @@
         // Use shell protocol if it's supported and the caller doesn't explicitly
         // disable it.
         if (!disable_shell_protocol) {
-            auto&& features = adb_get_feature_set();
-            if (!features.empty()) {
-                use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+            auto&& features = adb_get_feature_set(nullptr);
+            if (features) {
+                use_shell_protocol = CanUseFeature(*features, kFeatureShell2);
             } else {
                 // Device was unreachable.
                 attempt_connection = false;
@@ -1810,12 +1813,13 @@
         }
         return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
     } else if (!strcmp(argv[0], "remount")) {
-        auto&& features = adb_get_feature_set();
-        if (features.empty()) {
-            return 1;
+        std::string error;
+        auto&& features = adb_get_feature_set(&error);
+        if (!features) {
+            error_exit("%s", error.c_str());
         }
 
-        if (CanUseFeature(features, kFeatureRemountShell)) {
+        if (CanUseFeature(*features, kFeatureRemountShell)) {
             std::vector<const char*> args = {"shell"};
             args.insert(args.cend(), argv, argv + argc);
             return adb_shell_noinput(args.size(), args.data());
@@ -2034,11 +2038,12 @@
     } else if (!strcmp(argv[0], "track-jdwp")) {
         return adb_connect_command("track-jdwp");
     } else if (!strcmp(argv[0], "track-app")) {
-        auto&& features = adb_get_feature_set();
-        if (features.empty()) {
-            return 1;
+        std::string error;
+        auto&& features = adb_get_feature_set(&error);
+        if (!features) {
+            error_exit("%s", error.c_str());
         }
-        if (!CanUseFeature(features, kFeatureTrackApp)) {
+        if (!CanUseFeature(*features, kFeatureTrackApp)) {
             error_exit("track-app is not supported by the device");
         }
         TrackAppStreamsCallback callback;
@@ -2064,13 +2069,14 @@
         return 0;
     } else if (!strcmp(argv[0], "features")) {
         // Only list the features common to both the adb client and the device.
-        auto&& features = adb_get_feature_set();
-        if (features.empty()) {
-            return 1;
+        std::string error;
+        auto&& features = adb_get_feature_set(&error);
+        if (!features) {
+            error_exit("%s", error.c_str());
         }
 
-        for (const std::string& name : features) {
-            if (CanUseFeature(features, name)) {
+        for (const std::string& name : *features) {
+            if (CanUseFeature(*features, name)) {
                 printf("%s\n", name.c_str());
             }
         }
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index f2c673a..7185939 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -225,21 +225,22 @@
 
 class SyncConnection {
   public:
-    SyncConnection()
-        : acknowledgement_buffer_(sizeof(sync_status) + SYNC_DATA_MAX),
-          features_(adb_get_feature_set()) {
+    SyncConnection() : acknowledgement_buffer_(sizeof(sync_status) + SYNC_DATA_MAX) {
         acknowledgement_buffer_.resize(0);
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
-        if (features_.empty()) {
-            Error("failed to get feature set");
+        std::string error;
+        auto&& features = adb_get_feature_set(&error);
+        if (!features) {
+            Error("failed to get feature set: %s", error.c_str());
         } else {
-            have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
-            have_ls_v2_ = CanUseFeature(features_, kFeatureLs2);
-            have_sendrecv_v2_ = CanUseFeature(features_, kFeatureSendRecv2);
-            have_sendrecv_v2_brotli_ = CanUseFeature(features_, kFeatureSendRecv2Brotli);
-            have_sendrecv_v2_lz4_ = CanUseFeature(features_, kFeatureSendRecv2LZ4);
-            have_sendrecv_v2_dry_run_send_ = CanUseFeature(features_, kFeatureSendRecv2DryRunSend);
+            features_ = &*features;
+            have_stat_v2_ = CanUseFeature(*features, kFeatureStat2);
+            have_ls_v2_ = CanUseFeature(*features, kFeatureLs2);
+            have_sendrecv_v2_ = CanUseFeature(*features, kFeatureSendRecv2);
+            have_sendrecv_v2_brotli_ = CanUseFeature(*features, kFeatureSendRecv2Brotli);
+            have_sendrecv_v2_lz4_ = CanUseFeature(*features, kFeatureSendRecv2LZ4);
+            have_sendrecv_v2_dry_run_send_ = CanUseFeature(*features, kFeatureSendRecv2DryRunSend);
             std::string error;
             fd.reset(adb_connect("sync:", &error));
             if (fd < 0) {
@@ -283,7 +284,7 @@
         return compression;
     }
 
-    const FeatureSet& Features() const { return features_; }
+    const FeatureSet& Features() const { return *features_; }
 
     bool IsValid() { return fd >= 0; }
 
@@ -921,7 +922,7 @@
   private:
     std::deque<std::pair<std::string, std::string>> deferred_acknowledgements_;
     Block acknowledgement_buffer_;
-    const FeatureSet& features_;
+    const FeatureSet* features_ = nullptr;
     bool have_stat_v2_;
     bool have_ls_v2_;
     bool have_sendrecv_v2_;
diff --git a/adb/client/incremental_utils.h b/adb/client/incremental_utils.h
index d969d94..fe2914d 100644
--- a/adb/client/incremental_utils.h
+++ b/adb/client/incremental_utils.h
@@ -25,6 +25,8 @@
 
 #include <stdint.h>
 
+#include <android-base/off64_t.h>
+
 namespace incremental {
 
 using Size = int64_t;
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 884856d..3a2deb7 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -145,6 +145,7 @@
     static_libs: [
         "libhealthhalutils",
         "libsnapshot_nobinder",
+        "update_metadata-protos",
     ],
 
     header_libs: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 1486e87..0c2569d 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -358,7 +358,7 @@
                        const struct ext4_super_block* sb, int* fs_stat) {
     bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
     bool want_quota = entry.fs_mgr_flags.quota;
-    bool want_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
+    bool want_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
 
     if (has_quota == want_quota) {
         return;
@@ -521,7 +521,8 @@
 static void tune_casefold(const std::string& blk_device, const struct ext4_super_block* sb,
                           int* fs_stat) {
     bool has_casefold = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;
-    bool wants_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
+    bool wants_casefold =
+            android::base::GetBoolProperty("external_storage.casefold.enabled", false);
 
     if (!wants_casefold || has_casefold) return;
 
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 778c0c1..301c907 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -166,8 +166,8 @@
     bool needs_projid = false;
 
     if (entry.mount_point == "/data") {
-        needs_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
-        needs_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
+        needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
+        needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
     }
 
     if (entry.fs_type == "f2fs") {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index a209ea6..bd788f5 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -209,6 +209,7 @@
     static_libs: [
         "libfstab",
         "libsnapshot",
+        "update_metadata-protos",
     ],
     shared_libs: [
         "android.hardware.boot@1.0",
@@ -226,3 +227,20 @@
         "libutils",
     ],
 }
+
+cc_test {
+    name: "snapshot_power_test",
+    srcs: [
+        "power_test.cpp",
+    ],
+    static_libs: [
+        "libsnapshot",
+        "update_metadata-protos",
+    ],
+    shared_libs: [
+        "libbase",
+        "libfs_mgr_binder",
+        "liblog",
+    ],
+    gtest: false,
+}
diff --git a/fs_mgr/libsnapshot/PowerTest.md b/fs_mgr/libsnapshot/PowerTest.md
new file mode 100644
index 0000000..0b0cb5d
--- /dev/null
+++ b/fs_mgr/libsnapshot/PowerTest.md
@@ -0,0 +1,40 @@
+snapshot\_power\_test
+---------------------
+
+snapshot\_power\_test is a standalone test to simulate power failures during a snapshot-merge operation.
+
+### Test Setup
+
+Start by creating two large files that will be used as the pre-merge and post-merge state. You can take two different partition images (for example, a product.img from two separate builds), or just create random data:
+
+	dd if=/dev/urandom of=pre-merge count=1024 bs=1048576
+	dd if=/dev/urandom of=post-merge count=1024 bs=1048576
+
+Next, push these files to an unencrypted directory on the device:
+
+	adb push pre-merge /data/local/unencrypted
+	adb push post-merge /data/local/unencrypted
+
+Next, run the test setup:
+
+	adb sync data
+	adb shell /data/nativetest64/snapshot_power_test/snapshot_power_test \
+		/data/local/unencrypted/pre-merge \
+		/data/local/unencrypted/post-merge
+
+This will create the necessary fiemap-based images.
+
+### Running
+The actual test can be run via `run_power_test.sh`. Its syntax is:
+
+	run_power_test.sh <POST_MERGE_FILE>
+
+`POST_MERGE_FILE` should be the path on the device of the image to validate the merge against. Example:
+
+	run_power_test.sh /data/local/unencrypted/post-merge
+
+The device will begin the merge with a 5% chance of injecting a kernel crash every 10ms. The device should be capable of rebooting normally without user intervention. Once the merge has completed, the test will run a final check command to validate the contents of the snapshot against the post-merge file. It will error if there are any incorrect blocks.
+
+Two environment variables can be passed to `run_power_test.sh`:
+1. `FAIL_RATE` - A fraction between 0 and 100 (inclusive) indicating the probability the device should inject a kernel crash every 10ms.
+2. `DEVICE_SERIAL` - If multiple devices are attached to adb, this argument is passed as the serial to select (to `adb -s`).
diff --git a/fs_mgr/libsnapshot/power_test.cpp b/fs_mgr/libsnapshot/power_test.cpp
new file mode 100644
index 0000000..4d2548a
--- /dev/null
+++ b/fs_mgr/libsnapshot/power_test.cpp
@@ -0,0 +1,559 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iostream>
+#include <random>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parsedouble.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::dm::DmTable;
+using android::dm::DmTargetSnapshot;
+using android::dm::SnapshotStorageMode;
+using android::fiemap::ImageManager;
+using android::fs_mgr::Fstab;
+
+namespace android {
+namespace snapshot {
+
+static void usage() {
+    std::cerr << "Usage:\n";
+    std::cerr << "  create <orig-payload> <new-payload>\n";
+    std::cerr << "\n";
+    std::cerr << "  Create a snapshot device containing the contents of\n";
+    std::cerr << "  orig-payload, and then write the contents of new-payload.\n";
+    std::cerr << "  The original files are not modified.\n";
+    std::cerr << "\n";
+    std::cerr << "  merge <fail-rate>\n";
+    std::cerr << "\n";
+    std::cerr << "  Merge the snapshot previously started by create, and wait\n";
+    std::cerr << "  for it to complete. Once done, it is compared to the\n";
+    std::cerr << "  new-payload for consistency. The original files are not \n";
+    std::cerr << "  modified. If a fail-rate is passed (as a fraction between 0\n";
+    std::cerr << "  and 100), every 10ms the device has that percent change of\n";
+    std::cerr << "  injecting a kernel crash.\n";
+    std::cerr << "\n";
+    std::cerr << "  check <new-payload>\n";
+    std::cerr << "  Verify that all artifacts are correct after a merge\n";
+    std::cerr << "  completes.\n";
+    std::cerr << "\n";
+    std::cerr << "  cleanup\n";
+    std::cerr << "  Remove all ImageManager artifacts from create/merge.\n";
+}
+
+class PowerTest final {
+  public:
+    PowerTest();
+    bool Run(int argc, char** argv);
+
+  private:
+    bool OpenImageManager();
+    bool Create(int argc, char** argv);
+    bool Merge(int argc, char** argv);
+    bool Check(int argc, char** argv);
+    bool Cleanup();
+    bool CleanupImage(const std::string& name);
+    bool SetupImages(const std::string& first_file, borrowed_fd second_fd);
+    bool MapImages();
+    bool MapSnapshot(SnapshotStorageMode mode);
+    bool GetMergeStatus(DmTargetSnapshot::Status* status);
+
+    static constexpr char kSnapshotName[] = "snapshot-power-test";
+    static constexpr char kSnapshotImageName[] = "snapshot-power-test-image";
+    static constexpr char kSnapshotCowName[] = "snapshot-power-test-cow";
+
+    DeviceMapper& dm_;
+    std::unique_ptr<ImageManager> images_;
+    std::string image_path_;
+    std::string cow_path_;
+    std::string snapshot_path_;
+};
+
+PowerTest::PowerTest() : dm_(DeviceMapper::Instance()) {}
+
+bool PowerTest::Run([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+    if (!OpenImageManager()) {
+        return false;
+    }
+
+    if (argc < 2) {
+        usage();
+        return false;
+    }
+    if (argv[1] == "create"s) {
+        return Create(argc, argv);
+    } else if (argv[1] == "merge"s) {
+        return Merge(argc, argv);
+    } else if (argv[1] == "check"s) {
+        return Check(argc, argv);
+    } else if (argv[1] == "cleanup"s) {
+        return Cleanup();
+    } else {
+        usage();
+        return false;
+    }
+}
+
+bool PowerTest::OpenImageManager() {
+    std::vector<std::string> dirs = {
+            "/data/gsi/test",
+            "/metadata/gsi/test",
+    };
+    for (const auto& dir : dirs) {
+        if (mkdir(dir.c_str(), 0700) && errno != EEXIST) {
+            std::cerr << "mkdir " << dir << ": " << strerror(errno) << "\n";
+            return false;
+        }
+    }
+
+    images_ = ImageManager::Open("/metadata/gsi/test", "/data/gsi/test");
+    if (!images_) {
+        std::cerr << "Could not open ImageManager\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::Create(int argc, char** argv) {
+    if (argc < 4) {
+        usage();
+        return false;
+    }
+
+    std::string first = argv[2];
+    std::string second = argv[3];
+
+    unique_fd second_fd(open(second.c_str(), O_RDONLY));
+    if (second_fd < 0) {
+        std::cerr << "open " << second << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    if (!Cleanup()) {
+        return false;
+    }
+    if (!SetupImages(first, second_fd)) {
+        return false;
+    }
+    if (!MapSnapshot(SnapshotStorageMode::Persistent)) {
+        return false;
+    }
+
+    struct stat s;
+    if (fstat(second_fd, &s)) {
+        std::cerr << "fstat " << second << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    unique_fd snap_fd(open(snapshot_path_.c_str(), O_WRONLY));
+    if (snap_fd < 0) {
+        std::cerr << "open " << snapshot_path_ << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    uint8_t chunk[4096];
+    uint64_t written = 0;
+    while (written < s.st_size) {
+        uint64_t remaining = s.st_size - written;
+        size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
+        if (!android::base::ReadFully(second_fd, chunk, bytes)) {
+            std::cerr << "read " << second << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        if (!android::base::WriteFully(snap_fd, chunk, bytes)) {
+            std::cerr << "write " << snapshot_path_ << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        written += bytes;
+    }
+    if (fsync(snap_fd)) {
+        std::cerr << "fsync: " << strerror(errno) << "\n";
+        return false;
+    }
+
+    sync();
+
+    snap_fd = {};
+    if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
+        std::cerr << "could not delete dm device " << kSnapshotName << "\n";
+        return false;
+    }
+    if (!images_->UnmapImageIfExists(kSnapshotImageName)) {
+        std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
+        return false;
+    }
+    if (!images_->UnmapImageIfExists(kSnapshotCowName)) {
+        std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::Cleanup() {
+    if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
+        std::cerr << "could not delete dm device " << kSnapshotName << "\n";
+        return false;
+    }
+    if (!CleanupImage(kSnapshotImageName) || !CleanupImage(kSnapshotCowName)) {
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::CleanupImage(const std::string& name) {
+    if (!images_->UnmapImageIfExists(name)) {
+        std::cerr << "failed to unmap " << name << "\n";
+        return false;
+    }
+    if (images_->BackingImageExists(name) && !images_->DeleteBackingImage(name)) {
+        std::cerr << "failed to delete " << name << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::SetupImages(const std::string& first, borrowed_fd second_fd) {
+    unique_fd first_fd(open(first.c_str(), O_RDONLY));
+    if (first_fd < 0) {
+        std::cerr << "open " << first << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    struct stat s1, s2;
+    if (fstat(first_fd.get(), &s1)) {
+        std::cerr << "first stat: " << strerror(errno) << "\n";
+        return false;
+    }
+    if (fstat(second_fd.get(), &s2)) {
+        std::cerr << "second stat: " << strerror(errno) << "\n";
+        return false;
+    }
+
+    // Pick the bigger size of both images, rounding up to the nearest block.
+    uint64_t s1_size = (s1.st_size + 4095) & ~uint64_t(4095);
+    uint64_t s2_size = (s2.st_size + 4095) & ~uint64_t(4095);
+    uint64_t image_size = std::max(s1_size, s2_size) + (1024 * 1024 * 128);
+    if (!images_->CreateBackingImage(kSnapshotImageName, image_size, 0, nullptr)) {
+        std::cerr << "failed to create " << kSnapshotImageName << "\n";
+        return false;
+    }
+    // Use the same size for the cow.
+    if (!images_->CreateBackingImage(kSnapshotCowName, image_size, 0, nullptr)) {
+        std::cerr << "failed to create " << kSnapshotCowName << "\n";
+        return false;
+    }
+    if (!MapImages()) {
+        return false;
+    }
+
+    unique_fd image_fd(open(image_path_.c_str(), O_WRONLY));
+    if (image_fd < 0) {
+        std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    uint8_t chunk[4096];
+    uint64_t written = 0;
+    while (written < s1.st_size) {
+        uint64_t remaining = s1.st_size - written;
+        size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
+        if (!android::base::ReadFully(first_fd, chunk, bytes)) {
+            std::cerr << "read: " << strerror(errno) << "\n";
+            return false;
+        }
+        if (!android::base::WriteFully(image_fd, chunk, bytes)) {
+            std::cerr << "write: " << strerror(errno) << "\n";
+            return false;
+        }
+        written += bytes;
+    }
+    if (fsync(image_fd)) {
+        std::cerr << "fsync: " << strerror(errno) << "\n";
+        return false;
+    }
+
+    // Zero the first block of the COW.
+    unique_fd cow_fd(open(cow_path_.c_str(), O_WRONLY));
+    if (cow_fd < 0) {
+        std::cerr << "open: " << cow_path_ << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    memset(chunk, 0, sizeof(chunk));
+    if (!android::base::WriteFully(cow_fd, chunk, sizeof(chunk))) {
+        std::cerr << "read: " << strerror(errno) << "\n";
+        return false;
+    }
+    if (fsync(cow_fd)) {
+        std::cerr << "fsync: " << strerror(errno) << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::MapImages() {
+    if (!images_->MapImageDevice(kSnapshotImageName, 10s, &image_path_)) {
+        std::cerr << "failed to map " << kSnapshotImageName << "\n";
+        return false;
+    }
+    if (!images_->MapImageDevice(kSnapshotCowName, 10s, &cow_path_)) {
+        std::cerr << "failed to map " << kSnapshotCowName << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::MapSnapshot(SnapshotStorageMode mode) {
+    uint64_t sectors;
+    {
+        unique_fd fd(open(image_path_.c_str(), O_RDONLY));
+        if (fd < 0) {
+            std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        sectors = get_block_device_size(fd) / 512;
+    }
+
+    DmTable table;
+    table.Emplace<DmTargetSnapshot>(0, sectors, image_path_, cow_path_, mode, 8);
+    if (!dm_.CreateDevice(kSnapshotName, table, &snapshot_path_, 10s)) {
+        std::cerr << "failed to create snapshot device\n";
+        return false;
+    }
+    return true;
+}
+
+bool PowerTest::GetMergeStatus(DmTargetSnapshot::Status* status) {
+    std::vector<DeviceMapper::TargetInfo> targets;
+    if (!dm_.GetTableStatus(kSnapshotName, &targets)) {
+        std::cerr << "failed to get merge status\n";
+        return false;
+    }
+    if (targets.size() != 1) {
+        std::cerr << "merge device has wrong number of targets\n";
+        return false;
+    }
+    if (!DmTargetSnapshot::ParseStatusText(targets[0].data, status)) {
+        std::cerr << "could not parse merge target status text\n";
+        return false;
+    }
+    return true;
+}
+
+static std::string GetUserdataBlockDeviceName() {
+    Fstab fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+        return {};
+    }
+
+    auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
+    if (!entry) {
+        return {};
+    }
+
+    auto prefix = "/dev/block/"s;
+    if (!android::base::StartsWith(entry->blk_device, prefix)) {
+        return {};
+    }
+    return entry->blk_device.substr(prefix.size());
+}
+
+bool PowerTest::Merge(int argc, char** argv) {
+    // Start an f2fs GC to really stress things. :TODO: figure out data device
+    auto userdata_dev = GetUserdataBlockDeviceName();
+    if (userdata_dev.empty()) {
+        std::cerr << "could not locate userdata block device\n";
+        return false;
+    }
+
+    auto cmd =
+            android::base::StringPrintf("echo 1 > /sys/fs/f2fs/%s/gc_urgent", userdata_dev.c_str());
+    system(cmd.c_str());
+
+    if (dm_.GetState(kSnapshotName) == DmDeviceState::INVALID) {
+        if (!MapImages()) {
+            return false;
+        }
+        if (!MapSnapshot(SnapshotStorageMode::Merge)) {
+            return false;
+        }
+    }
+
+    std::random_device r;
+    std::default_random_engine re(r());
+    std::uniform_real_distribution<double> dist(0.0, 100.0);
+
+    std::optional<double> failure_rate;
+    if (argc >= 3) {
+        double d;
+        if (!android::base::ParseDouble(argv[2], &d)) {
+            std::cerr << "Could not parse failure rate as double: " << argv[2] << "\n";
+            return false;
+        }
+        failure_rate = d;
+    }
+
+    while (true) {
+        DmTargetSnapshot::Status status;
+        if (!GetMergeStatus(&status)) {
+            return false;
+        }
+        if (!status.error.empty()) {
+            std::cerr << "merge reported error: " << status.error << "\n";
+            return false;
+        }
+        if (status.sectors_allocated == status.metadata_sectors) {
+            break;
+        }
+
+        std::cerr << status.sectors_allocated << " / " << status.metadata_sectors << "\n";
+
+        if (failure_rate && *failure_rate >= dist(re)) {
+            system("echo 1 > /proc/sys/kernel/sysrq");
+            system("echo c > /proc/sysrq-trigger");
+        }
+
+        std::this_thread::sleep_for(10ms);
+    }
+
+    std::cout << "Merge completed.\n";
+    return true;
+}
+
+bool PowerTest::Check([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+    if (argc < 3) {
+        std::cerr << "Expected argument: <new-image-path>\n";
+        return false;
+    }
+    std::string md_path, image_path;
+    std::string canonical_path = argv[2];
+
+    if (!dm_.GetDmDevicePathByName(kSnapshotName, &md_path)) {
+        std::cerr << "could not get dm-path for merge device\n";
+        return false;
+    }
+    if (!images_->GetMappedImageDevice(kSnapshotImageName, &image_path)) {
+        std::cerr << "could not get image path\n";
+        return false;
+    }
+
+    unique_fd md_fd(open(md_path.c_str(), O_RDONLY));
+    if (md_fd < 0) {
+        std::cerr << "open: " << md_path << ": " << strerror(errno) << "\n";
+        return false;
+    }
+    unique_fd image_fd(open(image_path.c_str(), O_RDONLY));
+    if (image_fd < 0) {
+        std::cerr << "open: " << image_path << ": " << strerror(errno) << "\n";
+        return false;
+    }
+    unique_fd canonical_fd(open(canonical_path.c_str(), O_RDONLY));
+    if (canonical_fd < 0) {
+        std::cerr << "open: " << canonical_path << ": " << strerror(errno) << "\n";
+        return false;
+    }
+
+    struct stat s;
+    if (fstat(canonical_fd, &s)) {
+        std::cerr << "fstat: " << canonical_path << ": " << strerror(errno) << "\n";
+        return false;
+    }
+    uint64_t canonical_size = s.st_size;
+    uint64_t md_size = get_block_device_size(md_fd);
+    uint64_t image_size = get_block_device_size(image_fd);
+    if (image_size != md_size) {
+        std::cerr << "image size does not match merge device size\n";
+        return false;
+    }
+    if (canonical_size > image_size) {
+        std::cerr << "canonical size " << canonical_size << " is greater than image size "
+                  << image_size << "\n";
+        return false;
+    }
+
+    constexpr size_t kBlockSize = 4096;
+    uint8_t canonical_buffer[kBlockSize];
+    uint8_t image_buffer[kBlockSize];
+    uint8_t md_buffer[kBlockSize];
+
+    uint64_t remaining = canonical_size;
+    uint64_t blockno = 0;
+    while (remaining) {
+        size_t bytes = (size_t)std::min((uint64_t)kBlockSize, remaining);
+        if (!android::base::ReadFully(canonical_fd, canonical_buffer, bytes)) {
+            std::cerr << "read: " << canonical_buffer << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        if (!android::base::ReadFully(image_fd, image_buffer, bytes)) {
+            std::cerr << "read: " << image_buffer << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        if (!android::base::ReadFully(md_fd, md_buffer, bytes)) {
+            std::cerr << "read: " << md_buffer << ": " << strerror(errno) << "\n";
+            return false;
+        }
+        if (memcmp(canonical_buffer, image_buffer, bytes)) {
+            std::cerr << "canonical and image differ at block " << blockno << "\n";
+            return false;
+        }
+        if (memcmp(canonical_buffer, md_buffer, bytes)) {
+            std::cerr << "canonical and image differ at block " << blockno << "\n";
+            return false;
+        }
+
+        remaining -= bytes;
+        blockno++;
+    }
+
+    std::cout << "Images all match.\n";
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::snapshot::PowerTest test;
+
+    if (!test.Run(argc, argv)) {
+        std::cerr << "Unexpected error running test." << std::endl;
+        return 1;
+    }
+    fflush(stdout);
+    return 0;
+}
diff --git a/fs_mgr/libsnapshot/run_power_test.sh b/fs_mgr/libsnapshot/run_power_test.sh
new file mode 100755
index 0000000..dc03dc9
--- /dev/null
+++ b/fs_mgr/libsnapshot/run_power_test.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -e
+
+if [ -z "$FAIL_RATE" ]; then
+    FAIL_RATE=5.0
+fi
+if [ ! -z "$ANDROID_SERIAL" ]; then
+    DEVICE_ARGS=-s $ANDROID_SERIAL
+else
+    DEVICE_ARGS=
+fi
+
+TEST_BIN=/data/nativetest64/snapshot_power_test/snapshot_power_test
+
+while :
+do
+    adb $DEVICE_ARGS wait-for-device
+    adb $DEVICE_ARGS root
+    adb $DEVICE_ARGS shell rm $TEST_BIN
+    adb $DEVICE_ARGS sync data
+    set +e
+    output=$(adb $DEVICE_ARGS shell $TEST_BIN merge $FAIL_RATE 2>&1)
+    set -e
+    if [[ "$output" == *"Merge completed"* ]]; then
+        echo "Merge completed."
+        break
+    fi
+    if [[ "$output" == *"Unexpected error"* ]]; then
+        echo "Unexpected error."
+        exit 1
+    fi
+done
+
+adb $DEVICE_ARGS shell $TEST_BIN check $1
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index f82c082..c662838 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1753,7 +1753,6 @@
   protected:
     void SetUp() override {
         if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
-        GTEST_SKIP() << "WIP failure b/149738928";
 
         SnapshotTest::SetUp();
         userdata_ = std::make_unique<LowSpaceUserdata>();
diff --git a/init/Android.bp b/init/Android.bp
index 1b3aa18..edf9099 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -41,6 +41,7 @@
     "builtins.cpp",
     "devices.cpp",
     "firmware_handler.cpp",
+    "first_stage_console.cpp",
     "first_stage_init.cpp",
     "first_stage_mount.cpp",
     "fscrypt_init_extensions.cpp",
@@ -130,6 +131,7 @@
         "libpropertyinfoparser",
         "libsnapshot_init",
         "lib_apex_manifest_proto_lite",
+        "update_metadata-protos",
     ],
     shared_libs: [
         "libbacktrace",
diff --git a/init/Android.mk b/init/Android.mk
index b49fb3b..da94daf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -50,6 +50,7 @@
 LOCAL_SRC_FILES := \
     block_dev_initializer.cpp \
     devices.cpp \
+    first_stage_console.cpp \
     first_stage_init.cpp \
     first_stage_main.cpp \
     first_stage_mount.cpp \
@@ -112,6 +113,7 @@
     libext2_uuid \
     libprotobuf-cpp-lite \
     libsnapshot_init \
+    update_metadata-protos \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
new file mode 100644
index 0000000..cae53f4
--- /dev/null
+++ b/init/first_stage_console.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "first_stage_console.h"
+
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+static void RunScript() {
+    LOG(INFO) << "Attempting to run /first_stage.sh...";
+    pid_t pid = fork();
+    if (pid != 0) {
+        int status;
+        waitpid(pid, &status, 0);
+        LOG(INFO) << "/first_stage.sh exited with status " << status;
+        return;
+    }
+    const char* path = "/system/bin/sh";
+    const char* args[] = {path, "/first_stage.sh", nullptr};
+    int rv = execv(path, const_cast<char**>(args));
+    LOG(ERROR) << "unable to execv /first_stage.sh, returned " << rv << " errno " << errno;
+}
+
+namespace android {
+namespace init {
+
+void StartConsole() {
+    if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+        PLOG(ERROR) << "unable to create /dev/console";
+        return;
+    }
+    pid_t pid = fork();
+    if (pid != 0) {
+        int status;
+        waitpid(pid, &status, 0);
+        LOG(ERROR) << "console shell exited with status " << status;
+        return;
+    }
+    int fd = -1;
+    int tries = 50; // should timeout after 5s
+    // The device driver for console may not be ready yet so retry for a while in case of failure.
+    while (tries--) {
+        fd = open("/dev/console", O_RDWR);
+        if (fd != -1) {
+            break;
+        }
+        std::this_thread::sleep_for(100ms);
+    }
+    if (fd == -1) {
+        LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
+        _exit(127);
+    }
+    ioctl(fd, TIOCSCTTY, 0);
+    dup2(fd, STDIN_FILENO);
+    dup2(fd, STDOUT_FILENO);
+    dup2(fd, STDERR_FILENO);
+    close(fd);
+
+    RunScript();
+    const char* path = "/system/bin/sh";
+    const char* args[] = {path, nullptr};
+    int rv = execv(path, const_cast<char**>(args));
+    LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+    _exit(127);
+}
+
+bool FirstStageConsole(const std::string& cmdline) {
+    return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
new file mode 100644
index 0000000..7485339
--- /dev/null
+++ b/init/first_stage_console.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace init {
+
+void StartConsole();
+bool FirstStageConsole(const std::string& cmdline);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index ef8ffbe..5eca644 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -24,12 +24,10 @@
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <filesystem>
 #include <string>
-#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -39,6 +37,7 @@
 #include <private/android_filesystem_config.h>
 
 #include "debug_ramdisk.h"
+#include "first_stage_console.h"
 #include "first_stage_mount.h"
 #include "reboot_utils.h"
 #include "switch_root.h"
@@ -94,49 +93,6 @@
     }
 }
 
-void StartConsole() {
-    if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
-        PLOG(ERROR) << "unable to create /dev/console";
-        return;
-    }
-    pid_t pid = fork();
-    if (pid != 0) {
-        int status;
-        waitpid(pid, &status, 0);
-        LOG(ERROR) << "console shell exited with status " << status;
-        return;
-    }
-    int fd = -1;
-    int tries = 10;
-    // The device driver for console may not be ready yet so retry for a while in case of failure.
-    while (tries--) {
-        fd = open("/dev/console", O_RDWR);
-        if (fd != -1) {
-            break;
-        }
-        std::this_thread::sleep_for(100ms);
-    }
-    if (fd == -1) {
-        LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
-        _exit(127);
-    }
-    ioctl(fd, TIOCSCTTY, 0);
-    dup2(fd, STDIN_FILENO);
-    dup2(fd, STDOUT_FILENO);
-    dup2(fd, STDERR_FILENO);
-    close(fd);
-
-    const char* path = "/system/bin/sh";
-    const char* args[] = {path, nullptr};
-    int rv = execv(path, const_cast<char**>(args));
-    LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
-    _exit(127);
-}
-
-bool FirstStageConsole(const std::string& cmdline) {
-    return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
-}
-
 bool ForceNormalBoot(const std::string& cmdline) {
     return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2f91663..72f0450 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -203,6 +203,7 @@
 }
 
 static Result<void> CallVdc(const std::string& system, const std::string& cmd) {
+    LOG(INFO) << "Calling /system/bin/vdc " << system << " " << cmd;
     const char* vdc_argv[] = {"/system/bin/vdc", system.c_str(), cmd.c_str()};
     int status;
     if (logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true,
@@ -456,10 +457,14 @@
 #define ZRAM_RESET    "/sys/block/zram0/reset"
 #define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
 static Result<void> KillZramBackingDevice() {
+    if (access(ZRAM_BACK_DEV, F_OK) != 0 && errno == ENOENT) {
+        LOG(INFO) << "No zram backing device configured";
+        return {};
+    }
     std::string backing_dev;
-    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return {};
-
-    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return {};
+    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) {
+        return ErrnoError() << "Failed to read " << ZRAM_BACK_DEV;
+    }
 
     // cut the last "\n"
     backing_dev.erase(backing_dev.length() - 1);
@@ -478,6 +483,11 @@
                        << " failed";
     }
 
+    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) {
+        LOG(INFO) << backing_dev << " is not a loop device. Exiting early";
+        return {};
+    }
+
     // clear loopback device
     unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
     if (loop.get() < 0) {
@@ -785,7 +795,7 @@
     }
     auto sigterm_timeout = GetMillisProperty("init.userspace_reboot.sigterm.timeoutmillis", 5s);
     auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
-    LOG(INFO) << "Timeout to terminate services : " << sigterm_timeout.count() << "ms"
+    LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
               << "Timeout to kill services: " << sigkill_timeout.count() << "ms";
     StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
     if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index f3be0b8..e2bc297 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -16,22 +16,8 @@
 
 #pragma once
 
-#include <sys/types.h>
-
-/* deal with possible sys/cdefs.h conflict with fcntl.h */
-#ifdef __unused
-#define __unused_defined __unused
-#undef __unused
-#endif
-
-#include <fcntl.h> /* Pick up O_* macros */
-
-/* restore definitions from above */
-#ifdef __unused_defined
-#define __unused __attribute__((__unused__))
-#endif
-
 #include <stdint.h>
+#include <sys/types.h>
 
 #include <log/log_id.h>
 #include <log/log_time.h>
@@ -40,6 +26,8 @@
 extern "C" {
 #endif
 
+#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
+
 /*
  * Native log reading interface section. See logcat for sample code.
  *
@@ -114,13 +102,10 @@
                                       char* buf, size_t len);
 int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
 
-#ifndef O_NONBLOCK
+/* The below values are used for the `mode` argument of the below functions. */
+/* Note that 0x00000003 were previously used and should be considered reserved. */
 #define ANDROID_LOG_NONBLOCK 0x00000800
-#else
-#define ANDROID_LOG_NONBLOCK O_NONBLOCK
-#endif
 #define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
-#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
 #define ANDROID_LOG_PSTORE 0x80000000
 
 struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index a79beec..ab4adc4 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -3,6 +3,9 @@
 #ifndef _LIBS_LOG_LOG_H
 #define _LIBS_LOG_LOG_H
 
+/* Historically vendors have depended on this header being included. */
+#include <fcntl.h>
+
 #include <android/log.h>
 #include <log/log_id.h>
 #include <log/log_main.h>
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index c174b85..d15b367 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -330,7 +330,7 @@
   ErrnoRestorer errno_restorer;
 
   if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
-    return 0;
+    return -EPERM;
   }
 
   __android_log_message log_message = {
@@ -343,7 +343,7 @@
   ErrnoRestorer errno_restorer;
 
   if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
-    return 0;
+    return -EPERM;
   }
 
   __attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
@@ -360,7 +360,7 @@
   ErrnoRestorer errno_restorer;
 
   if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
-    return 0;
+    return -EPERM;
   }
 
   va_list ap;
@@ -380,7 +380,7 @@
   ErrnoRestorer errno_restorer;
 
   if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
-    return 0;
+    return -EPERM;
   }
 
   va_list ap;
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 9afc9a3..57c4c2e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -57,6 +57,7 @@
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "MemoryMte.cpp",
         "LocalUnwinder.cpp",
         "Regs.cpp",
         "RegsArm.cpp",
@@ -101,6 +102,16 @@
         "liblog",
         "liblzma",
     ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
 
 cc_library {
@@ -213,6 +224,7 @@
         "tests/MemoryRangesTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
+        "tests/MemoryMteTest.cpp",
         "tests/RegsInfoTest.cpp",
         "tests/RegsIterateTest.cpp",
         "tests/RegsStepIfSignalHandlerTest.cpp",
@@ -268,6 +280,16 @@
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
 
 cc_test {
@@ -373,12 +395,26 @@
 
     srcs: [
         "benchmarks/unwind_benchmarks.cpp",
+        "benchmarks/SymbolBenchmark.cpp",
+    ],
+
+    data: [
+        "benchmarks/files/*",
     ],
 
     shared_libs: [
         "libbase",
         "libunwindstack",
     ],
+
+    target: {
+        android: {
+            static_libs: [
+                "libmeminfo",
+                "libprocinfo",
+            ],
+        },
+    },
 }
 
 // Generates the elf data for use in the tests for .gnu_debugdata frames.
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 8de3d98..fac9085 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -324,6 +324,16 @@
   return ProcessVmRead(getpid(), addr, dst, size);
 }
 
+#if !defined(ANDROID_EXPERIMENTAL_MTE)
+long MemoryRemote::ReadTag(uint64_t) {
+  return -1;
+}
+
+long MemoryLocal::ReadTag(uint64_t) {
+  return -1;
+}
+#endif
+
 MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
                          uint64_t offset)
     : memory_(memory), begin_(begin), length_(length), offset_(offset) {}
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
index 769d907..d97640d 100644
--- a/libunwindstack/MemoryCache.h
+++ b/libunwindstack/MemoryCache.h
@@ -33,6 +33,7 @@
   virtual ~MemoryCache() = default;
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override { return impl_->ReadTag(addr); }
 
   void Clear() override { cache_.clear(); }
 
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
index 7e027cf..741f107 100644
--- a/libunwindstack/MemoryLocal.h
+++ b/libunwindstack/MemoryLocal.h
@@ -31,6 +31,7 @@
   bool IsLocal() const override { return true; }
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override;
 };
 
 }  // namespace unwindstack
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
new file mode 100644
index 0000000..d1d0ebc
--- /dev/null
+++ b/libunwindstack/MemoryMte.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/ptrace.h>
+
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+
+namespace unwindstack {
+
+long MemoryRemote::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+  return ptrace(PTRACE_PEEKTAG, pid_, (void*)addr, nullptr);
+#else
+  (void)addr;
+  return -1;
+#endif
+}
+
+long MemoryLocal::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+  // Check that the memory is readable first. This is racy with the ldg but there's not much
+  // we can do about it.
+  char data;
+  if (!mte_supported() || !Read(addr, &data, 1)) {
+    return -1;
+  }
+
+  __asm__ __volatile__(".arch_extension mte; ldg %0, [%0]" : "+r"(addr) : : "memory");
+  return (addr >> 56) & 0xf;
+#else
+  (void)addr;
+  return -1;
+#endif
+}
+
+}  // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
index db367d6..dd09c88 100644
--- a/libunwindstack/MemoryRemote.h
+++ b/libunwindstack/MemoryRemote.h
@@ -32,6 +32,7 @@
   virtual ~MemoryRemote() = default;
 
   size_t Read(uint64_t addr, void* dst, size_t size) override;
+  long ReadTag(uint64_t addr) override;
 
   pid_t pid() { return pid_; }
 
diff --git a/libunwindstack/benchmarks/SymbolBenchmark.cpp b/libunwindstack/benchmarks/SymbolBenchmark.cpp
new file mode 100644
index 0000000..a850ff0
--- /dev/null
+++ b/libunwindstack/benchmarks/SymbolBenchmark.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <err.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+static void Gather(uint64_t* rss_bytes) {
+  android::meminfo::ProcMemInfo proc_mem(getpid());
+  const std::vector<android::meminfo::Vma>& maps = proc_mem.MapsWithoutUsageStats();
+  for (auto& vma : maps) {
+    if (vma.name == "[anon:libc_malloc]" || android::base::StartsWith(vma.name, "[anon:scudo:") ||
+        android::base::StartsWith(vma.name, "[anon:GWP-ASan")) {
+      android::meminfo::Vma update_vma(vma);
+      if (!proc_mem.FillInVmaStats(update_vma)) {
+        err(1, "FillInVmaStats failed\n");
+      }
+      *rss_bytes += update_vma.usage.rss;
+    }
+  }
+}
+#endif
+
+static void BenchmarkSymbolLookup(benchmark::State& state, std::vector<uint64_t> offsets,
+                                  std::string elf_file, bool expect_found) {
+#if defined(__BIONIC__)
+  uint64_t rss_bytes = 0;
+#endif
+  uint64_t alloc_bytes = 0;
+  for (auto _ : state) {
+    state.PauseTiming();
+    unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(elf_file, 0).release());
+    if (!elf.Init() || !elf.valid()) {
+      errx(1, "Internal Error: Cannot open elf.");
+    }
+
+#if defined(__BIONIC__)
+    mallopt(M_PURGE, 0);
+    uint64_t rss_bytes_before = 0;
+    Gather(&rss_bytes_before);
+#endif
+    uint64_t alloc_bytes_before = mallinfo().uordblks;
+    state.ResumeTiming();
+
+    for (auto pc : offsets) {
+      std::string name;
+      uint64_t offset;
+      bool found = elf.GetFunctionName(pc, &name, &offset);
+      if (expect_found && !found) {
+        errx(1, "expected pc 0x%" PRIx64 " present, but not found.", pc);
+      } else if (!expect_found && found) {
+        errx(1, "expected pc 0x%" PRIx64 " not present, but found.", pc);
+      }
+    }
+
+    state.PauseTiming();
+#if defined(__BIONIC__)
+    mallopt(M_PURGE, 0);
+#endif
+    alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
+#if defined(__BIONIC__)
+    Gather(&rss_bytes);
+    rss_bytes -= rss_bytes_before;
+#endif
+    state.ResumeTiming();
+  }
+
+#if defined(__BIONIC__)
+  state.counters["RSS_BYTES"] = rss_bytes / static_cast<double>(state.iterations());
+#endif
+  state.counters["ALLOCATED_BYTES"] = alloc_bytes / static_cast<double>(state.iterations());
+}
+
+static void BenchmarkSymbolLookup(benchmark::State& state, uint64_t pc, std::string elf_file,
+                                  bool expect_found) {
+  BenchmarkSymbolLookup(state, std::vector<uint64_t>{pc}, elf_file, expect_found);
+}
+
+std::string GetElfFile() {
+  return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
+}
+
+std::string GetSortedElfFile() {
+  return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
+}
+
+void BM_symbol_not_present(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, 0, GetElfFile(), false);
+}
+BENCHMARK(BM_symbol_not_present);
+
+void BM_symbol_find_single(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, 0x22b2bc, GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single);
+
+void BM_symbol_find_single_many_times(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x22b2bc), GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_many_times);
+
+void BM_symbol_find_multiple(benchmark::State& state) {
+  BenchmarkSymbolLookup(state,
+                        std::vector<uint64_t>{0x22b2bc, 0xd5d30, 0x1312e8, 0x13582e, 0x1389c8},
+                        GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_multiple);
+
+void BM_symbol_not_present_from_sorted(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, 0, GetSortedElfFile(), false);
+}
+BENCHMARK(BM_symbol_not_present_from_sorted);
+
+void BM_symbol_find_single_from_sorted(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, 0x138638, GetSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_from_sorted);
+
+void BM_symbol_find_single_many_times_from_sorted(benchmark::State& state) {
+  BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x138638), GetSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_many_times_from_sorted);
+
+void BM_symbol_find_multiple_from_sorted(benchmark::State& state) {
+  BenchmarkSymbolLookup(state,
+                        std::vector<uint64_t>{0x138638, 0x84350, 0x14df18, 0x1f3a38, 0x1f3ca8},
+                        GetSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_multiple_from_sorted);
diff --git a/libunwindstack/benchmarks/files/boot_arm.oat b/libunwindstack/benchmarks/files/boot_arm.oat
new file mode 100644
index 0000000..51188eb
--- /dev/null
+++ b/libunwindstack/benchmarks/files/boot_arm.oat
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libart_arm.so b/libunwindstack/benchmarks/files/libart_arm.so
new file mode 100644
index 0000000..2201faf
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libart_arm.so
Binary files differ
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index ecd908a..26252b6 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -44,6 +44,7 @@
   virtual bool IsLocal() const { return false; }
 
   virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
+  virtual long ReadTag(uint64_t) { return -1; }
 
   bool ReadFully(uint64_t addr, void* dst, size_t size);
 
diff --git a/libunwindstack/tests/MemoryMteTest.cpp b/libunwindstack/tests/MemoryMteTest.cpp
new file mode 100644
index 0000000..3ae322e
--- /dev/null
+++ b/libunwindstack/tests/MemoryMteTest.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <gtest/gtest.h>
+
+#include <bionic/mte.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static uintptr_t CreateTagMapping() {
+  uintptr_t mapping =
+      reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
+                                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+  if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
+    return 0;
+  }
+#if defined(__aarch64__)
+  __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
+                       :
+                       : "r"(mapping + (1ULL << 56))
+                       : "memory");
+#endif
+  return mapping;
+}
+
+TEST(MemoryMteTest, remote_read_tag) {
+#if !defined(__aarch64__)
+  GTEST_SKIP() << "Requires aarch64";
+#else
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+#endif
+
+  uintptr_t mapping = CreateTagMapping();
+  ASSERT_NE(0U, mapping);
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(TestAttach(pid));
+
+  MemoryRemote remote(pid);
+
+  EXPECT_EQ(1, remote.ReadTag(mapping));
+  EXPECT_EQ(0, remote.ReadTag(mapping + 16));
+
+  ASSERT_TRUE(TestDetach(pid));
+}
+
+TEST(MemoryMteTest, local_read_tag) {
+#if !defined(__aarch64__)
+  GTEST_SKIP() << "Requires aarch64";
+#else
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+#endif
+
+  uintptr_t mapping = CreateTagMapping();
+  ASSERT_NE(0U, mapping);
+
+  MemoryLocal local;
+
+  EXPECT_EQ(1, local.ReadTag(mapping));
+  EXPECT_EQ(0, local.ReadTag(mapping + 16));
+}
+
+}  // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index c90dedc..385078d 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -26,8 +26,10 @@
 
 #include <vector>
 
-#include <android-base/test_utils.h>
 #include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
 #include <gtest/gtest.h>
 
 #include "MemoryRemote.h"
@@ -37,24 +39,7 @@
 
 namespace unwindstack {
 
-class MemoryRemoteTest : public ::testing::Test {
- protected:
-  static bool Attach(pid_t pid) {
-    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
-      return false;
-    }
-
-    return TestQuiescePid(pid);
-  }
-
-  static bool Detach(pid_t pid) {
-    return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
-  }
-
-  static constexpr size_t NS_PER_SEC = 1000000000ULL;
-};
-
-TEST_F(MemoryRemoteTest, read) {
+TEST(MemoryRemoteTest, read) {
   std::vector<uint8_t> src(1024);
   memset(src.data(), 0x4c, 1024);
 
@@ -66,7 +51,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -76,10 +61,10 @@
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_large) {
+TEST(MemoryRemoteTest, read_large) {
   static constexpr size_t kTotalPages = 245;
   std::vector<uint8_t> src(kTotalPages * getpagesize());
   for (size_t i = 0; i < kTotalPages; i++) {
@@ -95,7 +80,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -105,10 +90,10 @@
     ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_partial) {
+TEST(MemoryRemoteTest, read_partial) {
   char* mapping = static_cast<char*>(
       mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
   ASSERT_NE(MAP_FAILED, mapping);
@@ -128,7 +113,7 @@
   // Unmap from our process.
   ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -149,10 +134,10 @@
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_fail) {
+TEST(MemoryRemoteTest, read_fail) {
   int pagesize = getpagesize();
   void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
   memset(src, 0x4c, pagesize * 2);
@@ -169,7 +154,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -188,10 +173,10 @@
 
   ASSERT_EQ(0, munmap(src, pagesize));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_overflow) {
+TEST(MemoryRemoteTest, read_overflow) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     while (true)
@@ -201,7 +186,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -209,10 +194,10 @@
   std::vector<uint8_t> dst(200);
   ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_illegal) {
+TEST(MemoryRemoteTest, read_illegal) {
   pid_t pid;
   if ((pid = fork()) == 0) {
     while (true);
@@ -221,7 +206,7 @@
   ASSERT_LT(0, pid);
   TestScopedPidReaper reap(pid);
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
 
@@ -229,10 +214,10 @@
   ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
   ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
 
-  ASSERT_TRUE(Detach(pid));
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_mprotect_hole) {
+TEST(MemoryRemoteTest, read_mprotect_hole) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -250,7 +235,7 @@
 
   ASSERT_EQ(0, munmap(mapping, 3 * page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
   std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -263,9 +248,11 @@
   for (size_t i = read_size; i < dst.size(); ++i) {
     ASSERT_EQ(0xCC, dst[i]);
   }
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
-TEST_F(MemoryRemoteTest, read_munmap_hole) {
+TEST(MemoryRemoteTest, read_munmap_hole) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -285,7 +272,7 @@
   ASSERT_EQ(0, munmap(mapping, page_size));
   ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   MemoryRemote remote(pid);
   std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -297,11 +284,13 @@
   for (size_t i = read_size; i < dst.size(); ++i) {
     ASSERT_EQ(0xCC, dst[i]);
   }
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
 // Verify that the memory remote object chooses a memory read function
 // properly. Either process_vm_readv or ptrace.
-TEST_F(MemoryRemoteTest, read_choose_correctly) {
+TEST(MemoryRemoteTest, read_choose_correctly) {
   size_t page_size = getpagesize();
   void* mapping =
       mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -320,7 +309,7 @@
 
   ASSERT_EQ(0, munmap(mapping, 2 * page_size));
 
-  ASSERT_TRUE(Attach(pid));
+  ASSERT_TRUE(TestAttach(pid));
 
   // We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
   // Read from the PROT_NONE area first to force the choice of ptrace.
@@ -348,6 +337,8 @@
   bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
   ASSERT_EQ(sizeof(value), bytes);
   ASSERT_EQ(0xfcfcfcfcU, value);
+
+  ASSERT_TRUE(TestDetach(pid));
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index a4d7b9b..0685006 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -21,6 +21,7 @@
 #include <sys/ptrace.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 namespace unwindstack {
 
@@ -50,6 +51,18 @@
   return ready;
 }
 
+inline bool TestAttach(pid_t pid) {
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  return TestQuiescePid(pid);
+}
+
+inline bool TestDetach(pid_t pid) {
+  return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
 void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
 
 }  // namespace unwindstack
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 3d51de9..005d697 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -48,9 +48,10 @@
   // Modification time. The zipfile format specifies
   // that the first two little endian bytes contain the time
   // and the last two little endian bytes contain the date.
-  // See `GetModificationTime`.
+  // See `GetModificationTime`. Use signed integer to avoid the
+  // sub-overflow.
   // TODO: should be overridden by extra time field, if present.
-  uint32_t mod_time;
+  int32_t mod_time;
 
   // Returns `mod_time` as a broken-down struct tm.
   struct tm GetModificationTime() const;
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 7bf2120..014f881 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -1570,6 +1570,7 @@
   return true;
 }
 
+// This function returns the embedded timestamp as is; and doesn't perform validations.
 tm ZipEntryCommon::GetModificationTime() const {
   tm t = {};
 
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 3a1d36f..56a670a 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -145,6 +145,8 @@
 # libcore failure logging
 90100 exp_det_cert_pin_failure (certs|4)
 
+# 150000 - 160000 reserved for Android Automotive builds
+
 1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
 
 # for events that go to stats log buffer