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