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