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.internal.os; 17 18 import static android.os.BatteryStats.UID_TIMES_TYPE_ALL; 19 import static android.os.BatteryStats.Uid.NUM_PROCESS_STATE; 20 import static android.os.BatteryStats.Uid.PROCESS_STATE_BACKGROUND; 21 import static android.os.BatteryStats.Uid.PROCESS_STATE_CACHED; 22 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND; 23 import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE; 24 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; 25 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING; 26 import static android.os.BatteryStats.Uid.UID_PROCESS_TYPES; 27 28 import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS; 29 import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_TRACK_CPU_TIMES_BY_PROC_STATE; 30 31 import static junit.framework.Assert.assertNotNull; 32 import static junit.framework.Assert.assertNull; 33 import static junit.framework.Assert.assertTrue; 34 import static junit.framework.Assert.fail; 35 36 import com.android.frameworks.coretests.aidl.ICmdCallback; 37 import com.android.frameworks.coretests.aidl.ICmdReceiver; 38 39 import android.app.ActivityManager; 40 import android.app.KeyguardManager; 41 import android.content.ComponentName; 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.IntentFilter; 45 import android.content.ServiceConnection; 46 import android.content.pm.PackageManager; 47 import android.os.BatteryManager; 48 import android.os.BatteryStats; 49 import android.os.Bundle; 50 import android.os.IBinder; 51 import android.os.PowerManager; 52 import android.os.Process; 53 import android.os.SystemClock; 54 import android.provider.Settings; 55 import android.support.test.InstrumentationRegistry; 56 import android.support.test.filters.LargeTest; 57 import android.support.test.runner.AndroidJUnit4; 58 import android.support.test.uiautomator.UiDevice; 59 import android.text.TextUtils; 60 import android.util.DebugUtils; 61 import android.util.Log; 62 63 import org.junit.AfterClass; 64 import org.junit.BeforeClass; 65 import org.junit.Rule; 66 import org.junit.Test; 67 import org.junit.rules.TestName; 68 import org.junit.runner.RunWith; 69 70 import java.util.Arrays; 71 import java.util.concurrent.CountDownLatch; 72 import java.util.concurrent.TimeUnit; 73 import java.util.regex.Matcher; 74 import java.util.regex.Pattern; 75 76 @LargeTest 77 @RunWith(AndroidJUnit4.class) 78 public class BstatsCpuTimesValidationTest { 79 private static final String TAG = BstatsCpuTimesValidationTest.class.getSimpleName(); 80 81 private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp"; 82 private static final String TEST_ACTIVITY = TEST_PKG + ".TestActivity"; 83 private static final String TEST_SERVICE = TEST_PKG + ".TestService"; 84 private static final String ISOLATED_TEST_SERVICE = TEST_PKG + ".IsolatedTestService"; 85 86 private static final String EXTRA_KEY_CMD_RECEIVER = "cmd_receiver"; 87 private static final int FLAG_START_FOREGROUND = 1; 88 89 private static final int BATTERY_STATE_TIMEOUT_MS = 2000; 90 private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 200; 91 92 private static final int START_ACTIVITY_TIMEOUT_MS = 2000; 93 private static final int START_FG_SERVICE_TIMEOUT_MS = 2000; 94 private static final int START_SERVICE_TIMEOUT_MS = 2000; 95 private static final int START_ISOLATED_SERVICE_TIMEOUT_MS = 2000; 96 97 private static final int SETTING_UPDATE_TIMEOUT_MS = 2000; 98 private static final int SETTING_UPDATE_CHECK_INTERVAL_MS = 200; 99 100 private static final int GENERAL_TIMEOUT_MS = 4000; 101 private static final int GENERAL_INTERVAL_MS = 200; 102 103 private static final int WORK_DURATION_MS = 2000; 104 105 private static final String DESIRED_PROC_STATE_CPU_TIMES_DELAY = "0"; 106 107 private static boolean sBatteryStatsConstsUpdated; 108 private static String sOriginalBatteryStatsConsts; 109 110 private static Context sContext; 111 private static UiDevice sUiDevice; 112 private static int sTestPkgUid; 113 private static boolean sCpuFreqTimesAvailable; 114 private static boolean sPerProcStateTimesAvailable; 115 116 @Rule public TestName testName = new TestName(); 117 118 @BeforeClass 119 public static void setupOnce() throws Exception { 120 sContext = InstrumentationRegistry.getContext(); 121 sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 122 sContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG, 123 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); 124 sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_PKG, 0); 125 checkCpuTimesAvailability(); 126 if (sPerProcStateTimesAvailable && sCpuFreqTimesAvailable) { 127 setDesiredReadyDelay(); 128 } 129 } 130 131 @AfterClass 132 public static void tearDownOnce() throws Exception { 133 if (sBatteryStatsConstsUpdated) { 134 Settings.Global.putString(sContext.getContentResolver(), 135 Settings.Global.BATTERY_STATS_CONSTANTS, sOriginalBatteryStatsConsts); 136 } 137 batteryReset(); 138 } 139 140 private static void setDesiredReadyDelay() { 141 sOriginalBatteryStatsConsts = Settings.Global.getString(sContext.getContentResolver(), 142 Settings.Global.BATTERY_STATS_CONSTANTS); 143 String newBatteryStatsConstants; 144 final String newConstant = KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS 145 + "=" + DESIRED_PROC_STATE_CPU_TIMES_DELAY; 146 if (sOriginalBatteryStatsConsts == null || "null".equals(sOriginalBatteryStatsConsts)) { 147 // battery_stats_constants is initially empty, so just assign the desired value. 148 newBatteryStatsConstants = newConstant; 149 } else if (sOriginalBatteryStatsConsts.contains(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS)) { 150 // battery_stats_constants contains delay duration, so replace it 151 // with the desired value. 152 newBatteryStatsConstants = sOriginalBatteryStatsConsts.replaceAll( 153 KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS + "=\\d+", newConstant); 154 } else { 155 // battery_stats_constants didn't contain any delay, so append the desired value. 156 newBatteryStatsConstants = sOriginalBatteryStatsConsts + "," + newConstant; 157 } 158 Settings.Global.putString(sContext.getContentResolver(), 159 Settings.Global.BATTERY_STATS_CONSTANTS, newBatteryStatsConstants); 160 sBatteryStatsConstsUpdated = true; 161 } 162 163 // Checks cpu freq times of system uid as an indication of whether /proc/uid_time_in_state 164 // and /proc/uid/<uid>/time_in_state kernel nodes are available. 165 private static void checkCpuTimesAvailability() throws Exception { 166 batteryOn(); 167 SystemClock.sleep(GENERAL_TIMEOUT_MS); 168 batteryOff(); 169 final long[] totalCpuTimes = getAllCpuFreqTimes(Process.SYSTEM_UID); 170 sCpuFreqTimesAvailable = totalCpuTimes != null; 171 final long[] fgCpuTimes = getAllCpuFreqTimes(Process.SYSTEM_UID, 172 PROCESS_STATE_FOREGROUND); 173 sPerProcStateTimesAvailable = fgCpuTimes != null; 174 } 175 176 @Test 177 public void testCpuFreqTimes() throws Exception { 178 if (!sCpuFreqTimesAvailable) { 179 Log.w(TAG, "Skipping " + testName.getMethodName() 180 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 181 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 182 return; 183 } 184 185 batteryOnScreenOn(); 186 forceStop(); 187 resetBatteryStats(); 188 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 189 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 190 initialSnapshot); 191 doSomeWork(); 192 forceStop(); 193 194 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid); 195 assertCpuTimesValid(cpuTimesMs); 196 long actualCpuTimeMs = 0; 197 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 198 actualCpuTimeMs += cpuTimesMs[i]; 199 } 200 assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualCpuTimeMs); 201 batteryOffScreenOn(); 202 } 203 204 @Test 205 public void testCpuFreqTimes_screenOff() throws Exception { 206 if (!sCpuFreqTimesAvailable) { 207 Log.w(TAG, "Skipping " + testName.getMethodName() 208 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 209 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 210 return; 211 } 212 213 batteryOnScreenOff(); 214 forceStop(); 215 resetBatteryStats(); 216 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 217 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 218 initialSnapshot); 219 doSomeWork(); 220 forceStop(); 221 222 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid); 223 assertCpuTimesValid(cpuTimesMs); 224 long actualTotalCpuTimeMs = 0; 225 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 226 actualTotalCpuTimeMs += cpuTimesMs[i]; 227 } 228 assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualTotalCpuTimeMs); 229 long actualScreenOffCpuTimeMs = 0; 230 for (int i = cpuTimesMs.length / 2; i < cpuTimesMs.length; ++i) { 231 actualScreenOffCpuTimeMs += cpuTimesMs[i]; 232 } 233 assertApproximateValue("Incorrect screen-off cpu time", 234 WORK_DURATION_MS, actualScreenOffCpuTimeMs); 235 batteryOffScreenOn(); 236 } 237 238 @Test 239 public void testCpuFreqTimes_isolatedProcess() throws Exception { 240 if (!sCpuFreqTimesAvailable) { 241 Log.w(TAG, "Skipping " + testName.getMethodName() 242 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 243 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 244 return; 245 } 246 247 batteryOnScreenOn(); 248 forceStop(); 249 resetBatteryStats(); 250 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 251 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 252 initialSnapshot); 253 doSomeWorkInIsolatedProcess(); 254 forceStop(); 255 256 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid); 257 assertCpuTimesValid(cpuTimesMs); 258 long actualCpuTimeMs = 0; 259 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 260 actualCpuTimeMs += cpuTimesMs[i]; 261 } 262 assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualCpuTimeMs); 263 batteryOffScreenOn(); 264 } 265 266 @Test 267 public void testCpuFreqTimes_stateTop() throws Exception { 268 if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { 269 Log.w(TAG, "Skipping " + testName.getMethodName() 270 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 271 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 272 return; 273 } 274 275 batteryOnScreenOn(); 276 forceStop(); 277 resetBatteryStats(); 278 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 279 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 280 initialSnapshot); 281 assertNull("Initial top state snapshot should be null", 282 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP)); 283 284 doSomeWork(PROCESS_STATE_TOP); 285 forceStop(); 286 287 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); 288 final String msgCpuTimes = getAllCpuTimesMsg(); 289 assertCpuTimesValid(cpuTimesMs); 290 long actualCpuTimeMs = 0; 291 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 292 actualCpuTimeMs += cpuTimesMs[i]; 293 } 294 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 295 WORK_DURATION_MS, actualCpuTimeMs); 296 batteryOffScreenOn(); 297 } 298 299 @Test 300 public void testIsolatedCpuFreqTimes_stateService() throws Exception { 301 if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { 302 Log.w(TAG, "Skipping " + testName.getMethodName() 303 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 304 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 305 return; 306 } 307 308 batteryOnScreenOn(); 309 forceStop(); 310 resetBatteryStats(); 311 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 312 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 313 initialSnapshot); 314 assertNull("Initial top state snapshot should be null", 315 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP)); 316 317 final ICmdReceiver activityReceiver = ICmdReceiver.Stub.asInterface(startActivity()); 318 final ICmdReceiver isolatedReceiver = ICmdReceiver.Stub.asInterface(startIsolatedService()); 319 try { 320 assertProcState(PROCESS_STATE_TOP); 321 isolatedReceiver.doSomeWork(WORK_DURATION_MS); 322 } finally { 323 activityReceiver.finishHost(); 324 isolatedReceiver.finishHost(); 325 } 326 forceStop(); 327 328 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); 329 final String msgCpuTimes = getAllCpuTimesMsg(); 330 assertCpuTimesValid(cpuTimesMs); 331 long actualCpuTimeMs = 0; 332 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 333 actualCpuTimeMs += cpuTimesMs[i]; 334 } 335 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 336 WORK_DURATION_MS, actualCpuTimeMs); 337 batteryOffScreenOn(); 338 } 339 340 @Test 341 public void testCpuFreqTimes_stateTopSleeping() throws Exception { 342 if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { 343 Log.w(TAG, "Skipping " + testName.getMethodName() 344 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 345 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 346 return; 347 } 348 349 batteryOnScreenOff(); 350 forceStop(); 351 resetBatteryStats(); 352 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 353 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 354 initialSnapshot); 355 assertNull("Initial top state snapshot should be null", 356 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP_SLEEPING)); 357 358 doSomeWork(PROCESS_STATE_TOP_SLEEPING); 359 forceStop(); 360 361 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP_SLEEPING); 362 final String msgCpuTimes = getAllCpuTimesMsg(); 363 assertCpuTimesValid(cpuTimesMs); 364 long actualCpuTimeMs = 0; 365 for (int i = cpuTimesMs.length / 2; i < cpuTimesMs.length; ++i) { 366 actualCpuTimeMs += cpuTimesMs[i]; 367 } 368 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 369 WORK_DURATION_MS, actualCpuTimeMs); 370 batteryOffScreenOn(); 371 } 372 373 @Test 374 public void testCpuFreqTimes_stateFgService() throws Exception { 375 if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { 376 Log.w(TAG, "Skipping " + testName.getMethodName() 377 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 378 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 379 return; 380 } 381 382 batteryOnScreenOff(); 383 forceStop(); 384 resetBatteryStats(); 385 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 386 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 387 initialSnapshot); 388 assertNull("Initial top state snapshot should be null", 389 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_FOREGROUND_SERVICE)); 390 391 doSomeWork(PROCESS_STATE_FOREGROUND_SERVICE); 392 forceStop(); 393 394 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_FOREGROUND_SERVICE); 395 final String msgCpuTimes = getAllCpuTimesMsg(); 396 assertCpuTimesValid(cpuTimesMs); 397 long actualCpuTimeMs = 0; 398 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 399 actualCpuTimeMs += cpuTimesMs[i]; 400 } 401 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 402 WORK_DURATION_MS, actualCpuTimeMs); 403 batteryOffScreenOn(); 404 } 405 406 @Test 407 public void testCpuFreqTimes_stateFg() throws Exception { 408 if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { 409 Log.w(TAG, "Skipping " + testName.getMethodName() 410 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 411 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 412 return; 413 } 414 415 batteryOnScreenOn(); 416 forceStop(); 417 resetBatteryStats(); 418 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 419 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 420 initialSnapshot); 421 assertNull("Initial top state snapshot should be null", 422 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_FOREGROUND)); 423 424 doSomeWork(PROCESS_STATE_FOREGROUND); 425 forceStop(); 426 427 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_FOREGROUND); 428 final String msgCpuTimes = getAllCpuTimesMsg(); 429 assertCpuTimesValid(cpuTimesMs); 430 long actualCpuTimeMs = 0; 431 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 432 actualCpuTimeMs += cpuTimesMs[i]; 433 } 434 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 435 WORK_DURATION_MS, actualCpuTimeMs); 436 batteryOff(); 437 } 438 439 @Test 440 public void testCpuFreqTimes_stateBg() throws Exception { 441 if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { 442 Log.w(TAG, "Skipping " + testName.getMethodName() 443 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 444 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 445 return; 446 } 447 448 batteryOnScreenOff(); 449 forceStop(); 450 resetBatteryStats(); 451 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 452 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 453 initialSnapshot); 454 assertNull("Initial top state snapshot should be null", 455 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_BACKGROUND)); 456 457 doSomeWork(PROCESS_STATE_BACKGROUND); 458 forceStop(); 459 460 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_BACKGROUND); 461 final String msgCpuTimes = getAllCpuTimesMsg(); 462 assertCpuTimesValid(cpuTimesMs); 463 long actualCpuTimeMs = 0; 464 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 465 actualCpuTimeMs += cpuTimesMs[i]; 466 } 467 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 468 WORK_DURATION_MS, actualCpuTimeMs); 469 batteryOffScreenOn(); 470 } 471 472 @Test 473 public void testCpuFreqTimes_stateCached() throws Exception { 474 if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { 475 Log.w(TAG, "Skipping " + testName.getMethodName() 476 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 477 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 478 return; 479 } 480 481 batteryOnScreenOn(); 482 forceStop(); 483 resetBatteryStats(); 484 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 485 assertNull("Initial snapshot should be null, initial=" + Arrays.toString(initialSnapshot), 486 initialSnapshot); 487 assertNull("Initial top state snapshot should be null", 488 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_CACHED)); 489 490 doSomeWork(PROCESS_STATE_CACHED); 491 forceStop(); 492 493 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_CACHED); 494 final String msgCpuTimes = getAllCpuTimesMsg(); 495 assertCpuTimesValid(cpuTimesMs); 496 long actualCpuTimeMs = 0; 497 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 498 actualCpuTimeMs += cpuTimesMs[i]; 499 } 500 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 501 WORK_DURATION_MS, actualCpuTimeMs); 502 batteryOffScreenOn(); 503 } 504 505 @Test 506 public void testCpuFreqTimes_trackingDisabled() throws Exception { 507 if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { 508 Log.w(TAG, "Skipping " + testName.getMethodName() 509 + "; freqTimesAvailable=" + sCpuFreqTimesAvailable 510 + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); 511 return; 512 } 513 514 final String bstatsConstants = Settings.Global.getString(sContext.getContentResolver(), 515 Settings.Global.BATTERY_STATS_CONSTANTS); 516 try { 517 batteryOnScreenOn(); 518 forceStop(); 519 resetBatteryStats(); 520 final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); 521 assertNull("Initial snapshot should be null, initial=" 522 + Arrays.toString(initialSnapshot), initialSnapshot); 523 assertNull("Initial top state snapshot should be null", 524 getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP)); 525 526 doSomeWork(PROCESS_STATE_TOP); 527 forceStop(); 528 529 final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); 530 final String msgCpuTimes = getAllCpuTimesMsg(); 531 assertCpuTimesValid(cpuTimesMs); 532 long actualCpuTimeMs = 0; 533 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 534 actualCpuTimeMs += cpuTimesMs[i]; 535 } 536 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 537 WORK_DURATION_MS, actualCpuTimeMs); 538 539 updateTrackPerProcStateCpuTimesSetting(bstatsConstants, false); 540 541 doSomeWork(PROCESS_STATE_TOP); 542 forceStop(); 543 544 final long[] cpuTimesMs2 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); 545 assertCpuTimesValid(cpuTimesMs2); 546 assertCpuTimesEqual(cpuTimesMs2, cpuTimesMs, 20, 547 "Unexpected cpu times with tracking off"); 548 549 updateTrackPerProcStateCpuTimesSetting(bstatsConstants, true); 550 551 final long[] cpuTimesMs3 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); 552 assertCpuTimesValid(cpuTimesMs3); 553 assertCpuTimesEqual(cpuTimesMs3, cpuTimesMs, 20, 554 "Unexpected cpu times after turning on tracking"); 555 556 doSomeWork(PROCESS_STATE_TOP); 557 forceStop(); 558 559 final long[] cpuTimesMs4 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); 560 assertCpuTimesValid(cpuTimesMs4); 561 actualCpuTimeMs = 0; 562 for (int i = 0; i < cpuTimesMs.length / 2; ++i) { 563 actualCpuTimeMs += cpuTimesMs[i]; 564 } 565 assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, 566 2 * WORK_DURATION_MS, actualCpuTimeMs); 567 568 batteryOffScreenOn(); 569 } finally { 570 Settings.Global.putString(sContext.getContentResolver(), 571 Settings.Global.BATTERY_STATS_CONSTANTS, bstatsConstants); 572 } 573 } 574 575 private void assertCpuTimesEqual(long[] actual, long[] expected, long delta, String errMsg) { 576 for (int i = actual.length - 1; i >= 0; --i) { 577 if (actual[i] > expected[i] + delta || actual[i] < expected[i]) { 578 fail(errMsg + ", actual=" + actual + ", expected=" + expected + ", delta=" + delta); 579 } 580 } 581 } 582 583 private void updateTrackPerProcStateCpuTimesSetting(String originalConstants, boolean enabled) 584 throws Exception { 585 final String newConstants; 586 final String setting = KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=" + enabled; 587 if (originalConstants == null || "null".equals(originalConstants)) { 588 newConstants = setting; 589 } else if (originalConstants.contains(KEY_TRACK_CPU_TIMES_BY_PROC_STATE)) { 590 newConstants = originalConstants.replaceAll( 591 KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=(true|false)", setting); 592 } else { 593 newConstants = originalConstants + "," + setting; 594 } 595 Settings.Global.putString(sContext.getContentResolver(), 596 Settings.Global.BATTERY_STATS_CONSTANTS, newConstants); 597 assertTrackPerProcStateCpuTimesSetting(enabled); 598 } 599 600 private void assertTrackPerProcStateCpuTimesSetting(boolean enabled) throws Exception { 601 final String expectedValue = Boolean.toString(enabled); 602 assertDelayedCondition("Unexpected value for " + KEY_TRACK_CPU_TIMES_BY_PROC_STATE, () -> { 603 final String actualValue = getSettingValueFromDump(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); 604 return expectedValue.equals(actualValue) 605 ? null : "expected=" + expectedValue + ", actual=" + actualValue; 606 }, SETTING_UPDATE_TIMEOUT_MS, SETTING_UPDATE_CHECK_INTERVAL_MS); 607 } 608 609 private String getSettingValueFromDump(String key) throws Exception { 610 final String settingsDump = executeCmdSilent("dumpsys batterystats --settings"); 611 final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n'); 612 splitter.setString(settingsDump); 613 String next; 614 while (splitter.hasNext()) { 615 next = splitter.next(); 616 if (next.startsWith(key)) { 617 return next.split("=")[1]; 618 } 619 } 620 return null; 621 } 622 623 private void assertCpuTimesValid(long[] cpuTimes) { 624 assertNotNull(cpuTimes); 625 for (int i = 0; i < cpuTimes.length; ++i) { 626 if (cpuTimes[i] < 0) { 627 fail("Malformed cpu times data (-ve values): " + Arrays.toString(cpuTimes)); 628 } 629 } 630 final int numFreqs = cpuTimes.length / 2; 631 for (int i = 0; i < numFreqs; ++i) { 632 if (cpuTimes[i] < cpuTimes[numFreqs + i]) { 633 fail("Malformed cpu times data (screen-off > total)" + Arrays.toString(cpuTimes)); 634 } 635 } 636 } 637 638 private void assertApproximateValue(String errorPrefix, long expectedValue, long actualValue) { 639 assertValueRange(errorPrefix, actualValue, expectedValue * 0.5, expectedValue * 1.5); 640 } 641 642 private void assertValueRange(String errorPrefix, 643 long actualvalue, double minValue, double maxValue) { 644 final String errorMsg = String.format(errorPrefix + "; actual=%s; min=%s; max=%s", 645 actualvalue, minValue, maxValue); 646 assertTrue(errorMsg, actualvalue < maxValue); 647 assertTrue(errorMsg, actualvalue > minValue); 648 } 649 650 private void doSomeWork() throws Exception { 651 final ICmdReceiver receiver = ICmdReceiver.Stub.asInterface(startActivity()); 652 receiver.doSomeWork(WORK_DURATION_MS); 653 receiver.finishHost(); 654 } 655 656 private void doSomeWorkInIsolatedProcess() throws Exception { 657 final ICmdReceiver receiver = ICmdReceiver.Stub.asInterface(startIsolatedService()); 658 receiver.doSomeWork(WORK_DURATION_MS); 659 receiver.finishHost(); 660 } 661 662 private void doSomeWork(int procState) throws Exception { 663 final ICmdReceiver receiver; 664 switch (procState) { 665 case PROCESS_STATE_TOP: 666 receiver = ICmdReceiver.Stub.asInterface(startActivity()); 667 break; 668 case PROCESS_STATE_TOP_SLEEPING: 669 receiver = ICmdReceiver.Stub.asInterface(startActivity()); 670 break; 671 case PROCESS_STATE_FOREGROUND_SERVICE: 672 receiver = ICmdReceiver.Stub.asInterface(startForegroundService()); 673 break; 674 case PROCESS_STATE_FOREGROUND: 675 receiver = ICmdReceiver.Stub.asInterface(startService()); 676 receiver.showApplicationOverlay(); 677 break; 678 case PROCESS_STATE_BACKGROUND: 679 receiver = ICmdReceiver.Stub.asInterface(startService()); 680 break; 681 case PROCESS_STATE_CACHED: 682 receiver = ICmdReceiver.Stub.asInterface(startActivity()); 683 receiver.finishHost(); 684 break; 685 default: 686 throw new IllegalArgumentException("Unknown state: " + procState); 687 } 688 try { 689 assertProcState(procState); 690 receiver.doSomeWork(WORK_DURATION_MS); 691 } finally { 692 receiver.finishHost(); 693 } 694 } 695 696 private void assertProcState(String state) throws Exception { 697 final String expectedState = "(" + state + ")"; 698 assertDelayedCondition("", () -> { 699 final String uidStateStr = executeCmd("cmd activity get-uid-state " + sTestPkgUid); 700 final String actualState = uidStateStr.split(" ")[1]; 701 return expectedState.equals(actualState) ? null 702 : "expected=" + expectedState + ", actual" + actualState; 703 }); 704 } 705 706 private void assertProcState(int expectedState) throws Exception { 707 assertDelayedCondition("Unexpected proc state", () -> { 708 final String uidStateStr = executeCmd("cmd activity get-uid-state " + sTestPkgUid); 709 final int amProcState = Integer.parseInt(uidStateStr.split(" ")[0]); 710 final int actualState = BatteryStats.mapToInternalProcessState(amProcState); 711 return (actualState == expectedState) ? null 712 : "expected=" + getStateName(BatteryStats.Uid.class, expectedState) 713 + ", actual=" + getStateName(BatteryStats.Uid.class, actualState) 714 + ", amState=" + getStateName(ActivityManager.class, amProcState); 715 }); 716 } 717 718 private String getStateName(Class clazz, int procState) { 719 return DebugUtils.valueToString(clazz, "PROCESS_STATE_", procState); 720 } 721 722 private IBinder startIsolatedService() throws Exception { 723 final CountDownLatch latch = new CountDownLatch(1); 724 final IBinder[] binders = new IBinder[1]; 725 final ServiceConnection connection = new ServiceConnection() { 726 @Override 727 public void onServiceConnected(ComponentName name, IBinder service) { 728 binders[0] = service; 729 latch.countDown(); 730 } 731 732 @Override 733 public void onServiceDisconnected(ComponentName name) { 734 } 735 }; 736 final Intent launchIntent = new Intent() 737 .setComponent(new ComponentName(TEST_PKG, ISOLATED_TEST_SERVICE)); 738 sContext.bindService(launchIntent, connection, Context.BIND_AUTO_CREATE 739 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_FOREGROUND); 740 if (latch.await(START_ISOLATED_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 741 if (binders[0] == null) { 742 fail("Receiver binder should not be null"); 743 } 744 return binders[0]; 745 } else { 746 fail("Timed out waiting for the isolated test service to start"); 747 } 748 return null; 749 } 750 751 private IBinder startForegroundService() throws Exception { 752 final CountDownLatch latch = new CountDownLatch(1); 753 final Intent launchIntent = new Intent() 754 .setComponent(new ComponentName(TEST_PKG, TEST_SERVICE)) 755 .setFlags(FLAG_START_FOREGROUND); 756 final Bundle extras = new Bundle(); 757 final IBinder[] binders = new IBinder[1]; 758 extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() { 759 @Override 760 public void onLaunched(IBinder receiver) { 761 binders[0] = receiver; 762 latch.countDown(); 763 } 764 }); 765 launchIntent.putExtras(extras); 766 sContext.startForegroundService(launchIntent); 767 if (latch.await(START_FG_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 768 if (binders[0] == null) { 769 fail("Receiver binder should not be null"); 770 } 771 return binders[0]; 772 } else { 773 fail("Timed out waiting for the test fg service to start; testUid=" + sTestPkgUid); 774 } 775 return null; 776 } 777 778 private IBinder startService() throws Exception { 779 final CountDownLatch latch = new CountDownLatch(1); 780 final Intent launchIntent = new Intent() 781 .setComponent(new ComponentName(TEST_PKG, TEST_SERVICE)); 782 final Bundle extras = new Bundle(); 783 final IBinder[] binders = new IBinder[1]; 784 extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() { 785 @Override 786 public void onLaunched(IBinder receiver) { 787 binders[0] = receiver; 788 latch.countDown(); 789 } 790 }); 791 launchIntent.putExtras(extras); 792 sContext.startService(launchIntent); 793 if (latch.await(START_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 794 if (binders[0] == null) { 795 fail("Receiver binder should not be null"); 796 } 797 return binders[0]; 798 } else { 799 fail("Timed out waiting for the test service to start; testUid=" + sTestPkgUid); 800 } 801 return null; 802 } 803 804 private IBinder startActivity() throws Exception { 805 final CountDownLatch latch = new CountDownLatch(1); 806 final Intent launchIntent = new Intent() 807 .setComponent(new ComponentName(TEST_PKG, TEST_ACTIVITY)); 808 final Bundle extras = new Bundle(); 809 final IBinder[] binders = new IBinder[1]; 810 extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() { 811 @Override 812 public void onLaunched(IBinder receiver) { 813 binders[0] = receiver; 814 latch.countDown(); 815 } 816 }); 817 launchIntent.putExtras(extras) 818 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 819 sContext.startActivity(launchIntent); 820 if (latch.await(START_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 821 if (binders[0] == null) { 822 fail("Receiver binder should not be null"); 823 } 824 return binders[0]; 825 } else { 826 fail("Timed out waiting for the test activity to start; testUid=" + sTestPkgUid); 827 } 828 return null; 829 } 830 831 private static String getAllCpuTimesMsg() throws Exception { 832 final StringBuilder sb = new StringBuilder(); 833 sb.append("uid=" + sTestPkgUid + ";"); 834 sb.append(UID_TIMES_TYPE_ALL + "=" + getMsgCpuTimesSum(getAllCpuFreqTimes(sTestPkgUid))); 835 for (int i = 0; i < NUM_PROCESS_STATE; ++i) { 836 sb.append("|"); 837 sb.append(UID_PROCESS_TYPES[i] + "=" 838 + getMsgCpuTimesSum(getAllCpuFreqTimes(sTestPkgUid, i))); 839 } 840 return sb.toString(); 841 } 842 843 private static String getMsgCpuTimesSum(long[] cpuTimes) throws Exception { 844 if (cpuTimes == null) { 845 return "(0,0)"; 846 } 847 long totalTime = 0; 848 for (int i = 0; i < cpuTimes.length / 2; ++i) { 849 totalTime += cpuTimes[i]; 850 } 851 long screenOffTime = 0; 852 for (int i = cpuTimes.length / 2; i < cpuTimes.length; ++i) { 853 screenOffTime += cpuTimes[i]; 854 } 855 return "(" + totalTime + "," + screenOffTime + ")"; 856 } 857 858 private static long[] getAllCpuFreqTimes(int uid) throws Exception { 859 final String checkinDump = executeCmdSilent("dumpsys batterystats --checkin"); 860 final Pattern pattern = Pattern.compile(uid + ",l,ctf," + UID_TIMES_TYPE_ALL + ",(.*?)\n"); 861 final Matcher matcher = pattern.matcher(checkinDump); 862 if (!matcher.find()) { 863 return null; 864 } 865 return parseCpuTimesStr(matcher.group(1)); 866 } 867 868 private static long[] getAllCpuFreqTimes(int uid, int procState) throws Exception { 869 final String checkinDump = executeCmdSilent("dumpsys batterystats --checkin"); 870 final Pattern pattern = Pattern.compile( 871 uid + ",l,ctf," + UID_PROCESS_TYPES[procState] + ",(.*?)\n"); 872 final Matcher matcher = pattern.matcher(checkinDump); 873 if (!matcher.find()) { 874 return null; 875 } 876 return parseCpuTimesStr(matcher.group(1)); 877 } 878 879 private static long[] parseCpuTimesStr(String str) { 880 final String[] cpuTimesStr = str.split(","); 881 final int freqCount = Integer.parseInt(cpuTimesStr[0]); 882 if (cpuTimesStr.length != (2 * freqCount + 1)) { 883 fail("Malformed data: " + Arrays.toString(cpuTimesStr)); 884 } 885 final long[] cpuTimes = new long[freqCount * 2]; 886 for (int i = 0; i < cpuTimes.length; ++i) { 887 cpuTimes[i] = Long.parseLong(cpuTimesStr[i + 1]); 888 } 889 return cpuTimes; 890 } 891 892 private void resetBatteryStats() throws Exception { 893 executeCmd("dumpsys batterystats --reset"); 894 } 895 896 private void batteryOnScreenOn() throws Exception { 897 batteryOn(); 898 screenOn(); 899 } 900 901 private void batteryOnScreenOff() throws Exception { 902 batteryOn(); 903 screenoff(); 904 } 905 906 private void batteryOffScreenOn() throws Exception { 907 batteryOff(); 908 screenOn(); 909 } 910 911 private static void batteryOn() throws Exception { 912 executeCmd("dumpsys battery unplug"); 913 assertBatteryState(false /* pluggedIn */); 914 } 915 916 private static void batteryOff() throws Exception { 917 executeCmd("dumpsys battery set ac " + BatteryManager.BATTERY_PLUGGED_AC); 918 assertBatteryState(true /* pluggedIn */); 919 } 920 921 private static void batteryReset() throws Exception { 922 executeCmd("dumpsys battery reset"); 923 } 924 925 private void screenOn() throws Exception { 926 executeCmd("input keyevent KEYCODE_WAKEUP"); 927 executeCmd("wm dismiss-keyguard"); 928 assertKeyguardUnLocked(); 929 assertScreenInteractive(true); 930 } 931 932 private void screenoff() throws Exception { 933 executeCmd("input keyevent KEYCODE_SLEEP"); 934 assertScreenInteractive(false); 935 } 936 937 private void forceStop() throws Exception { 938 executeCmd("cmd activity force-stop " + TEST_PKG); 939 assertProcState("NONEXISTENT"); 940 } 941 942 private void assertKeyguardUnLocked() throws Exception { 943 final KeyguardManager keyguardManager = 944 (KeyguardManager) sContext.getSystemService(Context.KEYGUARD_SERVICE); 945 assertDelayedCondition("Unexpected Keyguard state", () -> 946 keyguardManager.isKeyguardLocked() ? "expected=unlocked" : null 947 ); 948 } 949 950 private void assertScreenInteractive(boolean interactive) throws Exception { 951 final PowerManager powerManager = 952 (PowerManager) sContext.getSystemService(Context.POWER_SERVICE); 953 assertDelayedCondition("Unexpected screen interactive state", () -> 954 interactive == powerManager.isInteractive() ? null : "expected=" + interactive 955 ); 956 } 957 958 private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition) 959 throws Exception { 960 assertDelayedCondition(errMsgPrefix, condition, GENERAL_TIMEOUT_MS, GENERAL_INTERVAL_MS); 961 } 962 963 private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition, 964 long timeoutMs, long checkIntervalMs) throws Exception { 965 final long endTime = SystemClock.uptimeMillis() + timeoutMs; 966 while (SystemClock.uptimeMillis() <= endTime) { 967 if (condition.getErrIfNotTrue() == null) { 968 return; 969 } 970 SystemClock.sleep(checkIntervalMs); 971 } 972 final String errMsg = condition.getErrIfNotTrue(); 973 if (errMsg != null) { 974 fail(errMsgPrefix + ": " + errMsg); 975 } 976 } 977 978 private static void assertBatteryState(boolean pluggedIn) throws Exception { 979 final long endTime = SystemClock.uptimeMillis() + BATTERY_STATE_TIMEOUT_MS; 980 while (isDevicePluggedIn() != pluggedIn && SystemClock.uptimeMillis() <= endTime) { 981 Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS); 982 } 983 if (isDevicePluggedIn() != pluggedIn) { 984 fail("Timed out waiting for the plugged-in state to change," 985 + " expected pluggedIn: " + pluggedIn); 986 } 987 } 988 989 private static boolean isDevicePluggedIn() { 990 final Intent batteryIntent = sContext.registerReceiver(null, 991 new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 992 return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0; 993 } 994 995 private static String executeCmd(String cmd) throws Exception { 996 final String result = sUiDevice.executeShellCommand(cmd).trim(); 997 Log.d(TAG, String.format("Result for '%s': %s", cmd, result)); 998 return result; 999 } 1000 1001 private static String executeCmdSilent(String cmd) throws Exception { 1002 return sUiDevice.executeShellCommand(cmd).trim(); 1003 } 1004 1005 private interface ExpectedCondition { 1006 String getErrIfNotTrue() throws Exception; 1007 } 1008 } 1009