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 
BatteryStatsHelper(Context context)120     public BatteryStatsHelper(Context context) {
121         this(context, true);
122     }
123 
BatteryStatsHelper(Context context, boolean collectBatteryBroadcast)124     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
125         mContext = context;
126         mCollectBatteryBroadcast = collectBatteryBroadcast;
127         mWifiOnly = checkWifiOnly(context);
128     }
129 
BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly)130     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
131         mContext = context;
132         mCollectBatteryBroadcast = collectBatteryBroadcast;
133         mWifiOnly = wifiOnly;
134     }
135 
checkWifiOnly(Context context)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 
storeStatsHistoryInFile(String fname)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 
statsFromFile(Context context, String fname)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 
dropFile(Context context, String fname)196     public static void dropFile(Context context, String fname) {
197         makeFilePath(context, fname).delete();
198     }
199 
makeFilePath(Context context, String fname)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. */
clearStats()205     public void clearStats() {
206         mStats = null;
207     }
208 
getStats()209     public BatteryStats getStats() {
210         if (mStats == null) {
211             load();
212         }
213         return mStats;
214     }
215 
getBatteryBroadcast()216     public Intent getBatteryBroadcast() {
217         if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
218             load();
219         }
220         return mBatteryBroadcast;
221     }
222 
getPowerProfile()223     public PowerProfile getPowerProfile() {
224         return mPowerProfile;
225     }
226 
create(BatteryStats stats)227     public void create(BatteryStats stats) {
228         mPowerProfile = new PowerProfile(mContext);
229         mStats = stats;
230     }
231 
create(Bundle icicle)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 
storeState()242     public void storeState() {
243         sStatsXfer = mStats;
244         sBatteryBroadcastXfer = mBatteryBroadcast;
245     }
246 
makemAh(double power)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      */
refreshStats(int statsType, int asUser)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      */
refreshStats(int statsType, List<UserHandle> asUsers)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      */
refreshStats(int statsType, SparseArray<UserHandle> asUsers)284     public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
285         refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
286                 SystemClock.uptimeMillis() * 1000);
287     }
288 
refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs, long rawUptimeUs)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 
processAppUsage(SparseArray<UserHandle> asUsers)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 
addPhoneUsage()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 
addScreenUsage()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 
addRadioUsage()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 
aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag)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 
addWiFiUsage()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 
addIdleUsage()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 
addBluetoothUsage()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 
addFlashlightUsage()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 
addUserUsage()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      */
getMobilePowerPerPacket()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      */
getMobilePowerPerMs()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      */
getWifiPowerPerPacket()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 
processMiscUsage()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 
addEntry(DrainType drainType, long time, double power)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 
addEntryNoTotal(DrainType drainType, long time, double power)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 
getUsageList()906     public List<BatterySipper> getUsageList() {
907         return mUsageList;
908     }
909 
getMobilemsppList()910     public List<BatterySipper> getMobilemsppList() {
911         return mMobilemsppList;
912     }
913 
getStatsPeriod()914     public long getStatsPeriod() { return mStatsPeriod; }
915 
getStatsType()916     public int getStatsType() { return mStatsType; };
917 
getMaxPower()918     public double getMaxPower() { return mMaxPower; }
919 
getMaxRealPower()920     public double getMaxRealPower() { return mMaxRealPower; }
921 
getTotalPower()922     public double getTotalPower() { return mTotalPower; }
923 
getComputedPower()924     public double getComputedPower() { return mComputedPower; }
925 
getMinDrainedPower()926     public double getMinDrainedPower() {
927         return mMinDrainedPower;
928     }
929 
getMaxDrainedPower()930     public double getMaxDrainedPower() {
931         return mMaxDrainedPower;
932     }
933 
getBatteryTimeRemaining()934     public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; }
935 
getChargeTimeRemaining()936     public long getChargeTimeRemaining() { return mChargeTimeRemaining; }
937 
readFully(FileInputStream stream)938     public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
939         return readFully(stream, stream.available());
940     }
941 
readFully(FileInputStream stream, int avail)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 
load()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 
getStats(IBatteryStats service)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