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 static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA;
     20 import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA;
     21 import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA;
     22 import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA;
     23 
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.hardware.Sensor;
     28 import android.hardware.SensorManager;
     29 import android.net.ConnectivityManager;
     30 import android.os.BatteryStats;
     31 import android.os.BatteryStats.Uid;
     32 import android.os.Bundle;
     33 import android.os.MemoryFile;
     34 import android.os.Parcel;
     35 import android.os.ParcelFileDescriptor;
     36 import android.os.Process;
     37 import android.os.RemoteException;
     38 import android.os.ServiceManager;
     39 import android.os.SystemClock;
     40 import android.os.UserHandle;
     41 import android.telephony.SignalStrength;
     42 import android.util.ArrayMap;
     43 import android.util.Log;
     44 import android.util.SparseArray;
     45 
     46 import com.android.internal.app.IBatteryStats;
     47 import com.android.internal.os.BatterySipper.DrainType;
     48 
     49 import java.io.File;
     50 import java.io.FileInputStream;
     51 import java.io.FileOutputStream;
     52 import java.io.IOException;
     53 import java.util.ArrayList;
     54 import java.util.Collections;
     55 import java.util.Comparator;
     56 import java.util.List;
     57 import java.util.Map;
     58 
     59 /**
     60  * A helper class for retrieving the power usage information for all applications and services.
     61  *
     62  * The caller must initialize this class as soon as activity object is ready to use (for example, in
     63  * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
     64  */
     65 public final class BatteryStatsHelper {
     66 
     67     private static final boolean DEBUG = false;
     68 
     69     private static final String TAG = BatteryStatsHelper.class.getSimpleName();
     70 
     71     private static BatteryStats sStatsXfer;
     72     private static Intent sBatteryBroadcastXfer;
     73     private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>();
     74 
     75     final private Context mContext;
     76     final private boolean mCollectBatteryBroadcast;
     77     final private boolean mWifiOnly;
     78 
     79     private IBatteryStats mBatteryInfo;
     80     private BatteryStats mStats;
     81     private Intent mBatteryBroadcast;
     82     private PowerProfile mPowerProfile;
     83 
     84     private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
     85     private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>();
     86     private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>();
     87     private final SparseArray<List<BatterySipper>> mUserSippers
     88             = new SparseArray<List<BatterySipper>>();
     89     private final SparseArray<Double> mUserPower = new SparseArray<Double>();
     90 
     91     private final List<BatterySipper> mMobilemsppList = new ArrayList<BatterySipper>();
     92 
     93     private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
     94 
     95     long mRawRealtime;
     96     long mRawUptime;
     97     long mBatteryRealtime;
     98     long mBatteryUptime;
     99     long mTypeBatteryRealtime;
    100     long mTypeBatteryUptime;
    101     long mBatteryTimeRemaining;
    102     long mChargeTimeRemaining;
    103 
    104     private long mStatsPeriod = 0;
    105     private double mMaxPower = 1;
    106     private double mMaxRealPower = 1;
    107     private double mComputedPower;
    108     private double mTotalPower;
    109     private double mWifiPower;
    110     private double mBluetoothPower;
    111     private double mMinDrainedPower;
    112     private double mMaxDrainedPower;
    113 
    114     // How much the apps together have kept the mobile radio active.
    115     private long mAppMobileActive;
    116 
    117     // How much the apps together have left WIFI running.
    118     private long mAppWifiRunning;
    119 
    120     public BatteryStatsHelper(Context context) {
    121         this(context, true);
    122     }
    123 
    124     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
    125         mContext = context;
    126         mCollectBatteryBroadcast = collectBatteryBroadcast;
    127         mWifiOnly = checkWifiOnly(context);
    128     }
    129 
    130     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
    131         mContext = context;
    132         mCollectBatteryBroadcast = collectBatteryBroadcast;
    133         mWifiOnly = wifiOnly;
    134     }
    135 
    136     public static boolean checkWifiOnly(Context context) {
    137         ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
    138                 Context.CONNECTIVITY_SERVICE);
    139         return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
    140     }
    141 
    142     public void storeStatsHistoryInFile(String fname) {
    143         synchronized (sFileXfer) {
    144             File path = makeFilePath(mContext, fname);
    145             sFileXfer.put(path, this.getStats());
    146             FileOutputStream fout = null;
    147             try {
    148                 fout = new FileOutputStream(path);
    149                 Parcel hist = Parcel.obtain();
    150                 getStats().writeToParcelWithoutUids(hist, 0);
    151                 byte[] histData = hist.marshall();
    152                 fout.write(histData);
    153             } catch (IOException e) {
    154                 Log.w(TAG, "Unable to write history to file", e);
    155             } finally {
    156                 if (fout != null) {
    157                     try {
    158                         fout.close();
    159                     } catch (IOException e) {
    160                     }
    161                 }
    162             }
    163         }
    164     }
    165 
    166     public static BatteryStats statsFromFile(Context context, String fname) {
    167         synchronized (sFileXfer) {
    168             File path = makeFilePath(context, fname);
    169             BatteryStats stats = sFileXfer.get(path);
    170             if (stats != null) {
    171                 return stats;
    172             }
    173             FileInputStream fin = null;
    174             try {
    175                 fin = new FileInputStream(path);
    176                 byte[] data = readFully(fin);
    177                 Parcel parcel = Parcel.obtain();
    178                 parcel.unmarshall(data, 0, data.length);
    179                 parcel.setDataPosition(0);
    180                 return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
    181             } catch (IOException e) {
    182                 Log.w(TAG, "Unable to read history to file", e);
    183             } finally {
    184                 if (fin != null) {
    185                     try {
    186                         fin.close();
    187                     } catch (IOException e) {
    188                     }
    189                 }
    190             }
    191         }
    192         return getStats(IBatteryStats.Stub.asInterface(
    193                         ServiceManager.getService(BatteryStats.SERVICE_NAME)));
    194     }
    195 
    196     public static void dropFile(Context context, String fname) {
    197         makeFilePath(context, fname).delete();
    198     }
    199 
    200     private static File makeFilePath(Context context, String fname) {
    201         return new File(context.getFilesDir(), fname);
    202     }
    203 
    204     /** Clears the current stats and forces recreating for future use. */
    205     public void clearStats() {
    206         mStats = null;
    207     }
    208 
    209     public BatteryStats getStats() {
    210         if (mStats == null) {
    211             load();
    212         }
    213         return mStats;
    214     }
    215 
    216     public Intent getBatteryBroadcast() {
    217         if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
    218             load();
    219         }
    220         return mBatteryBroadcast;
    221     }
    222 
    223     public PowerProfile getPowerProfile() {
    224         return mPowerProfile;
    225     }
    226 
    227     public void create(BatteryStats stats) {
    228         mPowerProfile = new PowerProfile(mContext);
    229         mStats = stats;
    230     }
    231 
    232     public void create(Bundle icicle) {
    233         if (icicle != null) {
    234             mStats = sStatsXfer;
    235             mBatteryBroadcast = sBatteryBroadcastXfer;
    236         }
    237         mBatteryInfo = IBatteryStats.Stub.asInterface(
    238                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
    239         mPowerProfile = new PowerProfile(mContext);
    240     }
    241 
    242     public void storeState() {
    243         sStatsXfer = mStats;
    244         sBatteryBroadcastXfer = mBatteryBroadcast;
    245     }
    246 
    247     public static String makemAh(double power) {
    248         if (power < .00001) return String.format("%.8f", power);
    249         else if (power < .0001) return String.format("%.7f", power);
    250         else if (power < .001) return String.format("%.6f", power);
    251         else if (power < .01) return String.format("%.5f", power);
    252         else if (power < .1) return String.format("%.4f", power);
    253         else if (power < 1) return String.format("%.3f", power);
    254         else if (power < 10) return String.format("%.2f", power);
    255         else if (power < 100) return String.format("%.1f", power);
    256         else return String.format("%.0f", power);
    257     }
    258 
    259     /**
    260      * Refreshes the power usage list.
    261      */
    262     public void refreshStats(int statsType, int asUser) {
    263         SparseArray<UserHandle> users = new SparseArray<UserHandle>(1);
    264         users.put(asUser, new UserHandle(asUser));
    265         refreshStats(statsType, users);
    266     }
    267 
    268     /**
    269      * Refreshes the power usage list.
    270      */
    271     public void refreshStats(int statsType, List<UserHandle> asUsers) {
    272         final int n = asUsers.size();
    273         SparseArray<UserHandle> users = new SparseArray<UserHandle>(n);
    274         for (int i = 0; i < n; ++i) {
    275             UserHandle userHandle = asUsers.get(i);
    276             users.put(userHandle.getIdentifier(), userHandle);
    277         }
    278         refreshStats(statsType, users);
    279     }
    280 
    281     /**
    282      * Refreshes the power usage list.
    283      */
    284     public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
    285         refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
    286                 SystemClock.uptimeMillis() * 1000);
    287     }
    288 
    289     public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
    290             long rawUptimeUs) {
    291         // Initialize mStats if necessary.
    292         getStats();
    293 
    294         mMaxPower = 0;
    295         mMaxRealPower = 0;
    296         mComputedPower = 0;
    297         mTotalPower = 0;
    298         mWifiPower = 0;
    299         mBluetoothPower = 0;
    300         mAppMobileActive = 0;
    301         mAppWifiRunning = 0;
    302 
    303         mUsageList.clear();
    304         mWifiSippers.clear();
    305         mBluetoothSippers.clear();
    306         mUserSippers.clear();
    307         mUserPower.clear();
    308         mMobilemsppList.clear();
    309 
    310         if (mStats == null) {
    311             return;
    312         }
    313 
    314         mStatsType = statsType;
    315         mRawUptime = rawUptimeUs;
    316         mRawRealtime = rawRealtimeUs;
    317         mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs);
    318         mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs);
    319         mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
    320         mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
    321         mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
    322         mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);
    323 
    324         if (DEBUG) {
    325             Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime="
    326                     + (rawUptimeUs/1000));
    327             Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime="
    328                     + (mBatteryUptime/1000));
    329             Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime="
    330                     + (mTypeBatteryUptime/1000));
    331         }
    332         mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
    333                 * mPowerProfile.getBatteryCapacity()) / 100;
    334         mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
    335                 * mPowerProfile.getBatteryCapacity()) / 100;
    336 
    337         processAppUsage(asUsers);
    338 
    339         // Before aggregating apps in to users, collect all apps to sort by their ms per packet.
    340         for (int i=0; i<mUsageList.size(); i++) {
    341             BatterySipper bs = mUsageList.get(i);
    342             bs.computeMobilemspp();
    343             if (bs.mobilemspp != 0) {
    344                 mMobilemsppList.add(bs);
    345             }
    346         }
    347         for (int i=0; i<mUserSippers.size(); i++) {
    348             List<BatterySipper> user = mUserSippers.valueAt(i);
    349             for (int j=0; j<user.size(); j++) {
    350                 BatterySipper bs = user.get(j);
    351                 bs.computeMobilemspp();
    352                 if (bs.mobilemspp != 0) {
    353                     mMobilemsppList.add(bs);
    354                 }
    355             }
    356         }
    357         Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
    358             @Override
    359             public int compare(BatterySipper lhs, BatterySipper rhs) {
    360                 if (lhs.mobilemspp < rhs.mobilemspp) {
    361                     return 1;
    362                 } else if (lhs.mobilemspp > rhs.mobilemspp) {
    363                     return -1;
    364                 }
    365                 return 0;
    366             }
    367         });
    368 
    369         processMiscUsage();
    370 
    371         if (DEBUG) {
    372             Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
    373                     + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
    374         }
    375         mTotalPower = mComputedPower;
    376         if (mStats.getLowDischargeAmountSinceCharge() > 1) {
    377             if (mMinDrainedPower > mComputedPower) {
    378                 double amount = mMinDrainedPower - mComputedPower;
    379                 mTotalPower = mMinDrainedPower;
    380                 addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);
    381             } else if (mMaxDrainedPower < mComputedPower) {
    382                 double amount = mComputedPower - mMaxDrainedPower;
    383                 addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);
    384             }
    385         }
    386 
    387         Collections.sort(mUsageList);
    388     }
    389 
    390     private void processAppUsage(SparseArray<UserHandle> asUsers) {
    391         final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
    392         SensorManager sensorManager = (SensorManager) mContext.getSystemService(
    393                 Context.SENSOR_SERVICE);
    394         final int which = mStatsType;
    395         final int speedSteps = mPowerProfile.getNumSpeedSteps();
    396         final double[] powerCpuNormal = new double[speedSteps];
    397         final long[] cpuSpeedStepTimes = new long[speedSteps];
    398         for (int p = 0; p < speedSteps; p++) {
    399             powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
    400         }
    401         final double mobilePowerPerPacket = getMobilePowerPerPacket();
    402         final double mobilePowerPerMs = getMobilePowerPerMs();
    403         final double wifiPowerPerPacket = getWifiPowerPerPacket();
    404         long appWakelockTimeUs = 0;
    405         BatterySipper osApp = null;
    406         mStatsPeriod = mTypeBatteryRealtime;
    407         SparseArray<? extends Uid> uidStats = mStats.getUidStats();
    408         final int NU = uidStats.size();
    409         for (int iu = 0; iu < NU; iu++) {
    410             Uid u = uidStats.valueAt(iu);
    411             double p; // in mAs
    412             double power = 0; // in mAs
    413             double highestDrain = 0;
    414             String packageWithHighestDrain = null;
    415             Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
    416             long cpuTime = 0;
    417             long cpuFgTime = 0;
    418             long wakelockTime = 0;
    419             long gpsTime = 0;
    420             if (processStats.size() > 0) {
    421                 // Process CPU time
    422                 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
    423                         : processStats.entrySet()) {
    424                     Uid.Proc ps = ent.getValue();
    425                     final long userTime = ps.getUserTime(which);
    426                     final long systemTime = ps.getSystemTime(which);
    427                     final long foregroundTime = ps.getForegroundTime(which);
    428                     cpuFgTime += foregroundTime * 10; // convert to millis
    429                     final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis
    430                     int totalTimeAtSpeeds = 0;
    431                     // Get the total first
    432                     for (int step = 0; step < speedSteps; step++) {
    433                         cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which);
    434                         totalTimeAtSpeeds += cpuSpeedStepTimes[step];
    435                     }
    436                     if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1;
    437                     // Then compute the ratio of time spent at each speed
    438                     double processPower = 0;
    439                     for (int step = 0; step < speedSteps; step++) {
    440                         double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds;
    441                         if (DEBUG && ratio != 0) Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
    442                                 + step + " ratio=" + makemAh(ratio) + " power="
    443                                 + makemAh(ratio*tmpCpuTime*powerCpuNormal[step] / (60*60*1000)));
    444                         processPower += ratio * tmpCpuTime * powerCpuNormal[step];
    445                     }
    446                     cpuTime += tmpCpuTime;
    447                     if (DEBUG && processPower != 0) {
    448                         Log.d(TAG, String.format("process %s, cpu power=%s",
    449                                 ent.getKey(), makemAh(processPower / (60*60*1000))));
    450                     }
    451                     power += processPower;
    452                     if (packageWithHighestDrain == null
    453                             || packageWithHighestDrain.startsWith("*")) {
    454                         highestDrain = processPower;
    455                         packageWithHighestDrain = ent.getKey();
    456                     } else if (highestDrain < processPower
    457                             && !ent.getKey().startsWith("*")) {
    458                         highestDrain = processPower;
    459                         packageWithHighestDrain = ent.getKey();
    460                     }
    461                 }
    462             }
    463             if (cpuFgTime > cpuTime) {
    464                 if (DEBUG && cpuFgTime > cpuTime + 10000) {
    465                     Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
    466                 }
    467                 cpuTime = cpuFgTime; // Statistics may not have been gathered yet.
    468             }
    469             power /= (60*60*1000);
    470 
    471             // Process wake lock usage
    472             Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats();
    473             for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry
    474                     : wakelockStats.entrySet()) {
    475                 Uid.Wakelock wakelock = wakelockEntry.getValue();
    476                 // Only care about partial wake locks since full wake locks
    477                 // are canceled when the user turns the screen off.
    478                 BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
    479                 if (timer != null) {
    480                     wakelockTime += timer.getTotalTimeLocked(mRawRealtime, which);
    481                 }
    482             }
    483             appWakelockTimeUs += wakelockTime;
    484             wakelockTime /= 1000; // convert to millis
    485 
    486             // Add cost of holding a wake lock
    487             p = (wakelockTime
    488                     * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60*60*1000);
    489             if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wake "
    490                     + wakelockTime + " power=" + makemAh(p));
    491             power += p;
    492 
    493             // Add cost of mobile traffic
    494             final long mobileRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
    495             final long mobileTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
    496             final long mobileRxB = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType);
    497             final long mobileTxB = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType);
    498             final long mobileActive = u.getMobileRadioActiveTime(mStatsType);
    499             if (mobileActive > 0) {
    500                 // We are tracking when the radio is up, so can use the active time to
    501                 // determine power use.
    502                 mAppMobileActive += mobileActive;
    503                 p = (mobilePowerPerMs * mobileActive) / 1000;
    504             } else {
    505                 // We are not tracking when the radio is up, so must approximate power use
    506                 // based on the number of packets.
    507                 p = (mobileRx + mobileTx) * mobilePowerPerPacket;
    508             }
    509             if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
    510                     + (mobileRx+mobileTx) + " active time " + mobileActive
    511                     + " power=" + makemAh(p));
    512             power += p;
    513 
    514             // Add cost of wifi traffic
    515             final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType);
    516             final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType);
    517             final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType);
    518             final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType);
    519             p = (wifiRx + wifiTx) * wifiPowerPerPacket;
    520             if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi packets "
    521                     + (mobileRx+mobileTx) + " power=" + makemAh(p));
    522             power += p;
    523 
    524             // Add cost of keeping WIFI running.
    525             long wifiRunningTimeMs = u.getWifiRunningTime(mRawRealtime, which) / 1000;
    526             mAppWifiRunning += wifiRunningTimeMs;
    527             p = (wifiRunningTimeMs
    528                     * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000);
    529             if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi running "
    530                     + wifiRunningTimeMs + " power=" + makemAh(p));
    531             power += p;
    532 
    533             // Add cost of WIFI scans
    534             long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000;
    535             p = (wifiScanTimeMs
    536                     * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / (60*60*1000);
    537             if (DEBUG) Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs
    538                     + " power=" + makemAh(p));
    539             power += p;
    540             for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
    541                 long batchScanTimeMs = u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000;
    542                 p = ((batchScanTimeMs
    543                         * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin))
    544                     ) / (60*60*1000);
    545                 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin
    546                         + " time=" + batchScanTimeMs + " power=" + makemAh(p));
    547                 power += p;
    548             }
    549 
    550             // Process Sensor usage
    551             SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
    552             int NSE = sensorStats.size();
    553             for (int ise=0; ise<NSE; ise++) {
    554                 Uid.Sensor sensor = sensorStats.valueAt(ise);
    555                 int sensorHandle = sensorStats.keyAt(ise);
    556                 BatteryStats.Timer timer = sensor.getSensorTime();
    557                 long sensorTime = timer.getTotalTimeLocked(mRawRealtime, which) / 1000;
    558                 double multiplier = 0;
    559                 switch (sensorHandle) {
    560                     case Uid.Sensor.GPS:
    561                         multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON);
    562                         gpsTime = sensorTime;
    563                         break;
    564                     default:
    565                         List<Sensor> sensorList = sensorManager.getSensorList(
    566                                 android.hardware.Sensor.TYPE_ALL);
    567                         for (android.hardware.Sensor s : sensorList) {
    568                             if (s.getHandle() == sensorHandle) {
    569                                 multiplier = s.getPower();
    570                                 break;
    571                             }
    572                         }
    573                 }
    574                 p = (multiplier * sensorTime) / (60*60*1000);
    575                 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle
    576                         + " time=" + sensorTime + " power=" + makemAh(p));
    577                 power += p;
    578             }
    579 
    580             if (DEBUG && power != 0) Log.d(TAG, String.format("UID %d: total power=%s",
    581                     u.getUid(), makemAh(power)));
    582 
    583             // Add the app to the list if it is consuming power
    584             final int userId = UserHandle.getUserId(u.getUid());
    585             if (power != 0 || u.getUid() == 0) {
    586                 BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u,
    587                         new double[] {power});
    588                 app.cpuTime = cpuTime;
    589                 app.gpsTime = gpsTime;
    590                 app.wifiRunningTime = wifiRunningTimeMs;
    591                 app.cpuFgTime = cpuFgTime;
    592                 app.wakeLockTime = wakelockTime;
    593                 app.mobileRxPackets = mobileRx;
    594                 app.mobileTxPackets = mobileTx;
    595                 app.mobileActive = mobileActive / 1000;
    596                 app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType);
    597                 app.wifiRxPackets = wifiRx;
    598                 app.wifiTxPackets = wifiTx;
    599                 app.mobileRxBytes = mobileRxB;
    600                 app.mobileTxBytes = mobileTxB;
    601                 app.wifiRxBytes = wifiRxB;
    602                 app.wifiTxBytes = wifiTxB;
    603                 app.packageWithHighestDrain = packageWithHighestDrain;
    604                 if (u.getUid() == Process.WIFI_UID) {
    605                     mWifiSippers.add(app);
    606                     mWifiPower += power;
    607                 } else if (u.getUid() == Process.BLUETOOTH_UID) {
    608                     mBluetoothSippers.add(app);
    609                     mBluetoothPower += power;
    610                 } else if (!forAllUsers && asUsers.get(userId) == null
    611                         && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) {
    612                     List<BatterySipper> list = mUserSippers.get(userId);
    613                     if (list == null) {
    614                         list = new ArrayList<BatterySipper>();
    615                         mUserSippers.put(userId, list);
    616                     }
    617                     list.add(app);
    618                     if (power != 0) {
    619                         Double userPower = mUserPower.get(userId);
    620                         if (userPower == null) {
    621                             userPower = power;
    622                         } else {
    623                             userPower += power;
    624                         }
    625                         mUserPower.put(userId, userPower);
    626                     }
    627                 } else {
    628                     mUsageList.add(app);
    629                     if (power > mMaxPower) mMaxPower = power;
    630                     if (power > mMaxRealPower) mMaxRealPower = power;
    631                     mComputedPower += power;
    632                 }
    633                 if (u.getUid() == 0) {
    634                     osApp = app;
    635                 }
    636             }
    637         }
    638 
    639         // The device has probably been awake for longer than the screen on
    640         // time and application wake lock time would account for.  Assign
    641         // this remainder to the OS, if possible.
    642         if (osApp != null) {
    643             long wakeTimeMillis = mBatteryUptime / 1000;
    644             wakeTimeMillis -= (appWakelockTimeUs / 1000)
    645                     + (mStats.getScreenOnTime(mRawRealtime, which) / 1000);
    646             if (wakeTimeMillis > 0) {
    647                 double power = (wakeTimeMillis
    648                         * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE))
    649                         /  (60*60*1000);
    650                 if (DEBUG) Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
    651                         + makemAh(power));
    652                 osApp.wakeLockTime += wakeTimeMillis;
    653                 osApp.value += power;
    654                 osApp.values[0] += power;
    655                 if (osApp.value > mMaxPower) mMaxPower = osApp.value;
    656                 if (osApp.value > mMaxRealPower) mMaxRealPower = osApp.value;
    657                 mComputedPower += power;
    658             }
    659         }
    660     }
    661 
    662     private void addPhoneUsage() {
    663         long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000;
    664         double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
    665                 * phoneOnTimeMs / (60*60*1000);
    666         if (phoneOnPower != 0) {
    667             BatterySipper bs = addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
    668         }
    669     }
    670 
    671     private void addScreenUsage() {
    672         double power = 0;
    673         long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;
    674         power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
    675         final double screenFullPower =
    676                 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
    677         for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
    678             double screenBinPower = screenFullPower * (i + 0.5f)
    679                     / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
    680             long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType)
    681                     / 1000;
    682             double p = screenBinPower*brightnessTime;
    683             if (DEBUG && p != 0) {
    684                 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
    685                         + " power=" + makemAh(p / (60 * 60 * 1000)));
    686             }
    687             power += p;
    688         }
    689         power /= (60*60*1000); // To hours
    690         if (power != 0) {
    691             addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
    692         }
    693     }
    694 
    695     private void addRadioUsage() {
    696         double power = 0;
    697         final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
    698         long signalTimeMs = 0;
    699         long noCoverageTimeMs = 0;
    700         for (int i = 0; i < BINS; i++) {
    701             long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, mRawRealtime, mStatsType)
    702                     / 1000;
    703             double p = (strengthTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i))
    704                         / (60*60*1000);
    705             if (DEBUG && p != 0) {
    706                 Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
    707                         + makemAh(p));
    708             }
    709             power += p;
    710             signalTimeMs += strengthTimeMs;
    711             if (i == 0) {
    712                 noCoverageTimeMs = strengthTimeMs;
    713             }
    714         }
    715         long scanningTimeMs = mStats.getPhoneSignalScanningTime(mRawRealtime, mStatsType)
    716                 / 1000;
    717         double p = (scanningTimeMs * mPowerProfile.getAveragePower(
    718                         PowerProfile.POWER_RADIO_SCANNING))
    719                         / (60*60*1000);
    720         if (DEBUG && p != 0) {
    721             Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + makemAh(p));
    722         }
    723         power += p;
    724         long radioActiveTimeUs = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType);
    725         long remainingActiveTime = (radioActiveTimeUs - mAppMobileActive) / 1000;
    726         if (remainingActiveTime > 0) {
    727             power += getMobilePowerPerMs() * remainingActiveTime;
    728         }
    729         if (power != 0) {
    730             BatterySipper bs =
    731                     addEntry(BatterySipper.DrainType.CELL, signalTimeMs, power);
    732             if (signalTimeMs != 0) {
    733                 bs.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
    734             }
    735             bs.mobileActive = remainingActiveTime;
    736             bs.mobileActiveCount = mStats.getMobileRadioActiveUnknownCount(mStatsType);
    737         }
    738     }
    739 
    740     private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
    741         for (int i=0; i<from.size(); i++) {
    742             BatterySipper wbs = from.get(i);
    743             if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime);
    744             bs.cpuTime += wbs.cpuTime;
    745             bs.gpsTime += wbs.gpsTime;
    746             bs.wifiRunningTime += wbs.wifiRunningTime;
    747             bs.cpuFgTime += wbs.cpuFgTime;
    748             bs.wakeLockTime += wbs.wakeLockTime;
    749             bs.mobileRxPackets += wbs.mobileRxPackets;
    750             bs.mobileTxPackets += wbs.mobileTxPackets;
    751             bs.mobileActive += wbs.mobileActive;
    752             bs.mobileActiveCount += wbs.mobileActiveCount;
    753             bs.wifiRxPackets += wbs.wifiRxPackets;
    754             bs.wifiTxPackets += wbs.wifiTxPackets;
    755             bs.mobileRxBytes += wbs.mobileRxBytes;
    756             bs.mobileTxBytes += wbs.mobileTxBytes;
    757             bs.wifiRxBytes += wbs.wifiRxBytes;
    758             bs.wifiTxBytes += wbs.wifiTxBytes;
    759         }
    760         bs.computeMobilemspp();
    761     }
    762 
    763     private void addWiFiUsage() {
    764         long onTimeMs = mStats.getWifiOnTime(mRawRealtime, mStatsType) / 1000;
    765         long runningTimeMs = mStats.getGlobalWifiRunningTime(mRawRealtime, mStatsType) / 1000;
    766         if (DEBUG) Log.d(TAG, "WIFI runningTime=" + runningTimeMs
    767                 + " app runningTime=" + mAppWifiRunning);
    768         runningTimeMs -= mAppWifiRunning;
    769         if (runningTimeMs < 0) runningTimeMs = 0;
    770         double wifiPower = (onTimeMs * 0 /* TODO */
    771                     * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)
    772                 + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON))
    773                 / (60*60*1000);
    774         if (DEBUG && wifiPower != 0) {
    775             Log.d(TAG, "Wifi: time=" + runningTimeMs + " power=" + makemAh(wifiPower));
    776         }
    777         if ((wifiPower+mWifiPower) != 0) {
    778             BatterySipper bs = addEntry(BatterySipper.DrainType.WIFI, runningTimeMs,
    779                     wifiPower + mWifiPower);
    780             aggregateSippers(bs, mWifiSippers, "WIFI");
    781         }
    782     }
    783 
    784     private void addIdleUsage() {
    785         long idleTimeMs = (mTypeBatteryRealtime
    786                 - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;
    787         double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))
    788                 / (60*60*1000);
    789         if (DEBUG && idlePower != 0) {
    790             Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower));
    791         }
    792         if (idlePower != 0) {
    793             addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);
    794         }
    795     }
    796 
    797     private void addBluetoothUsage() {
    798         long btOnTimeMs = mStats.getBluetoothOnTime(mRawRealtime, mStatsType) / 1000;
    799         double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON)
    800                 / (60*60*1000);
    801         if (DEBUG && btPower != 0) {
    802             Log.d(TAG, "Bluetooth: time=" + btOnTimeMs + " power=" + makemAh(btPower));
    803         }
    804         int btPingCount = mStats.getBluetoothPingCount();
    805         double pingPower = (btPingCount
    806                 * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD))
    807                 / (60*60*1000);
    808         if (DEBUG && pingPower != 0) {
    809             Log.d(TAG, "Bluetooth ping: count=" + btPingCount + " power=" + makemAh(pingPower));
    810         }
    811         btPower += pingPower;
    812         if ((btPower+mBluetoothPower) != 0) {
    813             BatterySipper bs = addEntry(BatterySipper.DrainType.BLUETOOTH, btOnTimeMs,
    814                     btPower + mBluetoothPower);
    815             aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
    816         }
    817     }
    818 
    819     private void addFlashlightUsage() {
    820         long flashlightOnTimeMs = mStats.getFlashlightOnTime(mRawRealtime, mStatsType) / 1000;
    821         double flashlightPower = flashlightOnTimeMs
    822                 * mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT) / (60*60*1000);
    823         if (flashlightPower != 0) {
    824             addEntry(BatterySipper.DrainType.FLASHLIGHT, flashlightOnTimeMs, flashlightPower);
    825         }
    826     }
    827 
    828     private void addUserUsage() {
    829         for (int i=0; i<mUserSippers.size(); i++) {
    830             final int userId = mUserSippers.keyAt(i);
    831             final List<BatterySipper> sippers = mUserSippers.valueAt(i);
    832             Double userPower = mUserPower.get(userId);
    833             double power = (userPower != null) ? userPower : 0.0;
    834             BatterySipper bs = addEntry(BatterySipper.DrainType.USER, 0, power);
    835             bs.userId = userId;
    836             aggregateSippers(bs, sippers, "User");
    837         }
    838     }
    839 
    840     /**
    841      * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
    842      */
    843     private double getMobilePowerPerPacket() {
    844         final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
    845         final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
    846                 / 3600;
    847 
    848         final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
    849         final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
    850         final long mobileData = mobileRx + mobileTx;
    851 
    852         final long radioDataUptimeMs
    853                 = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType) / 1000;
    854         final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
    855                 ? (mobileData / (double)radioDataUptimeMs)
    856                 : (((double)MOBILE_BPS) / 8 / 2048);
    857 
    858         return (MOBILE_POWER / mobilePps) / (60*60);
    859     }
    860 
    861     /**
    862      * Return estimated power (in mAs) of keeping the radio up
    863      */
    864     private double getMobilePowerPerMs() {
    865         return mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) / (60*60*1000);
    866     }
    867 
    868     /**
    869      * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
    870      */
    871     private double getWifiPowerPerPacket() {
    872         final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
    873         final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
    874                 / 3600;
    875         return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
    876     }
    877 
    878     private void processMiscUsage() {
    879         addUserUsage();
    880         addPhoneUsage();
    881         addScreenUsage();
    882         addFlashlightUsage();
    883         addWiFiUsage();
    884         addBluetoothUsage();
    885         addIdleUsage(); // Not including cellular idle power
    886         // Don't compute radio usage if it's a wifi-only device
    887         if (!mWifiOnly) {
    888             addRadioUsage();
    889         }
    890     }
    891 
    892     private BatterySipper addEntry(DrainType drainType, long time, double power) {
    893         mComputedPower += power;
    894         if (power > mMaxRealPower) mMaxRealPower = power;
    895         return addEntryNoTotal(drainType, time, power);
    896     }
    897 
    898     private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) {
    899         if (power > mMaxPower) mMaxPower = power;
    900         BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});
    901         bs.usageTime = time;
    902         mUsageList.add(bs);
    903         return bs;
    904     }
    905 
    906     public List<BatterySipper> getUsageList() {
    907         return mUsageList;
    908     }
    909 
    910     public List<BatterySipper> getMobilemsppList() {
    911         return mMobilemsppList;
    912     }
    913 
    914     public long getStatsPeriod() { return mStatsPeriod; }
    915 
    916     public int getStatsType() { return mStatsType; };
    917 
    918     public double getMaxPower() { return mMaxPower; }
    919 
    920     public double getMaxRealPower() { return mMaxRealPower; }
    921 
    922     public double getTotalPower() { return mTotalPower; }
    923 
    924     public double getComputedPower() { return mComputedPower; }
    925 
    926     public double getMinDrainedPower() {
    927         return mMinDrainedPower;
    928     }
    929 
    930     public double getMaxDrainedPower() {
    931         return mMaxDrainedPower;
    932     }
    933 
    934     public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; }
    935 
    936     public long getChargeTimeRemaining() { return mChargeTimeRemaining; }
    937 
    938     public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
    939         return readFully(stream, stream.available());
    940     }
    941 
    942     public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
    943         int pos = 0;
    944         byte[] data = new byte[avail];
    945         while (true) {
    946             int amt = stream.read(data, pos, data.length-pos);
    947             //Log.i("foo", "Read " + amt + " bytes at " + pos
    948             //        + " of avail " + data.length);
    949             if (amt <= 0) {
    950                 //Log.i("foo", "**** FINISHED READING: pos=" + pos
    951                 //        + " len=" + data.length);
    952                 return data;
    953             }
    954             pos += amt;
    955             avail = stream.available();
    956             if (avail > data.length-pos) {
    957                 byte[] newData = new byte[pos+avail];
    958                 System.arraycopy(data, 0, newData, 0, pos);
    959                 data = newData;
    960             }
    961         }
    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                 FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
    980                 try {
    981                     byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
    982                     Parcel parcel = Parcel.obtain();
    983                     parcel.unmarshall(data, 0, data.length);
    984                     parcel.setDataPosition(0);
    985                     BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
    986                             .createFromParcel(parcel);
    987                     stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED);
    988                     return stats;
    989                 } catch (IOException e) {
    990                     Log.w(TAG, "Unable to read statistics stream", e);
    991                 }
    992             }
    993         } catch (RemoteException e) {
    994             Log.w(TAG, "RemoteException:", e);
    995         }
    996         return new BatteryStatsImpl();
    997     }
    998 }
    999