• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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