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