1#!/bin/bash 2# 3# Runs a CUJ and collects PSI measurement. 4# Supported CUJs: 5# - Bootup 6# - User switch 7# - App startup 8# - Bootup with app launch 9# - Resume (Work in progress) 10set -E 11function trap_error() { 12 local return_value=$? 13 local line_no=$1 14 echo "Error at line ${line_no}: \"${BASH_COMMAND}\". Return code ${return_value}" \ 15 | tee -a ${OUTPUT_DIR}/fatal_log.txt 16 pkill -P $$ 17 aae_device_recover 18} 19trap 'trap_error $LINENO' ERR 20 21function clean_exit() { 22 execute_on_adb_connect "adb logcat -G 64K -b all -c" 23} 24trap 'clean_exit' EXIT 25 26readonly PSI_MONITOR_REPO_DIR="packages/services/Car/tools/psi_monitor" 27readonly ADB_WAIT_FOR_DEVICE_SCRIPT="adb_wait_for_device.sh" 28readonly PSI_MONITOR_SCRIPT="psi_monitor.sh" 29readonly PARSE_SCRIPT="parse_psi_events.py" 30readonly PLOT_SCRIPT="psi_plot.py" 31readonly GENERATE_KPIS_SCRIPT="generate_kpis.py" 32function setup_local_dir() { 33 if [[ -z ${ANDROID_BUILD_TOP} ]]; then 34 readonly LOCAL_SCRIPT_DIR=$(dirname ${0}) 35 else 36 readonly LOCAL_SCRIPT_DIR=${ANDROID_BUILD_TOP}/${PSI_MONITOR_REPO_DIR} 37 fi 38 if [[ ! -f ${LOCAL_SCRIPT_DIR}/${ADB_WAIT_FOR_DEVICE_SCRIPT} ]]; then 39 echo -e "${ADB_WAIT_FOR_DEVICE_SCRIPT} script not found in ${LOCAL_SCRIPT_DIR}" >&2 40 exit 1 41 fi 42 if [[ ! -f ${LOCAL_SCRIPT_DIR}/${PSI_MONITOR_SCRIPT} ]]; then 43 echo -e "PSI monitor script ${PSI_MONITOR_SCRIPT} not found in ${LOCAL_SCRIPT_DIR}" >&2 44 exit 1 45 fi 46 if [[ ! -f ${LOCAL_SCRIPT_DIR}/${PARSE_SCRIPT} ]]; then 47 echo -e "PSI parse script ${PARSE_SCRIPT} not found in ${LOCAL_SCRIPT_DIR}" >&2 48 exit 49 fi 50 if [[ ! -f ${LOCAL_SCRIPT_DIR}/${PLOT_SCRIPT} ]]; then 51 echo -e "${PLOT_SCRIPT} script not found in ${LOCAL_SCRIPT_DIR}" >&2 52 exit 1 53 fi 54 if [[ ! -f ${LOCAL_SCRIPT_DIR}/${GENERATE_KPIS_SCRIPT} ]]; then 55 echo -e "${GENERATE_KPIS_SCRIPT} script not found in ${LOCAL_SCRIPT_DIR}" >&2 56 exit 1 57 fi 58} 59 60setup_local_dir 61source ${LOCAL_SCRIPT_DIR}/${ADB_WAIT_FOR_DEVICE_SCRIPT} 62 63readonly DEVICE_OUT_DIR="/data/local/tmp/psi_monitor" 64readonly DEVICE_SCRIPT="/data/local/tmp/psi_monitor.sh" 65readonly MIN_BOOTUP_DURATION_SECONDS=60 66readonly MIN_USER_SWITCH_DURATION_SECONDS=30 67readonly POST_APP_STARTUP_MONITOR_DURATION_SECONDS=30 68readonly POST_BOOT_APPS_LAUNCH_TIMES=5 69readonly MIN_BOOT_WITH_APP_LAUNCH_DURATION_SECONDS=90 70readonly TOTAL_PSI_ENTRIES_TO_MONITOR_BASELINE=10 71 72OUTPUT_DIR=${PWD}/out 73PSI_AVG10_THRESHOLD=80 74MAX_PSI_MONITOR_DURATION_SECONDS=120 75PSI_MONITOR_GRACE_DURATION_SECONDS=20 76CUJ="" 77USER_SWITCH_ARGS="" 78APP_STARTUP_PACKAGE="" 79APP_STARTUP_TIMES=1 80POST_BOOT_APP_LAUNCH_STATE="threshold_met" 81MAX_WAIT_FOR_FIRST_CAR_LAUNCHER_DISPLAYED_LOG_MILLISECONDS=1500 82SHOW_PLOTS=true 83PRINT_CUJ_DESC_AND_EXIT=false 84 85function usage() { 86 echo "Runs a CUJ, collects PSI measurement, and generates PSI plots / KPIs ." 87 echo "Usage: run_cuj.sh [-o|--out_dir <output_dir>]"\ 88 "[-d|--max_duration_seconds <max_duration_seconds>]"\ 89 "[-g|--grace_duration_seconds <grace_duration_seconds>]"\ 90 "[-b|--bootup] [-u|--user_switch [<to_user_id> | <from_user_id>-<to_user_id>]]"\ 91 "[-a|--app_startup <app_package_name>] [--app_startup_times <app_startup_times>]"\ 92 "[-r|--resume]"\ 93 "[--bootup_with_app_launch <post_boot_app_launch_state>]" 94 95 echo "-o|--out_dir: Location to output the psi dump and logs" 96 echo "-t|--psi_avg10_threshold: PSI threshold level for peak CPU activity during post boot" 97 echo "-d|--max_duration_seconds: Maximum duration to monitor for PSI changes" 98 echo "-g|--grace_duration_seconds: Grace duration to wait before terminating the monitor" 99 echo "-b|--bootup: Run bootup CUJ" 100 echo "-u|--user_switch: Run user switch CUJ" 101 echo "-r|--resume: Run resume from suspend CUJ" 102 echo "-a|--app_startup: Run app startup CUJ" 103 echo "--app_startup_times: Number of times to launch the app during app startup CUJ" 104 echo "--bootup_with_app_launch: Run bootup with app launch CUJ. Must provide post boot app"\ 105 "launch state, which is the post boot state to launch apps. Following states are supported:" 106 echo "\t- threshold_met: Launch apps when PSI threshold is met" 107 echo "\t- baseline_met: Launch apps when PSI baseline is met" 108 echo "\t- immediate: Launch apps immediately after launcher is displayed" 109 echo "\--no_show_plots: Don't show plots at the end of the run. Useful when running this script "\ 110 "in a loop" 111 echo "--print_cuj_desc: Prints the CUJ description and exits" 112 echo "-h|--help: Show this help message" 113 114 echo "Example commands:" 115 echo -e "\t1. ./run_cuj.sh -d 90 -g 20 -t 20 --bootup_with_app_launch threshold_met" 116 echo -e "\t2. ./run_cuj.sh -d 90 -g 20 -t 20 --bootup_with_app_launch baseline_met" 117 echo -e "\t3. ./run_cuj.sh -d 90 -g 20 -a com.android.contacts --app_startup_times 10" 118 echo -e "\t4. ./run_cuj.sh -d 90 -g 20 -u 10-11" 119 echo -e "\t5. ./run_cuj.sh -d 90 -g 20 -t 20 -b" 120} 121 122function usage_err() { 123 err "$@\n" 124 usage 125} 126 127function check_and_set_cuj() { 128 if [[ ! -z ${CUJ} ]]; then 129 usage_err "Multiple CUJs specified on the command line: ${CUJ} and ${1}" 130 exit 1 131 fi 132 CUJ=$1 133} 134 135function parse_arguments() { 136 while [[ $# > 0 ]]; do 137 key="$1" 138 case $key in 139 -h|--help) 140 usage 141 exit 1;; 142 -o|--out_dir) 143 OUTPUT_DIR=${2} 144 shift;; 145 -t|--psi_avg10_threshold) 146 PSI_AVG10_THRESHOLD=${2} 147 shift;; 148 -d|--max_duration_seconds) 149 MAX_PSI_MONITOR_DURATION_SECONDS=${2} 150 shift;; 151 -g|--grace_duration_seconds) 152 PSI_MONITOR_GRACE_DURATION_SECONDS=${2} 153 shift;; 154 -b|--bootup) 155 check_and_set_cuj "bootup" 156 ;; 157 -u|--user_switch) 158 check_and_set_cuj "user-switch" 159 USER_SWITCH_ARGS=${2} 160 shift;; 161 -a|--app_startup) 162 check_and_set_cuj "app-startup" 163 APP_STARTUP_PACKAGE=${2} 164 shift;; 165 --app_startup_times) 166 APP_STARTUP_TIMES=${2} 167 shift;; 168 -r|--resume) 169 check_and_set_cuj "resume" 170 ;; 171 --bootup_with_app_launch) 172 check_and_set_cuj "bootup-with-app-launch" 173 POST_BOOT_APP_LAUNCH_STATE=${2} 174 shift;; 175 --no_show_plots) 176 SHOW_PLOTS=false 177 ;; 178 --print_cuj_desc) 179 PRINT_CUJ_DESC_AND_EXIT=true 180 ;; 181 *) 182 echo "${0}: Invalid option '${1}'" 183 usage 184 exit 1;; 185 esac 186 shift # past argument or value 187 done 188} 189 190function print_log() { 191 if [[ ${PRINT_CUJ_DESC_AND_EXIT} == true ]]; then 192 return 193 fi 194 echo -e "[$(date +'%Y-%m-%d %H:%M:%S%z')] ${@}" | tee -a ${OUTPUT_DIR}/run_cuj_log.txt 195} 196 197function err() { 198 echo -e "[$(date +'%Y-%m-%d %H:%M:%S%z')] $@" >&2 | tee -a ${OUTPUT_DIR}/run_cuj_err.txt 199} 200 201function check_arguments() { 202 readonly OUTPUT_DIR 203 mkdir -p ${OUTPUT_DIR} 204 if [[ ! -d ${OUTPUT_DIR} ]]; then 205 usage_err "Out dir ${OUTPUT_DIR} does not exist" 206 exit 1 207 fi 208 209 readonly PSI_AVG10_THRESHOLD 210 if [[ ${PSI_AVG10_THRESHOLD} != +([[:digit:]]) || ${PSI_AVG10_THRESHOLD} -le 0 \ 211 || ${PSI_AVG10_THRESHOLD} -ge 100 ]]; then 212 usage_err "PSI Avg10 threshold ${PSI_AVG10_THRESHOLD} is not a valid number. The value should"\ 213 "be between 1 and 99" 214 exit 1 215 fi 216 217 if [[ ${MAX_PSI_MONITOR_DURATION_SECONDS} != +([[:digit:]]) \ 218 || ${MAX_PSI_MONITOR_DURATION_SECONDS} -lt 10 \ 219 || ${MAX_PSI_MONITOR_DURATION_SECONDS} -gt 3600 ]]; then 220 usage_err "Max psi monitor duration seconds ${MAX_PSI_MONITOR_DURATION_SECONDS} is not"\ 221 "a valid number. The value should be between 10 and 3600" 222 exit 1 223 fi 224 225 readonly PSI_MONITOR_GRACE_DURATION_SECONDS 226 if [[ ${PSI_MONITOR_GRACE_DURATION_SECONDS} != +([[:digit:]]) \ 227 || ${PSI_MONITOR_GRACE_DURATION_SECONDS} -lt 10 \ 228 || ${PSI_MONITOR_GRACE_DURATION_SECONDS} -gt 3600 ]]; then 229 usage_err "Psi monitor grace duration seconds ${PSI_MONITOR_GRACE_DURATION_SECONDS} is not"\ 230 "a valid number. The value should be between 10 and 3600" 231 exit 1 232 fi 233 234 if [[ -z ${CUJ} ]]; then 235 CUJ="bootup" 236 print_log "CUJ not specified, defaulting to bootup" 237 fi 238 239 readonly CUJ 240} 241 242ALL_USER_IDS=() 243## 244# Reads user ids from the device and sets the global variable ALL_USER_IDS. 245# 246# Parses the output of "adb shell pm list users", which is in the format of 247# """ 248# Users: 249# UserInfo{0:Driver:813} running 250# UserInfo{10:Driver:412} running 251# """ 252function read_all_user_ids() { 253 IFS=' ' read -r ALL_USER_IDS <<< $(adb shell pm list users | grep "{" | cut -d'{' -f2 \ 254 | cut -d':' -f1 | tr '\n' ' ') 255} 256 257FROM_USER_ID="" 258TO_USER_ID="" 259function check_cuj_args() { 260 case ${CUJ} in 261 bootup) 262 if [[ ${MAX_PSI_MONITOR_DURATION_SECONDS} -lt ${MIN_BOOTUP_DURATION_SECONDS} ]]; then 263 MAX_PSI_MONITOR_DURATION_SECONDS=${MIN_BOOTUP_DURATION_SECONDS} 264 print_log "Max psi monitor duration seconds is set to ${MAX_PSI_MONITOR_DURATION_SECONDS}"\ 265 "to accommodate bootup CUJ" 266 fi 267 ;; 268 user-switch) 269 user_switch_args=() 270 IFS='-' read -r -a user_switch_args <<< "${USER_SWITCH_ARGS}" 271 if [[ ${#user_switch_args[@]} -lt 1 ||${#user_switch_args[@]} -gt 2 ]]; then 272 usage_err "Invalid user switch args: ${USER_SWITCH_ARGS}. It should be in the format of" \ 273 "<to_user_id> or <from_user_id>-<to_user_id>" 274 exit 1 275 elif [[ ${#user_switch_args[@]} -eq 2 ]]; then 276 FROM_USER_ID=${user_switch_args[0]} 277 TO_USER_ID=${user_switch_args[1]} 278 else 279 FROM_USER_ID=$(adb shell am get-current-user) 280 TO_USER_ID=${user_switch_args[0]} 281 fi 282 283 if [[ ${FROM_USER_ID} -eq 0 || ${TO_USER_ID} -eq 0 ]]; then 284 err "From user id and to user id should be non-zero or non system users" 285 exit 1 286 elif [[ ${FROM_USER_ID} -eq ${TO_USER_ID} ]]; then 287 err "From user id and to user id should be different" 288 exit 1 289 fi 290 291 read_all_user_ids 292 if [[ ! " ${ALL_USER_IDS[@]} " =~ " ${FROM_USER_ID} " ]]; then 293 err "From user id ${FROM_USER_ID} is not a valid user id."\ 294 "Valid user ids are: ${ALL_USER_IDS[@]}" 295 exit 1 296 elif [[ ! " ${ALL_USER_IDS[@]} " =~ " ${TO_USER_ID} " ]]; then 297 err "To user id ${TO_USER_ID} is not a valid user id."\ 298 "Valid user ids are: ${ALL_USER_IDS[@]}" 299 exit 1 300 fi 301 if [[ ${MAX_PSI_MONITOR_DURATION_SECONDS} -lt ${MIN_USER_SWITCH_DURATION_SECONDS} ]]; then 302 MAX_PSI_MONITOR_DURATION_SECONDS=${MIN_USER_SWITCH_DURATION_SECONDS} 303 print_log "Max psi monitor duration seconds is set to ${MAX_PSI_MONITOR_DURATION_SECONDS}"\ 304 "to accommodate user switch CUJ" 305 fi 306 ;; 307 app-startup) 308 if [[ -z ${APP_STARTUP_PACKAGE} ]]; then 309 usage_err "App package name is not specified for app startup CUJ" 310 exit 1 311 fi 312 if [[ ${APP_STARTUP_TIMES} != +([[:digit:]]) || ${APP_STARTUP_TIMES} -le 0 313 || ${APP_STARTUP_TIMES} -gt 100 ]]; then 314 usage_err "App startup times should be positive integer between 1 and 100" 315 exit 1 316 fi 317 min_duration=$(echo "${APP_STARTUP_TIMES} 318 + ${POST_APP_STARTUP_MONITOR_DURATION_SECONDS}" | bc) 319 if [[ ${MAX_PSI_MONITOR_DURATION_SECONDS} -lt ${min_duration} ]]; then 320 MAX_PSI_MONITOR_DURATION_SECONDS=${min_duration} 321 print_log "Max psi monitor duration seconds is set to ${MAX_PSI_MONITOR_DURATION_SECONDS}"\ 322 "to accommodate app startup CUJ" 323 fi 324 ;; 325 bootup-with-app-launch) 326 if [[ ${POST_BOOT_APP_LAUNCH_STATE} != "threshold_met" && \ 327 ${POST_BOOT_APP_LAUNCH_STATE} != "baseline_met" && \ 328 ${POST_BOOT_APP_LAUNCH_STATE} != "immediate" ]]; then 329 usage_err "Post boot app launch state should be either threshold_met, baseline_met or"\ 330 "immediate" 331 exit 1 332 fi 333 if [[ ${MAX_PSI_MONITOR_DURATION_SECONDS} -lt ${MIN_BOOT_WITH_APP_LAUNCH_DURATION_SECONDS} ]] 334 then 335 MAX_PSI_MONITOR_DURATION_SECONDS=${MIN_BOOT_WITH_APP_LAUNCH_DURATION_SECONDS} 336 print_log "Max psi monitor duration seconds is set to ${MAX_PSI_MONITOR_DURATION_SECONDS}"\ 337 "to accommodate bootup with app launch CUJ" 338 fi 339 esac 340} 341 342function setup() { 343 adb_wait_with_recovery 344 fetch_device_info 345 adb shell rm -rf ${DEVICE_SCRIPT} ${DEVICE_OUT_DIR} 346 adb push ${LOCAL_SCRIPT_DIR}/${PSI_MONITOR_SCRIPT} ${DEVICE_SCRIPT} 347 adb shell chmod 755 ${DEVICE_SCRIPT} 348 adb shell mkdir -p ${DEVICE_OUT_DIR} 349 adb shell setprop persist.debug.psi_monitor.cuj_completed false 350 adb logcat -G 16M -b all -c 351} 352 353FROM_USER_STOPPED_COUNT=0 354function get_from_user_stopped_count() { 355 echo $(adb logcat -d -b events | grep "ssm_user_stopped: ${FROM_USER_ID}" | wc -l) 356} 357 358# This function is called before starting the CUJ. It performs any setup required for the CUJ. 359# This function should leave the device in a state where the adb connection is active. 360function pre_start_cuj() { 361 case ${CUJ} in 362 bootup | bootup-with-app-launch) 363 print_log "Rebooting adb device"; adb reboot 364 adb_wait_with_recovery 365 adb logcat -G 16M 366 ;; 367 user-switch) 368 current_user_id=$(adb shell am get-current-user) 369 if [[ ${current_user_id} -eq ${FROM_USER_ID} ]]; then 370 return 371 else 372 print_log "Switching user to from user ${FROM_USER_ID}, which is different from current user"\ 373 "${current_user_id}" 374 adb shell am switch-user ${FROM_USER_ID} 375 print_log "Waiting for user switch to complete by sleeping for 60 seconds" 376 sleep 60 377 # User switch events from previous switches should be ignored. So, get the number of 378 # user switch events from the previous run. Then use this to track the current user switch 379 # event. 380 FROM_USER_STOPPED_COUNT=$(get_from_user_stopped_count) 381 fi 382 ;; 383 resume) 384 err "Resume is not yet supported. Device needs an extra board to support this."\ 385 "Refer to http://go/seahawk-str"; exit 1 386 adb shell cmd car_service hibernate --auto 387 adb_wait_with_recovery 388 ;; 389 app-startup) 390 res=$(adb shell pm list packages ${APP_STARTUP_PACKAGE}) 391 if [[ -z ${res} ]]; then 392 err "App package ${APP_STARTUP_PACKAGE} is not installed on the device" 393 exit 1 394 elif [[ ! ${res} =~ ^package:${APP_STARTUP_PACKAGE}.* ]]; then 395 err "Multiple app packages with prefix ${APP_STARTUP_PACKAGE} are installed on the device" 396 exit 1 397 else 398 APP_STARTUP_PACKAGE=$(echo ${res} | cut -d':' -f2) 399 fi 400 # Reset the device to the home screen before starting the CUJ. 401 adb shell am start com.android.car.carlauncher/.CarLauncher 402 ;; 403 *) 404 ;; 405 esac 406} 407 408function fetch_logs() { 409 rm -f ${OUTPUT_DIR}/filtered_logcat.txt 410 while true; do 411 adb_wait_with_recovery 412 adb logcat -v year -s ActivityTaskManager,SystemServerTimingAsync \ 413 >> ${OUTPUT_DIR}/filtered_logcat.txt 414 done 415} 416 417function run_psi_monitor() { 418 set +e 419 readonly TERM_SIGNAL_TIMEOUT=$(echo "${MAX_PSI_MONITOR_DURATION_SECONDS} \ 420 + ${PSI_MONITOR_GRACE_DURATION_SECONDS}" | bc) 421 readonly KILL_SIGNAL_DURATION=${PSI_MONITOR_GRACE_DURATION_SECONDS} 422 print_log "Starting PSI monitoring with timeout ${TERM_SIGNAL_TIMEOUT}s and kill duration"\ 423 "${KILL_SIGNAL_DURATION}s" 424 timeout --preserve-status -k ${KILL_SIGNAL_DURATION} ${TERM_SIGNAL_TIMEOUT} \ 425 adb shell ${DEVICE_SCRIPT} --out_dir ${DEVICE_OUT_DIR} \ 426 --psi_avg10_threshold ${PSI_AVG10_THRESHOLD} \ 427 --max_duration_seconds ${MAX_PSI_MONITOR_DURATION_SECONDS} \ 428 --total_psi_entries_to_monitor_baseline ${TOTAL_PSI_ENTRIES_TO_MONITOR_BASELINE} \ 429 > ${OUTPUT_DIR}/psi_monitor_log.txt 2> ${OUTPUT_DIR}/psi_monitor_log.txt 430 exit_status=$? 431 print_log "psi_monitor.sh timeout command exit status is '${exit_status}'" 432 set -e 433 return ${exit_status} 434} 435 436function start_activity() { 437 if [ $# -eq 1 ]; then 438 package=$1 439 elif [ $# -eq 2 ]; then 440 action=$1 441 component=$2 442 else 443 action=$1 444 category=$2 445 flag=$3 446 component=$4 447 fi 448 449 if [ $# -eq 1 ]; then 450 print_log "Starting activity for ${package}" 451 print_log $(adb shell am start -W --user current ${package}) 452 sleep 1 # Wait to allow the package to perform some init tasks. 453 return 454 fi 455 456 print_log "Starting activity ${component}" 457 if [ $# -eq 2 ]; then 458 print_log $(adb shell am start -W --user current -a ${action} -n ${component}) 459 else 460 print_log $(adb shell am start -W --user current -a ${action} -c ${category} -f ${flag} \ 461 -n ${component}) 462 fi 463 sleep 1 # Wait to allow the package to perform some init tasks. 464} 465 466function start_cuj() { 467 case ${CUJ} in 468 bootup | bootup-with-app-launch) 469 # Boot doesn't need to be started by the script as it is already done by the bootloader 470 # as part of the reboot. 471 ;; 472 user-switch) 473 print_log "Switching user to user ${TO_USER_ID}" 474 adb shell am switch-user ${TO_USER_ID} 475 ;; 476 resume) 477 # Device will be automatically woken up by the hibernate wakeup event. So, no action is 478 # needed. 479 ;; 480 app-startup) 481 print_log "Starting app startup CUJ for ${APP_STARTUP_PACKAGE}" 482 for i in $(seq 1 ${APP_STARTUP_TIMES}); do 483 adb shell am force-stop --user current ${APP_STARTUP_PACKAGE} 484 print_log "Starting app startup CUJ for ${APP_STARTUP_PACKAGE} for the ${i} time" 485 start_activity ${APP_STARTUP_PACKAGE} 486 done 487 ;; 488 esac 489} 490 491function wait_for_cuj_to_complete() { 492 print_log "Waiting for CUJ to complete" 493 case ${CUJ} in 494 bootup | bootup-with-app-launch) 495 while [[ $(adb shell getprop sys.boot_completed) != 1 ]]; do 496 sleep 0.5 497 # Device connection will be lost intermittently during bootup. So, wait for the device 498 # connection after every 0.5 seconds. 499 adb_wait_with_recovery 500 done 501 # PSI monitor script will detect threshold and baseline changes only after the CUJ is 502 # completed. So, for boot-with-app-launch CUJ, mark the CUJ as completed on boot completed. 503 # This will allow the CUJ to progress forward based on the threshold_met or baseline_met 504 # conditions. 505 ;; 506 user-switch) 507 while [[ $(get_from_user_stopped_count) -eq ${FROM_USER_STOPPED_COUNT} ]]; do 508 sleep 0.1 509 done 510 ;; 511 resume) 512 # When adb is available after the device is woken up, the CUJ is considered as completed. So, 513 # no need to wait for any other event. 514 ;; 515 app-startup) 516 # App startup CUJ is considered as completed when all the apps are started. So, no need to 517 # wait for any other event. 518 ;; 519 esac 520 print_log "CUJ completed, marking CUJ as completed" 521 adb shell setprop persist.debug.psi_monitor.cuj_completed true 522} 523 524function wait_for_threshold_met() { 525 print_log "Waiting for threshold met" 526 while [[ $(adb shell getprop persist.debug.psi_monitor.threshold_met) != true ]]; do 527 # The PSI values are read only every 1 seconds by the PSI monitor script. So, wait for 50% of 528 # this duration to avoid waiting for too long and polling the device too often. 529 sleep 0.5 530 done 531} 532 533function wait_for_baseline_met() { 534 print_log "Waiting for baseline met" 535 while [[ $(adb shell getprop persist.debug.psi_monitor.baseline_met) != true ]]; do 536 # The PSI values are read only every 1 seconds by the PSI monitor script. So, wait for 50% of 537 # this duration to avoid waiting for too long and polling the device too often. 538 sleep 0.5 539 done 540} 541 542function start_launcher_activity() { 543 start_activity android.intent.action.MAIN android.intent.category.HOME 0x14000000 \ 544 com.android.car.carlauncher/.CarLauncher 545} 546 547function start_app_grid_activity() { 548 start_activity com.android.car.carlauncher.ACTION_APP_GRID android.intent.category.HOME \ 549 0x24000000 com.android.car.carlauncher/.GASAppGridActivity 550} 551 552function start_assistant_activity() { 553 start_activity com.google.android.carassistant 554} 555 556function stop_assistant_activity() { 557 adb shell am force-stop --user current com.google.android.carassistant 558} 559 560function start_maps_activity() { 561 start_activity android.intent.action.VIEW \ 562 com.google.android.apps.maps/com.google.android.maps.MapsActivity 563} 564 565function stop_maps_activity() { 566 adb shell am force-stop --user current com.google.android.apps.maps 567} 568 569function start_play_store_activity() { 570 start_activity com.android.vending 571} 572 573function stop_play_store_activity() { 574 adb shell am force-stop --user current com.android.vending 575} 576 577function start_contacts_activity() { 578 start_activity com.android.contacts 579} 580 581function stop_contacts_activity() { 582 adb shell am force-stop --user current com.android.contacts 583} 584 585function get_component_displayed_count() { 586 echo $(grep "ActivityTaskManager: Displayed " ${OUTPUT_DIR}/filtered_logcat.txt | grep ${1} \ 587 | wc -l) 588} 589 590function wait_for_activity_displayed() { 591 max_wait_millis=$2 592 print_log "Waiting for activity ${1} to be displayed" 593 slept_millis=0 594 while [[ $(get_component_displayed_count ${1}) -eq 0 595 && ${slept_millis} -lt ${max_wait_millis} ]]; do 596 sleep 0.1 597 slept_millis=$(($slept_millis + 100)) 598 done 599 print_log "Activity ${1} completed" 600} 601 602function launch_app_on_post_boot() { 603 # Limited maps activity is shown in the car launcher UI. So, force stop maps while in app grid 604 # activity to ensure that the maps activity is not shown and the app performs a warm start during 605 # the test. 606 start_app_grid_activity 607 stop_maps_activity 608 for i in $(seq 1 ${POST_BOOT_APPS_LAUNCH_TIMES}); do 609 print_log "Launching apps on post boot for the ${i}th time" 610 # Start apps 611 start_assistant_activity 612 start_maps_activity 613 start_play_store_activity 614 start_contacts_activity 615 616 if [[ ${i} -lt ${POST_BOOT_APPS_LAUNCH_TIMES} ]]; then 617 # Switch to app grid and force stop apps, so these app can perform warm / cold start during 618 # next run instead of bringing the previous activity to the foreground. 619 start_app_grid_activity 620 stop_assistant_activity 621 stop_maps_activity 622 stop_play_store_activity 623 stop_contacts_activity 624 fi 625 done 626} 627 628function post_cuj_events() { 629 case ${CUJ} in 630 bootup-with-app-launch) 631 if [[ ${POST_BOOT_APP_LAUNCH_STATE} == "threshold_met" ]]; then 632 wait_for_threshold_met 633 elif [[ ${POST_BOOT_APP_LAUNCH_STATE} == "baseline_met" ]]; then 634 wait_for_baseline_met 635 else 636 # On immediate, wait for CarLauncher to be displayed and sleep for 2 seconds to ensure 637 # the device has performed some init for the CarLauncher. 638 wait_for_activity_displayed com.android.car.carlauncher/.CarLauncher \ 639 ${MAX_WAIT_FOR_FIRST_CAR_LAUNCHER_DISPLAYED_LOG_MILLISECONDS} 640 sleep 2 641 fi 642 launch_app_on_post_boot 643 ;; 644 *) 645 ;; 646 esac 647} 648 649function get_cuj_desc() { 650 desc=${CUJ} 651 case ${CUJ} in 652 bootup) 653 desc="Post boot" 654 ;; 655 bootup-with-app-launch) 656 desc="Post boot with app launch after " 657 case ${POST_BOOT_APP_LAUNCH_STATE} in 658 threshold_met) 659 desc+="PSI threshold ${PSI_AVG10_THRESHOLD}% is met)" 660 ;; 661 baseline_met) 662 desc+="PSI reaches baseline across ${TOTAL_PSI_ENTRIES_TO_MONITOR_BASELINE} entries" 663 ;; 664 immediate) 665 desc+="car launcher is displayed" 666 ;; 667 esac 668 ;; 669 user-switch) 670 desc="Post user switch from ${FROM_USER_ID} to ${TO_USER_ID}" 671 ;; 672 app-startup) 673 desc="App startup for ${APP_STARTUP_PACKAGE}" 674 ;; 675 resume) 676 desc="Post resume" 677 ;; 678 esac 679 echo ${desc} 680} 681 682function main() { 683 set -e 684 parse_arguments "$@" 685 check_arguments 686 check_cuj_args 687 if [[ ${PRINT_CUJ_DESC_AND_EXIT} == true ]]; then 688 echo $(get_cuj_desc) 689 exit 690 fi 691 setup 692 693 pre_start_cuj 694 # Disable SELinux enforcement to allow the PSI monitor script to read the interfaces at 695 # /proc/pressure. During pre_start_cuj the device may reboot, so perform these actions only after 696 # the device is up. 697 adb root; adb_wait_with_recovery 698 adb shell setenforce 0 699 700 fetch_logs & 701 fetch_logs_pid=$! 702 703 run_psi_monitor & 704 psi_monitor_pid=$! 705 706 start_cuj 707 708 wait_for_cuj_to_complete 709 710 post_cuj_events & 711 post_cuj_events_pid=$! 712 print_log "Triggered post CUJ events in the background at pid ${post_cuj_events_pid}" 713 714 print_log "Waiting for PSI monitoring to complete at pid ${psi_monitor_pid}" 715 set +e 716 wait ${psi_monitor_pid} 717 set -e 718 719 if [[ $(ps -p ${post_cuj_events_pid} > /dev/null) ]]; then 720 # If the post CUJ events are still running, kill them because the PSI monitor has completed. 721 print_log "Killing post CUJ events at pid ${post_cuj_events_pid}" 722 kill ${post_cuj_events_pid} 723 fi 724 725 print_log "Killing logcat at pid ${fetch_logs_pid}" 726 kill ${fetch_logs_pid} 727 728 adb_wait_with_recovery 729 adb pull ${DEVICE_OUT_DIR} ${OUTPUT_DIR} 730 psi_monitor_out=${OUTPUT_DIR}/$(basename ${DEVICE_OUT_DIR}) 731 processed_out=${OUTPUT_DIR}/processed; mkdir -p ${processed_out} 732 733 ${LOCAL_SCRIPT_DIR}/${PARSE_SCRIPT} --psi_dump ${psi_monitor_out}/psi_dump.txt \ 734 --psi_csv ${processed_out}/psi.csv --logcat ${OUTPUT_DIR}/filtered_logcat.txt \ 735 --events_csv ${processed_out}/events.csv 736 if [[ ${SHOW_PLOTS} == true ]]; then 737 ${LOCAL_SCRIPT_DIR}/${PLOT_SCRIPT} --psi_csv ${processed_out}/psi.csv \ 738 --events_csv ${processed_out}/events.csv --cuj_name "$(get_cuj_desc)" \ 739 --out_file ${processed_out}/psi_plot.html --show_plot 740 else 741 ${LOCAL_SCRIPT_DIR}/${PLOT_SCRIPT} --psi_csv ${processed_out}/psi.csv \ 742 --events_csv ${processed_out}/events.csv --cuj_name "$(get_cuj_desc)" \ 743 --out_file ${processed_out}/psi_plot.html 744 fi 745 ${LOCAL_SCRIPT_DIR}/${GENERATE_KPIS_SCRIPT} --psi_csv ${processed_out}/psi.csv \ 746 --events_csv ${processed_out}/events.csv --out_kpi_csv ${processed_out}/kpis.csv 747 748 # Capture a bugreport for investigation purposes. 749 adb bugreport ${OUTPUT_DIR} 750} 751 752main "$@" 753