1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.cts; 17 18 import com.android.ddmlib.IShellOutputReceiver; 19 import com.android.tradefed.log.LogUtil; 20 21 import com.google.common.base.Charsets; 22 23 import java.util.Arrays; 24 import java.util.Random; 25 import java.util.concurrent.TimeUnit; 26 import java.util.concurrent.atomic.AtomicBoolean; 27 28 /** 29 * Test for "dumpsys batterystats -c 30 * 31 * Validates reporting of battery stats based on different events 32 */ 33 public class BatteryStatsValidationTest extends ProtoDumpTestCase { 34 private static final String TAG = "BatteryStatsValidationTest"; 35 36 private static final String DEVICE_SIDE_TEST_APK = "CtsBatteryStatsApp.apk"; 37 private static final String DEVICE_SIDE_TEST_PACKAGE 38 = "com.android.server.cts.device.batterystats"; 39 private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT 40 = "com.android.server.cts.device.batterystats/.BatteryStatsBackgroundService"; 41 private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT 42 = "com.android.server.cts.device.batterystats/.BatteryStatsForegroundActivity"; 43 private static final String DEVICE_SIDE_JOB_COMPONENT 44 = "com.android.server.cts.device.batterystats/.SimpleJobService"; 45 private static final String DEVICE_SIDE_SYNC_COMPONENT 46 = "com.android.server.cts.device.batterystats.provider/" 47 + "com.android.server.cts.device.batterystats"; 48 49 // These constants are those in PackageManager. 50 public static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le"; 51 public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; 52 public static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps"; 53 public static final String FEATURE_WIFI = "android.hardware.wifi"; 54 55 56 // Low end of packet size. TODO: Get exact packet size 57 private static final int LOW_MTU = 1500; 58 // High end of packet size. TODO: Get exact packet size 59 private static final int HIGH_MTU = 2500; 60 61 // Constants from BatteryStatsBgVsFgActions.java (not directly accessible here). 62 public static final String KEY_ACTION = "action"; 63 public static final String ACTION_BLE_SCAN_OPTIMIZED = "action.ble_scan_optimized"; 64 public static final String ACTION_BLE_SCAN_UNOPTIMIZED = "action.ble_scan_unoptimized"; 65 public static final String ACTION_GPS = "action.gps"; 66 public static final String ACTION_JOB_SCHEDULE = "action.jobs"; 67 public static final String ACTION_SYNC = "action.sync"; 68 public static final String ACTION_WIFI_SCAN = "action.wifi_scan"; 69 public static final String ACTION_WIFI_DOWNLOAD = "action.wifi_download"; 70 public static final String ACTION_WIFI_UPLOAD = "action.wifi_upload"; 71 72 public static final String KEY_REQUEST_CODE = "request_code"; 73 public static final String BG_VS_FG_TAG = "BatteryStatsBgVsFgActions"; 74 75 @Override setUp()76 protected void setUp() throws Exception { 77 super.setUp(); 78 79 // Uninstall to clear the history in case it's still on the device. 80 getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE); 81 } 82 83 /** Smallest possible HTTP header. */ 84 private static final int MIN_HTTP_HEADER_BYTES = 26; 85 86 @Override tearDown()87 protected void tearDown() throws Exception { 88 getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE); 89 90 batteryOffScreenOn(); 91 super.tearDown(); 92 } 93 screenOff()94 protected void screenOff() throws Exception { 95 getDevice().executeShellCommand("dumpsys batterystats enable pretend-screen-off"); 96 } 97 98 /** 99 * This will turn the screen on for real, not just disabling pretend-screen-off 100 */ turnScreenOnForReal()101 protected void turnScreenOnForReal() throws Exception { 102 getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP"); 103 getDevice().executeShellCommand("wm dismiss-keyguard"); 104 } 105 batteryOnScreenOn()106 protected void batteryOnScreenOn() throws Exception { 107 getDevice().executeShellCommand("dumpsys battery unplug"); 108 getDevice().executeShellCommand("dumpsys batterystats disable pretend-screen-off"); 109 } 110 batteryOnScreenOff()111 protected void batteryOnScreenOff() throws Exception { 112 getDevice().executeShellCommand("dumpsys battery unplug"); 113 getDevice().executeShellCommand("dumpsys batterystats enable pretend-screen-off"); 114 } 115 batteryOffScreenOn()116 protected void batteryOffScreenOn() throws Exception { 117 getDevice().executeShellCommand("dumpsys battery reset"); 118 getDevice().executeShellCommand("dumpsys batterystats disable pretend-screen-off"); 119 } 120 forceStop()121 private void forceStop() throws Exception { 122 getDevice().executeShellCommand("am force-stop " + DEVICE_SIDE_TEST_PACKAGE); 123 } 124 resetBatteryStats()125 private void resetBatteryStats() throws Exception { 126 getDevice().executeShellCommand("dumpsys batterystats --reset"); 127 } 128 testAlarms()129 public void testAlarms() throws Exception { 130 batteryOnScreenOff(); 131 132 installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true); 133 134 runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsAlarmTest", "testAlarms"); 135 136 assertValueRange("wua", "*walarm*:com.android.server.cts.device.batterystats.ALARM", 137 5, 3, 3); 138 139 batteryOffScreenOn(); 140 } 141 testWakeLockDuration()142 public void testWakeLockDuration() throws Exception { 143 batteryOnScreenOff(); 144 145 installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true); 146 147 runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWakeLockTests", 148 "testHoldShortWakeLock"); 149 150 runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWakeLockTests", 151 "testHoldLongWakeLock"); 152 153 assertValueRange("wl", "BSShortWakeLock", 15, (long) (500 * 0.9), 500 * 2); // partial max duration 154 assertValueRange("wl", "BSLongWakeLock", 15, (long) (3000 * 0.9), 3000 * 2); // partial max duration 155 156 batteryOffScreenOn(); 157 } 158 testServiceForegroundDuration()159 public void testServiceForegroundDuration() throws Exception { 160 batteryOnScreenOff(); 161 installPackage(DEVICE_SIDE_TEST_APK, true); 162 163 getDevice().executeShellCommand( 164 "am start -n com.android.server.cts.device.batterystats/.SimpleActivity"); 165 assertValueRange("st", "", 5, 0, 0); // No foreground service time before test 166 runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsProcessStateTests", 167 "testForegroundService"); 168 assertValueRange("st", "", 5, (long) (2000 * 0.8), 4000); 169 170 batteryOffScreenOn(); 171 } 172 testBleScans()173 public void testBleScans() throws Exception { 174 if (isTV() || !hasFeature(FEATURE_BLUETOOTH_LE, true)) { 175 return; 176 } 177 178 batteryOnScreenOff(); 179 installPackage(DEVICE_SIDE_TEST_APK, true); 180 181 // Background test. 182 executeBackground(ACTION_BLE_SCAN_UNOPTIMIZED, 30_000); 183 assertValueRange("blem", "", 5, 1, 1); // ble_scan_count 184 assertValueRange("blem", "", 6, 1, 1); // ble_scan_count_bg 185 186 // Foreground test. 187 executeForeground(ACTION_BLE_SCAN_UNOPTIMIZED, 30_000); 188 assertValueRange("blem", "", 5, 2, 2); // ble_scan_count 189 assertValueRange("blem", "", 6, 1, 1); // ble_scan_count_bg 190 191 batteryOffScreenOn(); 192 } 193 194 testUnoptimizedBleScans()195 public void testUnoptimizedBleScans() throws Exception { 196 if (isTV()) { 197 return; 198 } 199 batteryOnScreenOff(); 200 installPackage(DEVICE_SIDE_TEST_APK, true); 201 202 // Ble scan time in BatteryStatsBgVsFgActions is 2 seconds, but be lenient. 203 final int minTime = 1500; // min single scan time in ms 204 final int maxTime = 3000; // max single scan time in ms 205 206 // Optimized - Background. 207 executeBackground(ACTION_BLE_SCAN_OPTIMIZED, 30_000); 208 assertValueRange("blem", "", 7, 1*minTime, 1*maxTime); // actualTime 209 assertValueRange("blem", "", 8, 1*minTime, 1*maxTime); // actualTimeBg 210 assertValueRange("blem", "", 11, 0, 0); // unoptimizedScanTotalTime 211 assertValueRange("blem", "", 12, 0, 0); // unoptimizedScanTotalTimeBg 212 assertValueRange("blem", "", 13, 0, 0); // unoptimizedScanMaxTime 213 assertValueRange("blem", "", 14, 0, 0); // unoptimizedScanMaxTimeBg 214 215 // Optimized - Foreground. 216 executeForeground(ACTION_BLE_SCAN_OPTIMIZED, 30_000); 217 assertValueRange("blem", "", 7, 2*minTime, 2*maxTime); // actualTime 218 assertValueRange("blem", "", 8, 1*minTime, 1*maxTime); // actualTimeBg 219 assertValueRange("blem", "", 11, 0, 0); // unoptimizedScanTotalTime 220 assertValueRange("blem", "", 12, 0, 0); // unoptimizedScanTotalTimeBg 221 assertValueRange("blem", "", 13, 0, 0); // unoptimizedScanMaxTime 222 assertValueRange("blem", "", 14, 0, 0); // unoptimizedScanMaxTimeBg 223 224 // Unoptimized - Background. 225 executeBackground(ACTION_BLE_SCAN_UNOPTIMIZED, 30_000); 226 assertValueRange("blem", "", 7, 3*minTime, 3*maxTime); // actualTime 227 assertValueRange("blem", "", 8, 2*minTime, 2*maxTime); // actualTimeBg 228 assertValueRange("blem", "", 11, 1*minTime, 1*maxTime); // unoptimizedScanTotalTime 229 assertValueRange("blem", "", 12, 1*minTime, 1*maxTime); // unoptimizedScanTotalTimeBg 230 assertValueRange("blem", "", 13, 1*minTime, 1*maxTime); // unoptimizedScanMaxTime 231 assertValueRange("blem", "", 14, 1*minTime, 1*maxTime); // unoptimizedScanMaxTimeBg 232 233 // Unoptimized - Foreground. 234 executeForeground(ACTION_BLE_SCAN_UNOPTIMIZED, 30_000); 235 assertValueRange("blem", "", 7, 4*minTime, 4*maxTime); // actualTime 236 assertValueRange("blem", "", 8, 2*minTime, 2*maxTime); // actualTimeBg 237 assertValueRange("blem", "", 11, 2*minTime, 2*maxTime); // unoptimizedScanTotalTime 238 assertValueRange("blem", "", 12, 1*minTime, 1*maxTime); // unoptimizedScanTotalTimeBg 239 assertValueRange("blem", "", 13, 1*minTime, 1*maxTime); // unoptimizedScanMaxTime 240 assertValueRange("blem", "", 14, 1*minTime, 1*maxTime); // unoptimizedScanMaxTimeBg 241 242 batteryOffScreenOn(); 243 } 244 testGpsUpdates()245 public void testGpsUpdates() throws Exception { 246 if (isTV() || !hasFeature(FEATURE_LOCATION_GPS, true)) { 247 return; 248 } 249 250 final String gpsSensorNumber = "-10000"; 251 252 batteryOnScreenOff(); 253 installPackage(DEVICE_SIDE_TEST_APK, true); 254 // Whitelist this app against background location request throttling 255 getDevice().executeShellCommand(String.format( 256 "settings put global location_background_throttle_package_whitelist %s", 257 DEVICE_SIDE_TEST_PACKAGE)); 258 259 // Background test. 260 executeBackground(ACTION_GPS, 60_000); 261 assertValueRange("sr", gpsSensorNumber, 6, 1, 1); // count 262 assertValueRange("sr", gpsSensorNumber, 7, 1, 1); // background_count 263 264 // Foreground test. 265 executeForeground(ACTION_GPS, 60_000); 266 assertValueRange("sr", gpsSensorNumber, 6, 2, 2); // count 267 assertValueRange("sr", gpsSensorNumber, 7, 1, 1); // background_count 268 269 batteryOffScreenOn(); 270 } 271 testJobBgVsFg()272 public void testJobBgVsFg() throws Exception { 273 if (isTV()) { 274 return; 275 } 276 batteryOnScreenOff(); 277 installPackage(DEVICE_SIDE_TEST_APK, true); 278 279 // Background test. 280 executeBackground(ACTION_JOB_SCHEDULE, 60_000); 281 assertValueRange("jb", "", 6, 1, 1); // count 282 assertValueRange("jb", "", 8, 1, 1); // background_count 283 284 // Foreground test. 285 executeForeground(ACTION_JOB_SCHEDULE, 60_000); 286 assertValueRange("jb", "", 6, 2, 2); // count 287 assertValueRange("jb", "", 8, 1, 1); // background_count 288 289 batteryOffScreenOn(); 290 } 291 testSyncBgVsFg()292 public void testSyncBgVsFg() throws Exception { 293 if (isTV()) { 294 return; 295 } 296 batteryOnScreenOff(); 297 installPackage(DEVICE_SIDE_TEST_APK, true); 298 299 // Background test. 300 executeBackground(ACTION_SYNC, 60_000); 301 // Allow one or two syncs in this time frame (not just one) due to unpredictable syncs. 302 assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 1, 2); // count 303 assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 1, 2); // background_count 304 305 // Foreground test. 306 executeForeground(ACTION_SYNC, 60_000); 307 assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 2, 4); // count 308 assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 1, 2); // background_count 309 310 batteryOffScreenOn(); 311 } 312 testWifiScans()313 public void testWifiScans() throws Exception { 314 if (isTV() || !hasFeature(FEATURE_WIFI, true)) { 315 return; 316 } 317 318 batteryOnScreenOff(); 319 installPackage(DEVICE_SIDE_TEST_APK, true); 320 // Whitelist this app against background wifi scan throttling 321 getDevice().executeShellCommand(String.format( 322 "settings put global wifi_scan_background_throttle_package_whitelist %s", 323 DEVICE_SIDE_TEST_PACKAGE)); 324 325 // Background count test. 326 executeBackground(ACTION_WIFI_SCAN, 120_000); 327 // Allow one or two scans because we try scanning twice and because we allow for the 328 // possibility that, when the test is started, a scan from a different uid was already being 329 // performed (causing the test to 'miss' a scan). 330 assertValueRange("wfl", "", 7, 1, 2); // scan_count 331 assertValueRange("wfl", "", 11, 1, 2); // scan_count_bg 332 333 // Foreground count test. 334 executeForeground(ACTION_WIFI_SCAN, 120_000); 335 assertValueRange("wfl", "", 7, 2, 4); // scan_count 336 assertValueRange("wfl", "", 11, 1, 2); // scan_count_bg 337 338 batteryOffScreenOn(); 339 } 340 341 /** 342 * Tests whether the on-battery realtime and total realtime values 343 * are properly updated in battery stats. 344 */ testRealtime()345 public void testRealtime() throws Exception { 346 batteryOnScreenOff(); 347 long startingValueRealtime = getLongValue(0, "bt", "", 7); 348 long startingValueBatteryRealtime = getLongValue(0, "bt", "", 5); 349 // After going on battery 350 Thread.sleep(2000); 351 batteryOffScreenOn(); 352 // After going off battery 353 Thread.sleep(2000); 354 355 long currentValueRealtime = getLongValue(0, "bt", "", 7); 356 long currentValueBatteryRealtime = getLongValue(0, "bt", "", 5); 357 358 // Total realtime increase should be 4000ms at least 359 assertTrue(currentValueRealtime >= startingValueRealtime + 4000); 360 // But not too much more 361 assertTrue(currentValueRealtime < startingValueRealtime + 6000); 362 // Battery on realtime should be more than 2000 but less than 4000 363 assertTrue(currentValueBatteryRealtime >= startingValueBatteryRealtime + 2000); 364 assertTrue(currentValueBatteryRealtime < startingValueBatteryRealtime + 4000); 365 } 366 367 /** 368 * Tests the total duration reported for jobs run on the job scheduler. 369 */ 370 public void testJobDuration() throws Exception { 371 batteryOnScreenOff(); 372 373 installPackage(DEVICE_SIDE_TEST_APK, true); 374 375 runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsJobDurationTests", 376 "testJobDuration"); 377 378 // Should be approximately 3000 ms. Use 0.8x and 2x as the lower and upper 379 // bounds to account for possible errors due to thread scheduling and cpu load. 380 assertValueRange("jb", DEVICE_SIDE_JOB_COMPONENT, 5, (long) (3000 * 0.8), 3000 * 2); 381 batteryOffScreenOn(); 382 } 383 384 /** 385 * Tests the total duration and # of syncs reported for sync activities. 386 */ 387 public void testSyncs() throws Exception { 388 batteryOnScreenOff(); 389 390 installPackage(DEVICE_SIDE_TEST_APK, true); 391 392 runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsSyncTest", "testRunSyncs"); 393 394 // First, check the count, which should be 10. 395 // (It could be 11, if the initial sync actually happened before getting cancelled.) 396 assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 10L, 11L); 397 398 // Should be approximately, but at least 10 seconds. Use 2x as the upper 399 // bounds to account for possible errors due to thread scheduling and cpu load. 400 assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 5, 10000, 10000 * 2); 401 } 402 403 /** 404 * Tests the total bytes reported for downloading over wifi. 405 */ 406 public void testWifiDownload() throws Exception { 407 if (isTV() || !hasFeature(FEATURE_WIFI, true)) { 408 return; 409 } 410 411 batteryOnScreenOff(); 412 installPackage(DEVICE_SIDE_TEST_APK, true); 413 414 final long FUZZ = 50 * 1024; 415 416 long prevBytes = getLongValue(getUid(), "nt", "", 6); 417 418 String requestCode = executeForeground(ACTION_WIFI_DOWNLOAD, 60_000); 419 long downloadedBytes = getDownloadedBytes(requestCode); 420 assertTrue(downloadedBytes > 0); 421 long min = prevBytes + downloadedBytes + MIN_HTTP_HEADER_BYTES; 422 long max = prevBytes + downloadedBytes + FUZZ; // Add some fuzzing. 423 assertValueRange("nt", "", 6, min, max); // wifi_bytes_rx 424 assertValueRange("nt", "", 10, min / HIGH_MTU, max / LOW_MTU); // wifi_packets_rx 425 426 // Do the background download 427 long prevBgBytes = getLongValue(getUid(), "nt", "", 20); 428 requestCode = executeBackground(ACTION_WIFI_DOWNLOAD, 60_000); 429 downloadedBytes = getDownloadedBytes(requestCode); 430 431 long minBg = prevBgBytes + downloadedBytes + MIN_HTTP_HEADER_BYTES; 432 long maxBg = prevBgBytes + downloadedBytes + FUZZ; 433 assertValueRange("nt", "", 20, minBg, maxBg); // wifi_bytes_bg_rx 434 assertValueRange("nt", "", 24, minBg / HIGH_MTU, maxBg / LOW_MTU); // wifi_packets_bg_rx 435 436 // Also increases total wifi counts. 437 min += downloadedBytes + MIN_HTTP_HEADER_BYTES; 438 max += downloadedBytes + FUZZ; 439 assertValueRange("nt", "", 6, min, max); // wifi_bytes_rx 440 assertValueRange("nt", "", 10, min / HIGH_MTU, max / LOW_MTU); // wifi_packets_rx 441 442 batteryOffScreenOn(); 443 } 444 445 /** 446 * Tests the total bytes reported for uploading over wifi. 447 */ testWifiUpload()448 public void testWifiUpload() throws Exception { 449 if (isTV() || !hasFeature(FEATURE_WIFI, true)) { 450 return; 451 } 452 453 batteryOnScreenOff(); 454 installPackage(DEVICE_SIDE_TEST_APK, true); 455 456 executeBackground(ACTION_WIFI_UPLOAD, 60_000); 457 int min = MIN_HTTP_HEADER_BYTES + (2 * 1024); 458 int max = min + (6 * 1024); // Add some fuzzing. 459 assertValueRange("nt", "", 21, min, max); // wifi_bytes_bg_tx 460 461 executeForeground(ACTION_WIFI_UPLOAD, 60_000); 462 assertValueRange("nt", "", 7, min * 2, max * 2); // wifi_bytes_tx 463 464 batteryOffScreenOn(); 465 } 466 getUid()467 private int getUid() throws Exception { 468 String uidLine = getDevice().executeShellCommand("cmd package list packages -U " 469 + DEVICE_SIDE_TEST_PACKAGE); 470 String[] uidLineParts = uidLine.split(":"); 471 // 3rd entry is package uid 472 assertTrue(uidLineParts.length > 2); 473 int uid = Integer.parseInt(uidLineParts[2].trim()); 474 assertTrue(uid > 10000); 475 return uid; 476 } 477 478 /** 479 * Verifies that the recorded time for the specified tag and name in the test package 480 * is within the specified range. 481 */ assertValueRange(String tag, String optionalAfterTag, int index, long min, long max)482 private void assertValueRange(String tag, String optionalAfterTag, 483 int index, long min, long max) throws Exception { 484 int uid = getUid(); 485 long value = getLongValue(uid, tag, optionalAfterTag, index); 486 487 assertTrue("Value " + value + " is less than min " + min, value >= min); 488 assertTrue("Value " + value + " is greater than max " + max, value <= max); 489 } 490 491 /** 492 * Returns a particular long value from a line matched by uid, tag and the optionalAfterTag. 493 */ getLongValue(int uid, String tag, String optionalAfterTag, int index)494 private long getLongValue(int uid, String tag, String optionalAfterTag, int index) 495 throws Exception { 496 String dumpsys = getDevice().executeShellCommand("dumpsys batterystats --checkin"); 497 String[] lines = dumpsys.split("\n"); 498 long value = 0; 499 if (optionalAfterTag == null) { 500 optionalAfterTag = ""; 501 } 502 for (int i = lines.length - 1; i >= 0; i--) { 503 String line = lines[i]; 504 if (line.contains(uid + ",l," + tag + "," + optionalAfterTag) 505 || (!optionalAfterTag.equals("") && 506 line.contains(uid + ",l," + tag + ",\"" + optionalAfterTag))) { 507 String[] wlParts = line.split(","); 508 value = Long.parseLong(wlParts[index]); 509 } 510 } 511 return value; 512 } 513 514 /** 515 * Runs a (background) service to perform the given action, and waits for 516 * the device to report that the action has finished (via a logcat message) before returning. 517 * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired 518 * action to perform. 519 * @param maxTimeMs max time to wait (in ms) for action to report that it has completed. 520 * @return A string, representing a random integer, assigned to this particular request for the 521 * device to perform the given action. This value can be used to receive 522 * communications via logcat from the device about this action. 523 */ executeBackground(String actionValue, int maxTimeMs)524 private String executeBackground(String actionValue, int maxTimeMs) throws Exception { 525 String requestCode = executeBackground(actionValue); 526 String searchString = getCompletedActionString(actionValue, requestCode); 527 checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs); 528 return requestCode; 529 } 530 531 /** 532 * Runs a (background) service to perform the given action. 533 * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired 534 * action to perform. 535 * @return A string, representing a random integer, assigned to this particular request for the 536 * device to perform the given action. This value can be used to receive 537 * communications via logcat from the device about this action. 538 */ executeBackground(String actionValue)539 private String executeBackground(String actionValue) throws Exception { 540 allowBackgroundServices(); 541 String requestCode = Integer.toString(new Random().nextInt()); 542 getDevice().executeShellCommand(String.format( 543 "am startservice -n '%s' -e %s %s -e %s %s", 544 DEVICE_SIDE_BG_SERVICE_COMPONENT, 545 KEY_ACTION, actionValue, 546 KEY_REQUEST_CODE, requestCode)); 547 return requestCode; 548 } 549 550 /** Required to successfully start a background service from adb in O. */ allowBackgroundServices()551 private void allowBackgroundServices() throws Exception { 552 getDevice().executeShellCommand(String.format( 553 "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE)); 554 } 555 556 /** 557 * Runs an activity (in the foreground) to perform the given action, and waits 558 * for the device to report that the action has finished (via a logcat message) before returning. 559 * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired 560 * action to perform. 561 * @param maxTimeMs max time to wait (in ms) for action to report that it has completed. 562 * @return A string, representing a random integer, assigned to this particular request for the 563 * device to perform the given action. This value can be used to receive 564 * communications via logcat from the device about this action. 565 */ executeForeground(String actionValue, int maxTimeMs)566 private String executeForeground(String actionValue, int maxTimeMs) throws Exception { 567 String requestCode = executeForeground(actionValue); 568 String searchString = getCompletedActionString(actionValue, requestCode); 569 checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs); 570 return requestCode; 571 } 572 573 /** 574 * Runs an activity (in the foreground) to perform the given action. 575 * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired 576 * action to perform. 577 * @return A string, representing a random integer, assigned to this particular request for the 578 * device to perform the given action. This value can be used to receive 579 * communications via logcat from the device about this action. 580 */ executeForeground(String actionValue)581 private String executeForeground(String actionValue) throws Exception { 582 String requestCode = Integer.toString(new Random().nextInt()); 583 getDevice().executeShellCommand(String.format( 584 "am start -n '%s' -e %s %s -e %s %s", 585 DEVICE_SIDE_FG_ACTIVITY_COMPONENT, 586 KEY_ACTION, actionValue, 587 KEY_REQUEST_CODE, requestCode)); 588 return requestCode; 589 } 590 591 /** 592 * The string that will be printed in the logcat when the action completes. This needs to be 593 * identical to {@link com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions#tellHostActionFinished}. 594 */ getCompletedActionString(String actionValue, String requestCode)595 private String getCompletedActionString(String actionValue, String requestCode) { 596 return String.format("Completed performing %s for request %s", actionValue, requestCode); 597 } 598 599 /** 600 * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with 601 * the given tag. 602 * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data). 603 * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs. 604 */ checkLogcatForText(String logcatTag, String text, int maxTimeMs)605 private void checkLogcatForText(String logcatTag, String text, int maxTimeMs) { 606 IShellOutputReceiver receiver = new IShellOutputReceiver() { 607 private final StringBuilder mOutputBuffer = new StringBuilder(); 608 private final AtomicBoolean mIsCanceled = new AtomicBoolean(false); 609 610 @Override 611 public void addOutput(byte[] data, int offset, int length) { 612 if (!isCancelled()) { 613 synchronized (mOutputBuffer) { 614 String s = new String(data, offset, length, Charsets.UTF_8); 615 mOutputBuffer.append(s); 616 if (checkBufferForText()) { 617 mIsCanceled.set(true); 618 } 619 } 620 } 621 } 622 623 private boolean checkBufferForText() { 624 if (mOutputBuffer.indexOf(text) > -1) { 625 return true; 626 } else { 627 // delete all old data (except the last few chars) since they don't contain text 628 // (presumably large chunks of data will be added at a time, so this is 629 // sufficiently efficient.) 630 int newStart = mOutputBuffer.length() - text.length(); 631 if (newStart > 0) { 632 mOutputBuffer.delete(0, newStart); 633 } 634 return false; 635 } 636 } 637 638 @Override 639 public boolean isCancelled() { 640 return mIsCanceled.get(); 641 } 642 643 @Override 644 public void flush() { 645 } 646 }; 647 648 try { 649 // Wait for at most maxTimeMs for logcat to display the desired text. 650 getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text), 651 receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0); 652 } catch (com.android.tradefed.device.DeviceNotAvailableException e) { 653 System.err.println(e); 654 } 655 } 656 657 /** 658 * Returns the bytes downloaded for the wifi transfer download tests. 659 * @param requestCode the output of executeForeground() or executeBackground() to identify in 660 * the logcat the line associated with the desired download information 661 */ getDownloadedBytes(String requestCode)662 private long getDownloadedBytes(String requestCode) throws Exception { 663 String log = getDevice().executeShellCommand( 664 String.format("logcat -d -s BatteryStatsWifiTransferTests -e 'request %s d=\\d+'", 665 requestCode)); 666 String[] lines = log.split("\n"); 667 long size = 0; 668 for (int i = lines.length - 1; i >= 0; i--) { 669 String[] parts = lines[i].split("d="); 670 String num = parts[parts.length - 1].trim(); 671 if (num.matches("\\d+")) { 672 size = Integer.parseInt(num); 673 } 674 } 675 return size; 676 } 677 678 /** Determine if device is just a TV and is not expected to have proper batterystats. */ isTV()679 private boolean isTV() throws Exception { 680 return hasFeature(FEATURE_LEANBACK_ONLY, false); 681 } 682 683 /** 684 * Determines if the device has the given feature. 685 * Prints a warning if its value differs from requiredAnswer. 686 */ hasFeature(String featureName, boolean requiredAnswer)687 private boolean hasFeature(String featureName, boolean requiredAnswer) throws Exception { 688 final String features = getDevice().executeShellCommand("pm list features"); 689 boolean hasIt = features.contains(featureName); 690 if (hasIt != requiredAnswer) { 691 LogUtil.CLog.w("Device does " + (requiredAnswer ? "not " : "") + "have feature " 692 + featureName); 693 } 694 return hasIt; 695 } 696 } 697