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.hardware.SensorManager;
23 import android.net.ConnectivityManager;
24 import android.os.BatteryStats;
25 import android.os.BatteryStats.Uid;
26 import android.os.Bundle;
27 import android.os.MemoryFile;
28 import android.os.Parcel;
29 import android.os.ParcelFileDescriptor;
30 import android.os.Process;
31 import android.os.RemoteException;
32 import android.os.ServiceManager;
33 import android.os.SystemClock;
34 import android.os.UserHandle;
35 import android.util.ArrayMap;
36 import android.util.Log;
37 import android.util.SparseArray;
38 
39 import com.android.internal.app.IBatteryStats;
40 import com.android.internal.os.BatterySipper.DrainType;
41 
42 import java.io.File;
43 import java.io.FileInputStream;
44 import java.io.FileOutputStream;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.List;
50 import java.util.Locale;
51 
52 /**
53  * A helper class for retrieving the power usage information for all applications and services.
54  *
55  * The caller must initialize this class as soon as activity object is ready to use (for example, in
56  * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
57  */
58 public final class BatteryStatsHelper {
59     static final boolean DEBUG = false;
60 
61     private static final String TAG = BatteryStatsHelper.class.getSimpleName();
62 
63     private static BatteryStats sStatsXfer;
64     private static Intent sBatteryBroadcastXfer;
65     private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>();
66 
67     final private Context mContext;
68     final private boolean mCollectBatteryBroadcast;
69     final private boolean mWifiOnly;
70 
71     private IBatteryStats mBatteryInfo;
72     private BatteryStats mStats;
73     private Intent mBatteryBroadcast;
74     private PowerProfile mPowerProfile;
75 
76     /**
77      * List of apps using power.
78      */
79     private final List<BatterySipper> mUsageList = new ArrayList<>();
80 
81     /**
82      * List of apps using wifi power.
83      */
84     private final List<BatterySipper> mWifiSippers = new ArrayList<>();
85 
86     /**
87      * List of apps using bluetooth power.
88      */
89     private final List<BatterySipper> mBluetoothSippers = new ArrayList<>();
90 
91     private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>();
92 
93     private final List<BatterySipper> mMobilemsppList = new ArrayList<>();
94 
95     private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
96 
97     long mRawRealtimeUs;
98     long mRawUptimeUs;
99     long mBatteryRealtimeUs;
100     long mBatteryUptimeUs;
101     long mTypeBatteryRealtimeUs;
102     long mTypeBatteryUptimeUs;
103     long mBatteryTimeRemainingUs;
104     long mChargeTimeRemainingUs;
105 
106     private long mStatsPeriod = 0;
107 
108     // The largest entry by power.
109     private double mMaxPower = 1;
110 
111     // The largest real entry by power (not undercounted or overcounted).
112     private double mMaxRealPower = 1;
113 
114     // Total computed power.
115     private double mComputedPower;
116     private double mTotalPower;
117     private double mMinDrainedPower;
118     private double mMaxDrainedPower;
119 
120     PowerCalculator mCpuPowerCalculator;
121     PowerCalculator mWakelockPowerCalculator;
122     MobileRadioPowerCalculator mMobileRadioPowerCalculator;
123     PowerCalculator mWifiPowerCalculator;
124     PowerCalculator mBluetoothPowerCalculator;
125     PowerCalculator mSensorPowerCalculator;
126     PowerCalculator mCameraPowerCalculator;
127     PowerCalculator mFlashlightPowerCalculator;
128 
129     boolean mHasWifiPowerReporting = false;
130     boolean mHasBluetoothPowerReporting = false;
131 
checkWifiOnly(Context context)132     public static boolean checkWifiOnly(Context context) {
133         ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
134                 Context.CONNECTIVITY_SERVICE);
135         return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
136     }
137 
checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile)138     public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
139         return stats.hasWifiActivityReporting() &&
140                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
141                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
142                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
143     }
144 
checkHasBluetoothPowerReporting(BatteryStats stats, PowerProfile profile)145     public static boolean checkHasBluetoothPowerReporting(BatteryStats stats,
146                                                           PowerProfile profile) {
147         return stats.hasBluetoothActivityReporting() &&
148                 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 &&
149                 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 &&
150                 profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0;
151     }
152 
BatteryStatsHelper(Context context)153     public BatteryStatsHelper(Context context) {
154         this(context, true);
155     }
156 
BatteryStatsHelper(Context context, boolean collectBatteryBroadcast)157     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
158         this(context, collectBatteryBroadcast, checkWifiOnly(context));
159     }
160 
BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly)161     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
162         mContext = context;
163         mCollectBatteryBroadcast = collectBatteryBroadcast;
164         mWifiOnly = wifiOnly;
165     }
166 
storeStatsHistoryInFile(String fname)167     public void storeStatsHistoryInFile(String fname) {
168         synchronized (sFileXfer) {
169             File path = makeFilePath(mContext, fname);
170             sFileXfer.put(path, this.getStats());
171             FileOutputStream fout = null;
172             try {
173                 fout = new FileOutputStream(path);
174                 Parcel hist = Parcel.obtain();
175                 getStats().writeToParcelWithoutUids(hist, 0);
176                 byte[] histData = hist.marshall();
177                 fout.write(histData);
178             } catch (IOException e) {
179                 Log.w(TAG, "Unable to write history to file", e);
180             } finally {
181                 if (fout != null) {
182                     try {
183                         fout.close();
184                     } catch (IOException e) {
185                     }
186                 }
187             }
188         }
189     }
190 
statsFromFile(Context context, String fname)191     public static BatteryStats statsFromFile(Context context, String fname) {
192         synchronized (sFileXfer) {
193             File path = makeFilePath(context, fname);
194             BatteryStats stats = sFileXfer.get(path);
195             if (stats != null) {
196                 return stats;
197             }
198             FileInputStream fin = null;
199             try {
200                 fin = new FileInputStream(path);
201                 byte[] data = readFully(fin);
202                 Parcel parcel = Parcel.obtain();
203                 parcel.unmarshall(data, 0, data.length);
204                 parcel.setDataPosition(0);
205                 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
206             } catch (IOException e) {
207                 Log.w(TAG, "Unable to read history to file", e);
208             } finally {
209                 if (fin != null) {
210                     try {
211                         fin.close();
212                     } catch (IOException e) {
213                     }
214                 }
215             }
216         }
217         return getStats(IBatteryStats.Stub.asInterface(
218                         ServiceManager.getService(BatteryStats.SERVICE_NAME)));
219     }
220 
dropFile(Context context, String fname)221     public static void dropFile(Context context, String fname) {
222         makeFilePath(context, fname).delete();
223     }
224 
makeFilePath(Context context, String fname)225     private static File makeFilePath(Context context, String fname) {
226         return new File(context.getFilesDir(), fname);
227     }
228 
229     /** Clears the current stats and forces recreating for future use. */
clearStats()230     public void clearStats() {
231         mStats = null;
232     }
233 
getStats()234     public BatteryStats getStats() {
235         if (mStats == null) {
236             load();
237         }
238         return mStats;
239     }
240 
getBatteryBroadcast()241     public Intent getBatteryBroadcast() {
242         if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
243             load();
244         }
245         return mBatteryBroadcast;
246     }
247 
getPowerProfile()248     public PowerProfile getPowerProfile() {
249         return mPowerProfile;
250     }
251 
create(BatteryStats stats)252     public void create(BatteryStats stats) {
253         mPowerProfile = new PowerProfile(mContext);
254         mStats = stats;
255     }
256 
create(Bundle icicle)257     public void create(Bundle icicle) {
258         if (icicle != null) {
259             mStats = sStatsXfer;
260             mBatteryBroadcast = sBatteryBroadcastXfer;
261         }
262         mBatteryInfo = IBatteryStats.Stub.asInterface(
263                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
264         mPowerProfile = new PowerProfile(mContext);
265     }
266 
storeState()267     public void storeState() {
268         sStatsXfer = mStats;
269         sBatteryBroadcastXfer = mBatteryBroadcast;
270     }
271 
makemAh(double power)272     public static String makemAh(double power) {
273         if (power == 0) return "0";
274 
275         final String format;
276         if (power < .00001) format = "%.8f";
277         else if (power < .0001) format = "%.7f";
278         else if (power < .001) format = "%.6f";
279         else if (power < .01) format = "%.5f";
280         else if (power < .1) format = "%.4f";
281         else if (power < 1) format = "%.3f";
282         else if (power < 10) format = "%.2f";
283         else if (power < 100) format = "%.1f";
284         else format = "%.0f";
285 
286         // Use English locale because this is never used in UI (only in checkin and dump).
287         return String.format(Locale.ENGLISH, format, power);
288     }
289 
290     /**
291      * Refreshes the power usage list.
292      */
refreshStats(int statsType, int asUser)293     public void refreshStats(int statsType, int asUser) {
294         SparseArray<UserHandle> users = new SparseArray<>(1);
295         users.put(asUser, new UserHandle(asUser));
296         refreshStats(statsType, users);
297     }
298 
299     /**
300      * Refreshes the power usage list.
301      */
refreshStats(int statsType, List<UserHandle> asUsers)302     public void refreshStats(int statsType, List<UserHandle> asUsers) {
303         final int n = asUsers.size();
304         SparseArray<UserHandle> users = new SparseArray<>(n);
305         for (int i = 0; i < n; ++i) {
306             UserHandle userHandle = asUsers.get(i);
307             users.put(userHandle.getIdentifier(), userHandle);
308         }
309         refreshStats(statsType, users);
310     }
311 
312     /**
313      * Refreshes the power usage list.
314      */
refreshStats(int statsType, SparseArray<UserHandle> asUsers)315     public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
316         refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
317                 SystemClock.uptimeMillis() * 1000);
318     }
319 
refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs)320     public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
321             long rawUptimeUs) {
322         // Initialize mStats if necessary.
323         getStats();
324 
325         mMaxPower = 0;
326         mMaxRealPower = 0;
327         mComputedPower = 0;
328         mTotalPower = 0;
329 
330         mUsageList.clear();
331         mWifiSippers.clear();
332         mBluetoothSippers.clear();
333         mUserSippers.clear();
334         mMobilemsppList.clear();
335 
336         if (mStats == null) {
337             return;
338         }
339 
340         if (mCpuPowerCalculator == null) {
341             mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
342         }
343         mCpuPowerCalculator.reset();
344 
345         if (mWakelockPowerCalculator == null) {
346             mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
347         }
348         mWakelockPowerCalculator.reset();
349 
350         if (mMobileRadioPowerCalculator == null) {
351             mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
352         }
353         mMobileRadioPowerCalculator.reset(mStats);
354 
355         // checkHasWifiPowerReporting can change if we get energy data at a later point, so
356         // always check this field.
357         final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile);
358         if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) {
359             mWifiPowerCalculator = hasWifiPowerReporting ?
360                     new WifiPowerCalculator(mPowerProfile) :
361                     new WifiPowerEstimator(mPowerProfile);
362             mHasWifiPowerReporting = hasWifiPowerReporting;
363         }
364         mWifiPowerCalculator.reset();
365 
366         final boolean hasBluetoothPowerReporting = checkHasBluetoothPowerReporting(mStats,
367                                                                                    mPowerProfile);
368         if (mBluetoothPowerCalculator == null ||
369                 hasBluetoothPowerReporting != mHasBluetoothPowerReporting) {
370             mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
371             mHasBluetoothPowerReporting = hasBluetoothPowerReporting;
372         }
373         mBluetoothPowerCalculator.reset();
374 
375         if (mSensorPowerCalculator == null) {
376             mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
377                     (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE));
378         }
379         mSensorPowerCalculator.reset();
380 
381         if (mCameraPowerCalculator == null) {
382             mCameraPowerCalculator = new CameraPowerCalculator(mPowerProfile);
383         }
384         mCameraPowerCalculator.reset();
385 
386         if (mFlashlightPowerCalculator == null) {
387             mFlashlightPowerCalculator = new FlashlightPowerCalculator(mPowerProfile);
388         }
389         mFlashlightPowerCalculator.reset();
390 
391         mStatsType = statsType;
392         mRawUptimeUs = rawUptimeUs;
393         mRawRealtimeUs = rawRealtimeUs;
394         mBatteryUptimeUs = mStats.getBatteryUptime(rawUptimeUs);
395         mBatteryRealtimeUs = mStats.getBatteryRealtime(rawRealtimeUs);
396         mTypeBatteryUptimeUs = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
397         mTypeBatteryRealtimeUs = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
398         mBatteryTimeRemainingUs = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
399         mChargeTimeRemainingUs = mStats.computeChargeTimeRemaining(rawRealtimeUs);
400 
401         if (DEBUG) {
402             Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime="
403                     + (rawUptimeUs/1000));
404             Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtimeUs /1000) + " uptime="
405                     + (mBatteryUptimeUs /1000));
406             Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtimeUs /1000) + " uptime="
407                     + (mTypeBatteryUptimeUs /1000));
408         }
409         mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
410                 * mPowerProfile.getBatteryCapacity()) / 100;
411         mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
412                 * mPowerProfile.getBatteryCapacity()) / 100;
413 
414         processAppUsage(asUsers);
415 
416         // Before aggregating apps in to users, collect all apps to sort by their ms per packet.
417         for (int i=0; i<mUsageList.size(); i++) {
418             BatterySipper bs = mUsageList.get(i);
419             bs.computeMobilemspp();
420             if (bs.mobilemspp != 0) {
421                 mMobilemsppList.add(bs);
422             }
423         }
424 
425         for (int i=0; i<mUserSippers.size(); i++) {
426             List<BatterySipper> user = mUserSippers.valueAt(i);
427             for (int j=0; j<user.size(); j++) {
428                 BatterySipper bs = user.get(j);
429                 bs.computeMobilemspp();
430                 if (bs.mobilemspp != 0) {
431                     mMobilemsppList.add(bs);
432                 }
433             }
434         }
435         Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
436             @Override
437             public int compare(BatterySipper lhs, BatterySipper rhs) {
438                 return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
439             }
440         });
441 
442         processMiscUsage();
443 
444         Collections.sort(mUsageList);
445 
446         // At this point, we've sorted the list so we are guaranteed the max values are at the top.
447         // We have only added real powers so far.
448         if (!mUsageList.isEmpty()) {
449             mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
450             final int usageListCount = mUsageList.size();
451             for (int i = 0; i < usageListCount; i++) {
452                 mComputedPower += mUsageList.get(i).totalPowerMah;
453             }
454         }
455 
456         if (DEBUG) {
457             Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
458                     + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
459         }
460 
461         mTotalPower = mComputedPower;
462         if (mStats.getLowDischargeAmountSinceCharge() > 1) {
463             if (mMinDrainedPower > mComputedPower) {
464                 double amount = mMinDrainedPower - mComputedPower;
465                 mTotalPower = mMinDrainedPower;
466                 BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
467 
468                 // Insert the BatterySipper in its sorted position.
469                 int index = Collections.binarySearch(mUsageList, bs);
470                 if (index < 0) {
471                     index = -(index + 1);
472                 }
473                 mUsageList.add(index, bs);
474                 mMaxPower = Math.max(mMaxPower, amount);
475             } else if (mMaxDrainedPower < mComputedPower) {
476                 double amount = mComputedPower - mMaxDrainedPower;
477 
478                 // Insert the BatterySipper in its sorted position.
479                 BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
480                 int index = Collections.binarySearch(mUsageList, bs);
481                 if (index < 0) {
482                     index = -(index + 1);
483                 }
484                 mUsageList.add(index, bs);
485                 mMaxPower = Math.max(mMaxPower, amount);
486             }
487         }
488     }
489 
processAppUsage(SparseArray<UserHandle> asUsers)490     private void processAppUsage(SparseArray<UserHandle> asUsers) {
491         final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
492         mStatsPeriod = mTypeBatteryRealtimeUs;
493 
494         BatterySipper osSipper = null;
495         final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
496         final int NU = uidStats.size();
497         for (int iu = 0; iu < NU; iu++) {
498             final Uid u = uidStats.valueAt(iu);
499             final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
500 
501             mCpuPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
502             mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
503             mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
504             mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
505             mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
506             mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
507             mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
508             mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
509 
510             final double totalPower = app.sumPower();
511             if (DEBUG && totalPower != 0) {
512                 Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
513                         makemAh(totalPower)));
514             }
515 
516             // Add the app to the list if it is consuming power.
517             if (totalPower != 0 || u.getUid() == 0) {
518                 //
519                 // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
520                 //
521                 final int uid = app.getUid();
522                 final int userId = UserHandle.getUserId(uid);
523                 if (uid == Process.WIFI_UID) {
524                     mWifiSippers.add(app);
525                 } else if (uid == Process.BLUETOOTH_UID) {
526                     mBluetoothSippers.add(app);
527                 } else if (!forAllUsers && asUsers.get(userId) == null
528                         && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
529                     // We are told to just report this user's apps as one large entry.
530                     List<BatterySipper> list = mUserSippers.get(userId);
531                     if (list == null) {
532                         list = new ArrayList<>();
533                         mUserSippers.put(userId, list);
534                     }
535                     list.add(app);
536                 } else {
537                     mUsageList.add(app);
538                 }
539 
540                 if (uid == 0) {
541                     osSipper = app;
542                 }
543             }
544         }
545 
546         if (osSipper != null) {
547             // The device has probably been awake for longer than the screen on
548             // time and application wake lock time would account for.  Assign
549             // this remainder to the OS, if possible.
550             mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtimeUs,
551                     mRawUptimeUs, mStatsType);
552             osSipper.sumPower();
553         }
554     }
555 
addPhoneUsage()556     private void addPhoneUsage() {
557         long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtimeUs, mStatsType) / 1000;
558         double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
559                 * phoneOnTimeMs / (60*60*1000);
560         if (phoneOnPower != 0) {
561             addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
562         }
563     }
564 
565     /**
566      * Screen power is the additional power the screen takes while the device is running.
567      */
addScreenUsage()568     private void addScreenUsage() {
569         double power = 0;
570         long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtimeUs, mStatsType) / 1000;
571         power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
572         final double screenFullPower =
573                 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
574         for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
575             double screenBinPower = screenFullPower * (i + 0.5f)
576                     / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
577             long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtimeUs, mStatsType)
578                     / 1000;
579             double p = screenBinPower*brightnessTime;
580             if (DEBUG && p != 0) {
581                 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
582                         + " power=" + makemAh(p / (60 * 60 * 1000)));
583             }
584             power += p;
585         }
586         power /= (60*60*1000); // To hours
587         if (power != 0) {
588             addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
589         }
590     }
591 
addRadioUsage()592     private void addRadioUsage() {
593         BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
594         mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs,
595                 mStatsType);
596         radio.sumPower();
597         if (radio.totalPowerMah > 0) {
598             mUsageList.add(radio);
599         }
600     }
601 
aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag)602     private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
603         for (int i=0; i<from.size(); i++) {
604             BatterySipper wbs = from.get(i);
605             if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
606             bs.add(wbs);
607         }
608         bs.computeMobilemspp();
609         bs.sumPower();
610     }
611 
612     /**
613      * Calculate the baseline power usage for the device when it is in suspend and idle.
614      * The device is drawing POWER_CPU_IDLE power at its lowest power state.
615      * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held.
616      */
addIdleUsage()617     private void addIdleUsage() {
618         final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) *
619                 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
620         final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) *
621                 mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
622         final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000);
623         if (DEBUG && totalPowerMah != 0) {
624             Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000)
625                     + " power=" + makemAh(suspendPowerMaMs / (60 * 60 * 1000)));
626             Log.d(TAG, "Idle: time=" + (mTypeBatteryUptimeUs / 1000)
627                     + " power=" + makemAh(idlePowerMaMs / (60 * 60 * 1000)));
628         }
629 
630         if (totalPowerMah != 0) {
631             addEntry(BatterySipper.DrainType.IDLE, mTypeBatteryRealtimeUs / 1000, totalPowerMah);
632         }
633     }
634 
635     /**
636      * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
637      * then only the WiFi process gets blamed here since we normalize power calculations and
638      * assign all the power drain to apps. If energy info is not reported, we attribute the
639      * difference between total running time of WiFi for all apps and the actual running time
640      * of WiFi to the WiFi subsystem.
641      */
addWiFiUsage()642     private void addWiFiUsage() {
643         BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
644         mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs, mStatsType);
645         aggregateSippers(bs, mWifiSippers, "WIFI");
646         if (bs.totalPowerMah > 0) {
647             mUsageList.add(bs);
648         }
649     }
650 
651     /**
652      * Bluetooth usage is not attributed to any apps yet, so the entire blame goes to the
653      * Bluetooth Category.
654      */
addBluetoothUsage()655     private void addBluetoothUsage() {
656         BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
657         mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
658                 mStatsType);
659         aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
660         if (bs.totalPowerMah > 0) {
661             mUsageList.add(bs);
662         }
663     }
664 
addUserUsage()665     private void addUserUsage() {
666         for (int i = 0; i < mUserSippers.size(); i++) {
667             final int userId = mUserSippers.keyAt(i);
668             BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
669             bs.userId = userId;
670             aggregateSippers(bs, mUserSippers.valueAt(i), "User");
671             mUsageList.add(bs);
672         }
673     }
674 
processMiscUsage()675     private void processMiscUsage() {
676         addUserUsage();
677         addPhoneUsage();
678         addScreenUsage();
679         addWiFiUsage();
680         addBluetoothUsage();
681         addIdleUsage(); // Not including cellular idle power
682         // Don't compute radio usage if it's a wifi-only device
683         if (!mWifiOnly) {
684             addRadioUsage();
685         }
686     }
687 
addEntry(DrainType drainType, long time, double power)688     private BatterySipper addEntry(DrainType drainType, long time, double power) {
689         BatterySipper bs = new BatterySipper(drainType, null, 0);
690         bs.usagePowerMah = power;
691         bs.usageTimeMs = time;
692         bs.sumPower();
693         mUsageList.add(bs);
694         return bs;
695     }
696 
getUsageList()697     public List<BatterySipper> getUsageList() {
698         return mUsageList;
699     }
700 
getMobilemsppList()701     public List<BatterySipper> getMobilemsppList() {
702         return mMobilemsppList;
703     }
704 
getStatsPeriod()705     public long getStatsPeriod() { return mStatsPeriod; }
706 
getStatsType()707     public int getStatsType() { return mStatsType; }
708 
getMaxPower()709     public double getMaxPower() { return mMaxPower; }
710 
getMaxRealPower()711     public double getMaxRealPower() { return mMaxRealPower; }
712 
getTotalPower()713     public double getTotalPower() { return mTotalPower; }
714 
getComputedPower()715     public double getComputedPower() { return mComputedPower; }
716 
getMinDrainedPower()717     public double getMinDrainedPower() {
718         return mMinDrainedPower;
719     }
720 
getMaxDrainedPower()721     public double getMaxDrainedPower() {
722         return mMaxDrainedPower;
723     }
724 
readFully(FileInputStream stream)725     public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
726         return readFully(stream, stream.available());
727     }
728 
readFully(FileInputStream stream, int avail)729     public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
730         int pos = 0;
731         byte[] data = new byte[avail];
732         while (true) {
733             int amt = stream.read(data, pos, data.length-pos);
734             //Log.i("foo", "Read " + amt + " bytes at " + pos
735             //        + " of avail " + data.length);
736             if (amt <= 0) {
737                 //Log.i("foo", "**** FINISHED READING: pos=" + pos
738                 //        + " len=" + data.length);
739                 return data;
740             }
741             pos += amt;
742             avail = stream.available();
743             if (avail > data.length-pos) {
744                 byte[] newData = new byte[pos+avail];
745                 System.arraycopy(data, 0, newData, 0, pos);
746                 data = newData;
747             }
748         }
749     }
750 
load()751     private void load() {
752         if (mBatteryInfo == null) {
753             return;
754         }
755         mStats = getStats(mBatteryInfo);
756         if (mCollectBatteryBroadcast) {
757             mBatteryBroadcast = mContext.registerReceiver(null,
758                     new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
759         }
760     }
761 
getStats(IBatteryStats service)762     private static BatteryStatsImpl getStats(IBatteryStats service) {
763         try {
764             ParcelFileDescriptor pfd = service.getStatisticsStream();
765             if (pfd != null) {
766                 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
767                     byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
768                     Parcel parcel = Parcel.obtain();
769                     parcel.unmarshall(data, 0, data.length);
770                     parcel.setDataPosition(0);
771                     BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
772                             .createFromParcel(parcel);
773                     return stats;
774                 } catch (IOException e) {
775                     Log.w(TAG, "Unable to read statistics stream", e);
776                 }
777             }
778         } catch (RemoteException e) {
779             Log.w(TAG, "RemoteException:", e);
780         }
781         return new BatteryStatsImpl();
782     }
783 }
784