• 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.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