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