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