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