Merge changes from topic "b/63736262"
* changes: adb: reboot: last boot command default bootstat: introduce sys.boot.reason
This commit is contained in:
commit
1f666a6be9
7 changed files with 1101 additions and 2 deletions
|
@ -150,6 +150,7 @@ static bool reboot_service_impl(int fd, const char* arg) {
|
|||
|
||||
sync();
|
||||
|
||||
if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
|
||||
std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
|
||||
if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
|
||||
WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
|
||||
|
|
|
@ -63,6 +63,7 @@ cc_binary {
|
|||
name: "bootstat",
|
||||
defaults: ["bootstat_defaults"],
|
||||
static_libs: ["libbootstat"],
|
||||
shared_libs: ["liblogcat"],
|
||||
init_rc: ["bootstat.rc"],
|
||||
srcs: ["bootstat.cpp"],
|
||||
}
|
||||
|
|
721
bootstat/boot_reason_test.sh
Executable file
721
bootstat/boot_reason_test.sh
Executable file
|
@ -0,0 +1,721 @@
|
|||
#! /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.logbootcomplete=1 to be true
|
||||
#
|
||||
# All test frames, and nothing else, must be function names prefixed and
|
||||
# specifiged with the pattern 'test_<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"
|
||||
|
||||
# Helper functions
|
||||
|
||||
[ "USAGE: inFastboot
|
||||
|
||||
Returns: true if device is in fastboot mode" ]
|
||||
inFastboot() {
|
||||
fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
|
||||
}
|
||||
|
||||
[ "USAGE: format_duration <seconds>
|
||||
|
||||
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
|
||||
[ 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
|
||||
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
|
||||
done
|
||||
${exit_function}
|
||||
}
|
||||
|
||||
[ "USAGE: EXPECT_EQ <lval> <rval> [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
|
||||
echo "ERROR: expected \"${lval}\" got \"${rval}\"" >&2
|
||||
if [ -n "${*}" ] ; then
|
||||
echo " ${*}" >&2
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
if [ -n "${*}" ] ; then
|
||||
if [ X"${lval}" != X"${rval}" ]; then
|
||||
echo "INFO: ok \"${lval}\"(=\"${rval}\") ${*}" >&2
|
||||
else
|
||||
echo "INFO: ok \"${lval}\" ${*}" >&2
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
[ "USAGE: EXPECT_PROPERTY <prop> <value>
|
||||
|
||||
Returns true if current return (regex) value is true and the result matches" ]
|
||||
EXPECT_PROPERTY() {
|
||||
save_ret=${?}
|
||||
property="${1}"
|
||||
value="${2}"
|
||||
shift 2
|
||||
val=`adb shell getprop ${property} 2>&1`
|
||||
EXPECT_EQ "${value}" "${val}" for Android property ${property} ||
|
||||
save_ret=${?}
|
||||
return ${save_ret}
|
||||
}
|
||||
|
||||
[ "USAGE: report_bootstat_logs <expected> ...
|
||||
|
||||
if not prefixed with a minus (-), <expected> will become a series of expected
|
||||
matches:
|
||||
|
||||
bootstat: Canonical boot reason: <expected_property_value>
|
||||
|
||||
If prefixed with a minus, <expected> 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=
|
||||
for i in ${*}; do
|
||||
if [ X"${i}" != X"${i#-}" ] ; then
|
||||
match="${match}
|
||||
${i#-}"
|
||||
else
|
||||
match="${match}
|
||||
bootstat: Canonical boot reason: ${i}"
|
||||
fi
|
||||
done
|
||||
adb logcat -b all -d |
|
||||
grep bootstat |
|
||||
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 --record_time_since_factory_reset
|
||||
bootstat: Service started: /system/bin/bootstat -l
|
||||
bootstat: Battery level at shutdown 100%
|
||||
bootstat: Battery level at startup 100%
|
||||
init : Parsing file /system/etc/init/bootstat.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 (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
|
||||
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
|
||||
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
|
||||
init : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/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 -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=${?}
|
||||
START=`date +%s`
|
||||
echo "${GREEN}[ RUN ]${NORMAL} ${TEST} ${*}"
|
||||
return ${save_ret}
|
||||
}
|
||||
|
||||
[ "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 = ${duration} ] ||
|
||||
echo INFO: ${TEST} test duration `format_duration ${duration}` >&2
|
||||
if [ ${save_ret} = 0 ]; then
|
||||
echo "${GREEN}[ OK ]${NORMAL} ${TEST} ${*}"
|
||||
else
|
||||
echo "${RED}[ FAILED ]${NORMAL} ${TEST} ${*}"
|
||||
fi
|
||||
return ${save_ret}
|
||||
}
|
||||
|
||||
[ "USAGE: wrap_test <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_property <property>
|
||||
|
||||
Check property for CTS compliance with our expectations. Return a cleansed
|
||||
string representing what is acceptable.
|
||||
|
||||
NB: must roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
|
||||
validate_property() {
|
||||
var=`adb shell getprop ${1} 2>&1`
|
||||
var=`echo -n ${var} |
|
||||
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,?*) ;;
|
||||
# Aliases
|
||||
*wdog* | *watchdog* ) var="watchdog" ;;
|
||||
*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" ;;
|
||||
*bootloader*) var="bootloader" ;;
|
||||
?*) var="reboot,${var}" ;;
|
||||
*) var="reboot" ;;
|
||||
esac
|
||||
echo ${var}
|
||||
}
|
||||
|
||||
#
|
||||
# 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 (system reason)
|
||||
- NB: all should have a value that is compliant with our known set." ]
|
||||
test_properties() {
|
||||
wait_for_screen
|
||||
retval=0
|
||||
check_set="ro.boot.bootreason persist.sys.boot.reason 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 persist.sys.boot.reason
|
||||
# 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
|
||||
# sys.boot.reason is last for a reason
|
||||
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() {
|
||||
echo "INFO: expected duration of ${TEST} test about 5 minutes or more" >&2
|
||||
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
|
||||
make -j50 >&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 persist.sys.boot.reason reboot,bootloader
|
||||
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() {
|
||||
echo "INFO: expected duration of ${TEST} test about 45 seconds" >&2
|
||||
adb shell su root touch /data/misc/bootstat/build_date >&2
|
||||
adb reboot ota
|
||||
wait_for_screen
|
||||
EXPECT_PROPERTY sys.boot.reason reboot,ota
|
||||
EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
|
||||
report_bootstat_logs reboot,ota
|
||||
}
|
||||
|
||||
[ "USAGE: [TEST=<test>] blind_reboot_test [<match>]
|
||||
|
||||
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>
|
||||
|
||||
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
|
||||
adb reboot ${TEST}
|
||||
wait_for_screen
|
||||
EXPECT_PROPERTY sys.boot.reason ${1}
|
||||
EXPECT_PROPERTY persist.sys.boot.reason reboot,${TEST}
|
||||
report_bootstat_logs ${1}
|
||||
}
|
||||
|
||||
[ "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() {
|
||||
echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
|
||||
adb shell su root rm /data/misc/bootstat/build_date >&2
|
||||
adb reboot >&2
|
||||
wait_for_screen
|
||||
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
|
||||
EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
|
||||
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() {
|
||||
echo "INFO: expected duration of ${TEST} test roughly a minute" >&2
|
||||
if ! inFastboot; then
|
||||
adb reboot-bootloader
|
||||
fi
|
||||
fastboot format userdata >&2
|
||||
fastboot reboot >&2
|
||||
wait_for_screen
|
||||
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
|
||||
EXPECT_PROPERTY persist.sys.boot.reason ""
|
||||
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 warm
|
||||
- (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
|
||||
+\"
|
||||
- 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() {
|
||||
echo "INFO: expected duration of ${TEST} test roughly two minutes" >&2
|
||||
echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null
|
||||
adb reboot warm >&2
|
||||
wait_for_screen
|
||||
adb shell su root \
|
||||
cat /proc/fs/pstore/console-ramoops \
|
||||
/proc/fs/pstore/console-ramoops-0 2>/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
|
||||
echo healthd: battery l=2 | adb shell su root tee /dev/kmsg >/dev/null
|
||||
adb reboot warm >&2
|
||||
wait_for_screen
|
||||
fi
|
||||
)
|
||||
|
||||
EXPECT_PROPERTY sys.boot.reason shutdown,battery
|
||||
EXPECT_PROPERTY persist.sys.boot.reason reboot,warm
|
||||
report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
|
||||
}
|
||||
|
||||
[ "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 reboot,unknown
|
||||
}
|
||||
|
||||
[ "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() {
|
||||
echo "INFO: expected duration of ${TEST} test > 2 minutes" >&2
|
||||
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
|
||||
report_bootstat_logs kernel_panic,sysrq
|
||||
}
|
||||
|
||||
[ "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() {
|
||||
echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
|
||||
echo " power on request" >&2
|
||||
adb shell setprop sys.powerctl shutdown,thermal
|
||||
sleep 5
|
||||
echo -n "WARNING: Please power device back up, waiting ... " >&2
|
||||
wait_for_screen -n >&2
|
||||
EXPECT_PROPERTY sys.boot.reason shutdown,thermal
|
||||
EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
|
||||
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() {
|
||||
echo "INFO: expected duration of ${TEST} test roughly a minute plus" >&2
|
||||
echo " power on request" >&2
|
||||
adb shell setprop sys.powerctl shutdown,userrequested
|
||||
sleep 5
|
||||
echo -n "WARNING: Please power device back up, waiting ... " >&2
|
||||
wait_for_screen -n >&2
|
||||
EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
|
||||
EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
|
||||
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() {
|
||||
echo "INFO: expected duration of ${TEST} test roughly 45 seconds" >&2
|
||||
adb shell reboot
|
||||
wait_for_screen
|
||||
EXPECT_PROPERTY sys.boot.reason reboot,shell
|
||||
EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
|
||||
report_bootstat_logs reboot,shell
|
||||
}
|
||||
|
||||
[ "USAGE: test_adb_reboot
|
||||
|
||||
adb reboot test:
|
||||
- adb reboot
|
||||
- (wait until screen is up, boot has completed)
|
||||
- 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
|
||||
adb reboot
|
||||
wait_for_screen
|
||||
EXPECT_PROPERTY sys.boot.reason reboot,adb
|
||||
EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
|
||||
report_bootstat_logs reboot,adb
|
||||
}
|
||||
|
||||
[ "USAGE: ${0##*/} [-s SERIAL] [tests]
|
||||
|
||||
Mainline executive to run the above tests" ]
|
||||
|
||||
# Rudimentary argument parsing
|
||||
|
||||
if [ ${#} -ge 2 -a X"-s" = X"${1}" ]; then
|
||||
export ANDROID_SERIAL="${2}"
|
||||
shift 2
|
||||
fi
|
||||
|
||||
if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
|
||||
echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
|
||||
echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if all conditions for the script are sane
|
||||
|
||||
if [ -z "${ANDROID_SERIAL}" ]; then
|
||||
ndev=`(
|
||||
adb devices | grep -v 'List of devices attached'
|
||||
fastboot devices
|
||||
) |
|
||||
grep -v "^[${SPACE}${TAB}]*\$" |
|
||||
wc -l`
|
||||
if [ ${ndev} -gt 1 ]; then
|
||||
echo "ERROR: no target device specified, ${ndev} connected" >&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_<function>s.
|
||||
eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
|
||||
if [ X"nothing" = X"${1}" ]; then
|
||||
shift 1
|
||||
fi
|
||||
fi
|
||||
if [ -z "$*" ]; then
|
||||
# automagically pick up all test_<function>, except test_optional_<function>.
|
||||
eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
|
||||
grep -v '^optional_'`
|
||||
if [ -z "${2}" ]; then
|
||||
# Hard coded should shell fail to find them above (search/permission issues)
|
||||
eval set ota cold factory_reset hard battery unknown kernel_panic warm \
|
||||
thermal_shutdown userrequested_shutdown shell_reboot adb_reboot
|
||||
fi
|
||||
if [ X"nothing" = X"${1}" ]; then
|
||||
shift 1
|
||||
fi
|
||||
fi
|
||||
echo "INFO: selected test(s): ${@}" >&2
|
||||
echo
|
||||
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}
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/klog.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
@ -32,11 +33,14 @@
|
|||
#include <vector>
|
||||
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android/log.h>
|
||||
#include <cutils/android_reboot.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <log/logcat.h>
|
||||
#include <metricslogger/metrics_logger.h>
|
||||
|
||||
#include "boot_event_record_store.h"
|
||||
|
@ -118,6 +122,14 @@ std::string GetProperty(const char* key) {
|
|||
return std::string(&temp[0], len);
|
||||
}
|
||||
|
||||
void SetProperty(const char* key, const std::string& val) {
|
||||
property_set(key, val.c_str());
|
||||
}
|
||||
|
||||
void SetProperty(const char* key, const char* val) {
|
||||
property_set(key, val);
|
||||
}
|
||||
|
||||
constexpr int32_t kUnknownBootReason = 1;
|
||||
|
||||
// A mapping from boot reason string, as read from the ro.boot.bootreason
|
||||
|
@ -175,7 +187,23 @@ const std::map<std::string, int32_t> kBootReasonMap = {
|
|||
{"reboot,cold", 48},
|
||||
{"reboot,recovery", 49},
|
||||
{"thermal_shutdown", 50},
|
||||
{"s3_wakeup", 51}
|
||||
{"s3_wakeup", 51},
|
||||
{"kernel_panic,sysrq", 52},
|
||||
{"kernel_panic,NULL", 53},
|
||||
{"kernel_panic,BUG", 54},
|
||||
{"bootloader", 55},
|
||||
{"cold", 56},
|
||||
{"hard", 57},
|
||||
{"warm", 58},
|
||||
{"recovery", 59},
|
||||
{"thermal-shutdown", 60},
|
||||
{"shutdown,thermal", 61},
|
||||
{"shutdown,battery", 62},
|
||||
{"reboot,ota", 63},
|
||||
{"reboot,factory_reset", 64},
|
||||
{"reboot,", 65},
|
||||
{"reboot,shell", 66},
|
||||
{"reboot,adb", 67},
|
||||
};
|
||||
|
||||
// Converts a string value representing the reason the system booted to an
|
||||
|
@ -191,6 +219,323 @@ int32_t BootReasonStrToEnum(const std::string& boot_reason) {
|
|||
return kUnknownBootReason;
|
||||
}
|
||||
|
||||
// Canonical list of supported primary reboot reasons.
|
||||
const std::vector<const std::string> knownReasons = {
|
||||
// kernel
|
||||
"watchdog",
|
||||
"kernel_panic",
|
||||
// strong
|
||||
"recovery", // Should not happen from ro.boot.bootreason
|
||||
"bootloader", // Should not happen from ro.boot.bootreason
|
||||
// blunt
|
||||
"cold",
|
||||
"hard",
|
||||
"warm",
|
||||
"shutdown", // Can not happen from ro.boot.bootreason
|
||||
"reboot", // Default catch-all for anything unknown
|
||||
};
|
||||
|
||||
// Returns true if the supplied reason prefix is considered detailed enough.
|
||||
bool isStrongRebootReason(const std::string& r) {
|
||||
for (auto &s : knownReasons) {
|
||||
if (s == "cold") break;
|
||||
// Prefix defined as terminated by a nul or comma (,).
|
||||
if (android::base::StartsWith(r, s.c_str()) &&
|
||||
((r.length() == s.length()) || (r[s.length()] == ','))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if the supplied reason prefix is associated with the kernel.
|
||||
bool isKernelRebootReason(const std::string& r) {
|
||||
for (auto &s : knownReasons) {
|
||||
if (s == "recovery") break;
|
||||
// Prefix defined as terminated by a nul or comma (,).
|
||||
if (android::base::StartsWith(r, s.c_str()) &&
|
||||
((r.length() == s.length()) || (r[s.length()] == ','))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if the supplied reason prefix is considered known.
|
||||
bool isKnownRebootReason(const std::string& r) {
|
||||
for (auto &s : knownReasons) {
|
||||
// Prefix defined as terminated by a nul or comma (,).
|
||||
if (android::base::StartsWith(r, s.c_str()) &&
|
||||
((r.length() == s.length()) || (r[s.length()] == ','))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the reboot reason should be improved, report true if is too blunt.
|
||||
bool isBluntRebootReason(const std::string& r) {
|
||||
if (isStrongRebootReason(r)) return false;
|
||||
|
||||
if (!isKnownRebootReason(r)) return true; // Can not support unknown as detail
|
||||
|
||||
size_t pos = 0;
|
||||
while ((pos = r.find(',', pos)) != std::string::npos) {
|
||||
++pos;
|
||||
std::string next(r.substr(pos));
|
||||
if (next.length() == 0) break;
|
||||
if (next[0] == ',') continue;
|
||||
if (!isKnownRebootReason(next)) return false; // Unknown subreason is good.
|
||||
if (isStrongRebootReason(next)) return false; // eg: reboot,reboot
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// std::transform Helper callback functions:
|
||||
// Converts a string value representing the reason the system booted to a
|
||||
// string complying with Android system standard reason.
|
||||
char tounderline(char c) { return ::isblank(c) ? '_' : c; }
|
||||
char toprintable(char c) { return ::isprint(c) ? c : '?'; }
|
||||
|
||||
const char system_reboot_reason_property[] = "sys.boot.reason";
|
||||
const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
|
||||
const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
|
||||
|
||||
// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
|
||||
std::string BootReasonStrToReason(const std::string& boot_reason) {
|
||||
std::string ret(GetProperty(system_reboot_reason_property));
|
||||
std::string reason(boot_reason);
|
||||
// If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
|
||||
if (reason == ret) ret = "";
|
||||
|
||||
// Cleanup boot_reason regarding acceptable character set
|
||||
std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
|
||||
std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
|
||||
std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
|
||||
|
||||
// Is the current system boot reason sys.boot.reason valid?
|
||||
if (!isKnownRebootReason(ret)) ret = "";
|
||||
|
||||
if (ret == "") {
|
||||
// Is the bootloader boot reason ro.boot.bootreason known?
|
||||
std::vector<std::string> words(android::base::Split(reason, ",_-"));
|
||||
for (auto &s : knownReasons) {
|
||||
std::string blunt;
|
||||
for (auto &r : words) {
|
||||
if (r == s) {
|
||||
if (isBluntRebootReason(s)) {
|
||||
blunt = s;
|
||||
} else {
|
||||
ret = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == "") ret = blunt;
|
||||
if (ret != "") break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == "") {
|
||||
// A series of checks to take some officially unsupported reasons
|
||||
// reported by the bootloader and find some logical and canonical
|
||||
// sense. In an ideal world, we would require those bootloaders
|
||||
// to behave and follow our standards.
|
||||
static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
|
||||
{"watchdog", "wdog"},
|
||||
{"cold,powerkey", "powerkey"},
|
||||
{"kernel_panic", "panic"},
|
||||
{"shutdown,thermal", "thermal"},
|
||||
{"warm,s3_wakeup", "s3_wakeup"},
|
||||
{"hard,hw_reset", "hw_reset"},
|
||||
{"bootloader", ""},
|
||||
};
|
||||
|
||||
// Either the primary or alias is found _somewhere_ in the reason string.
|
||||
for (auto &s : aliasReasons) {
|
||||
if (reason.find(s.first) != std::string::npos) {
|
||||
ret = s.first;
|
||||
break;
|
||||
}
|
||||
if (s.second.size() && (reason.find(s.second) != std::string::npos)) {
|
||||
ret = s.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If watchdog is the reason, see if there is a security angle?
|
||||
if (ret == "watchdog") {
|
||||
if (reason.find("sec") != std::string::npos) {
|
||||
ret += ",security";
|
||||
}
|
||||
}
|
||||
|
||||
// Check the other reason resources if the reason is still blunt.
|
||||
if (isBluntRebootReason(ret)) {
|
||||
// Check to see if last klog has some refinement hints.
|
||||
std::string content;
|
||||
if (!android::base::ReadFileToString("/sys/fs/pstore/console-ramoops-0",
|
||||
&content)) {
|
||||
android::base::ReadFileToString("/sys/fs/pstore/console-ramoops",
|
||||
&content);
|
||||
}
|
||||
|
||||
// The toybox reboot command used directly (unlikely)? But also
|
||||
// catches init's response to the Android's more controlled reboot command.
|
||||
if (content.rfind("reboot: Power down") != std::string::npos) {
|
||||
ret = "shutdown"; // Still too blunt, but more accurate.
|
||||
// ToDo: init should record the shutdown reason to kernel messages ala:
|
||||
// init: shutdown system with command 'last_reboot_reason'
|
||||
// so that if pstore has persistence we can get some details
|
||||
// that could be missing in last_reboot_reason_property.
|
||||
}
|
||||
|
||||
static const char cmd[] = "reboot: Restarting system with command '";
|
||||
size_t pos = content.rfind(cmd);
|
||||
if (pos != std::string::npos) {
|
||||
pos += strlen(cmd);
|
||||
std::string subReason(content.substr(pos));
|
||||
pos = subReason.find('\'');
|
||||
if (pos != std::string::npos) subReason.erase(pos);
|
||||
if (subReason != "") { // Will not land "reboot" as that is too blunt.
|
||||
if (isKernelRebootReason(subReason)) {
|
||||
ret = "reboot," + subReason; // User space can't talk kernel reasons.
|
||||
} else {
|
||||
ret = subReason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for kernel panics, but allowed to override reboot command.
|
||||
if (content.rfind("sysrq: SysRq : Trigger a crash") != std::string::npos) {
|
||||
// Can not happen, except on userdebug, during testing/debugging.
|
||||
ret = "kernel_panic,sysrq";
|
||||
} else if (content.rfind(
|
||||
"Unable to handle kernel NULL pointer dereference at virtual address")
|
||||
!= std::string::npos) {
|
||||
ret = "kernel_panic,NULL";
|
||||
} else if (content.rfind("Kernel BUG at ") != std::string::npos) {
|
||||
ret = "kernel_panic,BUG";
|
||||
} else if ((content.rfind("Power held for ") != std::string::npos) ||
|
||||
(content.rfind("charger: [") != std::string::npos)) {
|
||||
ret = "cold";
|
||||
}
|
||||
|
||||
// The following battery test should migrate to a default system health HAL
|
||||
|
||||
// Let us not worry if the reboot command was issued, for the cases of
|
||||
// reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
|
||||
// Same for bootloader and ro.boot.bootreasons of this set, but a dead
|
||||
// battery could conceivably lead to these, so worthy of override.
|
||||
if (isBluntRebootReason(ret)) {
|
||||
// Heuristic to determine if shutdown possibly because of a dead battery?
|
||||
// Really a hail-mary pass to find it in last klog content ...
|
||||
static const int battery_dead_threshold = 2; // percent
|
||||
static const char battery[] = "healthd: battery l=";
|
||||
pos = content.rfind(battery); // last one
|
||||
if (pos != std::string::npos) {
|
||||
int level = atoi(content.substr(pos + strlen(battery)).c_str());
|
||||
LOG(INFO) << "Battery level at shutdown " << level << "%";
|
||||
if (level <= battery_dead_threshold) {
|
||||
ret = "shutdown,battery";
|
||||
}
|
||||
} else { // Most likely
|
||||
// Content buffer no longer will have console data. Beware if more
|
||||
// checks added below, that depend on parsing console content.
|
||||
content = "";
|
||||
|
||||
LOG(DEBUG) << "Can not find last low battery in last console messages";
|
||||
android_logcat_context ctx = create_android_logcat();
|
||||
FILE *fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
|
||||
if (fp != nullptr) {
|
||||
android::base::ReadFdToString(fileno(fp), &content);
|
||||
}
|
||||
android_logcat_pclose(&ctx, fp);
|
||||
android_logcat_destroy(&ctx);
|
||||
static const char logcat_battery[] = "W/healthd ( 0): battery l=";
|
||||
const char* match = logcat_battery;
|
||||
|
||||
if (content == "") {
|
||||
// Service logd.klog not running, go to smaller buffer in the kernel.
|
||||
int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
|
||||
if (rc > 0) {
|
||||
ssize_t len = rc + 1024; // 1K Margin should it grow between calls.
|
||||
std::unique_ptr<char[]> buf(new char[len]);
|
||||
rc = klogctl(KLOG_READ_ALL, buf.get(), len);
|
||||
if (rc < len) {
|
||||
len = rc + 1;
|
||||
}
|
||||
buf[--len] = '\0';
|
||||
content = buf.get();
|
||||
}
|
||||
match = battery;
|
||||
}
|
||||
|
||||
pos = content.find(match); // The first one it finds.
|
||||
if (pos != std::string::npos) {
|
||||
pos += strlen(match);
|
||||
int level = atoi(content.substr(pos).c_str());
|
||||
LOG(INFO) << "Battery level at startup " << level << "%";
|
||||
if (level <= battery_dead_threshold) {
|
||||
ret = "shutdown,battery";
|
||||
}
|
||||
} else {
|
||||
LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is there a controlled shutdown hint in last_reboot_reason_property?
|
||||
if (isBluntRebootReason(ret)) {
|
||||
// Content buffer no longer will have console data. Beware if more
|
||||
// checks added below, that depend on parsing console content.
|
||||
content = GetProperty(last_reboot_reason_property);
|
||||
|
||||
// String is either "reboot,<reason>" or "shutdown,<reason>".
|
||||
// We will set if default reasons, only override with detail if thermal.
|
||||
if (!isBluntRebootReason(content)) {
|
||||
// Ok, we want it, let's squash it if secondReason is known.
|
||||
pos = content.find(',');
|
||||
if (pos != std::string::npos) {
|
||||
++pos;
|
||||
std::string secondReason(content.substr(pos));
|
||||
ret = isKnownRebootReason(secondReason) ? secondReason : content;
|
||||
} else {
|
||||
ret = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Other System Health HAL reasons?
|
||||
|
||||
// ToDo: /proc/sys/kernel/boot_reason needs a HAL interface to
|
||||
// possibly offer hardware-specific clues from the PMIC.
|
||||
}
|
||||
|
||||
// If unknown left over from above, make it "reboot,<boot_reason>"
|
||||
if (ret == "") {
|
||||
ret = "reboot";
|
||||
if (android::base::StartsWith(reason, "reboot")) {
|
||||
reason = reason.substr(strlen("reboot"));
|
||||
while (reason[0] == ',') {
|
||||
reason = reason.substr(1);
|
||||
}
|
||||
}
|
||||
if (reason != "") {
|
||||
ret += ",";
|
||||
ret += reason;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "Canonical boot reason: " << ret;
|
||||
if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
|
||||
// Rewrite as it must be old news, kernel reasons trump user space.
|
||||
SetProperty(last_reboot_reason_property, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Returns the appropriate metric key prefix for the boot_complete metric such
|
||||
// that boot metrics after a system update are labeled as ota_boot_complete;
|
||||
// otherwise, they are labeled as boot_complete. This method encapsulates the
|
||||
|
@ -212,9 +557,19 @@ std::string CalculateBootCompletePrefix() {
|
|||
if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
|
||||
boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
|
||||
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;
|
||||
|
@ -358,9 +713,23 @@ void RecordBootComplete() {
|
|||
// Records the boot_reason metric by querying the ro.boot.bootreason system
|
||||
// property.
|
||||
void RecordBootReason() {
|
||||
int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
|
||||
const std::string reason(GetProperty(bootloader_reboot_reason_property));
|
||||
|
||||
// Log the raw bootloader_boot_reason property value.
|
||||
int32_t boot_reason = BootReasonStrToEnum(reason);
|
||||
BootEventRecordStore boot_event_store;
|
||||
boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
|
||||
|
||||
// Log the scrubbed system_boot_reason.
|
||||
const std::string system_reason(BootReasonStrToReason(reason));
|
||||
int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
|
||||
boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
|
||||
|
||||
// Record the scrubbed system_boot_reason to the property
|
||||
SetProperty(system_reboot_reason_property, system_reason);
|
||||
if (reason == "") {
|
||||
SetProperty(bootloader_reboot_reason_property, system_reason);
|
||||
}
|
||||
}
|
||||
|
||||
// Records two metrics related to the user resetting a device: the time at
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# This file is the LOCAL_INIT_RC file for the bootstat command.
|
||||
|
||||
# mirror bootloader boot reason to system boot reason
|
||||
on property:ro.boot.bootreason=*
|
||||
setprop sys.boot.reason ${ro.boot.bootreason}
|
||||
|
||||
on post-fs-data
|
||||
mkdir /data/misc/bootstat 0700 system log
|
||||
# To deal with ota transition resulting from a change in DAC from
|
||||
|
|
|
@ -175,6 +175,8 @@ static const struct fs_path_config android_files[] = {
|
|||
CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
|
||||
CAP_MASK_LONG(CAP_SETGID),
|
||||
"system/bin/logd" },
|
||||
{ 00550, AID_SYSTEM, AID_LOG, CAP_MASK_LONG(CAP_SYSLOG),
|
||||
"system/bin/bootstat" },
|
||||
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
|
||||
CAP_MASK_LONG(CAP_SETGID),
|
||||
"system/bin/run-as" },
|
||||
|
|
|
@ -56,6 +56,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (argc > optind)
|
||||
optarg = argv[optind];
|
||||
if (!optarg || !optarg[0]) optarg = "shell";
|
||||
|
||||
prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
|
||||
if (prop_len >= sizeof(property_val)) {
|
||||
|
|
Loading…
Reference in a new issue