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