Merge "init+bootstat: reduce last reboot reason to canonical alias" am: 613a5a97e0 am: cfed5e795a am: 75374ba1d2
am: c0edd4a633

Change-Id: If2241b605f10ea2ea54a1feaf0631703d23ae55e
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index b76011a..5425fd3 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -32,6 +32,78 @@
   fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
 }
 
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+  adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "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
+    false
+  fi
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+  if inAdb && [ 1 -ne `adb shell getprop ro.debuggable` ]; then
+    false
+  fi
+}
+
+[ "USAGE: checkDebugBuild
+
+Returns: true if device is a userdebug or eng release" ]
+checkDebugBuild() {
+  if isDebuggable; then
+    echo "INFO: '${TEST}' test requires userdebug build"
+  else
+    echo "ERROR: '${TEST}' test requires userdebug build, skipping FAILURE"
+    false
+  fi >&2
+}
+
+[ "USAGE: enterPstore
+
+Prints a warning string requiring functional pstore
+
+Returns: pstore_ok variable set to true or false" ]
+enterPstore() {
+  if hasPstore; then
+    echo "INFO: '${TEST}' test requires functional and reliable pstore"
+    pstore_ok=true
+  else
+    echo "WARNING: '${TEST}' test requires functional pstore"
+    pstore_ok=false
+  fi >&2
+  ${pstore_ok}
+}
+
+[ "USAGE: exitPstore
+
+Prints an error string requiring functional pstore
+
+Returns: clears error if pstore dysfunctional" ]
+exitPstore() {
+  save_ret=${?}
+  if [ ${save_ret} != 0 ]; then
+    if hasPstore; then
+      return ${save_ret}
+    fi
+    if [ true = ${pstore_ok} ]; then
+      echo "WARNING: '${TEST}' test requires functional pstore"
+      return ${save_ret}
+    fi
+    echo "ERROR: '${TEST}' test requires functional pstore, skipping FAILURE"
+  fi >&2
+}
+
 [ "USAGE: format_duration <seconds>
 
 human readable output whole seconds, whole minutes or mm:ss" ]
@@ -77,17 +149,25 @@
   fi
   counter=0
   while true; do
-    [ 0 = ${counter} ] ||
-      adb wait-for-device </dev/null >/dev/null 2>/dev/null
-    vals=`adb shell getprop </dev/null 2>/dev/null |
-          sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
-    [ 0 = ${counter} ] ||
-      sleep 1
-    if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]; then
-      break
-    fi
-    if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]; then
-      break
+    if inFastboot; then
+      fastboot reboot
+    elif inAdb; then
+      if [ 0 != ${counter} ]; then
+        adb wait-for-device </dev/null >/dev/null 2>/dev/null
+      fi
+      if [ -n "`adb shell getprop sys.boot.reason </dev/null 2>/dev/null`" ]
+      then
+        vals=`adb shell getprop </dev/null 2>/dev/null |
+              sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+        if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
+        then
+          break
+        fi
+        if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
+        then
+          break
+        fi
+      fi
     fi
     counter=`expr ${counter} + 1`
     if [ ${counter} -gt ${timeout} ]; then
@@ -95,6 +175,7 @@
       echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
       return 1
     fi
+    sleep 1
   done
   ${exit_function}
 }
@@ -152,7 +233,7 @@
 report_bootstat_logs() {
   save_ret=${?}
   match=
-  for i in ${*}; do
+  for i in "${@}"; do
     if [ X"${i}" != X"${i#-}" ] ; then
       match="${match}
 ${i#-}"
@@ -211,7 +292,7 @@
   END=`date +%s`
   duration=`expr ${END} - ${START} 2>/dev/null`
   [ 0 = ${duration} ] ||
-    echo INFO: ${TEST} test duration `format_duration ${duration}` >&2
+    echo "INFO: '${TEST}' test duration `format_duration ${duration}`" >&2
   if [ ${save_ret} = 0 ]; then
     echo "${GREEN}[       OK ]${NORMAL} ${TEST} ${*}"
   else
@@ -329,7 +410,7 @@
 Decision to change the build itself rather than trick bootstat by
 rummaging through its data files was made." ]
 test_ota() {
-  echo "INFO: expected duration of ${TEST} test about 5 minutes or more" >&2
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 300`" >&2
   echo "      extended by build and flashing times" >&2
   if [ -z "${TARGET_PRODUCT}" -o \
        -z "${ANDROID_PRODUCT_OUT}" -o \
@@ -361,7 +442,7 @@
   popd >&2
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,bootloader
+  EXPECT_PROPERTY persist.sys.boot.reason bootloader
   report_bootstat_logs reboot,ota bootloader
 }
 
@@ -369,8 +450,8 @@
 
 fast and fake (touch build_date on device to make it different)" ]
 test_optional_ota() {
-  echo "INFO: expected duration of ${TEST} test about 45 seconds" >&2
-  echo "WARNING: ${TEST} requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb shell su root touch /data/misc/bootstat/build_date >&2
   adb reboot ota
   wait_for_screen
@@ -379,27 +460,28 @@
   report_bootstat_logs reboot,ota
 }
 
-[ "USAGE: [TEST=<test>] blind_reboot_test [<match>]
+[ "USAGE: [TEST=<test>] blind_reboot_test
 
 Simple tests helper
 - adb reboot <test>
 - (wait until screen is up, boot has completed)
 - adb shell getprop sys.boot.reason
-- NB: should report <test>, or overriden <match>
+- NB: should report <test>, or reboot,<test> depending on canonical rules
 
 We interleave the simple reboot tests between the hard/complex ones
 as a means of checking sanity and any persistent side effect of the
 other tests." ]
 blind_reboot_test() {
-  if [ -z "${1}" ]; then
-    set ${TEST}
-  fi
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+  case ${TEST} in
+    bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
+    *)                                           reason=reboot,${TEST} ;;
+  esac
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb reboot ${TEST}
   wait_for_screen
-  EXPECT_PROPERTY sys.boot.reason ${1}
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,${TEST}
-  report_bootstat_logs ${1}
+  EXPECT_PROPERTY sys.boot.reason ${reason}
+  EXPECT_PROPERTY persist.sys.boot.reason ${reason}
+  report_bootstat_logs ${reason}
 }
 
 [ "USAGE: test_cold
@@ -425,8 +507,8 @@
 Decision to rummage through bootstat data files was made as
 a _real_ factory_reset is too destructive to the device." ]
 test_factory_reset() {
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
-  echo "WARNING: ${TEST} requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb shell su root rm /data/misc/bootstat/build_date >&2
   adb reboot >&2
   wait_for_screen
@@ -449,7 +531,7 @@
 
 For realz, and disruptive" ]
 test_optional_factory_reset() {
-  echo "INFO: expected duration of ${TEST} test roughly a minute" >&2
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 60`" >&2
   if ! inFastboot; then
     adb reboot-bootloader
   fi
@@ -502,8 +584,9 @@
 - NB: should report reboot,battery
 - (replace set logd.kernel true to the above, and retry test)" ]
 test_battery() {
-  echo "INFO: expected duration of ${TEST} test roughly two minutes" >&2
-  echo "WARNING: ${TEST} requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 120`" >&2
+  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
@@ -529,8 +612,49 @@
     )
 
   EXPECT_PROPERTY sys.boot.reason shutdown,battery
-  EXPECT_PROPERTY persist.sys.boot.reason reboot,cold
+  EXPECT_PROPERTY persist.sys.boot.reason cold
   report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
+  exitPstore
+}
+
+[ "USAGE: test_optional_battery
+
+battery shutdown test:
+- adb shell setprop sys.powerctl shutdown,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,battery" ]
+test_optional_battery() {
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 60`" >&2
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,battery
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY persist.sys.boot.reason shutdown,battery
+  report_bootstat_logs shutdown,battery
+}
+
+[ "USAGE: test_optional_battery_thermal
+
+battery thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal,battery" ]
+test_optional_battery_thermal() {
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 60`" >&2
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,thermal,battery
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
+  EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal,battery
+  report_bootstat_logs shutdown,thermal,battery
 }
 
 [ "USAGE: test_unknown
@@ -542,7 +666,7 @@
 - NB: should report reboot,unknown
 - NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ]
 test_unknown() {
-  blind_reboot_test reboot,unknown
+  blind_reboot_test
 }
 
 [ "USAGE: test_kernel_panic
@@ -553,13 +677,16 @@
 - adb shell getprop sys.boot.reason
 - NB: should report kernel_panic,sysrq" ]
 test_kernel_panic() {
-  echo "INFO: expected duration of ${TEST} test > 2 minutes" >&2
-  echo "WARNING: ${TEST} requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 120`" >&2
+  panic_msg="kernel_panic,sysrq"
+  enterPstore || panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
   echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
   wait_for_screen
-  EXPECT_PROPERTY sys.boot.reason kernel_panic,sysrq
-  EXPECT_PROPERTY persist.sys.boot.reason kernel_panic,sysrq
+  EXPECT_PROPERTY sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
   report_bootstat_logs kernel_panic,sysrq
+  exitPstore
 }
 
 [ "USAGE: test_warm
@@ -582,7 +709,7 @@
 - adb shell getprop sys.boot.reason
 - NB: should report shutdown,thermal" ]
 test_thermal_shutdown() {
-  echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 60`" >&2
   echo "      power on request" >&2
   adb shell setprop sys.powerctl shutdown,thermal
   sleep 5
@@ -602,7 +729,7 @@
 - adb shell getprop sys.boot.reason
 - NB: should report shutdown,userrequested" ]
 test_userrequested_shutdown() {
-  echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
+  echo "INFO: expected duration of '${TEST}' test >`format_duration 60`" >&2
   echo "      power on request" >&2
   adb shell setprop sys.powerctl shutdown,userrequested
   sleep 5
@@ -621,7 +748,7 @@
 - adb shell getprop sys.boot.reason
 - NB: should report reboot,shell" ]
 test_shell_reboot() {
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb shell reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,shell
@@ -637,7 +764,7 @@
 - adb shell getprop sys.boot.reason
 - NB: should report reboot,adb" ]
 test_adb_reboot() {
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb reboot
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,adb
@@ -654,8 +781,8 @@
 - NB: should report reboot,its_just_so_hard
 - NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ]
 test_Its_Just_So_Hard_reboot() {
-  echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
-  echo "INFO: ${TEST} cleanup requires userdebug build" >&2
+  checkDebugBuild || return
+  echo "INFO: expected duration of '${TEST}' test ~`format_duration 45`" >&2
   adb shell 'reboot "Its Just So Hard"'
   wait_for_screen
   EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d26cf85..1954966 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -233,6 +233,7 @@
     "cold",
     "hard",
     "warm",
+    // super blunt
     "shutdown",    // Can not happen from ro.boot.bootreason
     "reboot",      // Default catch-all for anything unknown
     // clang-format on
@@ -543,19 +544,9 @@
       std::transform(content.begin(), content.end(), content.begin(), tounderline);
       std::transform(content.begin(), content.end(), content.begin(), toprintable);
 
-      // String is either "reboot,<reason>" or "shutdown,<reason>".
-      // We will set if default reasons, only override with detail if thermal.
-      if ((android::base::StartsWith(content, ret.c_str()) && (content[ret.length()] == ',')) ||
-          !isBluntRebootReason(content)) {
-        // Ok, we want it, let's squash it if secondReason is known.
-        size_t pos = content.find(',');
-        if (pos != std::string::npos) {
-          ++pos;
-          std::string secondReason(content.substr(pos));
-          ret = isKnownRebootReason(secondReason) ? secondReason : content;
-        } else {
-          ret = content;
-        }
+      // Anything in last is better than 'super-blunt' reboot or shutdown.
+      if ((ret == "") || (ret == "reboot") || (ret == "shutdown") || !isBluntRebootReason(content)) {
+        ret = content;
       }
     }
 
@@ -611,17 +602,11 @@
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
     LOG(INFO) << "Canonical boot reason: reboot,factory_reset";
     SetProperty(system_reboot_reason_property, "reboot,factory_reset");
-    if (GetProperty(bootloader_reboot_reason_property) == "") {
-      SetProperty(bootloader_reboot_reason_property, "reboot,factory_reset");
-    }
   } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
     LOG(INFO) << "Canonical boot reason: reboot,ota";
     SetProperty(system_reboot_reason_property, "reboot,ota");
-    if (GetProperty(bootloader_reboot_reason_property) == "") {
-      SetProperty(bootloader_reboot_reason_property, "reboot,ota");
-    }
   }
 
   return boot_complete_prefix;
diff --git a/init/reboot.cpp b/init/reboot.cpp
index a4a20f3..b17dbaf 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -56,6 +56,7 @@
 #include "service.h"
 #include "sigchld_handler.h"
 
+using android::base::Split;
 using android::base::StringPrintf;
 using android::base::Timer;
 
@@ -346,7 +347,16 @@
     Timer t;
     LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
 
-    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str());
+    // Ensure last reboot reason is reduced to canonical
+    // alias reported in bootloader or system boot reason.
+    size_t skip = 0;
+    std::vector<std::string> reasons = Split(reason, ",");
+    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+         reasons[1] == "hard" || reasons[1] == "warm")) {
+        skip = strlen("reboot,");
+    }
+    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
     sync();
 
     bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
@@ -469,7 +479,7 @@
 
 bool HandlePowerctlMessage(const std::string& command) {
     unsigned int cmd = 0;
-    std::vector<std::string> cmd_params = android::base::Split(command, ",");
+    std::vector<std::string> cmd_params = Split(command, ",");
     std::string reboot_target = "";
     bool run_fsck = false;
     bool command_invalid = false;