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