Merge "fs_mgr: overlay check shared blocks for / if /system is not" into qt-dev
diff --git a/adb/adb.cpp b/adb/adb.cpp
index e417f05..2dd22b3 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -280,6 +280,9 @@
     } else if (type == "sideload") {
         D("setting connection_state to kCsSideload");
         t->SetConnectionState(kCsSideload);
+    } else if (type == "rescue") {
+        D("setting connection_state to kCsRescue");
+        t->SetConnectionState(kCsRescue);
     } else {
         D("setting connection_state to kCsHost");
         t->SetConnectionState(kCsHost);
diff --git a/adb/adb.h b/adb/adb.h
index c60dcbc..3a6f059 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -107,6 +107,7 @@
     kCsHost,
     kCsRecovery,
     kCsSideload,
+    kCsRescue,
 };
 
 inline bool ConnectionStateIsOnline(ConnectionState state) {
@@ -116,6 +117,7 @@
         case kCsHost:
         case kCsRecovery:
         case kCsSideload:
+        case kCsRescue:
             return true;
         default:
             return false;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 43a3e5e..e2a17c5 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -190,7 +190,7 @@
         "scripting:\n"
         " wait-for[-TRANSPORT]-STATE\n"
         "     wait for device to be in the given state\n"
-        "     STATE: device, recovery, sideload, bootloader, or disconnect\n"
+        "     STATE: device, recovery, rescue, sideload, bootloader, or disconnect\n"
         "     TRANSPORT: usb, local, or any [default=any]\n"
         " get-state                print offline | bootloader | device\n"
         " get-serialno             print <serial-number>\n"
@@ -838,26 +838,25 @@
 
 #define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE)
 
-/*
- * The sideload-host protocol serves the data in a file (given on the
- * command line) to the client, using a simple protocol:
- *
- * - The connect message includes the total number of bytes in the
- *   file and a block size chosen by us.
- *
- * - The other side sends the desired block number as eight decimal
- *   digits (eg "00000023" for block 23).  Blocks are numbered from
- *   zero.
- *
- * - We send back the data of the requested block.  The last block is
- *   likely to be partial; when the last block is requested we only
- *   send the part of the block that exists, it's not padded up to the
- *   block size.
- *
- * - When the other side sends "DONEDONE" instead of a block number,
- *   we hang up.
- */
-static int adb_sideload_host(const char* filename) {
+// Connects to the sideload / rescue service on the device (served by minadbd) and sends over the
+// data in an OTA package.
+//
+// It uses a simple protocol as follows.
+//
+// - The connect message includes the total number of bytes in the file and a block size chosen by
+//   us.
+//
+// - The other side sends the desired block number as eight decimal digits (e.g. "00000023" for
+//   block 23). Blocks are numbered from zero.
+//
+// - We send back the data of the requested block. The last block is likely to be partial; when the
+//   last block is requested we only send the part of the block that exists, it's not padded up to
+//   the block size.
+//
+// - When the other side sends "DONEDONE" or "FAILFAIL" instead of a block number, we have done all
+//   the data transfer.
+//
+static int adb_sideload_install(const char* filename, bool rescue_mode) {
     // TODO: use a LinePrinter instead...
     struct stat sb;
     if (stat(filename, &sb) == -1) {
@@ -870,14 +869,18 @@
         return -1;
     }
 
-    std::string service =
-            android::base::StringPrintf("sideload-host:%" PRId64 ":%d",
-                                        static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
+    std::string service = android::base::StringPrintf(
+            "%s:%" PRId64 ":%d", rescue_mode ? "rescue-install" : "sideload-host",
+            static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
     unique_fd device_fd(adb_connect(service, &error));
     if (device_fd < 0) {
         fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
 
+        if (rescue_mode) {
+            return -1;
+        }
+
         // If this is a small enough package, maybe this is an older device that doesn't
         // support sideload-host. Try falling back to the older (<= K) sideload method.
         if (sb.st_size > INT_MAX) {
@@ -901,10 +904,14 @@
         }
         buf[8] = '\0';
 
-        if (strcmp("DONEDONE", buf) == 0) {
+        if (strcmp(kMinadbdServicesExitSuccess, buf) == 0 ||
+            strcmp(kMinadbdServicesExitFailure, buf) == 0) {
             printf("\rTotal xfer: %.2fx%*s\n",
                    static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
                    static_cast<int>(strlen(filename) + 10), "");
+            if (strcmp(kMinadbdServicesExitFailure, buf) == 0) {
+                return 1;
+            }
             return 0;
         }
 
@@ -954,6 +961,33 @@
     }
 }
 
+static int adb_wipe_devices() {
+    auto wipe_devices_message_size = strlen(kMinadbdServicesExitSuccess);
+    std::string error;
+    unique_fd fd(adb_connect(
+            android::base::StringPrintf("rescue-wipe:userdata:%zu", wipe_devices_message_size),
+            &error));
+    if (fd < 0) {
+        fprintf(stderr, "adb: wipe device connection failed: %s\n", error.c_str());
+        return 1;
+    }
+
+    std::string message(wipe_devices_message_size, '\0');
+    if (!ReadFdExactly(fd, message.data(), wipe_devices_message_size)) {
+        fprintf(stderr, "adb: failed to read wipe result: %s\n", strerror(errno));
+        return 1;
+    }
+
+    if (message == kMinadbdServicesExitSuccess) {
+        return 0;
+    }
+
+    if (message != kMinadbdServicesExitFailure) {
+        fprintf(stderr, "adb: got unexpected message from rescue wipe %s\n", message.c_str());
+    }
+    return 1;
+}
+
 /**
  * Run ppp in "notty" mode against a resource listed as the first parameter
  * eg:
@@ -1037,11 +1071,12 @@
     }
 
     if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" &&
-        components[3] != "recovery" && components[3] != "sideload" &&
+        components[3] != "recovery" && components[3] != "rescue" && components[3] != "sideload" &&
         components[3] != "disconnect") {
         fprintf(stderr,
                 "adb: unknown state %s; "
-                "expected 'any', 'bootloader', 'device', 'recovery', 'sideload', or 'disconnect'\n",
+                "expected 'any', 'bootloader', 'device', 'recovery', 'rescue', 'sideload', or "
+                "'disconnect'\n",
                 components[3].c_str());
         return false;
     }
@@ -1627,11 +1662,28 @@
         return adb_kill_server() ? 0 : 1;
     } else if (!strcmp(argv[0], "sideload")) {
         if (argc != 2) error_exit("sideload requires an argument");
-        if (adb_sideload_host(argv[1])) {
+        if (adb_sideload_install(argv[1], false /* rescue_mode */)) {
             return 1;
         } else {
             return 0;
         }
+    } else if (!strcmp(argv[0], "rescue")) {
+        // adb rescue getprop <prop>
+        // adb rescue install <filename>
+        // adb rescue wipe userdata
+        if (argc != 3) error_exit("rescue requires two arguments");
+        if (!strcmp(argv[1], "getprop")) {
+            return adb_connect_command(android::base::StringPrintf("rescue-getprop:%s", argv[2]));
+        } else if (!strcmp(argv[1], "install")) {
+            if (adb_sideload_install(argv[2], true /* rescue_mode */) != 0) {
+                return 1;
+            }
+        } else if (!strcmp(argv[1], "wipe") && !strcmp(argv[2], "userdata")) {
+            return adb_wipe_devices();
+        } else {
+            error_exit("invalid rescue argument");
+        }
+        return 0;
     } else if (!strcmp(argv[0], "tcpip")) {
         if (argc != 2) error_exit("tcpip requires an argument");
         int port;
diff --git a/adb/services.cpp b/adb/services.cpp
index cf346ba..335ffc4 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -227,6 +227,8 @@
             sinfo->state = kCsDevice;
         } else if (name == "-recovery") {
             sinfo->state = kCsRecovery;
+        } else if (name == "-rescue") {
+            sinfo->state = kCsRescue;
         } else if (name == "-sideload") {
             sinfo->state = kCsSideload;
         } else if (name == "-bootloader") {
diff --git a/adb/services.h b/adb/services.h
index 0ce25ba..6fc89d7 100644
--- a/adb/services.h
+++ b/adb/services.h
@@ -23,5 +23,10 @@
 constexpr char kShellServiceArgPty[] = "pty";
 constexpr char kShellServiceArgShellProtocol[] = "v2";
 
+// Special flags sent by minadbd. They indicate the end of sideload transfer and the result of
+// installation or wipe.
+constexpr char kMinadbdServicesExitSuccess[] = "DONEDONE";
+constexpr char kMinadbdServicesExitFailure[] = "FAILFAIL";
+
 unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func);
 #endif  // SERVICES_H_
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 15c3a9a..841865a 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -1012,6 +1012,8 @@
             return "host";
         case kCsRecovery:
             return "recovery";
+        case kCsRescue:
+            return "rescue";
         case kCsNoPerm:
             return UsbNoPermissionsShortHelpText();
         case kCsSideload:
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 71d3ecb..cb09433 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -25,6 +25,8 @@
 # Best guess to an average device's reboot time, refined as tests return
 DURATION_DEFAULT=45
 STOP_ON_FAILURE=false
+progname="${0##*/}"
+progpath="${0%${progname}}"
 
 # Helper functions
 
@@ -42,11 +44,40 @@
   adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
 }
 
+[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+  local args=
+  for i in "${@}"; do
+    [ -z "${args}" ] || args="${args} "
+    if [ X"${i}" != X"${i#\'}" ]; then
+      args="${args}${i}"
+    elif [ X"${i}" != X"${i#*\\}" ]; then
+      args="${args}`echo ${i} | sed 's/\\\\/\\\\\\\\/g'`"
+    elif [ X"${i}" != X"${i#* }" ]; then
+      args="${args}'${i}'"
+    elif [ X"${i}" != X"${i#*${TAB}}" ]; then
+      args="${args}'${i}'"
+    else
+      args="${args}${i}"
+    fi
+  done
+  adb shell "${args}"
+}
+
+[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+  adb_sh su root "${@}"
+}
+
 [ "USAGE: hasPstore
 
 Returns: true if device (likely) has pstore data" ]
 hasPstore() {
-  if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then
+  if inAdb && [ 0 -eq `adb_su ls /sys/fs/pstore </dev/null | wc -l` ]; then
     false
   fi
 }
@@ -55,7 +86,7 @@
 
 Returns the property value" ]
 get_property() {
-  adb shell getprop ${1} 2>&1 </dev/null
+  adb_sh getprop ${1} 2>&1 </dev/null
 }
 
 [ "USAGE: isDebuggable
@@ -89,18 +120,18 @@
 Returns: true if device supports and set boot reason injection" ]
 setBootloaderBootReason() {
   inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1
-  if [ -z "`adb shell ls /etc/init/bootstat-debug.rc 2>/dev/null`" ]; then
+  if [ -z "`adb_sh ls /etc/init/bootstat-debug.rc 2>/dev/null </dev/null`" ]; then
     echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2
     return 1
   fi
   checkDebugBuild || return 1
-  if adb shell su root "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" |
+  if adb_su "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" </dev/null |
      grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then
     echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2
     echo "       does not set androidboot.bootreason kernel parameter." >&2
     return 1
   fi
-  adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
+  adb_su setprop persist.test.boot.reason "'${1}'" 2>/dev/null </dev/null
   test_reason="`get_property persist.test.boot.reason`"
   if [ X"${test_reason}" != X"${1}" ]; then
     echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
@@ -299,7 +330,14 @@
   return ${save_ret}
 }
 
-[ "USAGE: report_bootstat_logs <expected> ...
+[ "USAGE: adb_date >/dev/stdout
+
+Returns: report device epoch time (suitable for logcat -t)" ]
+adb_date() {
+  adb_sh date +%s.%N </dev/null
+}
+
+[ "USAGE: report_bootstat_logs [-t<timestamp>] <expected> ...
 
 if not prefixed with a minus (-), <expected> will become a series of expected
 matches:
@@ -314,8 +352,11 @@
 report_bootstat_logs() {
   save_ret=${?}
   match=
+  timestamp=-d
   for i in "${@}"; do
-    if [ X"${i}" != X"${i#-}" ] ; then
+    if [ X"${i}" != X"${i#-t}" ]; then
+      timestamp="${i}"
+    elif [ X"${i}" != X"${i#-}" ]; then
       match="${match}
 ${i#-}"
     else
@@ -323,12 +364,13 @@
 bootstat: Canonical boot reason: ${i}"
     fi
   done
-  adb logcat -b all -d |
+  adb logcat -b all ${timestamp} |
   grep bootstat[^e] |
   grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
 bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
 bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
 bootstat: Service started: /system/bin/bootstat --record_boot_reason
+bootstat: Service started: /system/bin/bootstat --set_system_boot_reason
 bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
 bootstat: Service started: /system/bin/bootstat -l
 bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
@@ -341,6 +383,8 @@
 init    : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
 init    : processing action (boot) from (/system/etc/init/bootstat.rc
 init    : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init    : processing action (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc
+init    : processing action (zygote-start) from (/system/etc/init/bootstat.rc
 init    : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
  (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
  (/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
@@ -355,6 +399,8 @@
  (/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
  (/system/bin/bootstat --record_time_since_factory_reset)'...
  (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat --set_system_boot_reason)'...
+ (/system/bin/bootstat --set_system_boot_reason)' (pid${SPACE}
  (/system/bin/bootstat -l)'...
  (/system/bin/bootstat -l)' (pid " |
   grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
@@ -613,7 +659,7 @@
 test_optional_ota() {
   checkDebugBuild || return
   duration_test
-  adb shell su root touch /data/misc/bootstat/build_date >&2
+  adb_su touch /data/misc/bootstat/build_date >&2 </dev/null
   adb reboot ota
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,ota
@@ -679,7 +725,7 @@
 test_factory_reset() {
   checkDebugBuild || return
   duration_test
-  adb shell su root rm /data/misc/bootstat/build_date >&2
+  adb_su rm /data/misc/bootstat/build_date >&2 </dev/null
   adb reboot >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
@@ -715,7 +761,7 @@
   wait_for_screen
   ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
   EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
-  EXPECT_PROPERTY sys.boot.reason.last ""
+  EXPECT_PROPERTY sys.boot.reason.last "\(\|bootloader\)"
   check_boilerplate_properties
   report_bootstat_logs reboot,factory_reset bootloader \
     "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
@@ -766,12 +812,12 @@
   enterPstore
   # Send it _many_ times to combat devices with flakey pstore
   for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
-    echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+    echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
   done
   adb reboot cold >&2
   adb wait-for-device
   wait_for_screen
-  adb shell su root \
+  adb_su </dev/null \
     cat /proc/fs/pstore/console-ramoops \
         /proc/fs/pstore/console-ramoops-0 2>/dev/null |
     grep 'healthd: battery l=' |
@@ -780,7 +826,7 @@
       if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
         # retry
         for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
-          echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+          echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
         done
         adb reboot cold >&2
         adb wait-for-device
@@ -806,7 +852,7 @@
 test_optional_battery() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,battery
+  adb_sh setprop sys.powerctl shutdown,battery </dev/null
   sleep 5
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
@@ -827,7 +873,7 @@
 test_optional_battery_thermal() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,thermal,battery
+  adb_sh setprop sys.powerctl shutdown,thermal,battery </dev/null
   sleep 5
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
@@ -866,7 +912,7 @@
     panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
     pstore_ok=true
   fi
-  echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+  echo c | adb_su tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
   EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
@@ -893,8 +939,8 @@
     panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)"
     pstore_ok=true
   fi
-  echo "SysRq : Trigger a crash : 'test'" | adb shell su root tee /dev/kmsg
-  echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+  echo "SysRq : Trigger a crash : 'test'" | adb_su tee /dev/kmsg
+  echo c | adb_su tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
   EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
@@ -924,7 +970,7 @@
     pstore_ok=true
   fi
   echo "Kernel panic - not syncing: hung_task: blocked tasks" |
-    adb shell su root tee /dev/kmsg
+    adb_su tee /dev/kmsg
   adb reboot warm
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason ${panic_msg}
@@ -956,7 +1002,7 @@
 test_thermal_shutdown() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,thermal
+  adb_sh setprop sys.powerctl shutdown,thermal </dev/null
   sleep 5
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
@@ -977,7 +1023,7 @@
 test_userrequested_shutdown() {
   duration_test ">60"
   echo "      power on request" >&2
-  adb shell setprop sys.powerctl shutdown,userrequested
+  adb_sh setprop sys.powerctl shutdown,userrequested </dev/null
   sleep 5
   echo -n "WARNING: Please power device back up, waiting ... " >&2
   wait_for_screen -n >&2
@@ -996,7 +1042,7 @@
 - NB: should report reboot,shell" ]
 test_shell_reboot() {
   duration_test
-  adb shell reboot
+  adb_sh reboot </dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,shell
   EXPECT_PROPERTY sys.boot.reason.last reboot,shell
@@ -1032,7 +1078,7 @@
 test_optional_rescueparty() {
   blind_reboot_test
   echo "WARNING: legacy devices are allowed to fail following ro.boot.bootreason result" >&2
-  EXPECT_PROPERTY ro.boot.bootreason reboot,rescueparty
+  EXPECT_PROPERTY ro.boot.bootreason '\(reboot\|reboot,rescueparty\)'
 }
 
 [ "USAGE: test_Its_Just_So_Hard_reboot
@@ -1049,7 +1095,7 @@
   else
     duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
   fi
-  adb shell 'reboot "Its Just So Hard"'
+  adb_sh 'reboot "Its Just So Hard"' </dev/null
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
   EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
@@ -1146,7 +1192,113 @@
   run_bootloader
 }
 
-[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+[ "USAGE: run_kBootReasonMap [--boot_reason_enum] value expected
+
+bootloader boot reason injection tests:
+- if --boot_reason_enum run bootstat executable for result instead.
+- inject boot reason into sys.boot.reason
+- run bootstat --set_system_boot_reason
+- check for expected enum
+- " ]
+run_kBootReasonMap() {
+  if [ X"--boot_reason_enum" = X"${1}" ]; then
+    shift
+    local sys_expected="${1}"
+    shift
+    local enum_expected="${1}"
+    adb_su bootstat --boot_reason_enum="${sys_expected}" |
+      (
+        local retval=-1
+        while read -r id match; do
+          if [ ${retval} = -1 -a ${enum_expected} = ${id} ]; then
+            retval=0
+          fi
+          if [ ${enum_expected} != ${id} ]; then
+            echo "ERROR: ${enum_expected} ${sys_expected} got ${id} ${match}" >&2
+            retval=1
+          fi
+        done
+        exit ${retval}
+      )
+    return
+  fi
+  local sys_expected="${1}"
+  shift
+  local enum_expected="${1}"
+  adb_su setprop sys.boot.reason "${sys_expected}" </dev/null
+  adb_su bootstat --record_boot_reason </dev/null
+  # Check values
+  EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
+  local retval=${?}
+  local result=`adb_su stat -c %Y /data/misc/bootstat/system_boot_reason </dev/null 2>/dev/null`
+  [ "${enum_expected}" = "${result}" ] ||
+    (
+      [ -n "${result}" ] || result="<nothing>"
+      echo "ERROR: ${enum_expected} ${sys_expected} got ${result}" >&2
+      false
+    ) ||
+    retval=${?}
+  return ${retval}
+}
+
+[ "USAGE: filter_kBootReasonMap </dev/stdin >/dev/stdout
+
+convert any regex expressions into a series of non-regex test strings" ]
+filter_kBootReasonMap() {
+  while read -r id match; do
+    case ${match} in
+      'reboot,[empty]')
+        echo ${id}          # matches b/c of special case
+        echo ${id} reboot,y # matches b/c of regex
+        echo 1 reboot,empty # negative test (ID for unknown is 1)
+        ;;
+      reboot)
+        echo 1 reboo        # negative test (ID for unknown is 1)
+        ;;
+    esac
+    echo ${id} "${match}"   # matches b/c of exact
+  done
+}
+
+[ "USAGE: test_kBootReasonMap
+
+kBootReasonMap test
+- (wait until screen is up, boot has completed)
+- read bootstat for kBootReasonMap entries and test them all" ]
+test_kBootReasonMap() {
+  local tempfile="`mktemp`"
+  local arg=--boot_reason_enum
+  adb_su bootstat ${arg} </dev/null 2>/dev/null |
+    filter_kBootReasonMap >${tempfile}
+  if [ ! -s "${tempfile}" ]; then
+    wait_for_screen
+    arg=
+    sed -n <${progpath}bootstat.cpp \
+      '/kBootReasonMap = {/,/^};/s/.*{"\([^"]*\)", *\([0-9][0-9]*\)},.*/\2 \1/p' |
+      sed 's/\\\\/\\/g' |
+      filter_kBootReasonMap >${tempfile}
+  fi
+  T=`adb_date`
+  retval=0
+  while read -r enum string; do
+    if [ X"${string}" != X"${string#*[[].[]]}" -o X"${string}" != X"${string#*\\.}" ]; then
+      if [ 'reboot\.empty' != "${string}" ]; then
+        echo "WARNING: regex snuck through filter_kBootReasonMap ${enum} ${string}" >&2
+        enum=1
+      fi
+    fi
+    run_kBootReasonMap ${arg} "${string}" "${enum}" </dev/null || retval=${?}
+  done <${tempfile}
+  rm ${tempfile}
+  ( exit ${retval} )
+  # See filter_kBootReasonMap() for negative tests and add them here too
+  report_bootstat_logs -t${T} \
+    '-bootstat: Service started: bootstat --boot_reason_enum=' \
+    '-bootstat: Unknown boot reason: reboot,empty' \
+    '-bootstat: Unknown boot reason: reboo'
+}
+
+[ "USAGE: ${progname} [-s SERIAL] [tests]...
 
 Mainline executive to run the above tests" ]
 
@@ -1161,7 +1313,7 @@
 if [ X"--macros" != X"${1}" ]; then
 
   if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
-    echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+    echo "USAGE: ${progname} [-s SERIAL] [tests]..."
     echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
     exit 0
   fi
@@ -1210,7 +1362,7 @@
                Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
                bootloader_kernel_panic bootloader_oem_powerkey \
                bootloader_wdog_reset bootloader_cold bootloader_warm \
-               bootloader_hard bootloader_recovery
+               bootloader_hard bootloader_recovery kBootReasonMap
     fi
     if [ X"nothing" = X"${1}" ]; then
       shift 1
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1ce0ec4..617ea4f 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -89,7 +89,7 @@
 }
 
 void ShowHelp(const char* cmd) {
-  fprintf(stderr, "Usage: %s [options]\n", cmd);
+  fprintf(stderr, "Usage: %s [options]...\n", cmd);
   fprintf(stderr,
           "options include:\n"
           "  -h, --help              Show this help\n"
@@ -99,7 +99,8 @@
           "  --value                 Optional value to associate with the boot event\n"
           "  --record_boot_complete  Record metrics related to the time for the device boot\n"
           "  --record_boot_reason    Record the reason why the device booted\n"
-          "  --record_time_since_factory_reset Record the time since the device was reset\n");
+          "  --record_time_since_factory_reset  Record the time since the device was reset\n"
+          "  --boot_reason_enum=<reason>  Report the match to the kBootReasonMap table\n");
 }
 
 // Constructs a readable, printable string from the givencommand line
@@ -120,9 +121,10 @@
 // A mapping from boot reason string, as read from the ro.boot.bootreason
 // system property, to a unique integer ID. Viewers of log data dashboards for
 // the boot_reason metric may refer to this mapping to discern the histogram
-// values.
+// values.  Regex matching, to manage the scale, as a minimum require either
+// [, \ or * to be present in the string to switch to checking.
 const std::map<std::string, int32_t> kBootReasonMap = {
-    {"empty", kEmptyBootReason},
+    {"reboot,[empty]", kEmptyBootReason},
     {"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
     {"normal", 2},
     {"recovery", 3},
@@ -314,6 +316,16 @@
     return kEmptyBootReason;
   }
 
+  for (const auto& [match, id] : kBootReasonMap) {
+    // Regex matches as a minimum require either [, \ or * to be present.
+    if (match.find_first_of("[\\*") == match.npos) continue;
+    // enforce match from beginning to end
+    auto exact = match;
+    if (exact[0] != '^') exact = "^" + exact;
+    if (exact[exact.size() - 1] != '$') exact = exact + "$";
+    if (std::regex_search(boot_reason, std::regex(exact))) return id;
+  }
+
   LOG(INFO) << "Unknown boot reason: " << boot_reason;
   return kUnknownBootReason;
 }
@@ -1266,6 +1278,19 @@
   boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset);
 }
 
+// List the associated boot reason(s), if arg is nullptr then all.
+void PrintBootReasonEnum(const char* arg) {
+  int value = -1;
+  if (arg != nullptr) {
+    value = BootReasonStrToEnum(arg);
+  }
+  for (const auto& [match, id] : kBootReasonMap) {
+    if ((value < 0) || (value == id)) {
+      printf("%u\t%s\n", id, match.c_str());
+    }
+  }
+}
+
 }  // namespace
 
 int main(int argc, char** argv) {
@@ -1280,6 +1305,7 @@
   static const char boot_complete_str[] = "record_boot_complete";
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
+  static const char boot_reason_enum_str[] = "boot_reason_enum";
   static const struct option long_options[] = {
       // clang-format off
       { "help",                 no_argument,       NULL,   'h' },
@@ -1291,6 +1317,7 @@
       { boot_complete_str,      no_argument,       NULL,   0 },
       { boot_reason_str,        no_argument,       NULL,   0 },
       { factory_reset_str,      no_argument,       NULL,   0 },
+      { boot_reason_enum_str,   optional_argument, NULL,   0 },
       { NULL,                   0,                 NULL,   0 }
       // clang-format on
   };
@@ -1315,6 +1342,8 @@
           RecordBootReason();
         } else if (option_name == factory_reset_str) {
           RecordFactoryReset();
+        } else if (option_name == boot_reason_enum_str) {
+          PrintBootReasonEnum(optarg);
         } else {
           LOG(ERROR) << "Invalid option: " << option_name;
         }
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 70583af..eb4b1b8 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -23,6 +23,9 @@
 
 struct ThreadInfo {
   std::unique_ptr<unwindstack::Regs> registers;
+
+  pid_t uid;
+
   pid_t tid;
   std::string thread_name;
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 3196ce8..88c206f 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -343,6 +343,16 @@
   ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
 }
 
+TEST_F(TombstoneTest, dump_thread_info_uid) {
+  dump_thread_info(&log_, ThreadInfo{.uid = 1,
+                                     .pid = 2,
+                                     .tid = 3,
+                                     .thread_name = "some_thread",
+                                     .process_name = "some_process"});
+  std::string expected = "pid: 2, tid: 3, name: some_thread  >>> some_process <<<\nuid: 1\n";
+  ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
+}
+
 TEST_F(TombstoneTest, dump_timestamp) {
   setenv("TZ", "UTC", 1);
   tzset();
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index d1726cd..d246722 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -151,6 +151,7 @@
 
   _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", thread_info.pid,
        thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
+  _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
 }
 
 static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
@@ -615,6 +616,7 @@
 
 void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext) {
+  pid_t uid = getuid();
   pid_t pid = getpid();
   pid_t tid = gettid();
 
@@ -636,6 +638,7 @@
   std::map<pid_t, ThreadInfo> threads;
   threads[gettid()] = ThreadInfo{
       .registers = std::move(regs),
+      .uid = uid,
       .tid = tid,
       .thread_name = thread_name,
       .pid = pid,
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 012abd4..8a6df7d 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -634,7 +634,7 @@
         LERROR << mnt_type << " has no mkfs cookbook";
         return false;
     }
-    command += " " + scratch_device;
+    command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
     fs_mgr_set_blk_ro(scratch_device, false);
     auto ret = system(command.c_str());
     if (ret) {
diff --git a/init/README.md b/init/README.md
index d86f077..929f0e4 100644
--- a/init/README.md
+++ b/init/README.md
@@ -191,7 +191,7 @@
 
 `critical`
 > This is a device-critical service. If it exits more than four times in
-  four minutes, the device will reboot into bootloader.
+  four minutes or before boot completes, the device will reboot into bootloader.
 
 `disabled`
 > This service will not automatically start with its class.
@@ -412,6 +412,10 @@
   not already running.  See the start entry for more information on
   starting services.
 
+`class_start_post_data <serviceclass>`
+> Like `class_start`, but only considers services that were started
+  after /data was mounted. Only used for FDE devices.
+
 `class_stop <serviceclass>`
 > Stop and disable all services of the specified class if they are
   currently running.
@@ -421,6 +425,10 @@
   currently running, without disabling them. They can be restarted
   later using `class_start`.
 
+`class_reset_post_data <serviceclass>`
+> Like `class_reset`, but only considers services that were started
+  after /data was mounted. Only used for FDE devices.
+
 `class_restart <serviceclass>`
 > Restarts all services of the specified class.
 
@@ -490,6 +498,10 @@
 `loglevel <level>`
 > Sets the kernel log level to level. Properties are expanded within _level_.
 
+`mark_post_data`
+> Used to mark the point right after /data is mounted. Used to implement the
+  `class_reset_post_data` and `class_start_post_data` commands.
+
 `mkdir <path> [mode] [owner] [group]`
 > Create a directory at _path_, optionally with the given mode, owner, and
   group. If not provided, the directory is created with permissions 755 and
diff --git a/init/builtins.cpp b/init/builtins.cpp
index fc75072..34f229b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -104,23 +104,37 @@
     }
 }
 
-static Result<Success> do_class_start(const BuiltinArguments& args) {
+static Result<Success> class_start(const std::string& class_name, bool post_data_only) {
     // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
-    if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+    if (android::base::GetBoolProperty("persist.init.dont_start_class." + class_name, false))
         return Success();
     // Starting a class does not start services which are explicitly disabled.
     // They must  be started individually.
     for (const auto& service : ServiceList::GetInstance()) {
-        if (service->classnames().count(args[1])) {
+        if (service->classnames().count(class_name)) {
+            if (post_data_only && !service->is_post_data()) {
+                continue;
+            }
             if (auto result = service->StartIfNotDisabled(); !result) {
                 LOG(ERROR) << "Could not start service '" << service->name()
-                           << "' as part of class '" << args[1] << "': " << result.error();
+                           << "' as part of class '" << class_name << "': " << result.error();
             }
         }
     }
     return Success();
 }
 
+static Result<Success> do_class_start(const BuiltinArguments& args) {
+    return class_start(args[1], false /* post_data_only */);
+}
+
+static Result<Success> do_class_start_post_data(const BuiltinArguments& args) {
+    if (args.context != kInitContext) {
+        return Error() << "command 'class_start_post_data' only available in init context";
+    }
+    return class_start(args[1], true /* post_data_only */);
+}
+
 static Result<Success> do_class_stop(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Stop);
     return Success();
@@ -131,6 +145,14 @@
     return Success();
 }
 
+static Result<Success> do_class_reset_post_data(const BuiltinArguments& args) {
+    if (args.context != kInitContext) {
+        return Error() << "command 'class_reset_post_data' only available in init context";
+    }
+    ForEachServiceInClass(args[1], &Service::ResetIfPostData);
+    return Success();
+}
+
 static Result<Success> do_class_restart(const BuiltinArguments& args) {
     // Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
     if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
@@ -1119,6 +1141,12 @@
         {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
 }
 
+static Result<Success> do_mark_post_data(const BuiltinArguments& args) {
+    ServiceList::GetInstance().MarkPostData();
+
+    return Success();
+}
+
 static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) {
     glob_t glob_result;
     // @ is added to filter out the later paths, which are bind mounts of the places
@@ -1170,8 +1198,10 @@
         {"chmod",                   {2,     2,    {true,   do_chmod}}},
         {"chown",                   {2,     3,    {true,   do_chown}}},
         {"class_reset",             {1,     1,    {false,  do_class_reset}}},
+        {"class_reset_post_data",   {1,     1,    {false,  do_class_reset_post_data}}},
         {"class_restart",           {1,     1,    {false,  do_class_restart}}},
         {"class_start",             {1,     1,    {false,  do_class_start}}},
+        {"class_start_post_data",   {1,     1,    {false,  do_class_start_post_data}}},
         {"class_stop",              {1,     1,    {false,  do_class_stop}}},
         {"copy",                    {2,     2,    {true,   do_copy}}},
         {"domainname",              {1,     1,    {true,   do_domainname}}},
@@ -1191,6 +1221,7 @@
         {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
         {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
         {"loglevel",                {1,     1,    {false,  do_loglevel}}},
+        {"mark_post_data",          {0,     0,    {false,  do_mark_post_data}}},
         {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
         // TODO: Do mount operations in vendor_init.
         // mount_all is currently too complex to run in vendor_init as it queues action triggers,
diff --git a/init/service.cpp b/init/service.cpp
index f5c13b9..2f96681 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -362,7 +362,7 @@
 
     // Oneshot processes go into the disabled state on exit,
     // except when manually restarted.
-    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
+    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) {
         flags_ |= SVC_DISABLED;
     }
 
@@ -372,16 +372,20 @@
         return;
     }
 
-    // If we crash > 4 times in 4 minutes, reboot into bootloader or set crashing property
+    // If we crash > 4 times in 4 minutes or before boot_completed,
+    // reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
     if (((flags_ & SVC_CRITICAL) || !pre_apexd_) && !(flags_ & SVC_RESTART)) {
-        if (now < time_crashed_ + 4min) {
+        bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
+        if (now < time_crashed_ + 4min || !boot_completed) {
             if (++crash_count_ > 4) {
                 if (flags_ & SVC_CRITICAL) {
                     // Aborts into bootloader
-                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+                    LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+                               << (boot_completed ? "in 4 minutes" : "before boot completed");
                 } else {
-                    LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times in 4 minutes";
+                    LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
+                               << (boot_completed ? "in 4 minutes" : "before boot completed");
                     // Notifies update_verifier and apexd
                     property_set("ro.init.updatable_crashing", "1");
                 }
@@ -947,6 +951,8 @@
         pre_apexd_ = true;
     }
 
+    post_data_ = ServiceList::GetInstance().IsPostData();
+
     LOG(INFO) << "starting service '" << name_ << "'...";
 
     pid_t pid = -1;
@@ -1146,6 +1152,12 @@
     StopOrReset(SVC_RESET);
 }
 
+void Service::ResetIfPostData() {
+    if (post_data_) {
+        StopOrReset(SVC_RESET);
+    }
+}
+
 void Service::Stop() {
     StopOrReset(SVC_DISABLED);
 }
@@ -1339,6 +1351,14 @@
     }
 }
 
+void ServiceList::MarkPostData() {
+    post_data_ = true;
+}
+
+bool ServiceList::IsPostData() {
+    return post_data_;
+}
+
 void ServiceList::MarkServicesUpdate() {
     services_update_finished_ = true;
 
diff --git a/init/service.h b/init/service.h
index c42a5a3..dc2b128 100644
--- a/init/service.h
+++ b/init/service.h
@@ -81,6 +81,7 @@
     Result<Success> StartIfNotDisabled();
     Result<Success> Enable();
     void Reset();
+    void ResetIfPostData();
     void Stop();
     void Terminate();
     void Timeout();
@@ -124,6 +125,7 @@
     std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
     const std::vector<std::string>& args() const { return args_; }
     bool is_updatable() const { return updatable_; }
+    bool is_post_data() const { return post_data_; }
 
   private:
     using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
@@ -244,6 +246,8 @@
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
 
     bool pre_apexd_ = false;
+
+    bool post_data_ = false;
 };
 
 class ServiceList {
@@ -285,6 +289,8 @@
     const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
     const std::vector<Service*> services_in_shutdown_order() const;
 
+    void MarkPostData();
+    bool IsPostData();
     void MarkServicesUpdate();
     bool IsServicesUpdated() const { return services_update_finished_; }
     void DelayService(const Service& service);
@@ -292,6 +298,7 @@
   private:
     std::vector<std::unique_ptr<Service>> services_;
 
+    bool post_data_ = false;
     bool services_update_finished_ = false;
     std::vector<std::string> delayed_service_names_;
 };
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
index 5e89254..cb3757d 100644
--- a/libmeminfo/tools/procrank.cpp
+++ b/libmeminfo/tools/procrank.cpp
@@ -42,7 +42,6 @@
   public:
     ProcessRecord(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0)
         : pid_(-1),
-          procmem_(nullptr),
           oomadj_(OOM_SCORE_ADJ_MAX + 1),
           cmdline_(""),
           proportional_swap_(0),
@@ -79,15 +78,15 @@
         // The .c_str() assignment below then takes care of trimming the cmdline at the first
         // 0x00. This is how original procrank worked (luckily)
         cmdline_.resize(strlen(cmdline_.c_str()));
-        procmem_ = std::move(procmem);
+        usage_or_wss_ = get_wss ? procmem->Wss() : procmem->Usage();
+        swap_offsets_ = procmem->SwapOffsets();
         pid_ = pid;
     }
 
     bool valid() const { return pid_ != -1; }
 
     void CalculateSwap(const uint16_t* swap_offset_array, float zram_compression_ratio) {
-        const std::vector<uint16_t>& swp_offs = procmem_->SwapOffsets();
-        for (auto& off : swp_offs) {
+        for (auto& off : swap_offsets_) {
             proportional_swap_ += getpagesize() / swap_offset_array[off];
             unique_swap_ += swap_offset_array[off] == 1 ? getpagesize() : 0;
             zswap_ = proportional_swap_ * zram_compression_ratio;
@@ -103,18 +102,19 @@
     uint64_t zswap() const { return zswap_; }
 
     // Wrappers to ProcMemInfo
-    const std::vector<uint16_t>& SwapOffsets() const { return procmem_->SwapOffsets(); }
-    const MemUsage& Usage() const { return procmem_->Usage(); }
-    const MemUsage& Wss() const { return procmem_->Wss(); }
+    const std::vector<uint16_t>& SwapOffsets() const { return swap_offsets_; }
+    const MemUsage& Usage() const { return usage_or_wss_; }
+    const MemUsage& Wss() const { return usage_or_wss_; }
 
   private:
     pid_t pid_;
-    std::unique_ptr<ProcMemInfo> procmem_;
     int32_t oomadj_;
     std::string cmdline_;
     uint64_t proportional_swap_;
     uint64_t unique_swap_;
     uint64_t zswap_;
+    MemUsage usage_or_wss_;
+    std::vector<uint16_t> swap_offsets_;
 };
 
 // Show working set instead of memory consumption
@@ -171,7 +171,7 @@
     while ((dir = readdir(procdir.get()))) {
         if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
         if (!for_each_pid(pid)) return false;
-        pids->push_back(pid);
+        pids->emplace_back(pid);
     }
 
     return true;
@@ -471,7 +471,7 @@
         }
 
         // Skip processes with no memory mappings
-        uint64_t vss = proc.Usage().vss;
+        uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss;
         if (vss == 0) return true;
 
         // collect swap_offset counts from all processes in 1st pass
@@ -481,7 +481,7 @@
             return false;
         }
 
-        procs.push_back(std::move(proc));
+        procs.emplace_back(std::move(proc));
         return true;
     };
 
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5cc0857..e460b1a 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -220,7 +220,9 @@
       }
     }
 
-    if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
+    // Initialize the anonymous namespace with the first non-empty library path.
+    if (!library_path.empty() && !initialized_ &&
+        !InitPublicNamespace(library_path.c_str(), error_msg)) {
       return nullptr;
     }
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index dfde53c..cb45c42 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -405,6 +405,8 @@
     class_start early_hal
 
 on post-fs-data
+    mark_post_data
+
     # Start checkpoint before we touch data
     start vold
     exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
@@ -753,9 +755,6 @@
 on charger
     class_start charger
 
-on property:vold.decrypt=trigger_reset_main
-    class_reset main
-
 on property:vold.decrypt=trigger_load_persist_props
     load_persist_props
     start logd
@@ -773,6 +772,8 @@
 on property:vold.decrypt=trigger_restart_framework
     # A/B update verifier that marks a successful boot.
     exec_start update_verifier
+    class_start_post_data hal
+    class_start_post_data core
     class_start main
     class_start late_start
     setprop service.bootanim.exit 0
@@ -781,6 +782,8 @@
 on property:vold.decrypt=trigger_shutdown_framework
     class_reset late_start
     class_reset main
+    class_reset_post_data core
+    class_reset_post_data hal
 
 on property:sys.boot_completed=1
     bootchart stop