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.power.stats; 17 18 import android.annotation.Nullable; 19 import android.app.usage.NetworkStatsManager; 20 import android.bluetooth.BluetoothActivityEnergyInfo; 21 import android.bluetooth.BluetoothAdapter; 22 import android.content.Context; 23 import android.hardware.power.stats.EnergyConsumer; 24 import android.hardware.power.stats.EnergyConsumerResult; 25 import android.hardware.power.stats.EnergyConsumerType; 26 import android.net.wifi.WifiManager; 27 import android.os.BatteryConsumer; 28 import android.os.BatteryStats; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.OutcomeReceiver; 32 import android.os.Parcelable; 33 import android.os.SynchronousResultReceiver; 34 import android.os.SystemClock; 35 import android.os.connectivity.WifiActivityEnergyInfo; 36 import android.power.PowerStatsInternal; 37 import android.telephony.ModemActivityInfo; 38 import android.telephony.TelephonyManager; 39 import android.util.IntArray; 40 import android.util.Slog; 41 import android.util.SparseArray; 42 import android.util.SparseLongArray; 43 44 import com.android.internal.annotations.GuardedBy; 45 import com.android.internal.annotations.VisibleForTesting; 46 import com.android.internal.power.EnergyConsumerStats; 47 import com.android.server.LocalServices; 48 49 import java.util.concurrent.CompletableFuture; 50 import java.util.concurrent.ExecutionException; 51 import java.util.concurrent.Executor; 52 import java.util.concurrent.Future; 53 import java.util.concurrent.TimeUnit; 54 import java.util.concurrent.TimeoutException; 55 56 /** 57 * A Worker that fetches data from external sources (WiFi controller, bluetooth chipset) on a 58 * dedicated thread and updates BatteryStatsImpl with that information. 59 * 60 * As much work as possible is done without holding the BatteryStatsImpl lock, and only the 61 * readily available data is pushed into BatteryStatsImpl with the lock held. 62 */ 63 public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { 64 private static final String TAG = "BatteryExternalStatsWorker"; 65 private static final boolean DEBUG = false; 66 67 /** 68 * How long to wait on an individual subsystem to return its stats. 69 */ 70 private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000; 71 72 // There is some accuracy error in wifi reports so allow some slop in the results. 73 private static final long MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS = 750; 74 75 // Delay for clearing out battery stats for UIDs corresponding to a removed user 76 public static final int UID_QUICK_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS = 2_000; 77 78 // Delay for the _final_ clean-up of battery stats after a user removal - just in case 79 // some UIDs took longer than UID_QUICK_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS to 80 // stop running. 81 public static final int UID_FINAL_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS = 10_000; 82 83 // Various types of sync, passed to Handler 84 private static final int SYNC_UPDATE = 1; 85 private static final int SYNC_WAKELOCK_CHANGE = 2; 86 private static final int SYNC_BATTERY_LEVEL_CHANGE = 3; 87 private static final int SYNC_PROCESS_STATE_CHANGE = 4; 88 private static final int SYNC_USER_REMOVAL = 5; 89 90 private final Handler mHandler; 91 92 @GuardedBy("mStats") 93 private final BatteryStatsImpl mStats; 94 95 @GuardedBy("this") 96 @ExternalUpdateFlag 97 private int mUpdateFlags = 0; 98 99 @GuardedBy("this") 100 private String mCurrentReason = null; 101 102 @GuardedBy("this") 103 private boolean mOnBattery; 104 105 @GuardedBy("this") 106 private boolean mOnBatteryScreenOff; 107 108 @GuardedBy("this") 109 private int mScreenState; 110 111 @GuardedBy("this") 112 private int[] mPerDisplayScreenStates; 113 114 @GuardedBy("this") 115 private boolean mUseLatestStates = true; 116 117 // If both mStats and mWorkerLock need to be synchronized, mWorkerLock must be acquired first. 118 private final Object mWorkerLock = new Object(); 119 120 @GuardedBy("mWorkerLock") 121 private WifiManager mWifiManager = null; 122 123 @GuardedBy("mWorkerLock") 124 private TelephonyManager mTelephony = null; 125 126 @GuardedBy("mWorkerLock") 127 private PowerStatsInternal mPowerStatsInternal = null; 128 129 // WiFi keeps an accumulated total of stats. Keep the last WiFi stats so we can compute a delta. 130 // (This is unlike Bluetooth, where BatteryStatsImpl is left responsible for taking the delta.) 131 @GuardedBy("mWorkerLock") 132 private WifiActivityEnergyInfo mLastWifiInfo = null; 133 134 /** 135 * Maps an {@link EnergyConsumerType} to it's corresponding {@link EnergyConsumer#id}s, 136 * unless it is of {@link EnergyConsumer#type}=={@link EnergyConsumerType#OTHER} 137 */ 138 @GuardedBy("mWorkerLock") 139 @Nullable 140 private SparseArray<int[]> mEnergyConsumerTypeToIdMap = null; 141 142 /** Snapshot of energy consumers, or null if no EnergyConsumers are supported. */ 143 @GuardedBy("mWorkerLock") 144 @Nullable 145 private EnergyConsumerSnapshot mEnergyConsumerSnapshot = null; 146 147 /** 148 * Timestamp at which all external stats were last collected in 149 * {@link SystemClock#elapsedRealtime()} time base. 150 */ 151 @GuardedBy("this") 152 private long mLastCollectionTimeStamp; 153 154 final Injector mInjector; 155 156 @VisibleForTesting 157 public static class Injector { 158 private final Context mContext; 159 Injector(Context context)160 Injector(Context context) { 161 mContext = context; 162 } 163 getSystemService(Class<T> serviceClass)164 public <T> T getSystemService(Class<T> serviceClass) { 165 return mContext.getSystemService(serviceClass); 166 } 167 getLocalService(Class<T> serviceClass)168 public <T> T getLocalService(Class<T> serviceClass) { 169 return LocalServices.getService(serviceClass); 170 } 171 } 172 BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats, Handler handler)173 public BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats, Handler handler) { 174 this(new Injector(context), stats, handler); 175 } 176 177 @VisibleForTesting BatteryExternalStatsWorker(Injector injector, BatteryStatsImpl stats, Handler handler)178 BatteryExternalStatsWorker(Injector injector, BatteryStatsImpl stats, Handler handler) { 179 mInjector = injector; 180 mStats = stats; 181 mHandler = handler; 182 } 183 systemServicesReady()184 public void systemServicesReady() { 185 final WifiManager wm = mInjector.getSystemService(WifiManager.class); 186 final TelephonyManager tm = mInjector.getSystemService(TelephonyManager.class); 187 final PowerStatsInternal psi = mInjector.getLocalService(PowerStatsInternal.class); 188 final int voltageMv; 189 synchronized (mStats) { 190 voltageMv = mStats.getBatteryVoltageMvLocked(); 191 } 192 193 synchronized (mWorkerLock) { 194 mWifiManager = wm; 195 mTelephony = tm; 196 mPowerStatsInternal = psi; 197 198 boolean[] supportedStdBuckets = null; 199 String[] customBucketNames = null; 200 if (mPowerStatsInternal != null) { 201 final SparseArray<EnergyConsumer> idToConsumer 202 = populateEnergyConsumerSubsystemMapsLocked(); 203 if (idToConsumer != null) { 204 mEnergyConsumerSnapshot = new EnergyConsumerSnapshot(idToConsumer); 205 try { 206 final EnergyConsumerResult[] initialEcrs = getEnergyConsumptionData().get( 207 EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 208 // According to spec, initialEcrs will include 0s for consumers that haven't 209 // used any energy yet, as long as they are supported; however, 210 // attributed uid energies will be absent if their energy is 0. 211 mEnergyConsumerSnapshot.updateAndGetDelta(initialEcrs, voltageMv); 212 } catch (TimeoutException | InterruptedException e) { 213 Slog.w(TAG, "timeout or interrupt reading initial getEnergyConsumedAsync: " 214 + e); 215 // Continue running, later attempts to query may be successful. 216 } catch (ExecutionException e) { 217 Slog.wtf(TAG, "exception reading initial getEnergyConsumedAsync: " 218 + e.getCause()); 219 // Continue running, later attempts to query may be successful. 220 } 221 customBucketNames = mEnergyConsumerSnapshot.getOtherOrdinalNames(); 222 supportedStdBuckets = getSupportedEnergyBuckets(idToConsumer); 223 } 224 } 225 synchronized (mStats) { 226 mStats.initEnergyConsumerStatsLocked(supportedStdBuckets, customBucketNames); 227 mPerDisplayScreenStates = new int[mStats.getDisplayCount()]; 228 } 229 } 230 } 231 232 @Override scheduleSync(String reason, @ExternalUpdateFlag int flags)233 public synchronized void scheduleSync(String reason, @ExternalUpdateFlag int flags) { 234 scheduleSyncLocked(reason, flags); 235 } 236 237 @Override scheduleCpuSyncDueToRemovedUid(int uid)238 public synchronized void scheduleCpuSyncDueToRemovedUid(int uid) { 239 scheduleSyncLocked("remove-uid", UPDATE_CPU); 240 } 241 242 @Override scheduleSyncDueToScreenStateChange(@xternalUpdateFlag int flags, boolean onBattery, boolean onBatteryScreenOff, int screenState, int[] perDisplayScreenStates)243 public void scheduleSyncDueToScreenStateChange(@ExternalUpdateFlag int flags, boolean onBattery, 244 boolean onBatteryScreenOff, int screenState, int[] perDisplayScreenStates) { 245 synchronized (BatteryExternalStatsWorker.this) { 246 if (!mHandler.hasMessages(SYNC_UPDATE) || (mUpdateFlags & UPDATE_CPU) == 0) { 247 mOnBattery = onBattery; 248 mOnBatteryScreenOff = onBatteryScreenOff; 249 mUseLatestStates = false; 250 } 251 // always update screen state 252 mScreenState = screenState; 253 mPerDisplayScreenStates = perDisplayScreenStates; 254 scheduleSyncLocked("screen-state", flags); 255 } 256 } 257 258 @Override scheduleCpuSyncDueToWakelockChange(long delayMillis)259 public void scheduleCpuSyncDueToWakelockChange(long delayMillis) { 260 synchronized (BatteryExternalStatsWorker.this) { 261 scheduleDelayedSyncLocked(SYNC_WAKELOCK_CHANGE, 262 () -> { 263 scheduleSync("wakelock-change", UPDATE_CPU); 264 scheduleRunnable(() -> mStats.postBatteryNeedsCpuUpdateMsg()); 265 }, 266 delayMillis); 267 } 268 } 269 270 @Override cancelCpuSyncDueToWakelockChange()271 public void cancelCpuSyncDueToWakelockChange() { 272 mHandler.removeMessages(SYNC_WAKELOCK_CHANGE); 273 } 274 275 @GuardedBy("this") cancelSyncDueToBatteryLevelChangeLocked()276 private void cancelSyncDueToBatteryLevelChangeLocked() { 277 mHandler.removeMessages(SYNC_BATTERY_LEVEL_CHANGE); 278 } 279 280 @Override scheduleSyncDueToProcessStateChange(int flags, long delayMillis)281 public void scheduleSyncDueToProcessStateChange(int flags, long delayMillis) { 282 synchronized (BatteryExternalStatsWorker.this) { 283 scheduleDelayedSyncLocked(SYNC_PROCESS_STATE_CHANGE, 284 () -> scheduleSync("procstate-change", flags), 285 delayMillis); 286 } 287 } 288 cancelSyncDueToProcessStateChange()289 public void cancelSyncDueToProcessStateChange() { 290 mHandler.removeMessages(SYNC_PROCESS_STATE_CHANGE); 291 } 292 293 @Override scheduleCleanupDueToRemovedUser(int userId)294 public void scheduleCleanupDueToRemovedUser(int userId) { 295 // Initial quick clean-up after a user removal 296 mHandler.postDelayed(() -> { 297 synchronized (mStats) { 298 mStats.clearRemovedUserUidsLocked(userId); 299 } 300 }, SYNC_USER_REMOVAL, UID_QUICK_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS); 301 302 // Final clean-up after a user removal, to take care of UIDs that were running 303 // longer than expected 304 mHandler.postDelayed(() -> { 305 synchronized (mStats) { 306 mStats.clearRemovedUserUidsLocked(userId); 307 } 308 }, SYNC_USER_REMOVAL, UID_FINAL_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS); 309 } 310 311 /** 312 * Schedule a sync {@param syncRunnable} with a delay. If there's already a scheduled sync, a 313 * new sync won't be scheduled unless it is being scheduled to run immediately (delayMillis=0). 314 * 315 * @param lastScheduledSync the task which was earlier scheduled to run 316 * @param syncRunnable the task that needs to be scheduled to run 317 * @param delayMillis time after which {@param syncRunnable} needs to be scheduled 318 * @return scheduled {@link Future} which can be used to check if task is completed or to 319 * cancel it if needed 320 */ 321 @GuardedBy("this") scheduleDelayedSyncLocked(int what, Runnable syncRunnable, long delayMillis)322 private void scheduleDelayedSyncLocked(int what, Runnable syncRunnable, 323 long delayMillis) { 324 if (mHandler.hasMessages(what)) { 325 // If there's already a scheduled task, leave it as is if we're trying to 326 // re-schedule it again with a delay, otherwise cancel and re-schedule it. 327 if (delayMillis == 0) { 328 mHandler.removeMessages(what); 329 } else { 330 return; 331 } 332 } 333 334 mHandler.postDelayed(syncRunnable, what, delayMillis); 335 } 336 337 /** 338 * Schedule and async writing of battery stats to disk 339 */ scheduleWrite()340 public synchronized void scheduleWrite() { 341 scheduleSyncLocked("write", UPDATE_ALL); 342 mHandler.post(mWriteTask); 343 } 344 345 /** 346 * Schedules a task to run on the BatteryExternalStatsWorker thread. If scheduling more work 347 * within the task, never wait on the resulting Future. This will result in a deadlock. 348 */ scheduleRunnable(Runnable runnable)349 public synchronized void scheduleRunnable(Runnable runnable) { 350 mHandler.post(runnable); 351 } 352 shutdown()353 public void shutdown() { 354 mHandler.removeMessages(SYNC_UPDATE); 355 mHandler.removeMessages(SYNC_WAKELOCK_CHANGE); 356 mHandler.removeMessages(SYNC_BATTERY_LEVEL_CHANGE); 357 mHandler.removeMessages(SYNC_PROCESS_STATE_CHANGE); 358 mHandler.removeMessages(SYNC_USER_REMOVAL); 359 } 360 361 @GuardedBy("this") scheduleSyncLocked(String reason, @ExternalUpdateFlag int flags)362 private void scheduleSyncLocked(String reason, @ExternalUpdateFlag int flags) { 363 if (!mHandler.hasMessages(SYNC_UPDATE)) { 364 mUpdateFlags = flags; 365 mCurrentReason = reason; 366 mHandler.postDelayed(mSyncTask, SYNC_UPDATE, 0); 367 } 368 mUpdateFlags |= flags; 369 } 370 getLastCollectionTimeStamp()371 public long getLastCollectionTimeStamp() { 372 synchronized (this) { 373 return mLastCollectionTimeStamp; 374 } 375 } 376 377 private final Runnable mSyncTask = new Runnable() { 378 @Override 379 public void run() { 380 // Capture a snapshot of the state we are meant to process. 381 final int updateFlags; 382 final String reason; 383 final boolean onBattery; 384 final boolean onBatteryScreenOff; 385 final int screenState; 386 final int[] displayScreenStates; 387 final boolean useLatestStates; 388 synchronized (BatteryExternalStatsWorker.this) { 389 updateFlags = mUpdateFlags; 390 reason = mCurrentReason; 391 onBattery = mOnBattery; 392 onBatteryScreenOff = mOnBatteryScreenOff; 393 screenState = mScreenState; 394 displayScreenStates = mPerDisplayScreenStates; 395 useLatestStates = mUseLatestStates; 396 mUpdateFlags = 0; 397 mCurrentReason = null; 398 mUseLatestStates = true; 399 if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { 400 cancelSyncDueToBatteryLevelChangeLocked(); 401 } 402 if ((updateFlags & UPDATE_CPU) != 0) { 403 cancelCpuSyncDueToWakelockChange(); 404 } 405 if ((updateFlags & UPDATE_ON_PROC_STATE_CHANGE) == UPDATE_ON_PROC_STATE_CHANGE) { 406 cancelSyncDueToProcessStateChange(); 407 } 408 } 409 410 try { 411 synchronized (mWorkerLock) { 412 if (DEBUG) { 413 Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason); 414 } 415 try { 416 updateExternalStatsLocked(reason, updateFlags, onBattery, 417 onBatteryScreenOff, screenState, displayScreenStates, 418 useLatestStates); 419 } finally { 420 if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { 421 synchronized (mStats) { 422 // This helps mStats deal with ignoring data from prior to resets. 423 mStats.informThatAllExternalStatsAreFlushed(); 424 } 425 } 426 if (DEBUG) { 427 Slog.d(TAG, "end updateExternalStatsSync"); 428 } 429 } 430 } 431 432 if ((updateFlags & UPDATE_CPU) != 0) { 433 mStats.updateCpuTimesForAllUids(); 434 } 435 436 // Clean up any UIDs if necessary. 437 synchronized (mStats) { 438 mStats.clearPendingRemovedUidsLocked(); 439 } 440 } catch (Exception e) { 441 Slog.wtf(TAG, "Error updating external stats: ", e); 442 } 443 444 if ((updateFlags & RESET) != 0) { 445 synchronized (BatteryExternalStatsWorker.this) { 446 mLastCollectionTimeStamp = 0; 447 } 448 } else if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { 449 synchronized (BatteryExternalStatsWorker.this) { 450 mLastCollectionTimeStamp = SystemClock.elapsedRealtime(); 451 } 452 } 453 } 454 }; 455 456 private final Runnable mWriteTask = new Runnable() { 457 @Override 458 public void run() { 459 synchronized (mStats) { 460 mStats.writeAsyncLocked(); 461 } 462 } 463 }; 464 465 @GuardedBy("mWorkerLock") updateExternalStatsLocked(final String reason, int updateFlags, boolean onBattery, boolean onBatteryScreenOff, int screenState, int[] displayScreenStates, boolean useLatestStates)466 private void updateExternalStatsLocked(final String reason, int updateFlags, boolean onBattery, 467 boolean onBatteryScreenOff, int screenState, int[] displayScreenStates, 468 boolean useLatestStates) { 469 // We will request data from external processes asynchronously, and wait on a timeout. 470 SynchronousResultReceiver wifiReceiver = null; 471 SynchronousResultReceiver bluetoothReceiver = null; 472 CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null); 473 boolean railUpdated = false; 474 475 CompletableFuture<EnergyConsumerResult[]> futureECRs = 476 getEnergyConsumersLocked(updateFlags); 477 478 if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { 479 // We were asked to fetch WiFi data. 480 // Only fetch WiFi power data if it is supported. 481 if (mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported()) { 482 SynchronousResultReceiver tempWifiReceiver = new SynchronousResultReceiver("wifi"); 483 mWifiManager.getWifiActivityEnergyInfoAsync( 484 new Executor() { 485 @Override 486 public void execute(Runnable runnable) { 487 // run the listener on the binder thread, if it was run on the main 488 // thread it would deadlock since we would be waiting on ourselves 489 runnable.run(); 490 } 491 }, 492 info -> { 493 Bundle bundle = new Bundle(); 494 bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); 495 tempWifiReceiver.send(0, bundle); 496 } 497 ); 498 wifiReceiver = tempWifiReceiver; 499 } 500 synchronized (mStats) { 501 mStats.updateRailStatsLocked(); 502 } 503 railUpdated = true; 504 } 505 506 if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) { 507 @SuppressWarnings("GuardedBy") 508 PowerStatsCollector collector = mStats.getPowerStatsCollector( 509 BatteryConsumer.POWER_COMPONENT_BLUETOOTH); 510 if (collector.isEnabled()) { 511 collector.schedule(); 512 } else { 513 // We were asked to fetch Bluetooth data. 514 final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 515 if (adapter != null) { 516 SynchronousResultReceiver resultReceiver = 517 new SynchronousResultReceiver("bluetooth"); 518 adapter.requestControllerActivityEnergyInfo( 519 Runnable::run, 520 new BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback() { 521 @Override 522 public void onBluetoothActivityEnergyInfoAvailable( 523 BluetoothActivityEnergyInfo info) { 524 Bundle bundle = new Bundle(); 525 bundle.putParcelable( 526 BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); 527 resultReceiver.send(0, bundle); 528 } 529 530 @Override 531 public void onBluetoothActivityEnergyInfoError(int errorCode) { 532 Slog.w(TAG, "error reading Bluetooth stats: " + errorCode); 533 Bundle bundle = new Bundle(); 534 bundle.putParcelable( 535 BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, null); 536 resultReceiver.send(0, bundle); 537 } 538 } 539 ); 540 bluetoothReceiver = resultReceiver; 541 } 542 } 543 } 544 545 if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) { 546 @SuppressWarnings("GuardedBy") 547 PowerStatsCollector collector = mStats.getPowerStatsCollector( 548 BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO); 549 if (collector.isEnabled()) { 550 collector.schedule(); 551 } else { 552 // We were asked to fetch Telephony data. 553 if (mTelephony != null) { 554 CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>(); 555 mTelephony.requestModemActivityInfo(Runnable::run, 556 new OutcomeReceiver<ModemActivityInfo, 557 TelephonyManager.ModemActivityInfoException>() { 558 @Override 559 public void onResult(ModemActivityInfo result) { 560 temp.complete(result); 561 } 562 563 @Override 564 public void onError(TelephonyManager.ModemActivityInfoException e) { 565 Slog.w(TAG, "error reading modem stats:" + e); 566 temp.complete(null); 567 } 568 }); 569 modemFuture = temp; 570 } 571 } 572 if (!railUpdated) { 573 synchronized (mStats) { 574 mStats.updateRailStatsLocked(); 575 } 576 } 577 } 578 579 if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RPM) != 0) { 580 // Collect the latest low power stats without holding the mStats lock. 581 mStats.fillLowPowerStats(); 582 } 583 584 final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); 585 final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver); 586 ModemActivityInfo modemInfo = null; 587 try { 588 modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, 589 TimeUnit.MILLISECONDS); 590 } catch (TimeoutException | InterruptedException e) { 591 Slog.w(TAG, "timeout or interrupt reading modem stats: " + e); 592 } catch (ExecutionException e) { 593 Slog.w(TAG, "exception reading modem stats: " + e.getCause()); 594 } 595 596 final EnergyConsumerSnapshot.EnergyConsumerDeltaData energyConsumerDeltas; 597 if (mEnergyConsumerSnapshot == null || futureECRs == null) { 598 energyConsumerDeltas = null; 599 } else { 600 final int voltageMv; 601 synchronized (mStats) { 602 voltageMv = mStats.getBatteryVoltageMvLocked(); 603 } 604 605 EnergyConsumerResult[] ecrs; 606 try { 607 ecrs = futureECRs.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 608 } catch (TimeoutException | InterruptedException e) { 609 // TODO (b/180519623): Invalidate the EnergyConsumer derived data until next reset. 610 Slog.w(TAG, "timeout or interrupt reading getEnergyConsumedAsync: " + e); 611 ecrs = null; 612 } catch (ExecutionException e) { 613 Slog.wtf(TAG, "exception reading getEnergyConsumedAsync: " + e.getCause()); 614 ecrs = null; 615 } 616 617 energyConsumerDeltas = mEnergyConsumerSnapshot.updateAndGetDelta(ecrs, voltageMv); 618 } 619 620 final long elapsedRealtime = SystemClock.elapsedRealtime(); 621 final long uptime = SystemClock.uptimeMillis(); 622 final long elapsedRealtimeUs = elapsedRealtime * 1000; 623 final long uptimeUs = uptime * 1000; 624 625 // Now that we have finally received all the data, we can tell mStats about it. 626 synchronized (mStats) { 627 mStats.recordHistoryEventLocked( 628 elapsedRealtime, 629 uptime, 630 BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, 631 reason, 0); 632 633 if ((updateFlags & UPDATE_CPU) != 0) { 634 if (useLatestStates) { 635 onBattery = mStats.isOnBatteryLocked(); 636 onBatteryScreenOff = mStats.isOnBatteryScreenOffLocked(); 637 } 638 639 final long[] cpuClusterChargeUC; 640 if (energyConsumerDeltas == null) { 641 cpuClusterChargeUC = null; 642 } else { 643 cpuClusterChargeUC = energyConsumerDeltas.cpuClusterChargeUC; 644 } 645 mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC); 646 } 647 648 if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { 649 mStats.updateKernelWakelocksLocked(elapsedRealtimeUs); 650 mStats.updateKernelMemoryBandwidthLocked(elapsedRealtimeUs); 651 } 652 653 if ((updateFlags & UPDATE_RPM) != 0) { 654 mStats.updateRpmStatsLocked(elapsedRealtimeUs); 655 } 656 657 // Inform mStats about each applicable energy consumers (unless addressed elsewhere). 658 if (energyConsumerDeltas != null) { 659 final long[] displayChargeUC = energyConsumerDeltas.displayChargeUC; 660 if (displayChargeUC != null && displayChargeUC.length > 0) { 661 // If updating, pass in what BatteryExternalStatsWorker thinks 662 // displayScreenStates is. 663 mStats.updateDisplayEnergyConsumerStatsLocked(displayChargeUC, 664 displayScreenStates, elapsedRealtime); 665 } 666 667 final long gnssChargeUC = energyConsumerDeltas.gnssChargeUC; 668 if (gnssChargeUC != EnergyConsumerSnapshot.UNAVAILABLE) { 669 mStats.updateGnssEnergyConsumerStatsLocked(gnssChargeUC, elapsedRealtime); 670 } 671 672 final long cameraChargeUC = energyConsumerDeltas.cameraChargeUC; 673 if (cameraChargeUC != EnergyConsumerSnapshot.UNAVAILABLE) { 674 mStats.updateCameraEnergyConsumerStatsLocked(cameraChargeUC, elapsedRealtime); 675 } 676 } 677 // Inform mStats about each applicable custom energy bucket. 678 if (energyConsumerDeltas != null 679 && energyConsumerDeltas.otherTotalChargeUC != null) { 680 // Iterate over the custom (EnergyConsumerType.OTHER) ordinals. 681 for (int ord = 0; ord < energyConsumerDeltas.otherTotalChargeUC.length; ord++) { 682 long totalEnergy = energyConsumerDeltas.otherTotalChargeUC[ord]; 683 SparseLongArray uidEnergies = energyConsumerDeltas.otherUidChargesUC[ord]; 684 mStats.updateCustomEnergyConsumerStatsLocked(ord, totalEnergy, uidEnergies); 685 } 686 } 687 688 if (bluetoothInfo != null) { 689 if (bluetoothInfo.isValid()) { 690 final long btChargeUC = energyConsumerDeltas != null 691 ? energyConsumerDeltas.bluetoothChargeUC 692 : EnergyConsumerSnapshot.UNAVAILABLE; 693 mStats.updateBluetoothStateLocked(bluetoothInfo, 694 btChargeUC, elapsedRealtime, uptime); 695 } else { 696 Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo); 697 } 698 } 699 } 700 701 // WiFi and Modem state are updated without the mStats lock held, because they 702 // do some network stats retrieval before internally grabbing the mStats lock. 703 if (wifiInfo != null) { 704 if (wifiInfo.isValid()) { 705 final long wifiChargeUC = 706 energyConsumerDeltas != null ? energyConsumerDeltas.wifiChargeUC 707 : EnergyConsumerSnapshot.UNAVAILABLE; 708 final NetworkStatsManager networkStatsManager = mInjector.getSystemService( 709 NetworkStatsManager.class); 710 mStats.updateWifiState(extractDeltaLocked(wifiInfo), 711 wifiChargeUC, elapsedRealtime, uptime, networkStatsManager); 712 } else { 713 Slog.w(TAG, "wifi info is invalid: " + wifiInfo); 714 } 715 } 716 717 if (modemInfo != null) { 718 final long mobileRadioChargeUC = energyConsumerDeltas != null 719 ? energyConsumerDeltas.mobileRadioChargeUC : EnergyConsumerSnapshot.UNAVAILABLE; 720 final NetworkStatsManager networkStatsManager = mInjector.getSystemService( 721 NetworkStatsManager.class); 722 mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime, 723 uptime, networkStatsManager); 724 } 725 } 726 727 /** 728 * Helper method to extract the Parcelable controller info from a 729 * SynchronousResultReceiver. 730 */ awaitControllerInfo( @ullable SynchronousResultReceiver receiver)731 private static <T extends Parcelable> T awaitControllerInfo( 732 @Nullable SynchronousResultReceiver receiver) { 733 if (receiver == null) { 734 return null; 735 } 736 737 try { 738 final SynchronousResultReceiver.Result result = 739 receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS); 740 if (result.bundle != null) { 741 // This is the final destination for the Bundle. 742 result.bundle.setDefusable(true); 743 744 final T data = result.bundle.getParcelable( 745 BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY); 746 if (data != null) { 747 return data; 748 } 749 } 750 } catch (TimeoutException e) { 751 Slog.w(TAG, "timeout reading " + receiver.getName() + " stats"); 752 } 753 return null; 754 } 755 756 /** 757 * Return a delta WifiActivityEnergyInfo from the last WifiActivityEnergyInfo passed to the 758 * method. 759 */ 760 @VisibleForTesting 761 @GuardedBy("mWorkerLock") extractDeltaLocked(WifiActivityEnergyInfo latest)762 public WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) { 763 if (mLastWifiInfo == null) { 764 // This is the first time WifiActivityEnergyInfo has been collected since system boot. 765 // Use this first WifiActivityEnergyInfo as the starting point for all accumulations. 766 mLastWifiInfo = latest; 767 } 768 final long timePeriodMs = latest.getTimeSinceBootMillis() 769 - mLastWifiInfo.getTimeSinceBootMillis(); 770 final long lastScanMs = mLastWifiInfo.getControllerScanDurationMillis(); 771 final long lastIdleMs = mLastWifiInfo.getControllerIdleDurationMillis(); 772 final long lastTxMs = mLastWifiInfo.getControllerTxDurationMillis(); 773 final long lastRxMs = mLastWifiInfo.getControllerRxDurationMillis(); 774 final long lastEnergy = mLastWifiInfo.getControllerEnergyUsedMicroJoules(); 775 776 final long deltaTimeSinceBootMillis = latest.getTimeSinceBootMillis(); 777 final int deltaStackState = latest.getStackState(); 778 final long deltaControllerTxDurationMillis; 779 final long deltaControllerRxDurationMillis; 780 final long deltaControllerScanDurationMillis; 781 final long deltaControllerIdleDurationMillis; 782 final long deltaControllerEnergyUsedMicroJoules; 783 784 final long txTimeMs = latest.getControllerTxDurationMillis() - lastTxMs; 785 final long rxTimeMs = latest.getControllerRxDurationMillis() - lastRxMs; 786 final long idleTimeMs = latest.getControllerIdleDurationMillis() - lastIdleMs; 787 final long scanTimeMs = latest.getControllerScanDurationMillis() - lastScanMs; 788 789 final boolean wasReset; 790 if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0 || idleTimeMs < 0) { 791 // The stats were reset by the WiFi system (which is why our delta is negative). 792 // Returns the unaltered stats. The total on time should not exceed the time 793 // duration between reports. 794 final long totalOnTimeMs = latest.getControllerTxDurationMillis() 795 + latest.getControllerRxDurationMillis() 796 + latest.getControllerIdleDurationMillis(); 797 if (totalOnTimeMs <= timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) { 798 deltaControllerEnergyUsedMicroJoules = latest.getControllerEnergyUsedMicroJoules(); 799 deltaControllerRxDurationMillis = latest.getControllerRxDurationMillis(); 800 deltaControllerTxDurationMillis = latest.getControllerTxDurationMillis(); 801 deltaControllerIdleDurationMillis = latest.getControllerIdleDurationMillis(); 802 deltaControllerScanDurationMillis = latest.getControllerScanDurationMillis(); 803 } else { 804 deltaControllerEnergyUsedMicroJoules = 0; 805 deltaControllerRxDurationMillis = 0; 806 deltaControllerTxDurationMillis = 0; 807 deltaControllerIdleDurationMillis = 0; 808 deltaControllerScanDurationMillis = 0; 809 } 810 wasReset = true; 811 } else { 812 // These times seem to be the most reliable. 813 deltaControllerTxDurationMillis = txTimeMs; 814 deltaControllerRxDurationMillis = rxTimeMs; 815 deltaControllerScanDurationMillis = scanTimeMs; 816 deltaControllerIdleDurationMillis = idleTimeMs; 817 deltaControllerEnergyUsedMicroJoules = 818 Math.max(0, latest.getControllerEnergyUsedMicroJoules() - lastEnergy); 819 wasReset = false; 820 } 821 822 mLastWifiInfo = latest; 823 WifiActivityEnergyInfo delta = new WifiActivityEnergyInfo( 824 deltaTimeSinceBootMillis, 825 deltaStackState, 826 deltaControllerTxDurationMillis, 827 deltaControllerRxDurationMillis, 828 deltaControllerScanDurationMillis, 829 deltaControllerIdleDurationMillis, 830 deltaControllerEnergyUsedMicroJoules); 831 if (wasReset) { 832 Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta); 833 } 834 return delta; 835 } 836 837 /** 838 * Map the {@link EnergyConsumerType}s in the given energyArray to 839 * their corresponding {@link EnergyConsumerStats.StandardPowerBucket}s. 840 * Does not include custom energy buckets (which are always, by definition, supported). 841 * 842 * @return array with true for index i if standard energy bucket i is supported. 843 */ getSupportedEnergyBuckets( SparseArray<EnergyConsumer> idToConsumer)844 private static @Nullable boolean[] getSupportedEnergyBuckets( 845 SparseArray<EnergyConsumer> idToConsumer) { 846 if (idToConsumer == null) { 847 return null; 848 } 849 final boolean[] buckets = new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS]; 850 final int size = idToConsumer.size(); 851 for (int idx = 0; idx < size; idx++) { 852 final EnergyConsumer consumer = idToConsumer.valueAt(idx); 853 switch (consumer.type) { 854 case EnergyConsumerType.BLUETOOTH: 855 buckets[EnergyConsumerStats.POWER_BUCKET_BLUETOOTH] = true; 856 break; 857 case EnergyConsumerType.CPU_CLUSTER: 858 buckets[EnergyConsumerStats.POWER_BUCKET_CPU] = true; 859 break; 860 case EnergyConsumerType.GNSS: 861 buckets[EnergyConsumerStats.POWER_BUCKET_GNSS] = true; 862 break; 863 case EnergyConsumerType.MOBILE_RADIO: 864 buckets[EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO] = true; 865 buckets[EnergyConsumerStats.POWER_BUCKET_PHONE] = true; 866 break; 867 case EnergyConsumerType.DISPLAY: 868 buckets[EnergyConsumerStats.POWER_BUCKET_SCREEN_ON] = true; 869 buckets[EnergyConsumerStats.POWER_BUCKET_SCREEN_DOZE] = true; 870 buckets[EnergyConsumerStats.POWER_BUCKET_SCREEN_OTHER] = true; 871 break; 872 case EnergyConsumerType.WIFI: 873 buckets[EnergyConsumerStats.POWER_BUCKET_WIFI] = true; 874 break; 875 case EnergyConsumerType.CAMERA: 876 buckets[EnergyConsumerStats.POWER_BUCKET_CAMERA] = true; 877 break; 878 } 879 } 880 return buckets; 881 } 882 883 /** Get all {@link EnergyConsumerResult}s with the latest energy usage since boot. */ 884 @GuardedBy("mWorkerLock") 885 @Nullable getEnergyConsumptionData()886 private CompletableFuture<EnergyConsumerResult[]> getEnergyConsumptionData() { 887 return getEnergyConsumptionData(new int[0]); 888 } 889 890 /** 891 * Get {@link EnergyConsumerResult}s of the specified {@link EnergyConsumer} ids with the latest 892 * energy usage since boot. 893 */ 894 @GuardedBy("mWorkerLock") 895 @Nullable getEnergyConsumptionData(int[] consumerIds)896 private CompletableFuture<EnergyConsumerResult[]> getEnergyConsumptionData(int[] consumerIds) { 897 return mPowerStatsInternal.getEnergyConsumedAsync(consumerIds); 898 } 899 900 /** Fetch EnergyConsumerResult[] for supported subsystems based on the given updateFlags. */ 901 @VisibleForTesting 902 @GuardedBy("mWorkerLock") 903 @Nullable getEnergyConsumersLocked( @xternalUpdateFlag int flags)904 public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumersLocked( 905 @ExternalUpdateFlag int flags) { 906 if (mEnergyConsumerSnapshot == null || mPowerStatsInternal == null) return null; 907 908 if (flags == UPDATE_ALL) { 909 // Gotta catch 'em all... including custom (non-specific) subsystems 910 return getEnergyConsumptionData(); 911 } 912 913 final IntArray energyConsumerIds = new IntArray(); 914 if ((flags & UPDATE_BT) != 0) { 915 addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.BLUETOOTH); 916 } 917 if ((flags & UPDATE_CPU) != 0) { 918 addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CPU_CLUSTER); 919 } 920 if ((flags & UPDATE_DISPLAY) != 0) { 921 addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY); 922 } 923 if ((flags & UPDATE_RADIO) != 0) { 924 addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.MOBILE_RADIO); 925 } 926 if ((flags & UPDATE_WIFI) != 0) { 927 addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.WIFI); 928 } 929 if ((flags & UPDATE_CAMERA) != 0) { 930 addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CAMERA); 931 } 932 933 if (energyConsumerIds.size() == 0) { 934 return null; 935 } 936 return getEnergyConsumptionData(energyConsumerIds.toArray()); 937 } 938 939 @GuardedBy("mWorkerLock") addEnergyConsumerIdLocked( IntArray energyConsumerIds, @EnergyConsumerType int type)940 private void addEnergyConsumerIdLocked( 941 IntArray energyConsumerIds, @EnergyConsumerType int type) { 942 final int[] consumerIds = mEnergyConsumerTypeToIdMap.get(type); 943 if (consumerIds == null) return; 944 energyConsumerIds.addAll(consumerIds); 945 } 946 947 /** Populates the cached type->ids map, and returns the (inverse) id->EnergyConsumer map. */ 948 @GuardedBy("mWorkerLock") populateEnergyConsumerSubsystemMapsLocked()949 private @Nullable SparseArray<EnergyConsumer> populateEnergyConsumerSubsystemMapsLocked() { 950 if (mPowerStatsInternal == null) { 951 return null; 952 } 953 final EnergyConsumer[] energyConsumers = mPowerStatsInternal.getEnergyConsumerInfo(); 954 if (energyConsumers == null || energyConsumers.length == 0) { 955 return null; 956 } 957 958 // Maps id -> EnergyConsumer (1:1 map) 959 final SparseArray<EnergyConsumer> idToConsumer = new SparseArray<>(energyConsumers.length); 960 // Maps type -> {ids} (1:n map, since multiple ids might have the same type) 961 final SparseArray<IntArray> tempTypeToId = new SparseArray<>(); 962 963 // Add all expected EnergyConsumers to the maps 964 for (final EnergyConsumer consumer : energyConsumers) { 965 // Check for inappropriate ordinals 966 if (consumer.ordinal != 0) { 967 switch (consumer.type) { 968 case EnergyConsumerType.OTHER: 969 case EnergyConsumerType.CPU_CLUSTER: 970 case EnergyConsumerType.DISPLAY: 971 break; 972 default: 973 Slog.w(TAG, "EnergyConsumer '" + consumer.name + "' has unexpected ordinal " 974 + consumer.ordinal + " for type " + consumer.type); 975 continue; // Ignore this consumer 976 } 977 } 978 idToConsumer.put(consumer.id, consumer); 979 980 IntArray ids = tempTypeToId.get(consumer.type); 981 if (ids == null) { 982 ids = new IntArray(); 983 tempTypeToId.put(consumer.type, ids); 984 } 985 ids.add(consumer.id); 986 } 987 988 mEnergyConsumerTypeToIdMap = new SparseArray<>(tempTypeToId.size()); 989 // Populate mEnergyConsumerTypeToIdMap with EnergyConsumer type to ids mappings 990 final int size = tempTypeToId.size(); 991 for (int i = 0; i < size; i++) { 992 final int consumerType = tempTypeToId.keyAt(i); 993 final int[] consumerIds = tempTypeToId.valueAt(i).toArray(); 994 mEnergyConsumerTypeToIdMap.put(consumerType, consumerIds); 995 } 996 return idToConsumer; 997 } 998 } 999