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