Merge "Add fdsan capabilities for native handles"
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 354d02a..57762e6 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -100,13 +100,12 @@
     return false;
 }
 
-bool fs_mgr_overlayfs_setup(const char*, bool* change, bool) {
-    if (change) *change = false;
+bool fs_mgr_overlayfs_setup(const char*, bool*, bool) {
+    LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
     return false;
 }
 
-bool fs_mgr_overlayfs_teardown(const char*, bool* change) {
-    if (change) *change = false;
+bool fs_mgr_overlayfs_teardown(const char*, bool*) {
     return false;
 }
 
@@ -372,77 +371,97 @@
 
 constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
 
-bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
-    auto ret = true;
-    auto top = dir + kOverlayTopDir;
-    if (setfscreatecon(kOverlayfsFileContext)) {
-        ret = false;
-        PERROR << "setfscreatecon " << kOverlayfsFileContext;
-    }
-    auto save_errno = errno;
-    if (!mkdir(top.c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        ret = false;
-        PERROR << "mkdir " << top;
-    } else {
-        errno = save_errno;
-    }
-    setfscreatecon(nullptr);
+class AutoSetFsCreateCon final {
+  public:
+    AutoSetFsCreateCon() {}
+    AutoSetFsCreateCon(const std::string& context) { Set(context); }
+    ~AutoSetFsCreateCon() { Restore(); }
 
-    if (overlay) *overlay = std::move(top);
-    return ret;
+    bool Ok() const { return ok_; }
+    bool Set(const std::string& context) {
+        if (setfscreatecon(context.c_str())) {
+            PLOG(ERROR) << "setfscreatecon " << context;
+            return false;
+        }
+        ok_ = true;
+        return true;
+    }
+    bool Restore() {
+        if (restored_ || !ok_) {
+            return true;
+        }
+        if (setfscreatecon(nullptr)) {
+            PLOG(ERROR) << "setfscreatecon null";
+            return false;
+        }
+        restored_ = true;
+        return true;
+    }
+
+  private:
+    bool ok_ = false;
+    bool restored_ = false;
+};
+
+std::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {
+    auto top = dir + kOverlayTopDir;
+
+    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+    if (!createcon.Ok()) {
+        return {};
+    }
+    if (mkdir(top.c_str(), 0755) != 0 && errno != EEXIST) {
+        PERROR << "mkdir " << top;
+        return {};
+    }
+    if (!createcon.Restore()) {
+        return {};
+    }
+    return top;
 }
 
 bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
-                                bool* change) {
-    auto ret = true;
-    if (fs_mgr_overlayfs_already_mounted(mount_point)) return ret;
+                                bool* want_reboot) {
+    if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+        return true;
+    }
     auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
 
-    if (setfscreatecon(kOverlayfsFileContext)) {
-        ret = false;
-        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+    if (!createcon.Ok()) {
+        return false;
     }
-    auto save_errno = errno;
-    if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        ret = false;
+    if (mkdir(fsrec_mount_point.c_str(), 0755) != 0 && errno != EEXIST) {
         PERROR << "mkdir " << fsrec_mount_point;
-    } else {
-        errno = save_errno;
+        return false;
+    }
+    if (mkdir((fsrec_mount_point + kWorkName).c_str(), 0755) != 0 && errno != EEXIST) {
+        PERROR << "mkdir " << fsrec_mount_point << kWorkName;
+        return false;
+    }
+    if (!createcon.Restore()) {
+        return false;
     }
 
-    save_errno = errno;
-    if (!mkdir((fsrec_mount_point + kWorkName).c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        ret = false;
-        PERROR << "mkdir " << fsrec_mount_point << kWorkName;
-    } else {
-        errno = save_errno;
-    }
-    setfscreatecon(nullptr);
+    createcon = {};
 
     auto new_context = fs_mgr_get_context(mount_point);
-    if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
-        ret = false;
-        PERROR << "setfscreatecon " << new_context;
+    if (new_context.empty() || !createcon.Set(new_context)) {
+        return false;
     }
-    auto upper = fsrec_mount_point + kUpperName;
-    save_errno = errno;
-    if (!mkdir(upper.c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        ret = false;
-        PERROR << "mkdir " << upper;
-    } else {
-        errno = save_errno;
-    }
-    if (!new_context.empty()) setfscreatecon(nullptr);
 
-    return ret;
+    auto upper = fsrec_mount_point + kUpperName;
+    if (mkdir(upper.c_str(), 0755) != 0 && errno != EEXIST) {
+        PERROR << "mkdir " << upper;
+        return false;
+    }
+    if (!createcon.Restore()) {
+        return false;
+    }
+
+    if (want_reboot) *want_reboot = true;
+
+    return true;
 }
 
 uint32_t fs_mgr_overlayfs_slot_number() {
@@ -729,21 +748,23 @@
         }
 
         // use as the bound directory in /dev.
+        AutoSetFsCreateCon createcon;
         auto new_context = fs_mgr_get_context(entry.mount_point);
-        if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
-            PERROR << "setfscreatecon " << new_context;
+        if (new_context.empty() || !createcon.Set(new_context)) {
+            continue;
         }
         move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
                                 entry.shared_flag};
         const auto target = mkdtemp(new_entry.dir.data());
+        if (!createcon.Restore()) {
+            return false;
+        }
         if (!target) {
             retval = false;
             save_errno = errno;
             PERROR << "temporary directory for MS_BIND";
-            setfscreatecon(nullptr);
             continue;
         }
-        setfscreatecon(nullptr);
 
         if (!parent_private && !parent_made_private) {
             parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
@@ -814,20 +835,29 @@
 bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
                                     bool readonly = false) {
     if (readonly) {
-        if (!fs_mgr_access(device_path)) return false;
-    } else {
-        if (!fs_mgr_rw_access(device_path)) return false;
+        if (!fs_mgr_access(device_path)) {
+            LOG(ERROR) << "Path does not exist: " << device_path;
+            return false;
+        }
+    } else if (!fs_mgr_rw_access(device_path)) {
+        LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
+        return false;
     }
 
     auto f2fs = fs_mgr_is_f2fs(device_path);
     auto ext4 = fs_mgr_is_ext4(device_path);
-    if (!f2fs && !ext4) return false;
+    if (!f2fs && !ext4) {
+        LOG(ERROR) << "Scratch partition is not f2fs or ext4";
+        return false;
+    }
 
-    if (setfscreatecon(kOverlayfsFileContext)) {
-        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+    if (!createcon.Ok()) {
+        return false;
     }
     if (mkdir(kScratchMountPoint.c_str(), 0755) && (errno != EEXIST)) {
         PERROR << "create " << kScratchMountPoint;
+        return false;
     }
 
     FstabEntry entry;
@@ -859,7 +889,6 @@
     if (fs_mgr_overlayfs_already_mounted("/data", false)) {
         entry.fs_mgr_flags.check = true;
     }
-    auto save_errno = errno;
     if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
     if (!mounted) {
         if ((entry.fs_type == "f2fs") && ext4) {
@@ -869,12 +898,15 @@
             entry.fs_type = "f2fs";
             mounted = fs_mgr_do_mount_one(entry) == 0;
         }
-        if (!mounted) save_errno = errno;
     }
-    setfscreatecon(nullptr);
-    if (!mounted) rmdir(kScratchMountPoint.c_str());
-    errno = save_errno;
-    return mounted;
+    if (!createcon.Restore()) {
+        return false;
+    }
+    if (!mounted) {
+        rmdir(kScratchMountPoint.c_str());
+        return false;
+    }
+    return true;
 }
 
 const std::string kMkF2fs("/system/bin/make_f2fs");
@@ -962,7 +994,6 @@
     } else if (mnt_type == "ext4") {
         command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
     } else {
-        errno = ESRCH;
         LERROR << mnt_type << " has no mkfs cookbook";
         return false;
     }
@@ -995,8 +1026,7 @@
 }
 
 // Create or update a scratch partition within super.
-static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists,
-                                 bool* change) {
+static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists) {
     const auto partition_name = android::base::Basename(kScratchMountPoint);
 
     auto& dm = DeviceMapper::Instance();
@@ -1069,8 +1099,6 @@
             LERROR << "add partition " << partition_name;
             return false;
         }
-
-        if (change) *change = true;
     }
 
     if (changed || partition_create) {
@@ -1084,8 +1112,6 @@
         if (!CreateLogicalPartition(params, scratch_device)) {
             return false;
         }
-
-        if (change) *change = true;
     } else if (scratch_device->empty()) {
         *scratch_device = GetBootScratchDevice();
     }
@@ -1115,9 +1141,8 @@
     return ideal_size;
 }
 
-static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
+static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists) {
     *partition_exists = false;
-    if (change) *change = false;
 
     auto images = IImageManager::Open("remount", 10s);
     if (!images) {
@@ -1130,8 +1155,6 @@
         return true;
     }
 
-    if (change) *change = true;
-
     // Note: calling RemoveDisabledImages here ensures that we do not race with
     // clean_scratch_files and accidentally try to map an image that will be
     // deleted.
@@ -1173,12 +1196,11 @@
 }
 
 bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
-                                     bool* partition_exists, bool* change) {
+                                     bool* partition_exists) {
     // Use the DSU scratch device managed by gsid if within a DSU system.
     if (fs_mgr_is_dsu_running()) {
         *scratch_device = GetDsuScratchDevice();
         *partition_exists = !scratch_device->empty();
-        *change = false;
         return *partition_exists;
     }
 
@@ -1194,22 +1216,24 @@
     if (CanUseSuperPartition(fstab, &is_virtual_ab)) {
         bool can_use_data = false;
         if (is_virtual_ab && FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
-            return CreateScratchOnData(scratch_device, partition_exists, change);
+            return CreateScratchOnData(scratch_device, partition_exists);
         }
-        return CreateDynamicScratch(scratch_device, partition_exists, change);
+        return CreateDynamicScratch(scratch_device, partition_exists);
     }
 
-    errno = ENXIO;
     return false;
 }
 
 // Create and mount kScratchMountPoint storage if we have logical partitions
-bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab, bool* change) {
-    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab) {
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+        return true;
+    }
 
     std::string scratch_device;
     bool partition_exists;
-    if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists, change)) {
+    if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists)) {
+        LOG(ERROR) << "Failed to create scratch partition";
         return false;
     }
 
@@ -1217,22 +1241,19 @@
     auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
     if (partition_exists) {
         if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
-            if (!fs_mgr_access(kScratchMountPoint + kOverlayTopDir) &&
-                !fs_mgr_filesystem_has_space(kScratchMountPoint)) {
-                // declare it useless, no overrides and no free space
-                fs_mgr_overlayfs_umount_scratch();
-            } else {
-                if (change) *change = true;
+            if (fs_mgr_access(kScratchMountPoint + kOverlayTopDir) ||
+                fs_mgr_filesystem_has_space(kScratchMountPoint)) {
                 return true;
             }
+            // declare it useless, no overrides and no free space
+            fs_mgr_overlayfs_umount_scratch();
         }
-        // partition existed, but was not initialized; fall through to make it.
-        errno = 0;
     }
 
-    if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) return false;
-
-    if (change) *change = true;
+    if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) {
+        LOG(ERROR) << "Failed to format scratch partition";
+        return false;
+    }
 
     return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
 }
@@ -1355,24 +1376,23 @@
     return ret;
 }
 
-// Returns false if setup not permitted, errno set to last error.
-// If something is altered, set *change.
-bool fs_mgr_overlayfs_setup(const char* mount_point, bool* change, bool force) {
-    if (change) *change = false;
-    auto ret = false;
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
-    if (!fs_mgr_boot_completed()) {
-        errno = EBUSY;
-        PERROR << "setup";
-        return ret;
-    }
-
-    auto save_errno = errno;
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
+bool fs_mgr_overlayfs_setup(const char* mount_point, bool* want_reboot, bool just_disabled_verity) {
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+        LOG(ERROR) << "Overlayfs is not supported";
         return false;
     }
-    errno = save_errno;
+
+    if (!fs_mgr_boot_completed()) {
+        LOG(ERROR) << "Cannot setup overlayfs before persistent properties are ready";
+        return false;
+    }
+
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        LOG(ERROR) << "Could not read fstab";
+        return false;
+    }
+
     auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
     for (auto it = candidates.begin(); it != candidates.end();) {
         if (mount_point &&
@@ -1380,9 +1400,8 @@
             it = candidates.erase(it);
             continue;
         }
-        save_errno = errno;
-        auto verity_enabled = !force && fs_mgr_is_verity_enabled(*it);
-        if (errno == ENOENT || errno == ENXIO) errno = save_errno;
+
+        auto verity_enabled = !just_disabled_verity && fs_mgr_is_verity_enabled(*it);
         if (verity_enabled) {
             it = candidates.erase(it);
             continue;
@@ -1390,12 +1409,20 @@
         ++it;
     }
 
-    if (candidates.empty()) return ret;
+    if (candidates.empty()) {
+        if (mount_point) {
+            LOG(ERROR) << "No overlayfs candidate was found for " << mount_point;
+            return false;
+        }
+        return true;
+    }
 
     std::string dir;
     for (const auto& overlay_mount_point : OverlayMountPoints()) {
         if (overlay_mount_point == kScratchMountPoint) {
-            if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
+            if (!fs_mgr_overlayfs_setup_scratch(fstab)) {
+                continue;
+            }
         } else {
             if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
                 continue;
@@ -1405,17 +1432,21 @@
         break;
     }
     if (dir.empty()) {
-        if (change && *change) errno = ESRCH;
-        if (errno == EPERM) errno = save_errno;
-        return ret;
+        LOG(ERROR) << "Could not allocate backing storage for overlays";
+        return false;
     }
 
-    std::string overlay;
-    ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
-    for (const auto& entry : candidates) {
-        ret |= fs_mgr_overlayfs_setup_one(overlay, fs_mgr_mount_point(entry.mount_point), change);
+    const auto overlay = fs_mgr_overlayfs_setup_dir(dir);
+    if (overlay.empty()) {
+        return false;
     }
-    return ret;
+
+    bool ok = true;
+    for (const auto& entry : candidates) {
+        auto fstab_mount_point = fs_mgr_mount_point(entry.mount_point);
+        ok &= fs_mgr_overlayfs_setup_one(overlay, fstab_mount_point, want_reboot);
+    }
+    return ok;
 }
 
 struct MapInfo {
@@ -1736,6 +1767,7 @@
 std::string fs_mgr_get_context(const std::string& mount_point) {
     char* ctx = nullptr;
     if (getfilecon(mount_point.c_str(), &ctx) == -1) {
+        PLOG(ERROR) << "getfilecon " << mount_point;
         return "";
     }
 
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 4a927d0..2202fda 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -317,15 +317,15 @@
         }
 
         if (fs_mgr_wants_overlayfs(&entry)) {
-            bool change = false;
+            bool want_reboot = false;
             bool force = result->disabled_verity;
-            if (!fs_mgr_overlayfs_setup(mount_point.c_str(), &change, force)) {
+            if (!fs_mgr_overlayfs_setup(mount_point.c_str(), &want_reboot, force)) {
                 LOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
                 status = BAD_OVERLAY;
                 it = partitions->erase(it);
                 continue;
             }
-            if (change) {
+            if (want_reboot) {
                 LOG(INFO) << "Using overlayfs for " << mount_point;
                 result->reboot_later = true;
                 result->setup_overlayfs = true;
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index ec1d78f..590f66b 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -28,14 +28,20 @@
 
 bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
 bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
-bool fs_mgr_overlayfs_setup(const char* mount_point = nullptr, bool* change = nullptr,
-                            bool force = true);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
 bool fs_mgr_overlayfs_is_setup();
 bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
 bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
 std::string fs_mgr_get_context(const std::string& mount_point);
 
+// If "mount_point" is non-null, set up exactly one overlay.
+// If "mount_point" is null, setup any overlays.
+//
+// If |want_reboot| is non-null, and a reboot is needed to apply overlays, then
+// it will be true on return. The caller is responsible for initializing it.
+bool fs_mgr_overlayfs_setup(const char* mount_point = nullptr, bool* want_reboot = nullptr,
+                            bool just_disabled_verity = true);
+
 enum class OverlayfsValidResult {
     kNotSupported = 0,
     kOk,
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index a1e94dd..a6bdd6c 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -50,13 +50,15 @@
 ESCAPE="`echo | tr '\n' '\033'`"
 # A _real_ embedded carriage return character
 CR="`echo | tr '\n' '\r'`"
-GREEN="${ESCAPE}[32m"
-RED="${ESCAPE}[31m"
-YELLOW="${ESCAPE}[33m"
-BLUE="${ESCAPE}[34m"
-NORMAL="${ESCAPE}[0m"
-TMPDIR=${TMPDIR:-/tmp}
-print_time=false
+RED=
+GREEN=
+YELLOW=
+BLUE=
+NORMAL=
+color=false
+# Assume support color if stdout is terminal.
+[ -t 1 ] && color=true
+print_time=true
 start_time=`date +%s`
 ACTIVE_SLOT=
 OVERLAYFS_BACKING="cache mnt/scratch"
@@ -71,6 +73,9 @@
 
 [ "USAGE: LOG [RUN|OK|PASSED|WARNING|ERROR|FAILED|INFO] [message]..." ]
 LOG() {
+  if ${print_time}; then
+    echo -n "$(date '+%m-%d %T') "
+  fi >&2
   case "${1}" in
     R*)
       shift
@@ -192,7 +197,7 @@
 
 Returns: worrisome avc violations" ]
 avc_check() {
-  if ! ${overlayfs_supported:-false}; then
+  if ! ${overlayfs_needed:-false}; then
     return
   fi
   local L=`adb_logcat -b all -v brief -d \
@@ -231,17 +236,6 @@
     return ${ret}
 }
 
-[ "USAGE: adb_ls <dirfile> >stdout
-
-Returns: filename or directoru content to stdout with carriage returns skipped,
-         true if the ls had no errors" ]
-adb_ls() {
-    local OUTPUT="`adb_sh ls ${1} </dev/null 2>/dev/null`"
-    local ret=${?}
-    echo "${OUTPUT}" | tr -d '\r'
-    return ${ret}
-}
-
 [ "USAGE: adb_test <expression>
 
 Returns: exit status of the test expression" ]
@@ -256,6 +250,7 @@
   avc_check
   adb reboot remount-test </dev/null || true
   sleep 2
+  adb_wait "${ADB_WAIT}"
 }
 
 [ "USAGE: format_duration [<seconds>|<seconds>s|<minutes>m|<hours>h|<days>d]
@@ -599,22 +594,13 @@
 
 [ "USAGE: restore
 
-Do nothing: should be redefined when necessary.  Called after cleanup.
+Do nothing: should be redefined when necessary.
 
 Returns: reverses configurations" ]
 restore() {
   true
 }
 
-[ "USAGE: cleanup
-
-Do nothing: should be redefined when necessary
-
-Returns: cleans up any latent resources" ]
-cleanup() {
-  true
-}
-
 [ "USAGE: test_duration >/dev/stderr
 
 Prints the duration of the test
@@ -648,9 +634,6 @@
     shift 2
   fi >&2
   LOG FAILED "${@}"
-  cleanup
-  restore
-  test_duration
   exit 1
 }
 
@@ -728,42 +711,29 @@
   echo "${result}"
 }
 
-[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
+[ "USAGE: skip_administrative_mounts < /proc/mounts
 
 Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
 skip_administrative_mounts() {
   local exclude_filesystems=(
     "overlay" "tmpfs" "none" "sysfs" "proc" "selinuxfs" "debugfs" "bpf"
     "binfmt_misc" "cg2_bpf" "pstore" "tracefs" "adb" "mtp" "ptp" "devpts"
-    "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs"
+    "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs" "fuse"
   )
   local exclude_devices=(
     "\/sys\/kernel\/debug" "\/data\/media" "\/dev\/block\/loop[0-9]*"
+    "\/dev\/block\/vold\/[^ ]+"
     "${exclude_filesystems[@]}"
   )
   local exclude_mount_points=(
     "\/cache" "\/mnt\/scratch" "\/mnt\/vendor\/persist" "\/persist"
-    "\/metadata"
+    "\/metadata" "\/apex\/[^ ]+"
   )
-  if [ "data" = "${1}" ]; then
-    exclude_mount_points+=("\/data")
-  fi
   awk '$1 !~ /^('"$(join_with "|" "${exclude_devices[@]}")"')$/ &&
       $2 !~ /^('"$(join_with "|" "${exclude_mount_points[@]}")"')$/ &&
       $3 !~ /^('"$(join_with "|" "${exclude_filesystems[@]}")"')$/'
 }
 
-[ "USAGE: skip_unrelated_mounts < /proc/mounts
-
-or output from df
-
-Filters out all apex and vendor override administrative overlay mounts
-uninteresting to the test" ]
-skip_unrelated_mounts() {
-    grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
-      grep -v "[%] /\(data_mirror\|apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
-}
-
 [ "USAGE: surgically_wipe_overlayfs
 
 Surgically wipe any mounted overlayfs scratch files.
@@ -781,14 +751,17 @@
   ${wiped_anything}
 }
 
-[ "USAGE: is_overlayfs_mounted
+[ "USAGE: is_overlayfs_mounted [mountpoint]
 
-Returns: true if overlayfs is mounted" ]
+Diagnostic output of overlayfs df lines to stderr.
+
+Returns: true if overlayfs is mounted [on mountpoint]" ]
 is_overlayfs_mounted() {
   local df_output=$(adb_su df -k </dev/null)
   local df_header_line=$(echo "${df_output}" | head -1)
+  # KISS (we do not support sub-mounts for system partitions currently)
   local overlay_mounts=$(echo "${df_output}" | tail +2 |
-                         skip_unrelated_mounts |
+                         grep -vE "[%] /(apex|system|vendor)/[^ ]+$" |
                          awk '$1 == "overlay" || $6 == "/mnt/scratch"')
   if ! echo "${overlay_mounts}" | grep -q '^overlay '; then
     return 1
@@ -796,6 +769,9 @@
   ( echo "${df_header_line}"
     echo "${overlay_mounts}"
   ) >&2
+  if [ "${#}" -gt 0 ] && ! ( echo "${overlay_mounts}" | grep -qE " ${1}\$" ); then
+    return 1
+  fi >/dev/null 2>/dev/null
   return 0
 }
 
@@ -809,7 +785,7 @@
          --longoptions wait-adb:,wait-fastboot:
          --longoptions wait-screen,wait-display
          --longoptions no-wait-screen,no-wait-display
-         --longoptions gtest_print_time,print-time
+         --longoptions gtest_print_time,print-time,no-print-time
          --"
 if [ "Darwin" = "${HOSTOS}" ]; then
   GETOPTS=
@@ -824,12 +800,11 @@
                  s/--wait-adb/          /g
                  s/--wait-fastboot/               /g'`"
 fi
-OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:t" ${*}` ||
+OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:tT" ${*}` ||
   ( echo "${USAGE}" >&2 ; false ) ||
   die "getopt failure"
 set -- ${OPTIONS}
 
-color=false
 while [ ${#} -gt 0 ]; do
   case ${1} in
     -h | --help | -\?)
@@ -855,6 +830,9 @@
     -t | --print-time | --gtest_print_time)
       print_time=true
       ;;
+    -T | --no-print-time)
+      print_time=false
+      ;;
     -a | --wait-adb)
       ADB_WAIT=${2}
       shift
@@ -877,18 +855,45 @@
   esac
   shift
 done
-if ! ${color}; then
-  GREEN=""
-  RED=""
-  YELLOW=""
-  BLUE=""
-  NORMAL=""
+
+if ${color}; then
+  RED="${ESCAPE}[31m"
+  GREEN="${ESCAPE}[32m"
+  YELLOW="${ESCAPE}[33m"
+  BLUE="${ESCAPE}[34m"
+  NORMAL="${ESCAPE}[0m"
 fi
 
+TMPDIR=
+
+exit_handler() {
+  [ -n "${TMPDIR}" ] && rm -rf "${TMPDIR}"
+  local err=0
+  if ! restore; then
+    LOG ERROR "restore failed"
+    err=1
+  fi >&2
+  test_duration || true
+  if [ "${err}" != 0 ]; then
+    exit "${err}"
+  fi
+}
+trap 'exit_handler' EXIT
+
+TMPDIR=$(mktemp -d)
+
 if ${print_time}; then
   LOG INFO "start $(date)"
 fi
 
+if [ -z "${ANDROID_SERIAL}" ]; then
+  inAdb || die "no device or more than one device in adb mode"
+  D=$(adb devices | awk '$2 == "device" { print $1; exit }')
+  [ -n "${D}" ] || die "cannot get device serial"
+  ANDROID_SERIAL="${D}"
+fi
+export ANDROID_SERIAL
+
 inFastboot && die "device in fastboot mode"
 inRecovery && die "device in recovery mode"
 if ! inAdb; then
@@ -898,6 +903,9 @@
 inAdb || die "specified device not in adb mode"
 [ "1" = "$(get_property ro.debuggable)" ] || die "device not a debug build"
 [ "orange" = "$(get_property ro.boot.verifiedbootstate)" ] || die "device not bootloader unlocked"
+
+################################################################################
+# Collect characteristics of the device and report.
 can_restore_verity=true
 if [ "2" != "$(get_property partition.system.verified)" ]; then
   LOG WARNING "device might not support verity"
@@ -909,13 +917,6 @@
   enforcing=false
 fi
 
-# Do something.
-
-# Collect characteristics of the device and report.
-
-D=`get_property ro.serialno`
-[ -n "${D}" ] || D=`get_property ro.boot.serialno`
-[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
 USB_SERIAL=
 if [ -n "${ANDROID_SERIAL}" -a "Darwin" != "${HOSTOS}" ]; then
   USB_SERIAL="`find /sys/devices -name serial | grep usb || true`"
@@ -929,8 +930,8 @@
   USB_ADDRESS=${USB_SERIAL%/serial}
   USB_ADDRESS=usb${USB_ADDRESS##*/}
 fi
-[ -z "${ANDROID_SERIAL}${USB_ADDRESS}" ] ||
-  USB_DEVICE=`usb_devnum`
+USB_DEVICE=$(usb_devnum)
+[ -z "${ANDROID_SERIAL}${USB_ADDRESS}${USB_DEVICE}" ] ||
   LOG INFO "${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE}"
 BUILD_DESCRIPTION=`get_property ro.build.description`
 [ -z "${BUILD_DESCRIPTION}" ] ||
@@ -943,20 +944,32 @@
   LOG INFO "active slot is ${ACTIVE_SLOT}"
 
 # Acquire list of system partitions
+FSTAB_SUFFIXES=(
+  "$(get_property ro.boot.fstab_suffix)"
+  "$(get_property ro.boot.hardware)"
+  "$(get_property ro.boot.hardware.platform)"
+)
+FSTAB_PATTERN='\.('"$(join_with "|" "${FSTAB_SUFFIXES[@]}")"')$'
+FSTAB_FILE=$(adb_su ls -1 '/vendor/etc/fstab*' </dev/null |
+             grep -E "${FSTAB_PATTERN}" |
+             head -1)
 
 # KISS (assume system partition mount point is "/<partition name>")
-PARTITIONS=`adb_su cat /vendor/etc/fstab* </dev/null |
-              grep -v "^[#${SPACE}${TAB}]" |
-              skip_administrative_mounts |
-              awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
-              sort -u |
-              tr '\n' ' '`
-PARTITIONS="${PARTITIONS:-system vendor}"
+if [ -n "${FSTAB_FILE}" ]; then
+  PARTITIONS=$(adb_su grep -v "^[#${SPACE}${TAB}]" "${FSTAB_FILE}" |
+               skip_administrative_mounts |
+               awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
+               sort -u |
+               tr '\n' ' ')
+else
+  PARTITIONS="system vendor"
+fi
+
 # KISS (we do not support sub-mounts for system partitions currently)
-MOUNTS="`for i in ${PARTITIONS}; do
-           echo /${i}
-         done |
-         tr '\n' ' '`"
+# Ensure /system and /vendor mountpoints are in mounts list
+MOUNTS=$(for i in system vendor ${PARTITIONS}; do
+           echo "/${i}"
+         done | sort -u | tr '\n' ' ')
 LOG INFO "System Partitions list: ${PARTITIONS}"
 
 # Report existing partition sizes
@@ -982,37 +995,8 @@
       LOG INFO "partition ${name} device ${device} size ${size}K"
   done
 
-LOG RUN "Checking kernel support for overlayfs"
-
-overlayfs_supported=true
-adb_root || die "becoming root to mine kernel information"
-if ! adb_test -d /sys/module/overlay; then
-  if adb_sh grep -q "nodev${TAB}overlay" /proc/filesystems; then
-    LOG OK "overlay module present"
-  else
-    LOG WARNING "overlay module not present"
-    overlayfs_supported=false
-  fi
-fi >&2
-if ${overlayfs_supported}; then
-  if adb_test -f /sys/module/overlay/parameters/override_creds; then
-    LOG OK "overlay module supports override_creds"
-  else
-    case "$(adb_sh uname -r </dev/null)" in
-      4.[456789].* | 4.[1-9][0-9]* | [56789].*)
-        LOG WARNING "overlay module does not support override_creds"
-        overlayfs_supported=false
-        ;;
-      *)
-        LOG OK "overlay module uses caller's creds"
-        ;;
-    esac
-  fi
-fi
-
 restore() {
   LOG INFO "restoring device"
-  ${overlayfs_supported} || return 0
   inFastboot &&
     fastboot reboot &&
     adb_wait "${ADB_WAIT}" ||
@@ -1035,14 +1019,13 @@
     reboot=true
   fi >&2
   if ${reboot}; then
-    adb_reboot &&
-      adb_wait "${ADB_WAIT}"
+    adb_reboot
   fi
 }
 
 # If reboot too soon after fresh flash, could trip device update failure logic
 if ${screen_wait}; then
-  LOG WARNING "waiting for screen to come up. Consider --no-wait-screen option"
+  LOG INFO "waiting for screen to come up. Consider --no-wait-screen option"
 fi
 if ! wait_for_screen && ${screen_wait}; then
   screen_wait=false
@@ -1062,8 +1045,7 @@
 # having to go through enable-verity transition.
 if surgically_wipe_overlayfs; then
   LOG WARNING "rebooting before test"
-  adb_reboot &&
-    adb_wait ${ADB_WAIT} ||
+  adb_reboot ||
     die "lost device after reboot after overlay wipe $(usb_status)"
   adb_root ||
     die "lost device after elevation to root after wipe `usb_status`"
@@ -1072,28 +1054,25 @@
   die "overlay takeover unexpected at this phase"
 
 overlayfs_needed=true
-D=`adb_sh cat /proc/mounts </dev/null |
-   skip_administrative_mounts data`
-if echo "${D}" | grep /dev/root >/dev/null; then
-  D=`echo / /
-     echo "${D}" | grep -v /dev/root`
-fi
-D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
+D=$(adb_sh grep " ro," /proc/mounts </dev/null |
+    grep -v "^${data_device}" |
+    skip_administrative_mounts |
+    awk '{ print $1 }' |
+    sed 's|/dev/root|/|' |
+    sort -u)
 no_dedupe=true
 for d in ${D}; do
   adb_sh tune2fs -l $d </dev/null 2>&1 |
     grep "Filesystem features:.*shared_blocks" >/dev/null &&
   no_dedupe=false
 done
-D=`adb_sh df -k ${D} </dev/null |
-   sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
+D=$(adb_sh df -k ${D} </dev/null)
 echo "${D}" >&2
 if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
   overlayfs_needed=false
   # if device does not need overlays, then adb enable-verity will brick device
   can_restore_verity=false
-elif ! ${overlayfs_supported}; then
-  die "need overlayfs, but do not have it"
 fi
 LOG OK "no overlay present before setup"
 
@@ -1114,22 +1093,104 @@
 fi
 if ${overlayfs_needed}; then
   is_overlayfs_mounted ||
-    die "no overlay takeover after adb disable-verity -R"
+    die -d "no overlay takeover after adb disable-verity -R"
   LOG OK "overlay takeover after adb disable-verity -R"
 fi
 LOG OK "adb disable-verity -R"
 
+################################################################################
+LOG RUN "Checking kernel has overlayfs required patches"
 
-LOG RUN "Testing adb remount -R"
+adb_root || die "adb root"
+if adb_test -d /sys/module/overlay ||
+    adb_sh grep -q "nodev${TAB}overlay" /proc/filesystems; then
+  LOG OK "overlay module present"
+else
+  LOG INFO "overlay module not present"
+fi
+if is_overlayfs_mounted 2>/dev/null; then
+  if adb_test -f /sys/module/overlay/parameters/override_creds; then
+    LOG OK "overlay module supports override_creds"
+  else
+    case "$(adb_sh uname -r </dev/null)" in
+      4.[456789].* | 4.[1-9][0-9]* | [56789].*)
+        die "overlay module does not support override_creds"
+        ;;
+      *)
+        LOG OK "overlay module uses caller's creds"
+        ;;
+    esac
+  fi
+fi
 
+################################################################################
+# Precondition is a verity-disabled device with overlayfs already setup.
+LOG RUN "Testing raw remount commands"
+
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+  die "/system is not RO"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+  die "/vendor is not RO"
+
+T=$(adb_date)
+adb_su mount -o remount,rw /vendor ||
+  die -t "${T}" "mount -o remount,rw /vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+  die "/vendor is not RW after mount -o remount,rw"
+LOG OK "mount -o remount,rw"
+
+T=$(adb_date)
+adb_su mount -o remount,ro /vendor ||
+  die -t "${T}" "mount -o remount,ro /vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+  die "/vendor is not RO after mount -o remount,ro"
+LOG OK "mount -o remount,ro"
+
+T=$(adb_date)
+adb_su remount vendor >&2 ||
+  die -t "${T}" "adb remount vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+  die -t "${T}" "/vendor is not RW after adb remount vendor"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+  die -t "${T}" "/system is not RO after adb remount vendor"
+LOG OK "adb remount vendor"
+
+LOG INFO "Restoring device RO state and destroying overlayfs"
+T=$(adb_date)
+adb_su mount -o remount,ro /vendor ||
+  die -t "${T}" "mount -o remount,ro /vendor"
 if surgically_wipe_overlayfs; then
-  adb_reboot &&
-    adb_wait "${ADB_WAIT}" ||
+  adb_reboot ||
     die "lost device after reboot after overlay wipe $(usb_status)"
 fi
 is_overlayfs_mounted &&
   die "overlay takeover unexpected at this phase"
 
+################################################################################
+# Precondition is a verity-disabled device with overlayfs *not* setup.
+LOG RUN "Testing adb remount performs overlayfs setup from scratch"
+
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+  die "/vendor is not RO"
+T=$(adb_date)
+adb_su remount vendor >&2 ||
+  die -t "${T}" "adb remount vendor from scratch"
+if ${overlayfs_needed}; then
+  is_overlayfs_mounted /vendor ||
+    die -t "${T}" "expected overlay takeover /vendor"
+  is_overlayfs_mounted /system 2>/dev/null &&
+    die -t "${T}" "unexpected overlay takeover /system"
+fi
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+  die -t "${T}" "/vendor is not RW after adb remount vendor"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+  die -t "${T}" "/system is not RO after adb remount vendor"
+LOG OK "adb remount from scratch"
+
+################################################################################
+# Precondition is overlayfs partially setup by previous test.
+LOG RUN "Testing adb remount -R"
+
 T=$(adb_date)
 adb_su remount -R </dev/null >&2 ||
   die -t "${T}" "adb remount -R failed"
@@ -1142,150 +1203,154 @@
   die "verity not disabled after adb remount -R"
 fi
 if ${overlayfs_needed}; then
-  is_overlayfs_mounted ||
-    die "no overlay takeover after adb remount -R"
+  is_overlayfs_mounted /system ||
+    die -d "expected overlay takeover /system"
+  is_overlayfs_mounted /vendor 2>/dev/null ||
+    die -d "expected overlay takeover /vendor"
   LOG OK "overlay takeover after adb remount -R"
 fi
 LOG OK "adb remount -R"
 
+# For devices using overlayfs, remount -R should reboot after overlayfs setup.
+# For legacy device, manual reboot to ensure device clean state.
+if ! ${overlayfs_needed}; then
+  LOG WARNING "Reboot to RO (device doesn't use overlayfs)"
+  adb_reboot ||
+    die "lost device after reboot to RO $(usb_status)"
+fi
 
-LOG RUN "remount"
+################################################################################
+# Precondition is a verity-disabled device with overlayfs already setup.
+LOG RUN "Testing adb remount RW"
 
 # Feed log with selinux denials as baseline before overlays
 adb_unroot
 adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
 adb_root
 
-D=`adb remount 2>&1`
-ret=${?}
-echo "${D}" >&2
-[ ${ret} != 0 ] ||
-  [ X"${D}" = X"${D##*remount failed}" ] ||
-  die -t "${T}" "adb remount failed"
-D=`adb_sh df -k </dev/null` &&
-  H=`echo "${D}" | head -1` &&
-  D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "`
-ret=${?}
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+  die "/system is not RO"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+  die "/vendor is not RO"
+
+T=$(adb_date)
+adb remount >&2 ||
+  die -t "${T}" "adb remount"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null ||
+  die -t "${T}" "/system is not RW"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+  die -t "${T}" "/vendor is not RW"
+
+if ${overlayfs_needed}; then
+  is_overlayfs_mounted || die -t "${T}" "expected overlay takeover"
+else
+  is_overlayfs_mounted && die -t "${T}" "unexpected overlay takeover"
+fi
+
+# If scratch_partition && uses_dynamic_scratch, then scratch is on super.
+# If scratch_partition && !uses_dynamic_scratch, then scratch is super_other, system_other.
+# If !scratch_partition, then scratch is on /data via image_manager.
 uses_dynamic_scratch=false
 scratch_partition=
-virtual_ab=`get_property ro.virtual_ab.enabled`
+virtual_ab=$(get_property ro.virtual_ab.enabled)
 if ${overlayfs_needed}; then
-  if [ ${ret} != 0 ]; then
-    die -t ${T} "overlay takeover failed"
-  fi
-  echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
-   LOG WARNING "overlay takeover not complete"
-  if [ -z "${virtual_ab}" ]; then
+  M=$(adb_sh cat /proc/mounts </dev/null |
+      awk '$2 == "/mnt/scratch" { print $1, $3; exit }')
+  [ -z "${M}" ] && die "cannot find any scratch device mounted on /mnt/scratch"
+
+  scratch_device=$(echo "${M}" | awk '{ print $1 }')
+  scratch_filesystem=$(echo "${M}" | awk '{ print $2 }')
+  scratch_size=$(adb_sh df -k "${scratch_device}" </dev/null |
+                 tail +2 | head -1 | awk '{ print $2 }')
+  [ -z "${scratch_size}" ] && die "cannot get size of scratch device (${scratch_device})"
+
+  if [ -n "${virtual_ab}" ]; then
+    LOG INFO "using dynamic scratch partition on /data (VAB device)"
+  elif [[ "${scratch_device}" == /dev/block/by-name/* ]]; then
+    scratch_partition="${scratch_device##/dev/block/by-name/}"
+    LOG INFO "using physical scratch partition ${scratch_partition}"
+  else
+    uses_dynamic_scratch=true
     scratch_partition=scratch
+    LOG INFO "using dynamic scratch partition on super"
   fi
-  if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
-    LOG INFO "using ${scratch_partition} dynamic partition for overrides"
-  fi
-  M=`adb_sh cat /proc/mounts </dev/null |
-     sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
-  [ -n "${M}" ] &&
-    LOG INFO "scratch filesystem ${M}"
-  uses_dynamic_scratch=true
-  if [ "${M}" != "${M##*/dev/block/by-name/}" ]; then
-    uses_dynamic_scratch=false
-    scratch_partition="${M##*/dev/block/by-name/}"
-  fi
-  scratch_size=`adb_sh df -k /mnt/scratch </dev/null 2>/dev/null |
-                while read device kblocks used available use mounted on; do
-                  if [ "/mnt/scratch" = "\${mounted}" ]; then
-                    echo \${kblocks}
-                  fi
-                done` &&
-    [ -n "${scratch_size}" ] ||
-    die "scratch size"
-  LOG INFO "scratch size ${scratch_size}KB"
+  LOG INFO "scratch device ${scratch_device} filesystem ${scratch_filesystem} size ${scratch_size}KiB"
+
   for d in ${OVERLAYFS_BACKING}; do
     if adb_test -d /${d}/overlay/system/upper; then
       LOG INFO "/${d}/overlay is setup"
     fi
   done
 
-  ( echo "${H}" &&
-    echo "${D}"
-  ) >&2 &&
-    echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
-    die  "overlay takeover after remount"
-  !(adb_sh grep "^overlay " /proc/mounts </dev/null |
-    skip_unrelated_mounts |
-    grep " overlay ro,") ||
-    die "remount overlayfs missed a spot (ro)"
-  !(adb_sh grep -v noatime /proc/mounts </dev/null |
-    skip_administrative_mounts data |
-    skip_unrelated_mounts |
-    grep -v ' ro,') ||
+  data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
+  is_overlayfs_mounted /system 2>/dev/null ||
+    die -t "${T}" "expected overlay to takeover /system after remount"
+  # KISS (we do not support sub-mounts for system partitions currently)
+  adb_sh grep "^overlay " /proc/mounts </dev/null |
+    grep -vE "^overlay.* /(apex|system|vendor)/[^ ]" |
+    grep " overlay ro," &&
+    die "expected overlay to be RW after remount"
+  adb_sh grep -v noatime /proc/mounts </dev/null |
+    grep -v "^${data_device}" |
+    skip_administrative_mounts |
+    grep -v ' ro,' &&
     die "mounts are not noatime"
-  D=`adb_sh grep " rw," /proc/mounts </dev/null |
-     skip_administrative_mounts data`
-  if echo "${D}" | grep /dev/root >/dev/null; then
-    D=`echo / /
-       echo "${D}" | grep -v /dev/root`
-  fi
-  D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
-  bad_rw=false
+
+  D=$(adb_sh grep " rw," /proc/mounts </dev/null |
+      grep -v "^${data_device}" |
+      skip_administrative_mounts |
+      awk '{ print $1 }' |
+      sed 's|/dev/root|/|' |
+      sort -u)
+  if [ -n "${D}" ]; then
+    adb_sh df -k ${D} </dev/null |
+      sed -e 's/^Filesystem      /Filesystem (rw) /'
+  fi >&2
   for d in ${D}; do
-    if adb_sh tune2fs -l $d </dev/null 2>&1 |
-       grep "Filesystem features:.*shared_blocks" >/dev/null; then
-      bad_rw=true
-    else
-      d=`adb_sh df -k ${D} </dev/null |
-       sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
-      [ X"${d}" = X"${d##* 100[%] }" ] ||
-        bad_rw=true
+    if adb_sh tune2fs -l "${d}" </dev/null 2>&1 | grep -q "Filesystem features:.*shared_blocks" ||
+        adb_sh df -k "${d}" | grep -q " 100% "; then
+      die "remount overlayfs missed a spot (rw)"
     fi
   done
-  [ -z "${D}" ] ||
-    D=`adb_sh df -k ${D} </dev/null |
-       sed -e 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@' \
-           -e 's/^Filesystem      /Filesystem (rw) /'`
-  [ -z "${D}" ] || echo "${D}" >&2
-  ${bad_rw} && die "remount overlayfs missed a spot (rw)"
-else
-  if [ ${ret} = 0 ]; then
-    die -t ${T} "unexpected overlay takeover"
-  fi
 fi
 
-# Check something.
+LOG OK "adb remount RW"
 
+################################################################################
 LOG RUN "push content to ${MOUNTS}"
 
+adb_root || die "adb root"
 A="Hello World! $(date)"
-for i in ${MOUNTS}; do
+for i in ${MOUNTS} /system/priv-app; do
   echo "${A}" | adb_sh cat - ">${i}/hello"
   B="`adb_cat ${i}/hello`" ||
     die "${i#/} hello"
   check_eq "${A}" "${B}" ${i} before reboot
 done
-echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
-B="`adb_cat /system/priv-app/hello`" ||
-  die "system priv-app hello"
-check_eq "${A}" "${B}" /system/priv-app before reboot
 SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
 VENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`
 check_ne "${SYSTEM_INO}" "${VENDOR_INO}" vendor and system inode
 
-# Download libc.so, append some garbage, push back, and check if the file
-# is updated.
-tempdir="`mktemp -d`"
-cleanup() {
-  rm -rf ${tempdir}
-}
-adb pull /system/lib/bootstrap/libc.so ${tempdir} >/dev/null ||
-  die "pull libc.so from device"
-garbage="D105225BBFCB1EB8AB8EBDB7094646F0"
-echo "${garbage}" >> ${tempdir}/libc.so
-adb push ${tempdir}/libc.so /system/lib/bootstrap/libc.so >/dev/null ||
-  die "push libc.so to device"
-adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
-  die "pull libc.so from device"
-diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null ||
-  die "libc.so differ"
+# Edit build.prop and check if properties are updated.
+system_build_prop_original="${TMPDIR}/system_build.prop.original"
+system_build_prop_modified="${TMPDIR}/system_build.prop.modified"
+system_build_prop_fromdevice="${TMPDIR}/system_build.prop.fromdevice"
+adb pull /system/build.prop "${system_build_prop_original}" >/dev/null ||
+  die "adb pull /system/build.prop"
+# Prepend with extra newline in case the original file doesn't end with a newline.
+cat "${system_build_prop_original}" - <<EOF >"${system_build_prop_modified}"
 
+# Properties added by adb remount test
+test.adb.remount.system.build.prop=true
+EOF
+adb push "${system_build_prop_modified}" /system/build.prop >/dev/null ||
+  die "adb push /system/build.prop"
+adb pull /system/build.prop "${system_build_prop_fromdevice}" >/dev/null ||
+  die "adb pull /system/build.prop"
+diff "${system_build_prop_modified}" "${system_build_prop_fromdevice}" >/dev/null ||
+  die "/system/build.prop differs from pushed content"
+
+################################################################################
 LOG RUN "reboot to confirm content persistent"
 
 fixup_from_recovery() {
@@ -1295,15 +1360,12 @@
   adb_wait ${ADB_WAIT}
 }
 
-adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
+adb_reboot ||
   fixup_from_recovery ||
   die "reboot after override content added failed `usb_status`"
 
 if ${overlayfs_needed}; then
-  D=`adb_su df -k </dev/null` &&
-    H=`echo "${D}" | head -1` &&
-    D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` ||
+  is_overlayfs_mounted ||
     die -d "overlay takeover failed after reboot"
 
   adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
@@ -1324,15 +1386,13 @@
   adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
 fi
 # If overlayfs has a nested security problem, this will fail.
-B="`adb_ls /system/`" ||
-  die "adb ls /system"
-[ X"${B}" != X"${B#*priv-app}" ] ||
-  die "adb ls /system/priv-app"
+adb_sh ls /system >/dev/null || die "ls /system"
+adb_test -d /system/priv-app || die "[ -d /system/priv-app ]"
 B="`adb_cat /system/priv-app/hello`"
 check_eq "${A}" "${B}" /system/priv-app after reboot
+
 # Only root can read vendor if sepolicy permissions are as expected.
-adb_root ||
-  die "adb root"
+adb_root || die "adb root"
 for i in ${MOUNTS}; do
   B="`adb_cat ${i}/hello`"
   check_eq "${A}" "${B}" ${i#/} after reboot
@@ -1345,166 +1405,140 @@
 # Feed log with selinux denials as a result of overlays
 adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
 
-# Check if the updated libc.so is persistent after reboot.
-adb_root &&
-  adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
-  die "pull libc.so from device"
-diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
-rm -rf ${tempdir}
-cleanup() {
-  true
-}
-LOG OK "/system/lib/bootstrap/libc.so content remains after reboot"
+# Check if the updated build.prop is persistent after reboot.
+check_eq "true" "$(get_property 'test.adb.remount.system.build.prop')" "load modified build.prop"
+adb pull /system/build.prop "${system_build_prop_fromdevice}" >/dev/null ||
+  die "adb pull /system/build.prop"
+diff "${system_build_prop_modified}" "${system_build_prop_fromdevice}" >/dev/null ||
+  die "/system/build.prop differs from pushed content"
+LOG OK "/system/build.prop content remains after reboot"
 
-LOG RUN "flash vendor, confirm its content disappears"
+################################################################################
+LOG RUN "flash vendor, and confirm vendor override disappears"
 
-H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
-is_bootloader_fastboot=false
+is_bootloader_fastboot=true
 # cuttlefish?
-[ X"${H}" != X"${H#vsoc}" ] || is_bootloader_fastboot=true
+[[ "$(get_property ro.product.device)" == vsoc* ]] &&
+  is_bootloader_fastboot=false
 is_userspace_fastboot=false
 
 if ! ${is_bootloader_fastboot}; then
-  LOG WARNING "does not support fastboot, skipping"
-elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
-  LOG WARNING "build tree not setup, skipping"
-elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
-  LOG WARNING "vendor image missing, skipping"
-elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
-  LOG WARNING "wrong vendor image, skipping"
-elif [ -z "${ANDROID_HOST_OUT}" ]; then
-  LOG WARNING "please run lunch, skipping"
-elif ! (
-          adb_cat /vendor/build.prop |
-          cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
-       ) >/dev/null 2>/dev/null; then
-  LOG WARNING "vendor image signature mismatch, skipping"
+  LOG WARNING "does not support fastboot flash, skipping"
 else
   wait_for_screen
+  adb_root || die "adb root"
+
+  VENDOR_DEVICE_CANDIDATES=(
+    "/dev/block/mapper/vendor"{_${ACTIVE_SLOT},}
+    "/dev/block/by-name/vendor"{_${ACTIVE_SLOT},}
+  )
+  for b in "${VENDOR_DEVICE_CANDIDATES[@]}"; do
+    if adb_test -e "${b}"; then
+      adb pull "${b}" "${TMPDIR}/vendor.img" || die "adb pull ${b}"
+      LOG INFO "pulled ${b} from device as vendor.img"
+      break
+    fi
+  done
+  [ -f "${TMPDIR}/vendor.img" ] ||
+    die "cannot find block device of vendor partition"
+
   avc_check
   adb reboot fastboot </dev/null ||
     die "fastbootd not supported (wrong adb in path?)"
   any_wait ${ADB_WAIT} &&
     inFastboot ||
     die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
-  fastboot flash vendor ||
+  fastboot flash vendor "${TMPDIR}/vendor.img" ||
     ( fastboot reboot && false) ||
     die "fastboot flash vendor"
+  LOG OK "flashed vendor"
+
   fastboot_getvar is-userspace yes &&
     is_userspace_fastboot=true
+  # check ${scratch_partition} via fastboot
   if [ -n "${scratch_partition}" ]; then
     fastboot_getvar partition-type:${scratch_partition} raw ||
       ( fastboot reboot && false) ||
       die "fastboot can not see ${scratch_partition} parameters"
     if ${uses_dynamic_scratch}; then
-      # check ${scratch_partition} via fastboot
       fastboot_getvar has-slot:${scratch_partition} no &&
         fastboot_getvar is-logical:${scratch_partition} yes ||
         ( fastboot reboot && false) ||
         die "fastboot can not see ${scratch_partition} parameters"
-    else
-      fastboot_getvar is-logical:${scratch_partition} no ||
-        ( fastboot reboot && false) ||
-        die "fastboot can not see ${scratch_partition} parameters"
-    fi
-    if ! ${uses_dynamic_scratch}; then
-      fastboot reboot-bootloader ||
-        die "Reboot into fastboot"
-    fi
-    if ${uses_dynamic_scratch}; then
       LOG INFO "expect fastboot erase ${scratch_partition} to fail"
       fastboot erase ${scratch_partition} &&
         ( fastboot reboot || true) &&
         die "fastboot can erase ${scratch_partition}"
+    else
+      fastboot_getvar is-logical:${scratch_partition} no ||
+        ( fastboot reboot && false) ||
+        die "fastboot can not see ${scratch_partition} parameters"
+      fastboot reboot-bootloader ||
+        die "fastboot reboot bootloader"
     fi
     LOG INFO "expect fastboot format ${scratch_partition} to fail"
     fastboot format ${scratch_partition} &&
       ( fastboot reboot || true) &&
       die "fastboot can format ${scratch_partition}"
   fi
-  fastboot reboot ||
-    die "can not reboot out of fastboot"
-  LOG WARNING "adb after fastboot"
+
+  fastboot reboot || die "cannot reboot out of fastboot"
+  LOG INFO "reboot from fastboot"
   adb_wait ${ADB_WAIT} ||
     fixup_from_recovery ||
-    die "did not reboot after formatting ${scratch_partition} `usb_status`"
+    die "cannot reboot after flash vendor $(usb_status)"
   if ${overlayfs_needed}; then
-    adb_root &&
-      D=`adb_sh df -k </dev/null` &&
-      H=`echo "${D}" | head -1` &&
-      D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` &&
-      ( echo "${H}" &&
-        echo "${D}"
-      ) >&2 &&
-      echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+    is_overlayfs_mounted /system ||
       die  "overlay /system takeover after flash vendor"
-    echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
+    if is_overlayfs_mounted /vendor 2>/dev/null; then
       if ${is_userspace_fastboot}; then
         die  "overlay supposed to be minus /vendor takeover after flash vendor"
       else
-        LOG WARNING "user fastboot missing required to invalidate, ignoring a failure"
+        LOG WARNING "fastbootd missing required to invalidate, ignoring a failure"
         LOG WARNING "overlay supposed to be minus /vendor takeover after flash vendor"
       fi
+    fi
   fi
-  B="`adb_cat /system/hello`"
-  check_eq "${A}" "${B}" system after flash vendor
-  B="`adb_ls /system/`" ||
-    die "adb ls /system"
-  [ X"${B}" != X"${B#*priv-app}" ] ||
-    die "adb ls /system/priv-app"
-  B="`adb_cat /system/priv-app/hello`"
-  check_eq "${A}" "${B}" system/priv-app after flash vendor
-  adb_root ||
-    die "adb root"
-  B="`adb_cat /vendor/hello`"
-  if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
-    check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
-             vendor content after flash vendor
-  else
-    LOG WARNING "user fastboot missing required to invalidate, ignoring a failure"
-    check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
-             --warning vendor content after flash vendor
+  check_eq "${A}" "$(adb_cat /system/hello)" "/system content after flash vendor"
+  check_eq "${SYSTEM_INO}" "$(adb_sh stat --format=%i /system/hello </dev/null)" "system inode after flash vendor"
+  adb_sh ls /system >/dev/null || die "ls /system"
+  adb_test -d /system/priv-app || die "[ -d /system/priv-app ]"
+  check_eq "${A}" "$(adb_cat /system/priv-app/hello)" "/system/priv-app content after flash vendor"
+  adb_root || die "adb root"
+  if adb_test -e /vendor/hello; then
+    if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
+      die "vendor content after flash vendor"
+    else
+      LOG WARNING "fastbootd missing required to invalidate, ignoring a failure"
+      LOG WARNING "vendor content after flash vendor"
+    fi
   fi
-
-  check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
-
-fi
+  LOG OK "vendor override destroyed after flash verdor"
+fi >&2
 
 wait_for_screen
-LOG RUN "remove test content (cleanup)"
 
-T=`adb_date`
-H=`adb remount 2>&1`
-err=${?}
-L=
-D="${H%?Now reboot your device for settings to take effect*}"
-if [ X"${H}" != X"${D}" ]; then
-  LOG WARNING "adb remount requires a reboot after partial flash (legacy avb)"
-  L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
-  adb_reboot &&
-    adb_wait ${ADB_WAIT} &&
-    adb_root ||
-    die "failed to reboot"
-  T=`adb_date`
-  H=`adb remount 2>&1`
-  err=${?}
+################################################################################
+LOG RUN "Clean up test content"
+
+adb_root || die "adb root"
+T=$(adb_date)
+D=$(adb remount 2>&1) ||
+  die -t "${T}" "adb remount"
+echo "${D}" >&2
+if [[ "${D}" =~ [Rr]eboot ]]; then
+  LOG OK "adb remount calls for a reboot after partial flash"
+  # but we don't really want to, since rebooting just recreates the already tore
+  # down vendor overlay.
 fi
-echo "${H}" >&2
-[ ${err} = 0 ] &&
-  ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
-  adb_sh rm /system/hello /system/priv-app/hello </dev/null ||
-  ( [ -n "${L}" ] && echo "${L}" && false ) >&2 ||
-  die -t ${T} "cleanup hello"
-B="`adb_cat /system/hello`"
-check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
-B="`adb_cat /system/priv-app/hello`"
-check_eq "cat: /system/priv-app/hello: No such file or directory" "${B}" after rm
-B="`adb_cat /vendor/hello`"
-check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
-for i in ${MOUNTS}; do
-  adb_sh rm ${i}/hello </dev/null 2>/dev/null || true
+
+for i in ${MOUNTS} /system/priv-app; do
+  adb_sh rm "${i}/hello" 2>/dev/null || true
+  adb_test -e "${i}/hello" &&
+    die -t "${T}" "/${i}/hello lingers after rm"
 done
 
+################################################################################
 if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
 
   LOG RUN "test fastboot flash to ${scratch_partition} recovery"
@@ -1512,19 +1546,12 @@
   avc_check
   adb reboot fastboot </dev/null ||
     die "Reboot into fastbootd"
-  img=${TMPDIR}/adb-remount-test-${$}.img
-  cleanup() {
-    rm ${img}
-  }
+  img="${TMPDIR}/adb-remount-test-${$}.img"
   dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
     fastboot_wait ${FASTBOOT_WAIT} ||
     die "reboot into fastboot to flash scratch `usb_status`"
   fastboot flash --force ${scratch_partition} ${img}
   err=${?}
-  cleanup
-  cleanup() {
-    true
-  }
   fastboot reboot ||
     die "can not reboot out of fastboot"
   [ 0 -eq ${err} ] ||
@@ -1539,7 +1566,6 @@
   then
     LOG WARNING "adb disable-verity requires a reboot after partial flash"
     adb_reboot &&
-      adb_wait ${ADB_WAIT} &&
       adb_root ||
       die "failed to reboot"
     T=`adb_date`
@@ -1554,86 +1580,9 @@
     [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
     LOG OK "${scratch_partition} recreated" ||
     die -t ${T} "setup for overlayfs"
-  D=`adb remount 2>&1`
-  err=${?}
-  echo "${D}" >&2
-  [ ${err} != 0 ] ||
-    [ X"${D}" = X"${D##*remount failed}" ] ||
-    ( echo "${D}" && false ) >&2 ||
+  adb remount >&2 ||
     die -t ${T} "remount failed"
 fi
 
-LOG RUN "test raw remount commands"
-
-fixup_from_fastboot() {
-  inFastboot || return 1
-  if [ -n "${ACTIVE_SLOT}" ]; then
-    local active_slot=`get_active_slot`
-    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
-      LOG WARNING "Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
-    else
-      LOG WARNING "Active slot to be set to ${ACTIVE_SLOT}"
-    fi
-    fastboot --set-active=${ACTIVE_SLOT}
-  fi
-  fastboot reboot
-  adb_wait ${ADB_WAIT}
-}
-
-# Prerequisite is a prepped device from above.
-adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
-  fixup_from_fastboot ||
-  die "lost device after reboot to ro state `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/vendor is not read-only"
-adb_su mount -o rw,remount /vendor </dev/null ||
-  die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
-  die "/vendor is not read-write"
-LOG OK "mount -o rw,remount command works"
-
-# Prerequisite is a prepped device from above.
-adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
-  fixup_from_fastboot ||
-  die "lost device after reboot to ro state `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/vendor is not read-only"
-adb_su remount vendor </dev/null ||
-  die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
-  die "/vendor is not read-write"
-adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/vendor is not read-only"
-LOG OK "remount command works from setup"
-
-# Prerequisite is an overlayfs deconstructed device but with verity disabled.
-# This also saves a lot of 'noise' from the command doing a mkfs on backing
-# storage and all the related tuning and adjustment.
-surgically_wipe_overlayfs || true
-adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
-  fixup_from_fastboot ||
-  die "lost device after reboot after wipe `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/vendor is not read-only"
-adb_su remount vendor </dev/null ||
-  die "remount command"
-adb_su df -k </dev/null | skip_unrelated_mounts >&2
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
-  die "/vendor is not read-write"
-adb_sh grep " \(/system\|/\) .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/system is not read-only"
-LOG OK "remount command works from scratch"
-
-if ! restore; then
-  restore() {
-    true
-  }
-  die "failed to restore verity after remount from scratch test"
-fi
 
 LOG PASSED "adb remount test"
-
-test_duration
diff --git a/init/apex_init_util.cpp b/init/apex_init_util.cpp
index de9f547..d618a6e 100644
--- a/init/apex_init_util.cpp
+++ b/init/apex_init_util.cpp
@@ -81,16 +81,13 @@
 }
 
 Result<void> ParseApexConfigs(const std::string& apex_name) {
-    Result<std::vector<std::string>> configs = CollectApexConfigs(apex_name);
-    if (!configs.ok()) {
-        return configs.error();
-    }
+    auto configs = OR_RETURN(CollectApexConfigs(apex_name));
 
-    if (configs.value().empty()) {
+    if (configs.empty()) {
         return {};
     }
 
-    auto filtered_configs = FilterVersionedConfigs(configs.value(),
+    auto filtered_configs = FilterVersionedConfigs(configs,
                                     android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
     return ParseConfigs(filtered_configs);
 }
diff --git a/init/init.cpp b/init/init.cpp
index 9411b47..ce668d7 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -490,7 +490,6 @@
         return Error() << "Unable to stop all service from " << apex_name;
     }
     RemoveServiceAndActionFromApex(apex_name);
-    SetProperty("init.apex." + apex_name, "unloaded");
     return {};
 }
 
@@ -522,7 +521,6 @@
         return result.error();
     }
 
-    SetProperty("init.apex." + apex_name, "loaded");
     return {};
 }
 
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 529bbdf..8362390 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -280,6 +280,10 @@
 }
 
 TEST(init, StopServiceByApexName) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
     std::string_view script_template = R"init(
 service apex_test_service /system/bin/yes
     user shell
@@ -291,6 +295,10 @@
 }
 
 TEST(init, StopMultipleServicesByApexName) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
     std::string_view script_template = R"init(
 service apex_test_service_multiple_a /system/bin/yes
     user shell
@@ -307,6 +315,10 @@
 }
 
 TEST(init, StopServicesFromMultipleApexes) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
     std::string_view apex_script_template = R"init(
 service apex_test_service_multi_apex_a /system/bin/yes
     user shell
@@ -332,6 +344,10 @@
 }
 
 TEST(init, StopServicesFromApexAndNonApex) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
     std::string_view apex_script_template = R"init(
 service apex_test_service_apex_a /system/bin/yes
     user shell
@@ -357,6 +373,10 @@
 }
 
 TEST(init, StopServicesFromApexMixed) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
     std::string_view script_template = R"init(
 service apex_test_service_mixed_a /system/bin/yes
     user shell
@@ -535,20 +555,6 @@
     EXPECT_EQ(2, num_executed);
 }
 
-TEST(init, RespondToCtlApexMessages) {
-    if (getuid() != 0) {
-        GTEST_SKIP() << "Skipping test, must be run as root.";
-        return;
-    }
-
-    std::string apex_name = "com.android.apex.cts.shim";
-    SetProperty("ctl.apex_unload", apex_name);
-    EXPECT_TRUE(WaitForProperty("init.apex." + apex_name, "unloaded", 10s));
-
-    SetProperty("ctl.apex_load", apex_name);
-    EXPECT_TRUE(WaitForProperty("init.apex." + apex_name, "loaded", 10s));
-}
-
 TEST(init, RejectsCriticalAndOneshotService) {
     if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
         GTEST_SKIP() << "Test only valid for devices launching with R or later";
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 4e4bfd8..880674c 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -491,7 +491,7 @@
         return ErrnoError() << "zram_backing_dev: swapoff (" << backing_dev << ")"
                             << " failed";
     }
-    LOG(INFO) << "swapoff() took " << swap_timer;;
+    LOG(INFO) << "swapoff() took " << swap_timer;
 
     if (!WriteStringToFile("1", ZRAM_RESET)) {
         return Error() << "zram_backing_dev: reset (" << backing_dev << ")"
diff --git a/init/service.cpp b/init/service.cpp
index 99a0367..4cf409c 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -36,6 +36,8 @@
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
 
+#include <string>
+
 #include "lmkd_service.h"
 #include "service_list.h"
 #include "util.h"
@@ -53,6 +55,7 @@
 
 using android::base::boot_clock;
 using android::base::GetBoolProperty;
+using android::base::GetIntProperty;
 using android::base::GetProperty;
 using android::base::Join;
 using android::base::make_scope_guard;
@@ -320,6 +323,20 @@
             mount_namespace_.has_value() && *mount_namespace_ == NS_DEFAULT;
     const bool is_process_updatable = use_default_mount_ns && is_apex_updatable;
 
+#ifdef SEGV_MTEAERR
+    // As a precaution, we only upgrade a service once per reboot, to limit
+    // the potential impact.
+    // TODO(b/244471804): Once we have a kernel API to get sicode, compare it to MTEAERR here.
+    bool should_upgrade_mte = siginfo.si_code != CLD_EXITED && siginfo.si_status == SIGSEGV &&
+                              !upgraded_mte_;
+
+    if (should_upgrade_mte) {
+        LOG(INFO) << "Upgrading service " << name_ << " to sync MTE";
+        once_environment_vars_.emplace_back("BIONIC_MEMTAG_UPGRADE_SECS", "60");
+        upgraded_mte_ = true;
+    }
+#endif
+
     // If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
     // reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
@@ -484,6 +501,9 @@
         LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
     }
 
+    for (const auto& [key, value] : once_environment_vars_) {
+        setenv(key.c_str(), value.c_str(), 1);
+    }
     for (const auto& [key, value] : environment_vars_) {
         setenv(key.c_str(), value.c_str(), 1);
     }
@@ -628,6 +648,8 @@
         return ErrnoError() << "Failed to fork";
     }
 
+    once_environment_vars_.clear();
+
     if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
         std::string oom_str = std::to_string(oom_score_adjust_);
         std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
diff --git a/init/service.h b/init/service.h
index 6d9a0ca..ab19865 100644
--- a/init/service.h
+++ b/init/service.h
@@ -171,6 +171,7 @@
     android::base::boot_clock::time_point time_started_;  // time of last start
     android::base::boot_clock::time_point time_crashed_;  // first crash within inspection window
     int crash_count_;                     // number of times crashed within window
+    bool upgraded_mte_ = false;           // whether we upgraded async MTE -> sync MTE before
     std::chrono::minutes fatal_crash_window_ = 4min;  // fatal() when more than 4 crashes in it
     std::optional<std::string> fatal_reboot_target_;  // reboot target of fatal handler
 
@@ -183,6 +184,8 @@
     std::vector<SocketDescriptor> sockets_;
     std::vector<FileDescriptor> files_;
     std::vector<std::pair<std::string, std::string>> environment_vars_;
+    // Environment variables that only get applied to the next run.
+    std::vector<std::pair<std::string, std::string>> once_environment_vars_;
 
     Subcontext* subcontext_;
     Action onrestart_;  // Commands to execute on restart.
diff --git a/init/test_upgrade_mte/Android.bp b/init/test_upgrade_mte/Android.bp
new file mode 100644
index 0000000..eec48db
--- /dev/null
+++ b/init/test_upgrade_mte/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 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.
+
+cc_binary {
+  name: "mte_upgrade_test_helper",
+  srcs: ["mte_upgrade_test_helper.cpp"],
+  sanitize: {
+    memtag_heap: true,
+    diag: {
+      memtag_heap: false,
+    },
+  },
+  init_rc: [
+    "mte_upgrade_test.rc",
+  ],
+}
+
+java_test_host {
+    name: "mte_upgrade_test",
+    libs: ["tradefed"],
+    static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
+    srcs:  ["src/**/MteUpgradeTest.java", ":libtombstone_proto-src"],
+    data: [":mte_upgrade_test_helper", "mte_upgrade_test.rc" ],
+    test_config: "AndroidTest.xml",
+    test_suites: ["general-tests"],
+}
diff --git a/init/test_upgrade_mte/AndroidTest.xml b/init/test_upgrade_mte/AndroidTest.xml
new file mode 100644
index 0000000..b89cde8
--- /dev/null
+++ b/init/test_upgrade_mte/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs the MTE upgrade tests">
+    <option name="test-suite-tag" value="init_test_upgrade_mte" />
+    <option name="test-suite-tag" value="apct" />
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+      <option name="cleanup" value="true" />
+      <option name="remount-system" value="true" />
+      <option name="push" value="mte_upgrade_test.rc->/system/etc/init/mte_upgrade_test.rc" />
+      <option name="push" value="mte_upgrade_test_helper->/system/bin/mte_upgrade_test_helper" />
+      <option name="push" value="mte_upgrade_test_helper->/data/local/tmp/app_process64" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="mte_upgrade_test.jar" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/init/test_upgrade_mte/mte_upgrade_test.rc b/init/test_upgrade_mte/mte_upgrade_test.rc
new file mode 100644
index 0000000..a3e596c
--- /dev/null
+++ b/init/test_upgrade_mte/mte_upgrade_test.rc
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 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.
+
+service mte_upgrade_test_helper /system/bin/mte_upgrade_test_helper ${sys.mte_crash_test_uuid}
+  class late_start
+  disabled
+  seclabel u:r:su:s0
+
+service mte_upgrade_test_helper_overridden /system/bin/mte_upgrade_test_helper ${sys.mte_crash_test_uuid}
+  class late_start
+  disabled
+  seclabel u:r:su:s0
+  setenv BIONIC_MEMTAG_UPGRADE_SECS 0
diff --git a/init/test_upgrade_mte/mte_upgrade_test_helper.cpp b/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
new file mode 100644
index 0000000..10af06b
--- /dev/null
+++ b/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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 <linux/prctl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <unistd.h>
+
+int MaybeDowngrade() {
+    int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+    if (res == -1) return 1;
+    if (static_cast<unsigned long>(res) & PR_MTE_TCF_ASYNC) return 2;
+    time_t t = time(nullptr);
+    while (time(nullptr) - t < 100) {
+        res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+        if (static_cast<unsigned long>(res) & PR_MTE_TCF_ASYNC) {
+            return 0;
+        }
+        sleep(1);
+    }
+    return 3;
+}
+
+int main(int argc, char** argv) {
+    if (argc == 2 && strcmp(argv[1], "--check-downgrade") == 0) {
+        return MaybeDowngrade();
+    }
+    int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+    if (res == -1) abort();
+    if (argc == 2 && strcmp(argv[1], "--get-mode") == 0) {
+        if (res & PR_MTE_TCF_ASYNC) {
+            return 1;
+        }
+        if (res & PR_MTE_TCF_SYNC) {
+            return 2;
+        }
+        abort();
+    }
+
+    if (res & PR_MTE_TCF_ASYNC && res & PR_MTE_TCF_SYNC) {
+        // Disallow automatic upgrade from ASYNC mode.
+        if (prctl(PR_SET_TAGGED_ADDR_CTRL, res & ~PR_MTE_TCF_SYNC, 0, 0, 0) == -1) abort();
+    }
+    volatile char* f = (char*)malloc(1);
+    f[17] = 'x';
+    char buf[1];
+    read(1, buf, 1);
+    return 0;
+}
diff --git a/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java b/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java
new file mode 100644
index 0000000..f4e4a9c
--- /dev/null
+++ b/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.tests.init;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.server.os.TombstoneProtos.Tombstone;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MteUpgradeTest extends BaseHostJUnit4Test {
+    @Before
+    public void setUp() throws Exception {
+        CommandResult result =
+                getDevice().executeShellV2Command("/system/bin/mte_upgrade_test_helper --checking");
+        assumeTrue("mte_upgrade_test_binary needs to segfault", result.getExitCode() == 139);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Easier here than in a finally in testCrash, and doesn't really hurt.
+        getDevice().executeShellV2Command("stop mte_upgrade_test_helper");
+        getDevice().executeShellV2Command("stop mte_upgrade_test_helper_overridden");
+        getDevice().setProperty("sys.mte_crash_test_uuid", "");
+    }
+
+    Tombstone parseTombstone(String tombstonePath) throws Exception {
+        File tombstoneFile = getDevice().pullFile(tombstonePath);
+        InputStream istr = new FileInputStream(tombstoneFile);
+        Tombstone tombstoneProto;
+        try {
+            tombstoneProto = Tombstone.parseFrom(istr);
+        } finally {
+            istr.close();
+        }
+        return tombstoneProto;
+    }
+
+    @Test
+    public void testCrash() throws Exception {
+        String uuid = java.util.UUID.randomUUID().toString();
+        getDevice().reboot();
+        assertThat(getDevice().setProperty("sys.mte_crash_test_uuid", uuid)).isTrue();
+
+        CommandResult result = getDevice().executeShellV2Command("start mte_upgrade_test_helper");
+        assertThat(result.getExitCode()).isEqualTo(0);
+        java.lang.Thread.sleep(20000);
+        String[] tombstonesAfter = getDevice().getChildren("/data/tombstones");
+        ArrayList<String> segvCodeNames = new ArrayList<String>();
+        for (String tombstone : tombstonesAfter) {
+            if (!tombstone.endsWith(".pb")) {
+                continue;
+            }
+            String tombstoneFilename = "/data/tombstones/" + tombstone;
+            Tombstone tombstoneProto = parseTombstone(tombstoneFilename);
+            if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(uuid))) {
+                continue;
+            }
+            assertThat(tombstoneProto.getSignalInfo().getName()).isEqualTo("SIGSEGV");
+            segvCodeNames.add(tombstoneProto.getSignalInfo().getCodeName());
+            getDevice().deleteFile(tombstoneFilename);
+            // remove the non .pb file as well.
+            getDevice().deleteFile(tombstoneFilename.substring(0, tombstoneFilename.length() - 3));
+        }
+        assertThat(segvCodeNames.size()).isAtLeast(3);
+        assertThat(segvCodeNames.get(0)).isEqualTo("SEGV_MTEAERR");
+        assertThat(segvCodeNames.get(1)).isEqualTo("SEGV_MTESERR");
+        assertThat(segvCodeNames.get(2)).isEqualTo("SEGV_MTEAERR");
+    }
+
+    @Test
+    public void testCrashOverridden() throws Exception {
+        String uuid = java.util.UUID.randomUUID().toString();
+        getDevice().reboot();
+        assertThat(getDevice().setProperty("sys.mte_crash_test_uuid", uuid)).isTrue();
+
+        CommandResult result =
+                getDevice().executeShellV2Command("start mte_upgrade_test_helper_overridden");
+        assertThat(result.getExitCode()).isEqualTo(0);
+        java.lang.Thread.sleep(20000);
+        String[] tombstonesAfter = getDevice().getChildren("/data/tombstones");
+        ArrayList<String> segvCodeNames = new ArrayList<String>();
+        for (String tombstone : tombstonesAfter) {
+            if (!tombstone.endsWith(".pb")) {
+                continue;
+            }
+            String tombstoneFilename = "/data/tombstones/" + tombstone;
+            Tombstone tombstoneProto = parseTombstone(tombstoneFilename);
+            if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(uuid))) {
+                continue;
+            }
+            assertThat(tombstoneProto.getSignalInfo().getName()).isEqualTo("SIGSEGV");
+            segvCodeNames.add(tombstoneProto.getSignalInfo().getCodeName());
+            getDevice().deleteFile(tombstoneFilename);
+            // remove the non .pb file as well.
+            getDevice().deleteFile(tombstoneFilename.substring(0, tombstoneFilename.length() - 3));
+        }
+        assertThat(segvCodeNames.size()).isAtLeast(3);
+        assertThat(segvCodeNames.get(0)).isEqualTo("SEGV_MTEAERR");
+        assertThat(segvCodeNames.get(1)).isEqualTo("SEGV_MTEAERR");
+        assertThat(segvCodeNames.get(2)).isEqualTo("SEGV_MTEAERR");
+    }
+
+    @Test
+    public void testDowngrade() throws Exception {
+        CommandResult result =
+                getDevice()
+                        .executeShellV2Command(
+                                "MEMTAG_OPTIONS=async BIONIC_MEMTAG_UPGRADE_SECS=5"
+                                        + " /system/bin/mte_upgrade_test_helper --check-downgrade");
+        assertThat(result.getExitCode()).isEqualTo(0);
+    }
+
+    @Test
+    public void testAppProcess() throws Exception {
+        CommandResult result =
+                getDevice()
+                        .executeShellV2Command(
+                                "MEMTAG_OPTIONS=async BIONIC_MEMTAG_UPGRADE_SECS=5"
+                                        + " /data/local/tmp/app_process64 --get-mode");
+        assertThat(result.getExitCode()).isEqualTo(1);  // ASYNC
+    }
+}
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bdb8075..da5005c 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -41,9 +41,11 @@
  */
 
 #define AID_ROOT 0 /* traditional unix root user */
-/* The following are for LTP and should only be used for testing */
-#define AID_DAEMON 1 /* traditional unix daemon owner */
-#define AID_BIN 2    /* traditional unix binaries owner */
+
+/* The following are for tests like LTP and should only be used for testing. */
+#define AID_DAEMON 1 /* Traditional unix daemon owner. */
+#define AID_BIN 2    /* Traditional unix binaries owner. */
+#define AID_SYS 3    /* A group with the same gid on Linux/macOS/Android. */
 
 #define AID_SYSTEM 1000 /* system server */
 
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
index de9a452..3c0df79 100644
--- a/set-verity-state/set-verity-state.cpp
+++ b/set-verity-state/set-verity-state.cpp
@@ -80,17 +80,17 @@
 }
 
 bool overlayfs_setup(bool enable) {
-  auto change = false;
+  auto want_reboot = false;
   errno = 0;
-  if (enable ? fs_mgr_overlayfs_setup(nullptr, &change)
-             : fs_mgr_overlayfs_teardown(nullptr, &change)) {
-    if (change) {
+  if (enable ? fs_mgr_overlayfs_setup(nullptr, &want_reboot)
+             : fs_mgr_overlayfs_teardown(nullptr, &want_reboot)) {
+    if (want_reboot) {
       LOG(INFO) << (enable ? "Enabled" : "Disabled") << " overlayfs";
     }
   } else {
     LOG(ERROR) << "Failed to " << (enable ? "enable" : "disable") << " overlayfs";
   }
-  return change;
+  return want_reboot;
 }
 
 struct SetVerityStateResult {