1 /* 2 * Copyright (C) 2021 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 17 package com.android.server.power.stats; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyLong; 23 import static org.mockito.Mockito.doAnswer; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.spy; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.app.ActivityManager; 30 import android.content.Context; 31 import android.hardware.SensorManager; 32 import android.os.AggregateBatteryConsumer; 33 import android.os.BatteryConsumer; 34 import android.os.BatteryManager; 35 import android.os.BatteryStats; 36 import android.os.BatteryUsageStats; 37 import android.os.BatteryUsageStatsQuery; 38 import android.os.ConditionVariable; 39 import android.os.Parcel; 40 import android.os.Process; 41 import android.os.UidBatteryConsumer; 42 import android.platform.test.ravenwood.RavenwoodRule; 43 import android.util.SparseLongArray; 44 45 import androidx.test.InstrumentationRegistry; 46 import androidx.test.filters.SmallTest; 47 import androidx.test.runner.AndroidJUnit4; 48 49 import com.android.internal.os.BatteryStatsHistoryIterator; 50 import com.android.internal.os.MonotonicClock; 51 import com.android.internal.os.PowerProfile; 52 import com.android.server.power.stats.processor.MultiStatePowerAttributor; 53 54 import org.junit.Before; 55 import org.junit.Rule; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 59 import java.io.File; 60 import java.io.IOException; 61 import java.util.List; 62 63 @SmallTest 64 @RunWith(AndroidJUnit4.class) 65 public class BatteryUsageStatsProviderTest { 66 @Rule(order = 0) 67 public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() 68 .setProvideMainThread(true) 69 .build(); 70 71 private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; 72 private static final long MINUTE_IN_MS = 60 * 1000; 73 private static final double PRECISION = 0.00001; 74 75 @Rule(order = 1) 76 public final BatteryUsageStatsRule mStatsRule = 77 new BatteryUsageStatsRule(12345) 78 .createTempDirectory() 79 .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0) 80 .setAveragePower(PowerProfile.POWER_AUDIO, 720.0) 81 .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0); 82 83 private MockClock mMockClock = mStatsRule.getMockClock(); 84 private MonotonicClock mMonotonicClock = new MonotonicClock(666777, mMockClock); 85 private Context mContext; 86 87 @Before setup()88 public void setup() throws IOException { 89 if (RavenwoodRule.isUnderRavenwood()) { 90 mContext = mock(Context.class); 91 SensorManager sensorManager = mock(SensorManager.class); 92 when(mContext.getSystemService(SensorManager.class)).thenReturn(sensorManager); 93 } else { 94 mContext = InstrumentationRegistry.getContext(); 95 } 96 } 97 98 @Test test_getBatteryUsageStats()99 public void test_getBatteryUsageStats() throws IOException { 100 final BatteryUsageStats batteryUsageStats = prepareBatteryUsageStats(false); 101 102 final List<UidBatteryConsumer> uidBatteryConsumers = 103 batteryUsageStats.getUidBatteryConsumers(); 104 final UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(0); 105 assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND)) 106 .isEqualTo(20 * MINUTE_IN_MS); 107 assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND)) 108 .isEqualTo(40 * MINUTE_IN_MS); 109 assertThat(uidBatteryConsumer 110 .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND)) 111 .isEqualTo(20 * MINUTE_IN_MS); 112 assertThat(uidBatteryConsumer 113 .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND)) 114 .isEqualTo(20 * MINUTE_IN_MS); 115 assertThat(uidBatteryConsumer 116 .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE)) 117 .isEqualTo(20 * MINUTE_IN_MS); 118 assertThat(uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO)) 119 .isWithin(PRECISION).of(2.0); 120 assertThat( 121 uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 122 .isWithin(PRECISION).of(0.4); 123 124 assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(12345); 125 assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(180 * MINUTE_IN_MS); 126 batteryUsageStats.close(); 127 } 128 129 @Test batteryLevelInfo_charging()130 public void batteryLevelInfo_charging() throws IOException { 131 final BatteryUsageStats batteryUsageStats = prepareBatteryUsageStats(true); 132 assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000.0); 133 assertThat(batteryUsageStats.getChargeTimeRemainingMs()).isEqualTo(1_200_000); 134 batteryUsageStats.close(); 135 } 136 137 @Test batteryLevelInfo_onBattery()138 public void batteryLevelInfo_onBattery() throws IOException { 139 final BatteryUsageStats batteryUsageStats = prepareBatteryUsageStats(false); 140 assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000.0); 141 assertThat(batteryUsageStats.getBatteryTimeRemainingMs()).isEqualTo(600_000); 142 batteryUsageStats.close(); 143 } 144 145 @Test test_selectPowerComponents()146 public void test_selectPowerComponents() throws IOException { 147 BatteryStatsImpl batteryStats = prepareBatteryStats(false); 148 149 BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, 150 mock(PowerAttributor.class), mStatsRule.getPowerProfile(), 151 mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock, 152 mMonotonicClock); 153 154 final BatteryUsageStats batteryUsageStats = 155 provider.getBatteryUsageStats(batteryStats, 156 new BatteryUsageStatsQuery.Builder() 157 .includePowerComponents( 158 new int[]{BatteryConsumer.POWER_COMPONENT_AUDIO}) 159 .build() 160 ); 161 162 final List<UidBatteryConsumer> uidBatteryConsumers = 163 batteryUsageStats.getUidBatteryConsumers(); 164 final UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(0); 165 assertThat(uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO)) 166 .isWithin(PRECISION).of(2.0); 167 168 // FLASHLIGHT power estimation not requested, so the returned value is 0 169 assertThat( 170 uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 171 .isEqualTo(0); 172 173 batteryUsageStats.close(); 174 } 175 prepareBatteryStats(boolean plugInAtTheEnd)176 private BatteryStatsImpl prepareBatteryStats(boolean plugInAtTheEnd) { 177 BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); 178 batteryStats.onSystemReady(mContext); 179 180 synchronized (batteryStats) { 181 batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 182 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 0, 183 0, 0); 184 } 185 186 mStatsRule.setTime(10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); 187 synchronized (batteryStats) { 188 batteryStats.noteActivityResumedLocked(APP_UID); 189 } 190 191 mStatsRule.setTime(10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); 192 synchronized (batteryStats) { 193 batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP); 194 } 195 mStatsRule.setTime(30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); 196 synchronized (batteryStats) { 197 batteryStats.noteActivityPausedLocked(APP_UID); 198 } 199 mStatsRule.setTime(30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); 200 synchronized (batteryStats) { 201 batteryStats.noteUidProcessStateLocked(APP_UID, 202 ActivityManager.PROCESS_STATE_SERVICE); 203 } 204 mStatsRule.setTime(40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS); 205 synchronized (batteryStats) { 206 batteryStats.noteUidProcessStateLocked(APP_UID, 207 ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); 208 } 209 mStatsRule.setTime(50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS); 210 synchronized (batteryStats) { 211 batteryStats.noteUidProcessStateLocked(APP_UID, 212 ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE); 213 } 214 mStatsRule.setTime(60 * MINUTE_IN_MS, 60 * MINUTE_IN_MS); 215 synchronized (batteryStats) { 216 batteryStats.noteUidProcessStateLocked(APP_UID, 217 ActivityManager.PROCESS_STATE_BOUND_TOP); 218 } 219 mStatsRule.setTime(70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS); 220 synchronized (batteryStats) { 221 batteryStats.noteUidProcessStateLocked(APP_UID, 222 ActivityManager.PROCESS_STATE_CACHED_EMPTY); 223 } 224 synchronized (batteryStats) { 225 batteryStats.noteFlashlightOnLocked(APP_UID, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); 226 } 227 synchronized (batteryStats) { 228 batteryStats.noteFlashlightOffLocked(APP_UID, 80 * MINUTE_IN_MS + 4000, 229 80 * MINUTE_IN_MS + 4000); 230 } 231 232 synchronized (batteryStats) { 233 batteryStats.noteAudioOnLocked(APP_UID, 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS); 234 } 235 synchronized (batteryStats) { 236 batteryStats.noteAudioOffLocked(APP_UID, 90 * MINUTE_IN_MS + 10000, 237 90 * MINUTE_IN_MS + 10000); 238 } 239 240 synchronized (batteryStats) { 241 batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 242 100, /* plugType */ 0, 60, 72, 3700, 2_600_000, 4_000_000, 0, 243 100 * MINUTE_IN_MS, 100 * MINUTE_IN_MS, 21000); 244 } 245 246 synchronized (batteryStats) { 247 batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 248 100, /* plugType */ 0, 30, 72, 3700, 1_600_000, 4_000_000, 0, 249 110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS, 21000); 250 } 251 252 if (plugInAtTheEnd) { 253 synchronized (batteryStats) { 254 batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_CHARGING, 255 100, /* plugType */ BatteryManager.BATTERY_PLUGGED_USB, 30, 72, 3700, 256 1_600_000, 4_000_000, /* chargeTimeToFullSeconds */ 20 * 60, 257 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS, 22000); 258 } 259 } 260 261 setTime(180 * MINUTE_IN_MS); 262 263 return batteryStats; 264 } 265 prepareBatteryUsageStats(boolean plugInAtTheEnd)266 private BatteryUsageStats prepareBatteryUsageStats(boolean plugInAtTheEnd) { 267 BatteryStatsImpl batteryStats = prepareBatteryStats(plugInAtTheEnd); 268 269 MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(mContext, 270 mock(PowerStatsStore.class), mStatsRule.getPowerProfile(), 271 mStatsRule.getCpuScalingPolicies(), () -> 3500); 272 powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_AUDIO, 273 true); 274 powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, 275 true); 276 277 BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, 278 powerAttributor, mStatsRule.getPowerProfile(), 279 mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock, 280 mMonotonicClock); 281 282 return provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT); 283 } 284 285 @Test testWriteAndReadHistory()286 public void testWriteAndReadHistory() throws IOException { 287 MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); 288 synchronized (batteryStats) { 289 batteryStats.setRecordAllHistoryLocked(true); 290 } 291 batteryStats.forceRecordAllHistory(); 292 293 batteryStats.setNoAutoReset(true); 294 295 synchronized (batteryStats) { 296 batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 297 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000, 298 1_000_000, 1_000_000); 299 } 300 301 synchronized (batteryStats) { 302 batteryStats.noteAlarmStartLocked("foo", null, APP_UID, 3_000_000, 2_000_000); 303 } 304 synchronized (batteryStats) { 305 batteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000); 306 } 307 308 BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, 309 mock(PowerAttributor.class), mStatsRule.getPowerProfile(), 310 mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock, 311 mMonotonicClock); 312 313 final BatteryUsageStats batteryUsageStats = 314 provider.getBatteryUsageStats(batteryStats, 315 new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build()); 316 317 Parcel in = Parcel.obtain(); 318 batteryUsageStats.writeToParcel(in, 0); 319 batteryUsageStats.close(); 320 321 final byte[] bytes = in.marshall(); 322 323 Parcel out = Parcel.obtain(); 324 out.unmarshall(bytes, 0, bytes.length); 325 out.setDataPosition(0); 326 327 BatteryUsageStats unparceled = BatteryUsageStats.CREATOR.createFromParcel(out); 328 329 final BatteryStatsHistoryIterator iterator = 330 unparceled.iterateBatteryStatsHistory(); 331 BatteryStats.HistoryItem item; 332 333 assertThat(item = iterator.next()).isNotNull(); 334 assertHistoryItem(item, 335 BatteryStats.HistoryItem.CMD_RESET, BatteryStats.HistoryItem.EVENT_NONE, 336 null, 0, 3_600_000, 90, 1_000_000); 337 338 assertThat(item = iterator.next()).isNotNull(); 339 assertHistoryItem(item, 340 BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, 341 null, 0, 3_600_000, 90, 1_000_000); 342 assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0); 343 344 assertThat(item = iterator.next()).isNotNull(); 345 assertHistoryItem(item, 346 BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, 347 null, 0, 3_600_000, 90, 2_000_000); 348 assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isEqualTo(0); 349 350 assertThat(item = iterator.next()).isNotNull(); 351 assertHistoryItem(item, 352 BatteryStats.HistoryItem.CMD_UPDATE, 353 BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_START, 354 "foo", APP_UID, 3_600_000, 90, 3_000_000); 355 356 assertThat(item = iterator.next()).isNotNull(); 357 assertHistoryItem(item, 358 BatteryStats.HistoryItem.CMD_UPDATE, 359 BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_FINISH, 360 "foo", APP_UID, 3_600_000, 90, 3_001_000); 361 362 assertThat(iterator.hasNext()).isFalse(); 363 assertThat(iterator.next()).isNull(); 364 365 unparceled.close(); 366 } 367 368 @Test testWriteAndReadHistoryTags()369 public void testWriteAndReadHistoryTags() throws IOException { 370 MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); 371 synchronized (batteryStats) { 372 batteryStats.setRecordAllHistoryLocked(true); 373 } 374 batteryStats.forceRecordAllHistory(); 375 376 batteryStats.setNoAutoReset(true); 377 378 mStatsRule.setTime(1_000_000, 1_000_000); 379 380 synchronized (batteryStats) { 381 batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 382 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000, 383 1_000_000, 1_000_000); 384 } 385 386 // Add a large number of different history tags with strings of increasing length. 387 // These long strings will overflow the history buffer, at which point 388 // history will be written to disk and a new buffer started. 389 // As a result, we will only see a tail end of the sequence of events included 390 // in history. 391 for (int i = 1; i < 200; i++) { 392 StringBuilder sb = new StringBuilder().append(i).append(" "); 393 for (int j = 0; j <= i; j++) { 394 sb.append("word "); 395 } 396 final int uid = i; 397 synchronized (batteryStats) { 398 batteryStats.noteJobStartLocked(sb.toString(), uid); 399 } 400 } 401 402 BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, 403 mock(PowerAttributor.class), mStatsRule.getPowerProfile(), 404 mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock, 405 mMonotonicClock); 406 407 final BatteryUsageStats batteryUsageStats = 408 provider.getBatteryUsageStats(batteryStats, 409 new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build()); 410 411 Parcel parcel = Parcel.obtain(); 412 parcel.writeParcelable(batteryUsageStats, 0); 413 414 if (!RavenwoodRule.isUnderRavenwood()) { 415 assertThat(parcel.dataSize()).isAtMost(128_000); 416 } 417 418 batteryUsageStats.close(); 419 420 parcel.setDataPosition(0); 421 422 BatteryUsageStats unparceled = parcel.readParcelable(getClass().getClassLoader(), 423 BatteryUsageStats.class); 424 425 BatteryStatsHistoryIterator iterator = unparceled.iterateBatteryStatsHistory(); 426 BatteryStats.HistoryItem item; 427 428 assertThat(item = iterator.next()).isNotNull(); 429 assertThat(item.cmd).isEqualTo((int) BatteryStats.HistoryItem.CMD_RESET); 430 431 int expectedUid = 1; 432 while ((item = iterator.next()) != null) { 433 while (item.cmd != BatteryStats.HistoryItem.CMD_UPDATE 434 || item.eventCode == BatteryStats.HistoryItem.EVENT_NONE) { 435 assertThat(item = iterator.next()).isNotNull(); 436 } 437 int uid = item.eventTag.uid; 438 assertThat(uid).isEqualTo(expectedUid++); 439 assertThat(item.eventCode).isEqualTo( 440 BatteryStats.HistoryItem.EVENT_JOB | BatteryStats.HistoryItem.EVENT_FLAG_START); 441 assertThat(item.eventTag.string).startsWith(uid + " "); 442 assertThat(item.batteryChargeUah).isEqualTo(3_600_000); 443 assertThat(item.batteryLevel).isEqualTo(90); 444 assertThat(item.time).isEqualTo((long) 1_000_000); 445 } 446 447 assertThat(expectedUid).isEqualTo(200); 448 } 449 assertHistoryItem(BatteryStats.HistoryItem item, int command, int eventCode, String tag, int uid, int batteryChargeUah, int batteryLevel, long elapsedTimeMs)450 private void assertHistoryItem(BatteryStats.HistoryItem item, int command, int eventCode, 451 String tag, int uid, int batteryChargeUah, int batteryLevel, long elapsedTimeMs) { 452 assertThat(item.cmd).isEqualTo(command); 453 assertThat(item.eventCode).isEqualTo(eventCode); 454 if (tag == null) { 455 assertThat(item.eventTag).isNull(); 456 } else { 457 assertThat(item.eventTag.string).isEqualTo(tag); 458 assertThat(item.eventTag.uid).isEqualTo(uid); 459 } 460 assertThat(item.batteryChargeUah).isEqualTo(batteryChargeUah); 461 assertThat(item.batteryLevel).isEqualTo(batteryLevel); 462 463 assertThat(item.time).isEqualTo(elapsedTimeMs); 464 } 465 466 @Test shouldUpdateStats()467 public void shouldUpdateStats() { 468 final List<BatteryUsageStatsQuery> queries = List.of( 469 new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(1000).build(), 470 new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(2000).build() 471 ); 472 473 assertThat(BatteryUsageStatsProvider.shouldUpdateStats(queries, 474 10500, 10000)).isFalse(); 475 476 assertThat(BatteryUsageStatsProvider.shouldUpdateStats(queries, 477 11500, 10000)).isTrue(); 478 } 479 480 @Test testAggregateBatteryStats()481 public void testAggregateBatteryStats() throws IOException { 482 BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); 483 484 setTime(5 * MINUTE_IN_MS); 485 synchronized (batteryStats) { 486 batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND); 487 } 488 489 PowerStatsStore powerStatsStore = new PowerStatsStore( 490 new File(mStatsRule.getHistoryDir(), "powerstatsstore"), 491 mStatsRule.getHandler()); 492 powerStatsStore.reset(); 493 494 BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, 495 mock(PowerAttributor.class), mStatsRule.getPowerProfile(), 496 mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock, 497 mMonotonicClock); 498 499 batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore, 500 /* accumulateBatteryUsageStats */ false); 501 synchronized (batteryStats) { 502 batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND); 503 } 504 505 synchronized (batteryStats) { 506 batteryStats.noteFlashlightOnLocked(APP_UID, 507 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); 508 } 509 synchronized (batteryStats) { 510 batteryStats.noteFlashlightOffLocked(APP_UID, 511 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS); 512 } 513 setTime(25 * MINUTE_IN_MS); 514 synchronized (batteryStats) { 515 batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND); 516 } 517 518 synchronized (batteryStats) { 519 batteryStats.noteFlashlightOnLocked(APP_UID, 520 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); 521 } 522 synchronized (batteryStats) { 523 batteryStats.noteFlashlightOffLocked(APP_UID, 524 50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS); 525 } 526 setTime(55 * MINUTE_IN_MS); 527 synchronized (batteryStats) { 528 batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND); 529 } 530 531 // This section should be ignored because the timestamp is out or range 532 synchronized (batteryStats) { 533 batteryStats.noteFlashlightOnLocked(APP_UID, 534 60 * MINUTE_IN_MS, 60 * MINUTE_IN_MS); 535 } 536 synchronized (batteryStats) { 537 batteryStats.noteFlashlightOffLocked(APP_UID, 538 70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS); 539 } 540 setTime(75 * MINUTE_IN_MS); 541 synchronized (batteryStats) { 542 batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND); 543 } 544 545 // This section should be ignored because it represents the current stats session 546 synchronized (batteryStats) { 547 batteryStats.noteFlashlightOnLocked(APP_UID, 548 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); 549 } 550 synchronized (batteryStats) { 551 batteryStats.noteFlashlightOffLocked(APP_UID, 552 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS); 553 } 554 setTime(95 * MINUTE_IN_MS); 555 556 // Await completion 557 ConditionVariable done = new ConditionVariable(); 558 mStatsRule.getHandler().post(done::open); 559 done.block(); 560 561 // Include the first and the second snapshot, but not the third or current 562 BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder() 563 .aggregateSnapshots(20 * MINUTE_IN_MS, 60 * MINUTE_IN_MS) 564 .build(); 565 final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query); 566 567 assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS); 568 assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS); 569 assertThat(stats.getAggregateBatteryConsumer( 570 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 571 .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 572 .isWithin(0.0001) 573 .of(180.0); // 360 mA * 0.5 hours 574 assertThat(stats.getAggregateBatteryConsumer( 575 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 576 .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 577 .isEqualTo((10 + 20) * MINUTE_IN_MS); 578 final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream() 579 .filter(uid -> uid.getUid() == APP_UID).findFirst().get(); 580 assertThat(uidBatteryConsumer 581 .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 582 .isWithin(0.1) 583 .of(180.0); 584 585 stats.close(); 586 } 587 588 @Test accumulateBatteryUsageStats()589 public void accumulateBatteryUsageStats() throws Throwable { 590 accumulateBatteryUsageStats(10000000, 1); 591 // Accumulate every 200 bytes of battery history 592 accumulateBatteryUsageStats(200, 2); 593 accumulateBatteryUsageStats(50, 5); 594 // Accumulate on every invocation of accumulateBatteryUsageStats 595 accumulateBatteryUsageStats(0, 7); 596 } 597 accumulateBatteryUsageStats(int accumulatedBatteryUsageStatsSpanSize, int expectedNumberOfUpdates)598 private void accumulateBatteryUsageStats(int accumulatedBatteryUsageStatsSpanSize, 599 int expectedNumberOfUpdates) throws Throwable { 600 BatteryStatsImpl batteryStats = spy(mStatsRule.getBatteryStats()); 601 // Note - these two are in microseconds 602 when(batteryStats.computeBatteryTimeRemaining(anyLong())).thenReturn(111_000L); 603 when(batteryStats.computeChargeTimeRemaining(anyLong())).thenReturn(777_000L); 604 batteryStats.forceRecordAllHistory(); 605 606 setTime(5 * MINUTE_IN_MS); 607 608 // Capture the session start timestamp 609 synchronized (batteryStats) { 610 batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND); 611 } 612 613 PowerStatsStore powerStatsStore = spy(new PowerStatsStore( 614 new File(mStatsRule.getHistoryDir(), getClass().getSimpleName()), 615 mStatsRule.getHandler())); 616 powerStatsStore.reset(); 617 618 int[] count = new int[1]; 619 doAnswer(inv -> { 620 count[0]++; 621 return null; 622 }).when(powerStatsStore).storePowerStatsSpan(any(PowerStatsSpan.class)); 623 624 MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(mContext, 625 powerStatsStore, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), 626 () -> 3500); 627 for (int powerComponentId = 0; powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT; 628 powerComponentId++) { 629 powerAttributor.setPowerComponentSupported(powerComponentId, true); 630 } 631 powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_ANY, true); 632 633 BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, 634 powerAttributor, mStatsRule.getPowerProfile(), 635 mStatsRule.getCpuScalingPolicies(), powerStatsStore, 636 accumulatedBatteryUsageStatsSpanSize, mMockClock, mMonotonicClock); 637 638 provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler()); 639 640 synchronized (batteryStats) { 641 batteryStats.noteFlashlightOnLocked(APP_UID, 642 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); 643 } 644 645 provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler()); 646 647 synchronized (batteryStats) { 648 batteryStats.noteFlashlightOffLocked(APP_UID, 649 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS); 650 } 651 652 provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler()); 653 654 synchronized (batteryStats) { 655 batteryStats.noteFlashlightOnLocked(APP_UID, 656 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); 657 } 658 659 provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler()); 660 661 synchronized (batteryStats) { 662 batteryStats.noteFlashlightOffLocked(APP_UID, 663 50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS); 664 } 665 setTime(55 * MINUTE_IN_MS); 666 667 provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler()); 668 669 // This section has not been saved yet, but should be added to the accumulated totals 670 synchronized (batteryStats) { 671 batteryStats.noteFlashlightOnLocked(APP_UID, 672 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); 673 } 674 675 provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler()); 676 677 synchronized (batteryStats) { 678 batteryStats.noteFlashlightOffLocked(APP_UID, 679 110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS); 680 } 681 setTime(115 * MINUTE_IN_MS); 682 683 // Pick up the remainder of battery history that has not yet been accumulated 684 provider.accumulateBatteryUsageStats(batteryStats); 685 686 mStatsRule.waitForBackgroundThread(); 687 688 BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, 689 new BatteryUsageStatsQuery.Builder().accumulated().build()); 690 691 assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS); 692 assertThat(stats.getStatsEndTimestamp()).isEqualTo(115 * MINUTE_IN_MS); 693 694 assertThat(stats.getBatteryTimeRemainingMs()).isEqualTo(111); 695 assertThat(stats.getChargeTimeRemainingMs()).isEqualTo(777); 696 assertThat(stats.getBatteryCapacity()).isEqualTo(4000); // from PowerProfile 697 698 // Total: 10 + 20 + 30 = 60 699 assertThat(stats.getAggregateBatteryConsumer( 700 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 701 .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 702 .isWithin(0.0001) 703 .of(360.0); // 360 mA * 1.0 hour 704 assertThat(stats.getAggregateBatteryConsumer( 705 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) 706 .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 707 .isEqualTo(60 * MINUTE_IN_MS); 708 709 final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream() 710 .filter(uid -> uid.getUid() == APP_UID).findFirst().get(); 711 assertThat(uidBatteryConsumer 712 .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 713 .isWithin(0.1) 714 .of(360.0); 715 assertThat(uidBatteryConsumer 716 .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) 717 .isEqualTo(60 * MINUTE_IN_MS); 718 719 assertThat(count[0]).isEqualTo(expectedNumberOfUpdates); 720 721 stats.close(); 722 } 723 setTime(long timeMs)724 private void setTime(long timeMs) { 725 mMockClock.currentTime = timeMs; 726 mMockClock.realtime = timeMs; 727 } 728 729 @Test saveBatteryUsageStatsOnReset_incompatibleEnergyConsumers()730 public void saveBatteryUsageStatsOnReset_incompatibleEnergyConsumers() throws Throwable { 731 MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); 732 batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"}); 733 int componentId0 = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; 734 int componentId1 = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1; 735 736 synchronized (batteryStats) { 737 batteryStats.getUidStatsLocked(APP_UID); 738 739 SparseLongArray uidEnergies = new SparseLongArray(); 740 uidEnergies.put(APP_UID, 30_000_000); 741 batteryStats.updateCustomEnergyConsumerStatsLocked(0, 100_000_000, uidEnergies); 742 batteryStats.updateCustomEnergyConsumerStatsLocked(1, 200_000_000, uidEnergies); 743 } 744 745 BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, 746 mock(PowerAttributor.class), mStatsRule.getPowerProfile(), 747 mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock, 748 mMonotonicClock); 749 750 PowerStatsStore powerStatsStore = mock(PowerStatsStore.class); 751 doAnswer(invocation -> { 752 BatteryUsageStats stats = invocation.getArgument(1); 753 AggregateBatteryConsumer device = stats.getAggregateBatteryConsumer( 754 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); 755 assertThat(device.getCustomPowerComponentName(componentId0)).isEqualTo("FOO"); 756 assertThat(device.getCustomPowerComponentName(componentId1)).isEqualTo("BAR"); 757 assertThat(device.getConsumedPower(componentId0)) 758 .isWithin(PRECISION).of(27.77777); 759 assertThat(device.getConsumedPower(componentId1)) 760 .isWithin(PRECISION).of(55.55555); 761 762 UidBatteryConsumer uid = stats.getUidBatteryConsumers().get(0); 763 assertThat(uid.getConsumedPower(componentId0)) 764 .isWithin(PRECISION).of(8.33333); 765 assertThat(uid.getConsumedPower(componentId1)) 766 .isWithin(PRECISION).of(8.33333); 767 stats.close(); 768 return null; 769 }).when(powerStatsStore).storeBatteryUsageStatsAsync(anyLong(), any()); 770 771 mStatsRule.getBatteryStats().saveBatteryUsageStatsOnReset(provider, powerStatsStore, 772 /* accumulateBatteryUsageStats */ false); 773 774 // Make an incompatible change of supported energy components. This will trigger 775 // a BatteryStats reset, which will generate a snapshot of battery stats. 776 mStatsRule.initMeasuredEnergyStatsLocked( 777 new String[]{"COMPONENT1"}); 778 779 mStatsRule.waitForBackgroundThread(); 780 781 verify(powerStatsStore).storeBatteryUsageStatsAsync(anyLong(), any()); 782 } 783 784 @Test testAggregateBatteryStats_incompatibleSnapshot()785 public void testAggregateBatteryStats_incompatibleSnapshot() throws IOException { 786 MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); 787 batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"}); 788 789 PowerStatsStore powerStatsStore = mock(PowerStatsStore.class); 790 791 PowerStatsSpan span0 = new PowerStatsSpan(0); 792 span0.addTimeFrame(0, 1000, 1234); 793 span0.addSection(new BatteryUsageStatsSection( 794 new BatteryUsageStats.Builder(batteryStats.getCustomEnergyConsumerNames()) 795 .setStatsDuration(1234).build())); 796 797 PowerStatsSpan span1 = new PowerStatsSpan(1); 798 span1.addTimeFrame(0, 2000, 4321); 799 span1.addSection(new BatteryUsageStatsSection( 800 new BatteryUsageStats.Builder(new String[]{"different"}) 801 .setStatsDuration(4321).build())); 802 803 when(powerStatsStore.getTableOfContents()).thenReturn( 804 List.of(span0.getMetadata(), span1.getMetadata())); 805 806 when(powerStatsStore.loadPowerStatsSpan(0, BatteryUsageStatsSection.TYPE)) 807 .thenReturn(span0); 808 when(powerStatsStore.loadPowerStatsSpan(1, BatteryUsageStatsSection.TYPE)) 809 .thenReturn(span1); 810 811 span1.close(); 812 813 BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, 814 mock(PowerAttributor.class), mStatsRule.getPowerProfile(), 815 mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock, 816 mMonotonicClock); 817 818 BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder() 819 .aggregateSnapshots(0, 3000) 820 .build(); 821 final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query); 822 assertThat(stats.getCustomPowerComponentNames()) 823 .isEqualTo(batteryStats.getCustomEnergyConsumerNames()); 824 assertThat(stats.getStatsDuration()).isEqualTo(1234); 825 826 stats.close(); 827 } 828 } 829