[go: nahoru, domu]

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;
50import 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 */
58public 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
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
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
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
153    public BatteryStatsHelper(Context context) {
154        this(context, true);
155    }
156
157    public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
158        this(context, collectBatteryBroadcast, checkWifiOnly(context));
159    }
160
161    public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
162        mContext = context;
163        mCollectBatteryBroadcast = collectBatteryBroadcast;
164        mWifiOnly = wifiOnly;
165    }
166
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
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
221    public static void dropFile(Context context, String fname) {
222        makeFilePath(context, fname).delete();
223    }
224
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. */
230    public void clearStats() {
231        mStats = null;
232    }
233
234    public BatteryStats getStats() {
235        if (mStats == null) {
236            load();
237        }
238        return mStats;
239    }
240
241    public Intent getBatteryBroadcast() {
242        if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
243            load();
244        }
245        return mBatteryBroadcast;
246    }
247
248    public PowerProfile getPowerProfile() {
249        return mPowerProfile;
250    }
251
252    public void create(BatteryStats stats) {
253        mPowerProfile = new PowerProfile(mContext);
254        mStats = stats;
255    }
256
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
267    public void storeState() {
268        sStatsXfer = mStats;
269        sBatteryBroadcastXfer = mBatteryBroadcast;
270    }
271
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     */
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     */
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     */
315    public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
316        refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
317                SystemClock.uptimeMillis() * 1000);
318    }
319
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
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
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     */
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
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
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     */
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     */
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     */
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
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
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
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
697    public List<BatterySipper> getUsageList() {
698        return mUsageList;
699    }
700
701    public List<BatterySipper> getMobilemsppList() {
702        return mMobilemsppList;
703    }
704
705    public long getStatsPeriod() { return mStatsPeriod; }
706
707    public int getStatsType() { return mStatsType; }
708
709    public double getMaxPower() { return mMaxPower; }
710
711    public double getMaxRealPower() { return mMaxRealPower; }
712
713    public double getTotalPower() { return mTotalPower; }
714
715    public double getComputedPower() { return mComputedPower; }
716
717    public double getMinDrainedPower() {
718        return mMinDrainedPower;
719    }
720
721    public double getMaxDrainedPower() {
722        return mMaxDrainedPower;
723    }
724
725    public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
726        return readFully(stream, stream.available());
727    }
728
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
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
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