blob: c22a5e9d423f686bee743361bef8e199ff57a0ad [file] [log] [blame]
Dianne Hackborna7c837f2014-01-15 16:20:44 -08001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.os;
18
19import static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA;
20import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA;
21import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA;
22import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA;
23
24import android.content.Context;
25import android.hardware.Sensor;
26import android.hardware.SensorManager;
27import android.net.ConnectivityManager;
28import android.os.BatteryStats;
29import android.os.BatteryStats.Uid;
30import android.os.Bundle;
31import android.os.Parcel;
32import android.os.Process;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.os.SystemClock;
36import android.os.UserHandle;
37import android.telephony.SignalStrength;
38import android.util.Log;
39import android.util.SparseArray;
40
41import com.android.internal.app.IBatteryStats;
42import com.android.internal.os.BatterySipper.DrainType;
43
Dianne Hackborna7c837f2014-01-15 16:20:44 -080044import java.util.ArrayList;
45import java.util.Collections;
46import java.util.List;
47import java.util.Map;
48
49/**
50 * A helper class for retrieving the power usage information for all applications and services.
51 *
52 * The caller must initialize this class as soon as activity object is ready to use (for example, in
53 * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
54 */
55public class BatteryStatsHelper {
56
Dianne Hackborneaf2ac42014-02-07 13:01:07 -080057 private static final boolean DEBUG = false;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080058
59 private static final String TAG = BatteryStatsHelper.class.getSimpleName();
60
61 private static BatteryStats sStatsXfer;
62
63 final private Context mContext;
64
65 private IBatteryStats mBatteryInfo;
66 private BatteryStats mStats;
67 private PowerProfile mPowerProfile;
68
69 private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
70 private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>();
71 private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>();
72 private final SparseArray<List<BatterySipper>> mUserSippers
73 = new SparseArray<List<BatterySipper>>();
74 private final SparseArray<Double> mUserPower = new SparseArray<Double>();
75
76 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
77 private int mAsUser = 0;
78
79 long mBatteryRealtime;
80 long mBatteryUptime;
81 long mTypeBatteryRealtime;
82 long mTypeBatteryUptime;
83
84 private long mStatsPeriod = 0;
85 private double mMaxPower = 1;
Dianne Hackborn099bc622014-01-22 13:39:16 -080086 private double mComputedPower;
Dianne Hackborna7c837f2014-01-15 16:20:44 -080087 private double mTotalPower;
88 private double mWifiPower;
89 private double mBluetoothPower;
90 private double mMinDrainedPower;
91 private double mMaxDrainedPower;
92
93 // How much the apps together have left WIFI running.
94 private long mAppWifiRunning;
95
96 public BatteryStatsHelper(Context context) {
97 mContext = context;
98 }
99
100 /** Clears the current stats and forces recreating for future use. */
101 public void clearStats() {
102 mStats = null;
103 }
104
105 public BatteryStats getStats() {
106 if (mStats == null) {
107 load();
108 }
109 return mStats;
110 }
111
112 public PowerProfile getPowerProfile() {
113 return mPowerProfile;
114 }
115
116 public void create(BatteryStats stats) {
117 mPowerProfile = new PowerProfile(mContext);
118 mStats = stats;
119 }
120
121 public void create(Bundle icicle) {
122 if (icicle != null) {
123 mStats = sStatsXfer;
124 }
125 mBatteryInfo = IBatteryStats.Stub.asInterface(
126 ServiceManager.getService(BatteryStats.SERVICE_NAME));
127 mPowerProfile = new PowerProfile(mContext);
128 }
129
130 public void storeState() {
131 sStatsXfer = mStats;
132 }
133
134 public static String makemAh(double power) {
135 if (power < .0001) return String.format("%.8f", power);
136 else if (power < .0001) return String.format("%.7f", power);
137 else if (power < .001) return String.format("%.6f", power);
138 else if (power < .01) return String.format("%.5f", power);
139 else if (power < .1) return String.format("%.4f", power);
140 else if (power < 1) return String.format("%.3f", power);
141 else if (power < 10) return String.format("%.2f", power);
142 else if (power < 100) return String.format("%.1f", power);
143 else return String.format("%.0f", power);
144 }
145
146 /**
147 * Refreshes the power usage list.
148 */
149 public void refreshStats(int statsType, int asUser) {
150 refreshStats(statsType, asUser, SystemClock.elapsedRealtime() * 1000,
151 SystemClock.uptimeMillis() * 1000);
152 }
153
154 public void refreshStats(int statsType, int asUser, long rawRealtimeNano, long rawUptimeNano) {
155 // Initialize mStats if necessary.
156 getStats();
157
158 mMaxPower = 0;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800159 mComputedPower = 0;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800160 mTotalPower = 0;
161 mWifiPower = 0;
162 mBluetoothPower = 0;
163 mAppWifiRunning = 0;
164
165 mUsageList.clear();
166 mWifiSippers.clear();
167 mBluetoothSippers.clear();
168 mUserSippers.clear();
169 mUserPower.clear();
170
171 if (mStats == null) {
172 return;
173 }
174
175 mStatsType = statsType;
176 mAsUser = asUser;
177 mBatteryUptime = mStats.getBatteryUptime(rawUptimeNano);
178 mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeNano);
179 mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeNano, mStatsType);
180 mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeNano, mStatsType);
181
182 if (DEBUG) {
183 Log.d(TAG, "Raw time: realtime=" + (rawRealtimeNano/1000) + " uptime="
184 + (rawUptimeNano/1000));
185 Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime="
186 + (mBatteryUptime/1000));
187 Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime="
188 + (mTypeBatteryUptime/1000));
189 }
190 mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
191 * mPowerProfile.getBatteryCapacity()) / 100;
192 mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
193 * mPowerProfile.getBatteryCapacity()) / 100;
194
195 processAppUsage();
196 processMiscUsage();
197
198 if (DEBUG) {
Dianne Hackborn099bc622014-01-22 13:39:16 -0800199 Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800200 + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
201 }
Dianne Hackborn099bc622014-01-22 13:39:16 -0800202 mTotalPower = mComputedPower;
203 if (mStats.getLowDischargeAmountSinceCharge() > 1) {
204 if (mMinDrainedPower > mComputedPower) {
205 double amount = mMinDrainedPower - mComputedPower;
206 mTotalPower = mMinDrainedPower;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800207 addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800208 } else if (mMaxDrainedPower < mComputedPower) {
209 double amount = mComputedPower - mMaxDrainedPower;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800210 addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);
211 }
212 }
213
214 Collections.sort(mUsageList);
215 }
216
217 private void processAppUsage() {
218 SensorManager sensorManager = (SensorManager) mContext.getSystemService(
219 Context.SENSOR_SERVICE);
220 final int which = mStatsType;
221 final int speedSteps = mPowerProfile.getNumSpeedSteps();
222 final double[] powerCpuNormal = new double[speedSteps];
223 final long[] cpuSpeedStepTimes = new long[speedSteps];
224 for (int p = 0; p < speedSteps; p++) {
225 powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
226 }
227 final double mobilePowerPerPacket = getMobilePowerPerPacket();
228 final double wifiPowerPerPacket = getWifiPowerPerPacket();
229 long appWakelockTime = 0;
230 BatterySipper osApp = null;
231 mStatsPeriod = mTypeBatteryRealtime;
232 SparseArray<? extends Uid> uidStats = mStats.getUidStats();
233 final int NU = uidStats.size();
234 for (int iu = 0; iu < NU; iu++) {
235 Uid u = uidStats.valueAt(iu);
236 double p; // in mAs
237 double power = 0; // in mAs
238 double highestDrain = 0;
239 String packageWithHighestDrain = null;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800240 Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
241 long cpuTime = 0;
242 long cpuFgTime = 0;
243 long wakelockTime = 0;
244 long gpsTime = 0;
245 if (processStats.size() > 0) {
246 // Process CPU time
247 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
248 : processStats.entrySet()) {
249 Uid.Proc ps = ent.getValue();
250 final long userTime = ps.getUserTime(which);
251 final long systemTime = ps.getSystemTime(which);
252 final long foregroundTime = ps.getForegroundTime(which);
253 cpuFgTime += foregroundTime * 10; // convert to millis
254 final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis
255 int totalTimeAtSpeeds = 0;
256 // Get the total first
257 for (int step = 0; step < speedSteps; step++) {
258 cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which);
259 totalTimeAtSpeeds += cpuSpeedStepTimes[step];
260 }
261 if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1;
262 // Then compute the ratio of time spent at each speed
263 double processPower = 0;
264 for (int step = 0; step < speedSteps; step++) {
265 double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds;
266 if (DEBUG && ratio != 0) Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
267 + step + " ratio=" + makemAh(ratio) + " power="
268 + makemAh(ratio*tmpCpuTime*powerCpuNormal[step] / (60*60*1000)));
269 processPower += ratio * tmpCpuTime * powerCpuNormal[step];
270 }
271 cpuTime += tmpCpuTime;
272 if (DEBUG && processPower != 0) {
273 Log.d(TAG, String.format("process %s, cpu power=%s",
274 ent.getKey(), makemAh(processPower / (60*60*1000))));
275 }
276 power += processPower;
277 if (packageWithHighestDrain == null
278 || packageWithHighestDrain.startsWith("*")) {
279 highestDrain = processPower;
280 packageWithHighestDrain = ent.getKey();
281 } else if (highestDrain < processPower
282 && !ent.getKey().startsWith("*")) {
283 highestDrain = processPower;
284 packageWithHighestDrain = ent.getKey();
285 }
286 }
287 }
288 if (cpuFgTime > cpuTime) {
289 if (DEBUG && cpuFgTime > cpuTime + 10000) {
290 Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
291 }
292 cpuTime = cpuFgTime; // Statistics may not have been gathered yet.
293 }
294 power /= (60*60*1000);
295
296 // Process wake lock usage
297 Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats();
298 for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry
299 : wakelockStats.entrySet()) {
300 Uid.Wakelock wakelock = wakelockEntry.getValue();
301 // Only care about partial wake locks since full wake locks
302 // are canceled when the user turns the screen off.
303 BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
304 if (timer != null) {
305 wakelockTime += timer.getTotalTimeLocked(mBatteryRealtime, which);
306 }
307 }
308 wakelockTime /= 1000; // convert to millis
309 appWakelockTime += wakelockTime;
310
311 // Add cost of holding a wake lock
312 p = (wakelockTime
313 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60*60*1000);
314 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wake "
315 + wakelockTime + " power=" + makemAh(p));
316 power += p;
317
318 // Add cost of mobile traffic
319 final long mobileRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
320 final long mobileTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
321 final long mobileRxB = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType);
322 final long mobileTxB = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType);
323 p = (mobileRx + mobileTx) * mobilePowerPerPacket;
324 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
325 + (mobileRx+mobileTx) + " power=" + makemAh(p));
326 power += p;
327
328 // Add cost of wifi traffic
329 final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType);
330 final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType);
331 final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType);
332 final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType);
333 p = (wifiRx + wifiTx) * wifiPowerPerPacket;
334 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi packets "
335 + (mobileRx+mobileTx) + " power=" + makemAh(p));
336 power += p;
337
338 // Add cost of keeping WIFI running.
339 long wifiRunningTimeMs = u.getWifiRunningTime(mBatteryRealtime, which) / 1000;
340 mAppWifiRunning += wifiRunningTimeMs;
341 p = (wifiRunningTimeMs
342 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000);
343 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi running "
344 + wifiRunningTimeMs + " power=" + makemAh(p));
345 power += p;
346
347 // Add cost of WIFI scans
348 long wifiScanTimeMs = u.getWifiScanTime(mBatteryRealtime, which) / 1000;
349 p = (wifiScanTimeMs
350 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / (60*60*1000);
351 if (DEBUG) Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs
352 + " power=" + makemAh(p));
353 power += p;
354 for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
355 long batchScanTimeMs = u.getWifiBatchedScanTime(bin, mBatteryRealtime, which) / 1000;
356 p = ((batchScanTimeMs
357 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin))
358 ) / (60*60*1000);
359 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin
360 + " time=" + batchScanTimeMs + " power=" + makemAh(p));
361 power += p;
362 }
363
364 // Process Sensor usage
365 Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
366 for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry
367 : sensorStats.entrySet()) {
368 Uid.Sensor sensor = sensorEntry.getValue();
369 int sensorHandle = sensor.getHandle();
370 BatteryStats.Timer timer = sensor.getSensorTime();
371 long sensorTime = timer.getTotalTimeLocked(mBatteryRealtime, which) / 1000;
372 double multiplier = 0;
373 switch (sensorHandle) {
374 case Uid.Sensor.GPS:
375 multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON);
376 gpsTime = sensorTime;
377 break;
378 default:
379 List<Sensor> sensorList = sensorManager.getSensorList(
380 android.hardware.Sensor.TYPE_ALL);
381 for (android.hardware.Sensor s : sensorList) {
382 if (s.getHandle() == sensorHandle) {
383 multiplier = s.getPower();
384 break;
385 }
386 }
387 }
388 p = (multiplier * sensorTime) / (60*60*1000);
389 if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle
390 + " time=" + sensorTime + " power=" + makemAh(p));
391 power += p;
392 }
393
394 if (DEBUG && power != 0) Log.d(TAG, String.format("UID %d: total power=%s",
395 u.getUid(), makemAh(power)));
396
397 // Add the app to the list if it is consuming power
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800398 final int userId = UserHandle.getUserId(u.getUid());
399 if (power != 0 || u.getUid() == 0) {
400 BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u,
401 new double[] {power});
402 app.cpuTime = cpuTime;
403 app.gpsTime = gpsTime;
404 app.wifiRunningTime = wifiRunningTimeMs;
405 app.cpuFgTime = cpuFgTime;
406 app.wakeLockTime = wakelockTime;
407 app.mobileRxPackets = mobileRx;
408 app.mobileTxPackets = mobileTx;
409 app.wifiRxPackets = wifiRx;
410 app.wifiTxPackets = wifiTx;
411 app.mobileRxBytes = mobileRxB;
412 app.mobileTxBytes = mobileTxB;
413 app.wifiRxBytes = wifiRxB;
414 app.wifiTxBytes = wifiTxB;
415 app.packageWithHighestDrain = packageWithHighestDrain;
416 if (u.getUid() == Process.WIFI_UID) {
417 mWifiSippers.add(app);
Dianne Hackbornae19a062014-01-16 16:18:23 -0800418 mWifiPower += power;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800419 } else if (u.getUid() == Process.BLUETOOTH_UID) {
420 mBluetoothSippers.add(app);
Dianne Hackbornae19a062014-01-16 16:18:23 -0800421 mBluetoothPower += power;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800422 } else if (mAsUser != UserHandle.USER_ALL && userId != mAsUser
423 && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) {
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800424 List<BatterySipper> list = mUserSippers.get(userId);
425 if (list == null) {
426 list = new ArrayList<BatterySipper>();
427 mUserSippers.put(userId, list);
428 }
429 list.add(app);
Dianne Hackbornae19a062014-01-16 16:18:23 -0800430 if (power != 0) {
431 Double userPower = mUserPower.get(userId);
432 if (userPower == null) {
433 userPower = power;
434 } else {
435 userPower += power;
436 }
437 mUserPower.put(userId, userPower);
438 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800439 } else {
440 mUsageList.add(app);
Dianne Hackbornae19a062014-01-16 16:18:23 -0800441 if (power > mMaxPower) mMaxPower = power;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800442 mComputedPower += power;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800443 }
444 if (u.getUid() == 0) {
445 osApp = app;
446 }
447 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800448 }
449
450 // The device has probably been awake for longer than the screen on
451 // time and application wake lock time would account for. Assign
452 // this remainder to the OS, if possible.
453 if (osApp != null) {
454 long wakeTimeMillis = mBatteryUptime / 1000;
455 wakeTimeMillis -= appWakelockTime
456 + (mStats.getScreenOnTime(mBatteryRealtime, which) / 1000);
457 if (wakeTimeMillis > 0) {
458 double power = (wakeTimeMillis
459 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE))
460 / (60*60*1000);
461 if (DEBUG) Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
462 + makemAh(power));
463 osApp.wakeLockTime += wakeTimeMillis;
464 osApp.value += power;
465 osApp.values[0] += power;
466 if (osApp.value > mMaxPower) mMaxPower = osApp.value;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800467 mComputedPower += power;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800468 }
469 }
470 }
471
472 private void addPhoneUsage() {
473 long phoneOnTimeMs = mStats.getPhoneOnTime(mBatteryRealtime, mStatsType) / 1000;
474 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
475 * phoneOnTimeMs / (60*60*1000);
Dianne Hackborn099bc622014-01-22 13:39:16 -0800476 if (phoneOnPower != 0) {
477 addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
478 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800479 }
480
481 private void addScreenUsage() {
482 double power = 0;
483 long screenOnTimeMs = mStats.getScreenOnTime(mBatteryRealtime, mStatsType) / 1000;
484 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
485 final double screenFullPower =
486 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
487 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
488 double screenBinPower = screenFullPower * (i + 0.5f)
489 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
490 long brightnessTime = mStats.getScreenBrightnessTime(i, mBatteryRealtime, mStatsType)
491 / 1000;
492 double p = screenBinPower*brightnessTime;
493 if (DEBUG && p != 0) {
494 Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
Dianne Hackborn099bc622014-01-22 13:39:16 -0800495 + " power=" + makemAh(p / (60 * 60 * 1000)));
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800496 }
497 power += p;
498 }
499 power /= (60*60*1000); // To hours
Dianne Hackborn099bc622014-01-22 13:39:16 -0800500 if (power != 0) {
501 addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
502 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800503 }
504
505 private void addRadioUsage() {
506 double power = 0;
507 final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
508 long signalTimeMs = 0;
509 long noCoverageTimeMs = 0;
510 for (int i = 0; i < BINS; i++) {
511 long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, mBatteryRealtime, mStatsType)
512 / 1000;
513 double p = (strengthTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i))
514 / (60*60*1000);
515 if (DEBUG && p != 0) {
516 Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
517 + makemAh(p));
518 }
519 power += p;
520 signalTimeMs += strengthTimeMs;
521 if (i == 0) {
522 noCoverageTimeMs = strengthTimeMs;
523 }
524 }
525 long scanningTimeMs = mStats.getPhoneSignalScanningTime(mBatteryRealtime, mStatsType)
526 / 1000;
527 double p = (scanningTimeMs * mPowerProfile.getAveragePower(
528 PowerProfile.POWER_RADIO_SCANNING))
529 / (60*60*1000);
530 if (DEBUG && p != 0) {
531 Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + makemAh(p));
532 }
533 power += p;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800534 if (power != 0) {
535 BatterySipper bs =
536 addEntry(BatterySipper.DrainType.CELL, signalTimeMs, power);
537 if (signalTimeMs != 0) {
538 bs.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
539 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800540 }
541 }
542
543 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
544 for (int i=0; i<from.size(); i++) {
545 BatterySipper wbs = from.get(i);
546 if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime);
547 bs.cpuTime += wbs.cpuTime;
548 bs.gpsTime += wbs.gpsTime;
549 bs.wifiRunningTime += wbs.wifiRunningTime;
550 bs.cpuFgTime += wbs.cpuFgTime;
551 bs.wakeLockTime += wbs.wakeLockTime;
552 bs.mobileRxPackets += wbs.mobileRxPackets;
553 bs.mobileTxPackets += wbs.mobileTxPackets;
554 bs.wifiRxPackets += wbs.wifiRxPackets;
555 bs.wifiTxPackets += wbs.wifiTxPackets;
556 bs.mobileRxBytes += wbs.mobileRxBytes;
557 bs.mobileTxBytes += wbs.mobileTxBytes;
558 bs.wifiRxBytes += wbs.wifiRxBytes;
559 bs.wifiTxBytes += wbs.wifiTxBytes;
560 }
561 }
562
563 private void addWiFiUsage() {
564 long onTimeMs = mStats.getWifiOnTime(mBatteryRealtime, mStatsType) / 1000;
565 long runningTimeMs = mStats.getGlobalWifiRunningTime(mBatteryRealtime, mStatsType) / 1000;
566 if (DEBUG) Log.d(TAG, "WIFI runningTime=" + runningTimeMs
567 + " app runningTime=" + mAppWifiRunning);
568 runningTimeMs -= mAppWifiRunning;
569 if (runningTimeMs < 0) runningTimeMs = 0;
570 double wifiPower = (onTimeMs * 0 /* TODO */
571 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)
572 + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON))
573 / (60*60*1000);
574 if (DEBUG && wifiPower != 0) {
575 Log.d(TAG, "Wifi: time=" + runningTimeMs + " power=" + makemAh(wifiPower));
576 }
Dianne Hackborn099bc622014-01-22 13:39:16 -0800577 if ((wifiPower+mWifiPower) != 0) {
578 BatterySipper bs = addEntry(BatterySipper.DrainType.WIFI, runningTimeMs,
579 wifiPower + mWifiPower);
580 aggregateSippers(bs, mWifiSippers, "WIFI");
581 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800582 }
583
584 private void addIdleUsage() {
585 long idleTimeMs = (mTypeBatteryRealtime
586 - mStats.getScreenOnTime(mBatteryRealtime, mStatsType)) / 1000;
587 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))
588 / (60*60*1000);
589 if (DEBUG && idlePower != 0) {
590 Log.d(TAG, "Idle: time=" + idleTimeMs + " power=" + makemAh(idlePower));
591 }
Dianne Hackborn099bc622014-01-22 13:39:16 -0800592 if (idlePower != 0) {
593 addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);
594 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800595 }
596
597 private void addBluetoothUsage() {
598 long btOnTimeMs = mStats.getBluetoothOnTime(mBatteryRealtime, mStatsType) / 1000;
599 double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON)
600 / (60*60*1000);
601 if (DEBUG && btPower != 0) {
602 Log.d(TAG, "Bluetooth: time=" + btOnTimeMs + " power=" + makemAh(btPower));
603 }
604 int btPingCount = mStats.getBluetoothPingCount();
605 double pingPower = (btPingCount
606 * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD))
607 / (60*60*1000);
608 if (DEBUG && pingPower != 0) {
609 Log.d(TAG, "Bluetooth ping: count=" + btPingCount + " power=" + makemAh(pingPower));
610 }
611 btPower += pingPower;
Dianne Hackborn099bc622014-01-22 13:39:16 -0800612 if ((btPower+mBluetoothPower) != 0) {
613 BatterySipper bs = addEntry(BatterySipper.DrainType.BLUETOOTH, btOnTimeMs,
614 btPower + mBluetoothPower);
615 aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
616 }
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800617 }
618
619 private void addUserUsage() {
620 for (int i=0; i<mUserSippers.size(); i++) {
621 final int userId = mUserSippers.keyAt(i);
622 final List<BatterySipper> sippers = mUserSippers.valueAt(i);
623 Double userPower = mUserPower.get(userId);
624 double power = (userPower != null) ? userPower : 0.0;
625 BatterySipper bs = addEntry(BatterySipper.DrainType.USER, 0, power);
626 bs.userId = userId;
627 aggregateSippers(bs, sippers, "User");
628 }
629 }
630
631 /**
632 * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
633 */
634 private double getMobilePowerPerPacket() {
635 final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
636 final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
637 / 3600;
638
639 final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
640 final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
641 final long mobileData = mobileRx + mobileTx;
642
643 final long radioDataUptimeMs = mStats.getRadioDataUptime() / 1000;
644 final double mobilePps = radioDataUptimeMs != 0
645 ? mobileData / (double)radioDataUptimeMs
646 : (((double)MOBILE_BPS) / 8 / 2048);
647
648 return (MOBILE_POWER / mobilePps) / (60*60);
649 }
650
651 /**
652 * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
653 */
654 private double getWifiPowerPerPacket() {
655 final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
656 final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
657 / 3600;
658 return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
659 }
660
661 private void processMiscUsage() {
662 addUserUsage();
663 addPhoneUsage();
664 addScreenUsage();
665 addWiFiUsage();
666 addBluetoothUsage();
667 addIdleUsage(); // Not including cellular idle power
668 // Don't compute radio usage if it's a wifi-only device
669 ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
670 Context.CONNECTIVITY_SERVICE);
671 if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
672 addRadioUsage();
673 }
674 }
675
676 private BatterySipper addEntry(DrainType drainType, long time, double power) {
Dianne Hackborn099bc622014-01-22 13:39:16 -0800677 mComputedPower += power;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800678 return addEntryNoTotal(drainType, time, power);
679 }
680
681 private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) {
682 if (power > mMaxPower) mMaxPower = power;
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800683 BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});
684 bs.usageTime = time;
685 mUsageList.add(bs);
686 return bs;
687 }
688
689 public List<BatterySipper> getUsageList() {
690 return mUsageList;
691 }
692
693 public long getStatsPeriod() { return mStatsPeriod; }
694
695 public int getStatsType() { return mStatsType; };
696
697 public double getMaxPower() { return mMaxPower; }
698
699 public double getTotalPower() { return mTotalPower; }
700
Dianne Hackborn099bc622014-01-22 13:39:16 -0800701 public double getComputedPower() { return mComputedPower; }
702
Dianne Hackborna7c837f2014-01-15 16:20:44 -0800703 public double getMinDrainedPower() {
704 return mMinDrainedPower;
705 }
706
707 public double getMaxDrainedPower() {
708 return mMaxDrainedPower;
709 }
710
711 private void load() {
712 if (mBatteryInfo == null) {
713 return;
714 }
715 try {
716 byte[] data = mBatteryInfo.getStatistics();
717 Parcel parcel = Parcel.obtain();
718 parcel.unmarshall(data, 0, data.length);
719 parcel.setDataPosition(0);
720 BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
721 .createFromParcel(parcel);
722 stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED);
723 mStats = stats;
724 } catch (RemoteException e) {
725 Log.e(TAG, "RemoteException:", e);
726 }
727 }
728}