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