1 /*
2  * Copyright (C) 2009 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 
17 package com.android.internal.os;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.hardware.SensorManager;
26 import android.net.ConnectivityManager;
27 import android.os.BatteryStats;
28 import android.os.BatteryStats.Uid;
29 import android.os.Bundle;
30 import android.os.MemoryFile;
31 import android.os.Parcel;
32 import android.os.ParcelFileDescriptor;
33 import android.os.Process;
34 import android.os.RemoteException;
35 import android.os.SELinux;
36 import android.os.ServiceManager;
37 import android.os.SystemClock;
38 import android.os.UserHandle;
39 import android.text.format.DateUtils;
40 import android.util.ArrayMap;
41 import android.util.Log;
42 import android.util.SparseArray;
43 import android.util.SparseLongArray;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.internal.app.IBatteryStats;
47 import com.android.internal.os.BatterySipper.DrainType;
48 import com.android.internal.util.ArrayUtils;
49 
50 import java.io.File;
51 import java.io.FileInputStream;
52 import java.io.FileOutputStream;
53 import java.io.IOException;
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.Comparator;
57 import java.util.List;
58 import java.util.Locale;
59 
60 /**
61  * A helper class for retrieving the power usage information for all applications and services.
62  *
63  * The caller must initialize this class as soon as activity object is ready to use (for example, in
64  * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
65  */
66 public class BatteryStatsHelper {
67     static final boolean DEBUG = false;
68 
69     private static final String TAG = BatteryStatsHelper.class.getSimpleName();
70 
71     private static BatteryStats sStatsXfer;
72     private static Intent sBatteryBroadcastXfer;
73     private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>();
74 
75     final private Context mContext;
76     final private boolean mCollectBatteryBroadcast;
77     final private boolean mWifiOnly;
78 
79     @UnsupportedAppUsage
80     private IBatteryStats mBatteryInfo;
81     private BatteryStats mStats;
82     private Intent mBatteryBroadcast;
83     @UnsupportedAppUsage
84     private PowerProfile mPowerProfile;
85 
86     private String[] mSystemPackageArray;
87     private String[] mServicepackageArray;
88     private PackageManager mPackageManager;
89 
90     /**
91      * List of apps using power.
92      */
93     @UnsupportedAppUsage
94     private final List<BatterySipper> mUsageList = new ArrayList<>();
95 
96     /**
97      * List of apps using wifi power.
98      */
99     private final List<BatterySipper> mWifiSippers = new ArrayList<>();
100 
101     /**
102      * List of apps using bluetooth power.
103      */
104     private final List<BatterySipper> mBluetoothSippers = new ArrayList<>();
105 
106     private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>();
107 
108     private final List<BatterySipper> mMobilemsppList = new ArrayList<>();
109 
110     private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
111 
112     long mRawRealtimeUs;
113     long mRawUptimeUs;
114     long mBatteryRealtimeUs;
115     long mBatteryUptimeUs;
116     long mTypeBatteryRealtimeUs;
117     long mTypeBatteryUptimeUs;
118     long mBatteryTimeRemainingUs;
119     long mChargeTimeRemainingUs;
120 
121     private long mStatsPeriod = 0;
122 
123     // The largest entry by power.
124     private double mMaxPower = 1;
125 
126     // The largest real entry by power (not undercounted or overcounted).
127     private double mMaxRealPower = 1;
128 
129     // Total computed power.
130     private double mComputedPower;
131     private double mTotalPower;
132     private double mMinDrainedPower;
133     private double mMaxDrainedPower;
134 
135     PowerCalculator mCpuPowerCalculator;
136     PowerCalculator mWakelockPowerCalculator;
137     MobileRadioPowerCalculator mMobileRadioPowerCalculator;
138     PowerCalculator mWifiPowerCalculator;
139     PowerCalculator mBluetoothPowerCalculator;
140     PowerCalculator mSensorPowerCalculator;
141     PowerCalculator mCameraPowerCalculator;
142     PowerCalculator mFlashlightPowerCalculator;
143     PowerCalculator mMemoryPowerCalculator;
144     PowerCalculator mMediaPowerCalculator;
145 
146     boolean mHasWifiPowerReporting = false;
147     boolean mHasBluetoothPowerReporting = false;
148 
checkWifiOnly(Context context)149     public static boolean checkWifiOnly(Context context) {
150         ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
151                 Context.CONNECTIVITY_SERVICE);
152         if (cm == null) {
153             return false;
154         }
155         return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
156     }
157 
checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile)158     public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
159         return stats.hasWifiActivityReporting() &&
160                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
161                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
162                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
163     }
164 
checkHasBluetoothPowerReporting(BatteryStats stats, PowerProfile profile)165     public static boolean checkHasBluetoothPowerReporting(BatteryStats stats,
166             PowerProfile profile) {
167         return stats.hasBluetoothActivityReporting() &&
168                 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 &&
169                 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 &&
170                 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0;
171     }
172 
173     @UnsupportedAppUsage
BatteryStatsHelper(Context context)174     public BatteryStatsHelper(Context context) {
175         this(context, true);
176     }
177 
178     @UnsupportedAppUsage
BatteryStatsHelper(Context context, boolean collectBatteryBroadcast)179     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
180         this(context, collectBatteryBroadcast, checkWifiOnly(context));
181     }
182 
183     @UnsupportedAppUsage
BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly)184     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
185         mContext = context;
186         mCollectBatteryBroadcast = collectBatteryBroadcast;
187         mWifiOnly = wifiOnly;
188         mPackageManager = context.getPackageManager();
189 
190         final Resources resources = context.getResources();
191         mSystemPackageArray = resources.getStringArray(
192                 com.android.internal.R.array.config_batteryPackageTypeSystem);
193         mServicepackageArray = resources.getStringArray(
194                 com.android.internal.R.array.config_batteryPackageTypeService);
195     }
196 
storeStatsHistoryInFile(String fname)197     public void storeStatsHistoryInFile(String fname) {
198         synchronized (sFileXfer) {
199             File path = makeFilePath(mContext, fname);
200             sFileXfer.put(path, this.getStats());
201             FileOutputStream fout = null;
202             try {
203                 fout = new FileOutputStream(path);
204                 Parcel hist = Parcel.obtain();
205                 getStats().writeToParcelWithoutUids(hist, 0);
206                 byte[] histData = hist.marshall();
207                 fout.write(histData);
208             } catch (IOException e) {
209                 Log.w(TAG, "Unable to write history to file", e);
210             } finally {
211                 if (fout != null) {
212                     try {
213                         fout.close();
214                     } catch (IOException e) {
215                     }
216                 }
217             }
218         }
219     }
220 
statsFromFile(Context context, String fname)221     public static BatteryStats statsFromFile(Context context, String fname) {
222         synchronized (sFileXfer) {
223             File path = makeFilePath(context, fname);
224             BatteryStats stats = sFileXfer.get(path);
225             if (stats != null) {
226                 return stats;
227             }
228             FileInputStream fin = null;
229             try {
230                 fin = new FileInputStream(path);
231                 byte[] data = readFully(fin);
232                 Parcel parcel = Parcel.obtain();
233                 parcel.unmarshall(data, 0, data.length);
234                 parcel.setDataPosition(0);
235                 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
236             } catch (IOException e) {
237                 Log.w(TAG, "Unable to read history to file", e);
238             } finally {
239                 if (fin != null) {
240                     try {
241                         fin.close();
242                     } catch (IOException e) {
243                     }
244                 }
245             }
246         }
247         return getStats(IBatteryStats.Stub.asInterface(
248                 ServiceManager.getService(BatteryStats.SERVICE_NAME)));
249     }
250 
251     @UnsupportedAppUsage
dropFile(Context context, String fname)252     public static void dropFile(Context context, String fname) {
253         makeFilePath(context, fname).delete();
254     }
255 
makeFilePath(Context context, String fname)256     private static File makeFilePath(Context context, String fname) {
257         return new File(context.getFilesDir(), fname);
258     }
259 
260     /** Clears the current stats and forces recreating for future use. */
261     @UnsupportedAppUsage
clearStats()262     public void clearStats() {
263         mStats = null;
264     }
265 
266     @UnsupportedAppUsage
getStats()267     public BatteryStats getStats() {
268         if (mStats == null) {
269             load();
270         }
271         return mStats;
272     }
273 
274     @UnsupportedAppUsage
getBatteryBroadcast()275     public Intent getBatteryBroadcast() {
276         if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
277             load();
278         }
279         return mBatteryBroadcast;
280     }
281 
getPowerProfile()282     public PowerProfile getPowerProfile() {
283         return mPowerProfile;
284     }
285 
create(BatteryStats stats)286     public void create(BatteryStats stats) {
287         mPowerProfile = new PowerProfile(mContext);
288         mStats = stats;
289     }
290 
291     @UnsupportedAppUsage
create(Bundle icicle)292     public void create(Bundle icicle) {
293         if (icicle != null) {
294             mStats = sStatsXfer;
295             mBatteryBroadcast = sBatteryBroadcastXfer;
296         }
297         mBatteryInfo = IBatteryStats.Stub.asInterface(
298                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
299         mPowerProfile = new PowerProfile(mContext);
300     }
301 
302     @UnsupportedAppUsage
storeState()303     public void storeState() {
304         sStatsXfer = mStats;
305         sBatteryBroadcastXfer = mBatteryBroadcast;
306     }
307 
makemAh(double power)308     public static String makemAh(double power) {
309         if (power == 0) return "0";
310 
311         final String format;
312         if (power < .00001) {
313             format = "%.8f";
314         } else if (power < .0001) {
315             format = "%.7f";
316         } else if (power < .001) {
317             format = "%.6f";
318         } else if (power < .01) {
319             format = "%.5f";
320         } else if (power < .1) {
321             format = "%.4f";
322         } else if (power < 1) {
323             format = "%.3f";
324         } else if (power < 10) {
325             format = "%.2f";
326         } else if (power < 100) {
327             format = "%.1f";
328         } else {
329             format = "%.0f";
330         }
331 
332         // Use English locale because this is never used in UI (only in checkin and dump).
333         return String.format(Locale.ENGLISH, format, power);
334     }
335 
336     /**
337      * Refreshes the power usage list.
338      */
339     @UnsupportedAppUsage
refreshStats(int statsType, int asUser)340     public void refreshStats(int statsType, int asUser) {
341         SparseArray<UserHandle> users = new SparseArray<>(1);
342         users.put(asUser, new UserHandle(asUser));
343         refreshStats(statsType, users);
344     }
345 
346     /**
347      * Refreshes the power usage list.
348      */
349     @UnsupportedAppUsage
refreshStats(int statsType, List<UserHandle> asUsers)350     public void refreshStats(int statsType, List<UserHandle> asUsers) {
351         final int n = asUsers.size();
352         SparseArray<UserHandle> users = new SparseArray<>(n);
353         for (int i = 0; i < n; ++i) {
354             UserHandle userHandle = asUsers.get(i);
355             users.put(userHandle.getIdentifier(), userHandle);
356         }
357         refreshStats(statsType, users);
358     }
359 
360     /**
361      * Refreshes the power usage list.
362      */
363     @UnsupportedAppUsage
refreshStats(int statsType, SparseArray<UserHandle> asUsers)364     public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
365         refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
366                 SystemClock.uptimeMillis() * 1000);
367     }
368 
refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs)369     public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
370             long rawUptimeUs) {
371         if (statsType != BatteryStats.STATS_SINCE_CHARGED) {
372             Log.w(TAG, "refreshStats called for statsType " + statsType + " but only "
373                     + "STATS_SINCE_CHARGED is supported. Using STATS_SINCE_CHARGED instead.");
374         }
375 
376         // Initialize mStats if necessary.
377         getStats();
378 
379         mMaxPower = 0;
380         mMaxRealPower = 0;
381         mComputedPower = 0;
382         mTotalPower = 0;
383 
384         mUsageList.clear();
385         mWifiSippers.clear();
386         mBluetoothSippers.clear();
387         mUserSippers.clear();
388         mMobilemsppList.clear();
389 
390         if (mStats == null) {
391             return;
392         }
393 
394         if (mCpuPowerCalculator == null) {
395             mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
396         }
397         mCpuPowerCalculator.reset();
398 
399         if (mMemoryPowerCalculator == null) {
400             mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile);
401         }
402         mMemoryPowerCalculator.reset();
403 
404         if (mWakelockPowerCalculator == null) {
405             mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
406         }
407         mWakelockPowerCalculator.reset();
408 
409         if (mMobileRadioPowerCalculator == null) {
410             mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
411         }
412         mMobileRadioPowerCalculator.reset(mStats);
413 
414         // checkHasWifiPowerReporting can change if we get energy data at a later point, so
415         // always check this field.
416         final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile);
417         if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) {
418             mWifiPowerCalculator = hasWifiPowerReporting ?
419                     new WifiPowerCalculator(mPowerProfile) :
420                     new WifiPowerEstimator(mPowerProfile);
421             mHasWifiPowerReporting = hasWifiPowerReporting;
422         }
423         mWifiPowerCalculator.reset();
424 
425         final boolean hasBluetoothPowerReporting = checkHasBluetoothPowerReporting(mStats,
426                 mPowerProfile);
427         if (mBluetoothPowerCalculator == null ||
428                 hasBluetoothPowerReporting != mHasBluetoothPowerReporting) {
429             mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
430             mHasBluetoothPowerReporting = hasBluetoothPowerReporting;
431         }
432         mBluetoothPowerCalculator.reset();
433 
434         mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
435                 (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE),
436                 mStats, rawRealtimeUs, statsType);
437         mSensorPowerCalculator.reset();
438 
439         if (mCameraPowerCalculator == null) {
440             mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile);
441         }
442         mCameraPowerCalculator.reset();
443 
444         if (mFlashlightPowerCalculator == null) {
445             mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile);
446         }
447         mFlashlightPowerCalculator.reset();
448 
449         if (mMediaPowerCalculator == null) {
450             mMediaPowerCalculator = new MediaPowerCalculator(mPowerProfile);
451         }
452         mMediaPowerCalculator.reset();
453 
454         mStatsType = statsType;
455         mRawUptimeUs = rawUptimeUs;
456         mRawRealtimeUs = rawRealtimeUs;
457         mBatteryUptimeUs = mStats.getBatteryUptime(rawUptimeUs);
458         mBatteryRealtimeUs = mStats.getBatteryRealtime(rawRealtimeUs);
459         mTypeBatteryUptimeUs = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
460         mTypeBatteryRealtimeUs = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
461         mBatteryTimeRemainingUs = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
462         mChargeTimeRemainingUs = mStats.computeChargeTimeRemaining(rawRealtimeUs);
463 
464         if (DEBUG) {
465             Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs / 1000) + " uptime="
466                     + (rawUptimeUs / 1000));
467             Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtimeUs / 1000) + " uptime="
468                     + (mBatteryUptimeUs / 1000));
469             Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtimeUs / 1000) + " uptime="
470                     + (mTypeBatteryUptimeUs / 1000));
471         }
472         mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
473                 * mPowerProfile.getBatteryCapacity()) / 100;
474         mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
475                 * mPowerProfile.getBatteryCapacity()) / 100;
476 
477         processAppUsage(asUsers);
478 
479         // Before aggregating apps in to users, collect all apps to sort by their ms per packet.
480         for (int i = 0; i < mUsageList.size(); i++) {
481             BatterySipper bs = mUsageList.get(i);
482             bs.computeMobilemspp();
483             if (bs.mobilemspp != 0) {
484                 mMobilemsppList.add(bs);
485             }
486         }
487 
488         for (int i = 0; i < mUserSippers.size(); i++) {
489             List<BatterySipper> user = mUserSippers.valueAt(i);
490             for (int j = 0; j < user.size(); j++) {
491                 BatterySipper bs = user.get(j);
492                 bs.computeMobilemspp();
493                 if (bs.mobilemspp != 0) {
494                     mMobilemsppList.add(bs);
495                 }
496             }
497         }
498         Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
499             @Override
500             public int compare(BatterySipper lhs, BatterySipper rhs) {
501                 return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
502             }
503         });
504 
505         processMiscUsage();
506 
507         Collections.sort(mUsageList);
508 
509         // At this point, we've sorted the list so we are guaranteed the max values are at the top.
510         // We have only added real powers so far.
511         if (!mUsageList.isEmpty()) {
512             mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
513             final int usageListCount = mUsageList.size();
514             for (int i = 0; i < usageListCount; i++) {
515                 mComputedPower += mUsageList.get(i).totalPowerMah;
516             }
517         }
518 
519         if (DEBUG) {
520             Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
521                     + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
522         }
523 
524         mTotalPower = mComputedPower;
525         if (mStats.getLowDischargeAmountSinceCharge() > 1) {
526             if (mMinDrainedPower > mComputedPower) {
527                 double amount = mMinDrainedPower - mComputedPower;
528                 mTotalPower = mMinDrainedPower;
529                 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
530 
531                 // Insert the BatterySipper in its sorted position.
532                 int index = Collections.binarySearch(mUsageList, bs);
533                 if (index < 0) {
534                     index = -(index + 1);
535                 }
536                 mUsageList.add(index, bs);
537                 mMaxPower = Math.max(mMaxPower, amount);
538             } else if (mMaxDrainedPower < mComputedPower) {
539                 double amount = mComputedPower - mMaxDrainedPower;
540 
541                 // Insert the BatterySipper in its sorted position.
542                 BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
543                 int index = Collections.binarySearch(mUsageList, bs);
544                 if (index < 0) {
545                     index = -(index + 1);
546                 }
547                 mUsageList.add(index, bs);
548                 mMaxPower = Math.max(mMaxPower, amount);
549             }
550         }
551 
552         // Smear it!
553         final double hiddenPowerMah = removeHiddenBatterySippers(mUsageList);
554         final double totalRemainingPower = getTotalPower() - hiddenPowerMah;
555         if (Math.abs(totalRemainingPower) > 1e-3) {
556             for (int i = 0, size = mUsageList.size(); i < size; i++) {
557                 final BatterySipper sipper = mUsageList.get(i);
558                 if (!sipper.shouldHide) {
559                     sipper.proportionalSmearMah = hiddenPowerMah
560                             * ((sipper.totalPowerMah + sipper.screenPowerMah)
561                             / totalRemainingPower);
562                     sipper.sumPower();
563                 }
564             }
565         }
566     }
567 
processAppUsage(SparseArray<UserHandle> asUsers)568     private void processAppUsage(SparseArray<UserHandle> asUsers) {
569         final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
570         mStatsPeriod = mTypeBatteryRealtimeUs;
571 
572         BatterySipper osSipper = null;
573         final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
574         final int NU = uidStats.size();
575         for (int iu = 0; iu < NU; iu++) {
576             final Uid u = uidStats.valueAt(iu);
577             final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
578 
579             mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
580             mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
581             mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
582                     mStatsType);
583             mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
584             mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
585                     mStatsType);
586             mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
587             mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
588             mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
589                     mStatsType);
590             mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
591 
592             final double totalPower = app.sumPower();
593             if (DEBUG && totalPower != 0) {
594                 Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
595                         makemAh(totalPower)));
596             }
597 
598             // Add the app to the list if it is consuming power.
599             if (totalPower != 0 || u.getUid() == 0) {
600                 //
601                 // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
602                 //
603                 final int uid = app.getUid();
604                 final int userId = UserHandle.getUserId(uid);
605                 if (uid == Process.WIFI_UID) {
606                     mWifiSippers.add(app);
607                 } else if (uid == Process.BLUETOOTH_UID) {
608                     mBluetoothSippers.add(app);
609                 } else if (!forAllUsers && asUsers.get(userId) == null
610                         && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
611                     // We are told to just report this user's apps as one large entry.
612                     List<BatterySipper> list = mUserSippers.get(userId);
613                     if (list == null) {
614                         list = new ArrayList<>();
615                         mUserSippers.put(userId, list);
616                     }
617                     list.add(app);
618                 } else {
619                     mUsageList.add(app);
620                 }
621 
622                 if (uid == 0) {
623                     osSipper = app;
624                 }
625             }
626         }
627 
628         if (osSipper != null) {
629             // The device has probably been awake for longer than the screen on
630             // time and application wake lock time would account for.  Assign
631             // this remainder to the OS, if possible.
632             mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtimeUs,
633                     mRawUptimeUs, mStatsType);
634             osSipper.sumPower();
635         }
636     }
637 
addPhoneUsage()638     private void addPhoneUsage() {
639         long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtimeUs, mStatsType) / 1000;
640         double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
641                 * phoneOnTimeMs / (60 * 60 * 1000);
642         if (phoneOnPower != 0) {
643             addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
644         }
645     }
646 
647     /**
648      * Screen power is the additional power the screen takes while the device is running.
649      */
addScreenUsage()650     private void addScreenUsage() {
651         double power = 0;
652         long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtimeUs, mStatsType) / 1000;
653         power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
654         final double screenFullPower =
655                 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
656         for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
657             double screenBinPower = screenFullPower * (i + 0.5f)
658                     / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
659             long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtimeUs, mStatsType)
660                     / 1000;
661             double p = screenBinPower * brightnessTime;
662             if (DEBUG && p != 0) {
663                 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
664                         + " power=" + makemAh(p / (60 * 60 * 1000)));
665             }
666             power += p;
667         }
668         power /= (60 * 60 * 1000); // To hours
669         if (power != 0) {
670             addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
671         }
672     }
673 
674     /**
675      * Ambient display power is the additional power the screen takes while in ambient display/
676      * screen doze/ always-on display (interchangeable terms) mode. Ambient display power should
677      * be hidden {@link #shouldHideSipper(BatterySipper)}, but should not be included in smearing
678      * {@link #removeHiddenBatterySippers(List)}.
679      */
addAmbientDisplayUsage()680     private void addAmbientDisplayUsage() {
681         long ambientDisplayMs = mStats.getScreenDozeTime(mRawRealtimeUs, mStatsType) / 1000;
682         double power = mPowerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY)
683                 * ambientDisplayMs / (60 * 60 * 1000);
684         if (power > 0) {
685             addEntry(DrainType.AMBIENT_DISPLAY, ambientDisplayMs, power);
686         }
687     }
688 
addRadioUsage()689     private void addRadioUsage() {
690         BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
691         mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs,
692                 mStatsType);
693         radio.sumPower();
694         if (radio.totalPowerMah > 0) {
695             mUsageList.add(radio);
696         }
697     }
698 
aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag)699     private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
700         for (int i = 0; i < from.size(); i++) {
701             BatterySipper wbs = from.get(i);
702             if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
703             bs.add(wbs);
704         }
705         bs.computeMobilemspp();
706         bs.sumPower();
707     }
708 
709     /**
710      * Calculate the baseline power usage for the device when it is in suspend and idle.
711      * The device is drawing POWER_CPU_SUSPEND power at its lowest power state.
712      * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held.
713      */
addIdleUsage()714     private void addIdleUsage() {
715         final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) *
716                 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND);
717         final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) *
718                 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
719         final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000);
720         if (DEBUG && totalPowerMah != 0) {
721             Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000)
722                     + " power=" + makemAh(suspendPowerMaMs / (60 * 60 * 1000)));
723             Log.d(TAG, "Idle: time=" + (mTypeBatteryUptimeUs / 1000)
724                     + " power=" + makemAh(idlePowerMaMs / (60 * 60 * 1000)));
725         }
726 
727         if (totalPowerMah != 0) {
728             addEntry(BatterySipper.DrainType.IDLE, mTypeBatteryRealtimeUs / 1000, totalPowerMah);
729         }
730     }
731 
732     /**
733      * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
734      * then only the WiFi process gets blamed here since we normalize power calculations and
735      * assign all the power drain to apps. If energy info is not reported, we attribute the
736      * difference between total running time of WiFi for all apps and the actual running time
737      * of WiFi to the WiFi subsystem.
738      */
addWiFiUsage()739     private void addWiFiUsage() {
740         BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
741         mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
742                 mStatsType);
743         aggregateSippers(bs, mWifiSippers, "WIFI");
744         if (bs.totalPowerMah > 0) {
745             mUsageList.add(bs);
746         }
747     }
748 
749     /**
750      * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the
751      * Bluetooth Category.
752      */
addBluetoothUsage()753     private void addBluetoothUsage() {
754         BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
755         mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
756                 mStatsType);
757         aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
758         if (bs.totalPowerMah > 0) {
759             mUsageList.add(bs);
760         }
761     }
762 
addUserUsage()763     private void addUserUsage() {
764         for (int i = 0; i < mUserSippers.size(); i++) {
765             final int userId = mUserSippers.keyAt(i);
766             BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
767             bs.userId = userId;
768             aggregateSippers(bs, mUserSippers.valueAt(i), "User");
769             mUsageList.add(bs);
770         }
771     }
772 
addMemoryUsage()773     private void addMemoryUsage() {
774         BatterySipper memory = new BatterySipper(DrainType.MEMORY, null, 0);
775         mMemoryPowerCalculator.calculateRemaining(memory, mStats, mRawRealtimeUs, mRawUptimeUs,
776                 mStatsType);
777         memory.sumPower();
778         if (memory.totalPowerMah > 0) {
779             mUsageList.add(memory);
780         }
781     }
782 
processMiscUsage()783     private void processMiscUsage() {
784         addUserUsage();
785         addPhoneUsage();
786         addScreenUsage();
787         addAmbientDisplayUsage();
788         addWiFiUsage();
789         addBluetoothUsage();
790         addMemoryUsage();
791         addIdleUsage(); // Not including cellular idle power
792         // Don't compute radio usage if it's a wifi-only device
793         if (!mWifiOnly) {
794             addRadioUsage();
795         }
796     }
797 
addEntry(DrainType drainType, long time, double power)798     private BatterySipper addEntry(DrainType drainType, long time, double power) {
799         BatterySipper bs = new BatterySipper(drainType, null, 0);
800         bs.usagePowerMah = power;
801         bs.usageTimeMs = time;
802         bs.sumPower();
803         mUsageList.add(bs);
804         return bs;
805     }
806 
807     @UnsupportedAppUsage
getUsageList()808     public List<BatterySipper> getUsageList() {
809         return mUsageList;
810     }
811 
getMobilemsppList()812     public List<BatterySipper> getMobilemsppList() {
813         return mMobilemsppList;
814     }
815 
getStatsPeriod()816     public long getStatsPeriod() {
817         return mStatsPeriod;
818     }
819 
getStatsType()820     public int getStatsType() {
821         return mStatsType;
822     }
823 
824     @UnsupportedAppUsage
getMaxPower()825     public double getMaxPower() {
826         return mMaxPower;
827     }
828 
getMaxRealPower()829     public double getMaxRealPower() {
830         return mMaxRealPower;
831     }
832 
833     @UnsupportedAppUsage
getTotalPower()834     public double getTotalPower() {
835         return mTotalPower;
836     }
837 
getComputedPower()838     public double getComputedPower() {
839         return mComputedPower;
840     }
841 
getMinDrainedPower()842     public double getMinDrainedPower() {
843         return mMinDrainedPower;
844     }
845 
getMaxDrainedPower()846     public double getMaxDrainedPower() {
847         return mMaxDrainedPower;
848     }
849 
readFully(FileInputStream stream)850     public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
851         return readFully(stream, stream.available());
852     }
853 
readFully(FileInputStream stream, int avail)854     public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
855         int pos = 0;
856         byte[] data = new byte[avail];
857         while (true) {
858             int amt = stream.read(data, pos, data.length - pos);
859             //Log.i("foo", "Read " + amt + " bytes at " + pos
860             //        + " of avail " + data.length);
861             if (amt <= 0) {
862                 //Log.i("foo", "**** FINISHED READING: pos=" + pos
863                 //        + " len=" + data.length);
864                 return data;
865             }
866             pos += amt;
867             avail = stream.available();
868             if (avail > data.length - pos) {
869                 byte[] newData = new byte[pos + avail];
870                 System.arraycopy(data, 0, newData, 0, pos);
871                 data = newData;
872             }
873         }
874     }
875 
876     /**
877      * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on
878      * foreground activity time.
879      *
880      * @param sippers sipper list that need to check and remove
881      * @return the total power of the hidden items of {@link BatterySipper}
882      * for proportional smearing
883      */
removeHiddenBatterySippers(List<BatterySipper> sippers)884     public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
885         double proportionalSmearPowerMah = 0;
886         BatterySipper screenSipper = null;
887         for (int i = sippers.size() - 1; i >= 0; i--) {
888             final BatterySipper sipper = sippers.get(i);
889             sipper.shouldHide = shouldHideSipper(sipper);
890             if (sipper.shouldHide) {
891                 if (sipper.drainType != DrainType.OVERCOUNTED
892                         && sipper.drainType != DrainType.SCREEN
893                         && sipper.drainType != DrainType.AMBIENT_DISPLAY
894                         && sipper.drainType != DrainType.UNACCOUNTED
895                         && sipper.drainType != DrainType.BLUETOOTH
896                         && sipper.drainType != DrainType.WIFI
897                         && sipper.drainType != DrainType.IDLE) {
898                     // Don't add it if it is overcounted, unaccounted or screen
899                     proportionalSmearPowerMah += sipper.totalPowerMah;
900                 }
901             }
902 
903             if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
904                 screenSipper = sipper;
905             }
906         }
907 
908         smearScreenBatterySipper(sippers, screenSipper);
909 
910         return proportionalSmearPowerMah;
911     }
912 
913     /**
914      * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
915      * time.
916      */
smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper)917     public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
918         long totalActivityTimeMs = 0;
919         final SparseLongArray activityTimeArray = new SparseLongArray();
920         for (int i = 0, size = sippers.size(); i < size; i++) {
921             final BatteryStats.Uid uid = sippers.get(i).uidObj;
922             if (uid != null) {
923                 final long timeMs = getProcessForegroundTimeMs(uid,
924                         BatteryStats.STATS_SINCE_CHARGED);
925                 activityTimeArray.put(uid.getUid(), timeMs);
926                 totalActivityTimeMs += timeMs;
927             }
928         }
929 
930         if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
931             final double screenPowerMah = screenSipper.totalPowerMah;
932             for (int i = 0, size = sippers.size(); i < size; i++) {
933                 final BatterySipper sipper = sippers.get(i);
934                 sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
935                         / totalActivityTimeMs;
936             }
937         }
938     }
939 
940     /**
941      * Check whether we should hide the battery sipper.
942      */
shouldHideSipper(BatterySipper sipper)943     public boolean shouldHideSipper(BatterySipper sipper) {
944         final DrainType drainType = sipper.drainType;
945 
946         return drainType == DrainType.IDLE
947                 || drainType == DrainType.CELL
948                 || drainType == DrainType.SCREEN
949                 || drainType == DrainType.AMBIENT_DISPLAY
950                 || drainType == DrainType.UNACCOUNTED
951                 || drainType == DrainType.OVERCOUNTED
952                 || isTypeService(sipper)
953                 || isTypeSystem(sipper);
954     }
955 
956     /**
957      * Check whether {@code sipper} is type service
958      */
isTypeService(BatterySipper sipper)959     public boolean isTypeService(BatterySipper sipper) {
960         final String[] packages = mPackageManager.getPackagesForUid(sipper.getUid());
961         if (packages == null) {
962             return false;
963         }
964 
965         for (String packageName : packages) {
966             if (ArrayUtils.contains(mServicepackageArray, packageName)) {
967                 return true;
968             }
969         }
970 
971         return false;
972     }
973 
974     /**
975      * Check whether {@code sipper} is type system
976      */
isTypeSystem(BatterySipper sipper)977     public boolean isTypeSystem(BatterySipper sipper) {
978         final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
979         sipper.mPackages = mPackageManager.getPackagesForUid(uid);
980         // Classify all the sippers to type system if the range of uid is 0...FIRST_APPLICATION_UID
981         if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
982             return true;
983         } else if (sipper.mPackages != null) {
984             for (final String packageName : sipper.mPackages) {
985                 if (ArrayUtils.contains(mSystemPackageArray, packageName)) {
986                     return true;
987                 }
988             }
989         }
990 
991         return false;
992     }
993 
convertUsToMs(long timeUs)994     public long convertUsToMs(long timeUs) {
995         return timeUs / 1000;
996     }
997 
convertMsToUs(long timeMs)998     public long convertMsToUs(long timeMs) {
999         return timeMs * 1000;
1000     }
1001 
1002     @VisibleForTesting
getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)1003     public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
1004         final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
1005         if (timer != null) {
1006             return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
1007         }
1008 
1009         return 0;
1010     }
1011 
1012     @VisibleForTesting
getProcessForegroundTimeMs(BatteryStats.Uid uid, int which)1013     public long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) {
1014         final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
1015         final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP};
1016 
1017         long timeUs = 0;
1018         for (int type : foregroundTypes) {
1019             final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which);
1020             timeUs += localTime;
1021         }
1022 
1023         // Return the min value of STATE_TOP time and foreground activity time, since both of these
1024         // time have some errors.
1025         return convertUsToMs(
1026                 Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)));
1027     }
1028 
1029     @VisibleForTesting
setPackageManager(PackageManager packageManager)1030     public void setPackageManager(PackageManager packageManager) {
1031         mPackageManager = packageManager;
1032     }
1033 
1034     @VisibleForTesting
setSystemPackageArray(String[] array)1035     public void setSystemPackageArray(String[] array) {
1036         mSystemPackageArray = array;
1037     }
1038 
1039     @VisibleForTesting
setServicePackageArray(String[] array)1040     public void setServicePackageArray(String[] array) {
1041         mServicepackageArray = array;
1042     }
1043 
1044     @UnsupportedAppUsage
load()1045     private void load() {
1046         if (mBatteryInfo == null) {
1047             return;
1048         }
1049         mStats = getStats(mBatteryInfo);
1050         if (mCollectBatteryBroadcast) {
1051             mBatteryBroadcast = mContext.registerReceiver(null,
1052                     new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
1053         }
1054     }
1055 
getStats(IBatteryStats service)1056     private static BatteryStatsImpl getStats(IBatteryStats service) {
1057         try {
1058             ParcelFileDescriptor pfd = service.getStatisticsStream();
1059             if (pfd != null) {
1060                 if (false) {
1061                     Log.d(TAG, "selinux context: "
1062                             + SELinux.getFileContext(pfd.getFileDescriptor()));
1063                 }
1064                 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
1065                     byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
1066                     Parcel parcel = Parcel.obtain();
1067                     parcel.unmarshall(data, 0, data.length);
1068                     parcel.setDataPosition(0);
1069                     BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
1070                             .createFromParcel(parcel);
1071                     return stats;
1072                 } catch (IOException e) {
1073                     Log.w(TAG, "Unable to read statistics stream", e);
1074                 }
1075             }
1076         } catch (RemoteException e) {
1077             Log.w(TAG, "RemoteException:", e);
1078         }
1079         return new BatteryStatsImpl();
1080     }
1081 }
1082