#! /bin/bash # # Bootstat boot reason tests # # throughout testing: # - manual tests can only run on eng/userdebug builds # - watch adb logcat -b all -d -s bootstat # - watch adb logcat -b all -d | audit2allow # - wait until screen is up, boot has completed, can mean wait for # sys.boot_completed=1 and sys.bootstat.first_boot_completed=1 to be true # # All test frames, and nothing else, must be function names prefixed and # specifiged with the pattern 'test_() {' as this is also how the # script discovers the full list of tests by inspecting its own code. # # Helper variables SPACE=" " ESCAPE="" TAB=" " GREEN="${ESCAPE}[38;5;40m" RED="${ESCAPE}[38;5;196m" NORMAL="${ESCAPE}[0m" # 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 [ "USAGE: inFastboot Returns: true if device is in fastboot mode" ] inFastboot() { 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: adb_sh /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 /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_su ls /sys/fs/pstore Returns the property value" ] get_property() { adb_sh getprop ${1} 2>&1 &2 } [ "USAGE: setBootloaderBootReason [value] 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_sh ls /etc/init/bootstat-debug.rc 2>/dev/null &2 return 1 fi checkDebugBuild || return 1 if adb_su "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" /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_su setprop persist.test.boot.reason "'${1}'" 2>/dev/null &2 return 1 fi } [ "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" duration_prefix="~" duration_estimate=1 fi >&2 } [ "USAGE: format_duration human readable output whole seconds, whole minutes or mm:ss" ] format_duration() { if [ -z "${1}" ]; then echo unknown return fi seconds=`expr ${1} % 60` minutes=`expr ${1} / 60` if [ 0 -eq ${minutes} ]; then if [ 1 -eq ${1} ]; then echo 1 second return fi echo ${1} seconds return elif [ 60 -eq ${1} ]; then echo 1 minute return elif [ 0 -eq ${seconds} ]; then echo ${minutes} minutes return fi echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10` } wait_for_screen_timeout=900 [ "USAGE: wait_for_screen [-n] [TIMEOUT] -n - echo newline at exit TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ] wait_for_screen() { exit_function=true if [ X"-n" = X"${1}" ]; then exit_function=echo shift fi timeout=${wait_for_screen_timeout} if [ ${#} -gt 0 ]; then timeout=${1} shift fi counter=0 while true; do if inFastboot; then fastboot reboot elif inAdb; then if [ 0 != ${counter} ]; then adb wait-for-device /dev/null 2>/dev/null fi if [ -n "`get_property sys.boot.reason`" ] then vals=`get_property | sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\|bootstat[.]first_boot_completed\)[]]: [[]\([01]\)[]]$/\1=\2/p'` if [ X"${vals}" != X"${vals##*boot_completed=1}" ]; then if [ X"${vals}" != X"${vals##*logbootcomple=1}" ]; then sleep 1 break fi if [ X"${vals}" != X"${vals##*bootstat.first_boot_completed=1}" ]; then sleep 1 break fi fi fi fi counter=`expr ${counter} + 1` if [ ${counter} -gt ${timeout} ]; then ${exit_function} echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2 return 1 fi sleep 1 done ${exit_function} } [ "USAGE: EXPECT_EQ [message] Returns true if (regex) lval matches rval" ] EXPECT_EQ() { lval="${1}" rval="${2}" shift 2 if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval% *}" ]; then echo "ERROR: expected \"${lval}\"" >&2 echo " got \"${rval}\"" | sed ': again N s/\(\n\)\([^ ]\)/\1 \2/ t again' >&2 if [ -n "${*}" ] ; then echo " ${*}" >&2 fi else echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2 fi return 1 fi if [ -n "${*}" ] ; then if [ X"${lval}" != X"${rval}" ]; then if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then echo "INFO: ok \"${lval}\"" >&2 echo " = \"${rval}\"" | sed ': again N s/\(\n\)\([^ ]\)/\1 \2/ t again' >&2 if [ -n "${*}" ] ; then echo " ${*}" >&2 fi else echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2 fi else echo "INFO: ok \"${lval}\" ${*}" >&2 fi fi return 0 } BAD_BOOTLOADER_REASON= [ "USAGE: EXPECT_PROPERTY [--allow_failure] Returns true (0) if current return (regex) value is true and the result matches and the incoming return value is true as well (wired-or)" ] EXPECT_PROPERTY() { save_ret=${?} property="${1}" value="${2}" shift 2 val=`get_property ${property}` EXPECT_EQ "${value}" "${val}" for Android property ${property} local_ret=${?} if [ 0 != ${local_ret} -a "ro.boot.bootreason" = "${property}" ]; then if [ -z "${BAD_BOOTLOADER_REASON}" ]; then BAD_BOOTLOADER_REASON=${val} elif [ X"${BAD_BOOTLOADER_REASON}" = X"${val}" ]; then local_ret=0 fi fi if [ 0 != ${local_ret} ]; then if [ -z "${1}" ] ; then save_ret=${local_ret} fi fi return ${save_ret} } [ "USAGE: adb_date >/dev/stdout Returns: report device epoch time (suitable for logcat -t)" ] adb_date() { adb_sh date +%s.%N ] ... if not prefixed with a minus (-), will become a series of expected matches: bootstat: Canonical boot reason: If prefixed with a minus, will look for an exact match after removing the minux prefix. All expected content is _dropped_ from the output and in essence forms a known blacklist, unexpected content will show. Report any logs, minus a known blacklist, preserve the current exit status" ] report_bootstat_logs() { save_ret=${?} match= timestamp=-d for i in "${@}"; do if [ X"${i}" != X"${i#-t}" ]; then timestamp="${i}" elif [ X"${i}" != X"${i#-}" ]; then match="${match} ${i#-}" else match="${match} bootstat: Canonical boot reason: ${i}" fi done 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 bootstat: Battery level at shutdown 100% bootstat: Battery level at startup 100% init : Parsing file /system/etc/init/bootstat.rc... init : Parsing file /system/etc/init/bootstat-debug.rc... init : processing action (persist.test.boot.reason=*) from (/system/etc/init/bootstat-debug.rc: init : Command 'setprop ro.boot.bootreason \${persist.test.boot.reason}' action=persist.test.boot.reason=* (/system/etc/init/bootstat-debug.rc: 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 (sys.bootstat.first_zygote_start=0 && zygote-start) from (/system/etc/init/bootstat.rc init : processing action (sys.boot_completed=1 && sys.bootstat.first_boot_completed=0) 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)' (/system/bin/bootstat -r post_decrypt_time_elapsed)' init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc: init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc: init : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc: init : Command 'exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc (/system/bin/bootstat --record_boot_complete)'... (/system/bin/bootstat --record_boot_complete)' (pid${SPACE} (/system/bin/bootstat --record_boot_reason)'... (/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 return ${save_ret} } [ "USAGE: start_test [message] Record start of test, preserve exit status" ] start_test() { save_ret=${?} duration_prefix="~" duration_estimate=1 START=`date +%s` echo "${GREEN}[ RUN ]${NORMAL} ${TEST} ${*}" return ${save_ret} } duration_sum_diff=0 duration_num=0 [ "USAGE: duration_test [[prefix]seconds] Report the adjusted and expected test duration" ] duration_test() { duration_prefix=${1%%[0123456789]*} if [ -z "${duration_prefix}" ]; then duration_prefix="~" fi duration_estimate="${1#${duration_prefix}}" if [ -z "${duration_estimate}" ]; then duration_estimate="${DURATION_DEFAULT}" fi duration_new_estimate="${duration_estimate}" if [ 0 -ne ${duration_num} ]; then duration_new_estimate=`expr ${duration_new_estimate} + \ \( ${duration_num} / 2 + ${duration_sum_diff} \) / ${duration_num}` # guard against catastrophe if [ -z "${duration_new_estimate}" ]; then duration_new_estimate=${duration_estimate} fi fi # negative values are so undignified if [ 0 -ge ${duration_new_estimate} ]; then duration_new_estimate=1 fi echo "INFO: expected duration of '${TEST}' test" \ "${duration_prefix}`format_duration ${duration_new_estimate}`" >&2 } [ "USAGE: end_test [message] Document duration and success of test, preserve exit status" ] end_test() { save_ret=${?} END=`date +%s` duration=`expr ${END} - ${START} 2>/dev/null` [ 0 -ge ${duration} ] || echo "INFO: '${TEST}' test duration `format_duration ${duration}`" >&2 if [ ${save_ret} = 0 ]; then if [ 0 -lt ${duration} -a 0 -lt ${duration_estimate} -a \( \ X"~" = X"${duration_prefix}" -o \ ${duration_estimate} -gt ${duration} \) ]; then duration_sum_diff=`expr ${duration_sum_diff} + \ ${duration} - ${duration_estimate}` duration_num=`expr ${duration_num} + 1` fi echo "${GREEN}[ OK ]${NORMAL} ${TEST} ${*}" else echo "${RED}[ FAILED ]${NORMAL} ${TEST} ${*}" if ${STOP_ON_FAILURE}; then exit ${save_ret} fi fi return ${save_ret} } [ "USAGE: wrap_test [message] All tests below are wrapped with this helper" ] wrap_test() { if [ -z "${1}" -o X"nothing" = X"${1}" ]; then return fi TEST=${1} shift start_test ${1} eval test_${TEST} end_test ${2} } [ "USAGE: validate_reason Check property for CTS compliance with our expectations. Return a cleansed string representing what is acceptable. NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ] validate_reason() { var=`echo -n ${*} | tr '[A-Z]' '[a-z]' | tr ' \f\t\r\n' '_____'` case ${var} in watchdog | watchdog,?* ) ;; kernel_panic | kernel_panic,?* ) ;; recovery | recovery,?* ) ;; bootloader | bootloader,?* ) ;; cold | cold,?* ) ;; hard | hard,?* ) ;; warm | warm,?* ) ;; shutdown | shutdown,?* ) ;; reboot,reboot | reboot,reboot,* ) var=${var#reboot,} ; var=${var%,} ;; reboot,cold | reboot,cold,* ) var=${var#reboot,} ; var=${var%,} ;; reboot,hard | reboot,hard,* ) var=${var#reboot,} ; var=${var%,} ;; reboot,warm | reboot,warm,* ) var=${var#reboot,} ; var=${var%,} ;; reboot,recovery | reboot,recovery,* ) var=${var#reboot,} ; var=${var%,} ;; reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;; reboot | reboot,?* ) ;; # Aliases and Heuristics *wdog* | *watchdog* ) var="watchdog" ;; *powerkey* | *power_key* | *PowerKey* ) var="cold,powerkey" ;; *panic* | *kernel_panic* ) var="kernel_panic" ;; *thermal* ) var="shutdown,thermal" ;; *s3_wakeup* ) var="warm,s3_wakeup" ;; *hw_reset* ) var="hard,hw_reset" ;; *usb* ) var="cold,charger" ;; *rtc* ) var="cold,rtc" ;; *2sec_reboot* ) var="cold,rtc,2sec" ;; *wdt_by_pass_pwk* ) var="warm" ;; wdt ) var="reboot" ;; *tool_by_pass_pwk* ) var="reboot,tool" ;; *bootloader* ) var="bootloader" ;; * ) var="reboot" ;; esac echo ${var} } [ "USAGE: validate_property Check property for CTS compliance with our expectations. Return a cleansed string representing what is acceptable. NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ] validate_property() { val=`get_property ${1}` ret=`validate_reason "${val}"` if [ "reboot" = "${ret}" ]; then ret=`validate_reason "reboot,${val}"` fi echo ${ret} } [ "USAGE: check_boilerblate_properties Check for common property values" ] check_boilerplate_properties() { EXPECT_PROPERTY persist.sys.boot.reason "" save_ret=${?} reason=`validate_property sys.boot.reason` ( exit ${save_ret} ) # because one can not just do ?=${save_ret} EXPECT_PROPERTY persist.sys.boot.reason.history "${reason},[1-9][0-9]*\(\|[^0-9].*\)" } # # Actual test frames # [ "USAGE: test_properties properties test - (wait until screen is up, boot has completed) - adb shell getprop ro.boot.bootreason (bootloader reason) - adb shell getprop persist.sys.boot.reason (last reason) - adb shell getprop sys.boot.reason.last (last last reason) - adb shell getprop sys.boot.reason (system reason) - NB: all should have a value that is compliant with our known set." ] test_properties() { duration_test 1 wait_for_screen retval=0 # sys.boot.reason is last for a reason check_set="ro.boot.bootreason sys.boot.reason.last sys.boot.reason" bootloader="" # NB: this test could fail if performed _after_ optional_factory_reset test # and will report # ERROR: expected "reboot" got "" # for Android property sys.boot.reason.last # following is mitigation for the persist.sys.boot.reason, skip it if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then check_set="ro.boot.bootreason sys.boot.reason" bootloader="bootloader" fi for prop in ${check_set}; do reason=`validate_property ${prop}` EXPECT_PROPERTY ${prop} ${reason} || retval=${?} done check_boilerplate_properties || retval=${?} report_bootstat_logs ${reason} ${bootloader} return ${retval} } [ "USAGE: test_ota ota test - rm out/.kati_stamp-* out/build_date.txt out/build_number.txt - rm out/target/product/*/*/*.prop - rm -r out/target/product/*/obj/ETC/system_build_prop_intermediates - m - NB: ro.build.date.utc should update - fastboot flashall - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report ota Decision to change the build itself rather than trick bootstat by rummaging through its data files was made." ] test_ota() { duration_test ">300" echo " extended by build and flashing times" >&2 if [ -z "${TARGET_PRODUCT}" -o \ -z "${ANDROID_PRODUCT_OUT}" -o \ -z "${ANDROID_BUILD_TOP}" -o \ -z "${TARGET_BUILD_VARIANT}" ]; then echo "ERROR: Missing envsetup.sh and lunch" >&2 return 1 fi rm ${ANDROID_PRODUCT_OUT%/out/*}/out/.kati_stamp-* || true rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_date.txt || true rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_number.txt || true rm ${ANDROID_PRODUCT_OUT}/*/*.prop || true rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates || true pushd ${ANDROID_BUILD_TOP} >&2 build/soong/soong_ui.bash --make-mode >&2 if [ ${?} != 0 ]; then popd >&2 return 1 fi if ! inFastboot; then adb reboot-bootloader >&2 fi fastboot flashall >&2 popd >&2 wait_for_screen EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)" EXPECT_PROPERTY sys.boot.reason.last bootloader check_boilerplate_properties report_bootstat_logs reboot,ota bootloader } [ "USAGE: test_optional_ota fast and fake (touch build_date on device to make it different)" ] test_optional_ota() { checkDebugBuild || return duration_test adb_su touch /data/misc/bootstat/build_date >&2 ] blind_reboot_test Simple tests helper - adb reboot - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report , or reboot, 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() { duration_test case ${TEST} in bootloader | recovery | cold | hard | warm ) reason=${TEST} ;; *) reason=reboot,${TEST#optional_} ;; esac adb reboot ${TEST#optional_} wait_for_screen bootloader_reason=`validate_property ro.boot.bootreason` EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason} # to make sys.boot.reason report user friendly reasons=${reason} if [ "${bootloader_reason}" != "${reason}" -a -n "${bootloader_reason}" ]; then reasons="\(${reason}\|${bootloader_reason}\)" fi EXPECT_PROPERTY sys.boot.reason ${reasons} EXPECT_PROPERTY sys.boot.reason.last ${reason} check_boilerplate_properties report_bootstat_logs ${reason} ${bootloader_reason} } [ "USAGE: test_cold cold test - adb reboot cold - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report cold" ] test_cold() { blind_reboot_test } [ "USAGE: test_factory_reset factory_reset test - adb shell su root rm /data/misc/bootstat/build_date - adb reboot - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report factory_reset Decision to rummage through bootstat data files was made as a _real_ factory_reset is too destructive to the device." ] test_factory_reset() { checkDebugBuild || return duration_test adb_su rm /data/misc/bootstat/build_date >&2 &2 wait_for_screen EXPECT_PROPERTY sys.boot.reason reboot,factory_reset EXPECT_PROPERTY sys.boot.reason.last "reboot,.*" check_boilerplate_properties report_bootstat_logs reboot,factory_reset reboot, reboot,adb \ "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date" } [ "USAGE: test_optional_factory_reset factory_reset test - adb reboot-bootloader - fastboot format userdata - fastboot reboot - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report factory_reset For realz, and disruptive" ] test_optional_factory_reset() { duration_test 60 if ! inFastboot; then adb reboot-bootloader fi fastboot format userdata >&2 save_ret=${?} if [ 0 != ${save_ret} ]; then echo "ERROR: fastboot can not format userdata" >&2 fi fastboot reboot >&2 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 "\(\|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" \ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \ "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date" \ "-bootstat: Failed to read /data/misc/bootstat/factory_reset: No such file or directory" \ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/factory_reset" } [ "USAGE: test_hard hard test: - adb reboot hard - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report hard" ] test_hard() { blind_reboot_test } [ "USAGE: test_battery battery test (trick): - echo healthd: battery l=2 | adb shell su root tee /dev/kmsg - adb reboot cold - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report reboot,battery, unless healthd managed to log before reboot in above trick. - Bonus points (manual extras) - Make sure the following is added to the /init.rc file in post-fs section before logd is started: + setprop logd.kernel false + rm /sys/fs/pstore/console-ramoops + rm /sys/fs/pstore/console-ramoops-0 + write /dev/kmsg \"healthd: battery l=2${SPACE} +\" - adb reboot fs - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report reboot,battery - (replace set logd.kernel true to the above, and retry test)" ] test_battery() { checkDebugBuild || return duration_test 120 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_su tee /dev/kmsg >/dev/null done adb reboot cold >&2 adb wait-for-device wait_for_screen adb_su /dev/null | grep 'healthd: battery l=' | tail -1 | grep 'healthd: battery l=2 ' >/dev/null || ( 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_su tee /dev/kmsg >/dev/null done adb reboot cold >&2 adb wait-for-device wait_for_screen fi ) EXPECT_PROPERTY sys.boot.reason shutdown,battery EXPECT_PROPERTY sys.boot.reason.last cold check_boilerplate_properties 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() { duration_test ">60" echo " power on request" >&2 adb_sh setprop sys.powerctl shutdown,battery &2 wait_for_screen -n >&2 EXPECT_PROPERTY sys.boot.reason shutdown,battery EXPECT_PROPERTY sys.boot.reason.last shutdown,battery check_boilerplate_properties 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() { duration_test ">60" echo " power on request" >&2 adb_sh setprop sys.powerctl shutdown,thermal,battery &2 wait_for_screen -n >&2 EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery check_boilerplate_properties report_bootstat_logs shutdown,thermal,battery } [ "USAGE: test_unknown unknown test - adb reboot unknown - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report reboot,unknown - NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ] test_unknown() { blind_reboot_test } [ "USAGE: test_kernel_panic kernel_panic test: - echo c | adb shell su root tee /proc/sysrq-trigger - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report kernel_panic,sysrq" ] test_kernel_panic() { checkDebugBuild || return duration_test ">90" panic_msg="kernel_panic,sysrq" enterPstore if [ ${?} != 0 ]; then echo " or functional bootloader" >&2 panic_msg="\(kernel_panic,sysrq\|kernel_panic\)" pstore_ok=true fi 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} check_boilerplate_properties report_bootstat_logs kernel_panic,sysrq exitPstore } [ "USAGE: test_kernel_panic_subreason kernel_panic_subreason test: - echo SysRq : Trigger a crash : 'test' | adb shell su root tee /dev/kmsg - echo c | adb shell su root tee /proc/sysrq-trigger - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report kernel_panic,sysrq,test" ] test_kernel_panic_subreason() { checkDebugBuild || return duration_test ">90" panic_msg="kernel_panic,sysrq,test" enterPstore if [ ${?} != 0 ]; then echo " or functional bootloader" >&2 panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)" pstore_ok=true fi 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} check_boilerplate_properties report_bootstat_logs kernel_panic,sysrq,test \ "-bootstat: Unknown boot reason: kernel_panic,sysrq,test" exitPstore } [ "USAGE: test_kernel_panic_hung kernel_panic_hung test: - echo Kernel panic - not synching: hung_task: blocked tasks | adb shell su root tee /dev/kmsg - adb reboot warm - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report kernel_panic,hung" ] test_kernel_panic_hung() { checkDebugBuild || return duration_test panic_msg="kernel_panic,hung" enterPstore if [ ${?} != 0 ]; then echo " or functional bootloader" >&2 panic_msg="\(kernel_panic,hung\|reboot,hung\)" pstore_ok=true fi echo "Kernel panic - not syncing: hung_task: blocked tasks" | adb_su tee /dev/kmsg adb reboot warm wait_for_screen EXPECT_PROPERTY sys.boot.reason ${panic_msg} EXPECT_PROPERTY sys.boot.reason.last ${panic_msg} check_boilerplate_properties report_bootstat_logs kernel_panic,hung exitPstore } [ "USAGE: test_warm warm test - adb reboot warm - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report warm" ] test_warm() { blind_reboot_test } [ "USAGE: test_thermal_shutdown thermal shutdown test: - adb shell setprop sys.powerctl shutdown,thermal - (power up the device) - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report shutdown,thermal" ] test_thermal_shutdown() { duration_test ">60" echo " power on request" >&2 adb_sh setprop sys.powerctl shutdown,thermal &2 wait_for_screen -n >&2 EXPECT_PROPERTY sys.boot.reason shutdown,thermal EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal check_boilerplate_properties report_bootstat_logs shutdown,thermal } [ "USAGE: test_userrequested_shutdown userrequested shutdown test: - adb shell setprop sys.powerctl shutdown,userrequested - (power up the device) - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report shutdown,userrequested" ] test_userrequested_shutdown() { duration_test ">60" echo " power on request" >&2 adb_sh setprop sys.powerctl shutdown,userrequested &2 wait_for_screen -n >&2 EXPECT_PROPERTY sys.boot.reason shutdown,userrequested EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested check_boilerplate_properties report_bootstat_logs shutdown,userrequested } [ "USAGE: test_shell_reboot shell reboot test: - adb shell reboot - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - NB: should report reboot,shell" ] test_shell_reboot() { duration_test adb_sh reboot &2 EXPECT_PROPERTY ro.boot.bootreason '\(reboot\|reboot,rescueparty\)' } [ "USAGE: test_Its_Just_So_Hard_reboot Its Just So Hard reboot test: - adb shell reboot 'Its Just So Hard' - (wait until screen is up, boot has completed) - adb shell getprop sys.boot.reason - 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() { if isDebuggable; then # see below duration_test else duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}` fi adb_sh 'reboot "Its Just So Hard"' &1 return fi duration_test if [ X"warm" = X"${bootloader_expected}" ]; then last_expected=cold else last_expected=warm fi adb reboot ${last_expected} wait_for_screen # Reset so that other tests do not get unexpected injection setBootloaderBootReason # Determine the expected values sys_expected="${2}" if [ -z "${sys_expected}" ]; then sys_expected="`validate_reason ${bootloader_expected}`" if [ "reboot" = "${sys_expected}" ]; then sys_expected="${last_expected}" fi else sys_expected=`validate_reason ${sys_expected}` fi case ${sys_expected} in kernel_panic | kernel_panic,* | watchdog | watchdog,* ) last_expected=${sys_expected} ;; esac # Check values EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}" EXPECT_PROPERTY sys.boot.reason "${sys_expected}" EXPECT_PROPERTY sys.boot.reason.last "${last_expected}" check_boilerplate_properties report_bootstat_logs "${sys_expected}" } [ "USAGE: test_bootloader_ bootloader boot reasons test injection" ] test_bootloader_normal() { run_bootloader } test_bootloader_watchdog() { run_bootloader } test_bootloader_kernel_panic() { run_bootloader } test_bootloader_oem_powerkey() { run_bootloader } test_bootloader_wdog_reset() { run_bootloader } test_bootloader_cold() { run_bootloader } test_bootloader_warm() { run_bootloader } test_bootloader_hard() { run_bootloader } test_bootloader_recovery() { run_bootloader } [ "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` [ "${enum_expected}" = "${result}" ] || ( [ -n "${result}" ] || result="" echo "ERROR: ${enum_expected} ${sys_expected} got ${result}" >&2 false ) || retval=${?} return ${retval} } [ "USAGE: filter_kBootReasonMap /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 reboog # negative test (ID for unknown is 1) ;; 'reboot,pmic_off_fault,.*') echo ${id} reboot,pmic_off_fault,hello,world echo ${id} reboot,pmic_off_fault, echo 1 reboot,pmic_off_fault ;; 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() { checkDebugBuild || return duration_test 15 local tempfile="`mktemp`" local arg=--boot_reason_enum adb_su bootstat ${arg} /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}" &2 echo "${RED}[ FAILED ]${NORMAL}" exit 1 fi echo "WARNING: no target device specified" >&2 fi ret=0 # Test Series if [ X"all" = X"${*}" ]; then # automagically pick up all test_s. eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 , except test_optional_. eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 &2 echo # Prepare device setBootloaderBootReason 2>/dev/null # Start pouring through the tests. failures= successes= for t in "${@}"; do wrap_test ${t} retval=${?} if [ 0 = ${retval} ]; then if [ -z "${successes}" ]; then successes=${t} else successes="${successes} ${t}" fi else ret=${retval} if [ -z "${failures}" ]; then failures=${t} else failures="${failures} ${t}" fi fi echo done if [ -n "${successes}" ]; then echo "${GREEN}[ PASSED ]${NORMAL} ${successes}" fi if [ -n "${failures}" ]; then echo "${RED}[ FAILED ]${NORMAL} ${failures}" fi exit ${ret} fi