2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.fuelgauge;
19 import com.android.internal.app.IBatteryStats;
20 import com.android.internal.os.BatteryStatsImpl;
21 import com.android.internal.os.PowerProfile;
22 import com.android.settings.R;
23 import com.android.settings.applications.InstalledAppDetails;
24 import com.android.settings.fuelgauge.PowerUsageDetail.DrainType;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.hardware.SensorManager;
30 import android.os.BatteryStats;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.Message;
34 import android.os.Parcel;
35 import android.os.Process;
36 import android.os.RemoteException;
37 import android.os.ServiceManager;
38 import android.os.SystemClock;
39 import android.os.BatteryStats.Uid;
40 import android.preference.Preference;
41 import android.preference.PreferenceActivity;
42 import android.preference.PreferenceFragment;
43 import android.preference.PreferenceGroup;
44 import android.preference.PreferenceScreen;
45 import android.util.Log;
46 import android.util.SparseArray;
47 import android.view.Menu;
48 import android.view.MenuInflater;
49 import android.view.MenuItem;
51 import java.io.PrintWriter;
52 import java.io.StringWriter;
53 import java.io.Writer;
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.List;
60 * Displays a list of apps and subsystems that consume power, ordered by how much power was
61 * consumed since the last time it was unplugged.
63 public class PowerUsageSummary extends PreferenceFragment implements Runnable {
65 private static final boolean DEBUG = false;
67 private static final String TAG = "PowerUsageSummary";
69 private static final int MENU_STATS_TYPE = Menu.FIRST;
70 private static final int MENU_STATS_REFRESH = Menu.FIRST + 1;
72 private static BatteryStatsImpl sStatsXfer;
74 IBatteryStats mBatteryInfo;
75 BatteryStatsImpl mStats;
76 private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
77 private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>();
78 private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>();
80 private PreferenceGroup mAppListGroup;
82 private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
84 private static final int MIN_POWER_THRESHOLD = 5;
85 private static final int MAX_ITEMS_TO_LIST = 10;
87 private long mStatsPeriod = 0;
88 private double mMaxPower = 1;
89 private double mTotalPower;
90 private double mWifiPower;
91 private double mBluetoothPower;
92 private PowerProfile mPowerProfile;
94 // How much the apps together have left WIFI running.
95 private long mAppWifiRunning;
97 /** Queue for fetching name and icon for an application */
98 private ArrayList<BatterySipper> mRequestQueue = new ArrayList<BatterySipper>();
99 private Thread mRequestThread;
100 private boolean mAbort;
103 public void onCreate(Bundle icicle) {
104 super.onCreate(icicle);
106 if (icicle != null) {
110 addPreferencesFromResource(R.xml.power_usage_summary);
111 mBatteryInfo = IBatteryStats.Stub.asInterface(
112 ServiceManager.getService("batteryinfo"));
113 mAppListGroup = (PreferenceGroup) findPreference("app_list");
114 mPowerProfile = new PowerProfile(getActivity());
115 setHasOptionsMenu(true);
119 public void onResume() {
126 public void onPause() {
127 synchronized (mRequestQueue) {
130 mHandler.removeMessages(MSG_UPDATE_NAME_ICON);
135 public void onDestroy() {
137 if (getActivity().isChangingConfigurations()) {
143 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
144 if (preference instanceof BatteryHistoryPreference) {
145 Parcel hist = Parcel.obtain();
146 mStats.writeToParcelWithoutUids(hist, 0);
147 byte[] histData = hist.marshall();
148 Bundle args = new Bundle();
149 args.putByteArray(BatteryHistoryDetail.EXTRA_STATS, histData);
150 PreferenceActivity pa = (PreferenceActivity)getActivity();
151 pa.startPreferencePanel(BatteryHistoryDetail.class.getName(), args,
152 R.string.history_details_title, null, null, 0);
153 return super.onPreferenceTreeClick(preferenceScreen, preference);
155 if (!(preference instanceof PowerGaugePreference)) {
158 PowerGaugePreference pgp = (PowerGaugePreference) preference;
159 BatterySipper sipper = pgp.getInfo();
160 Bundle args = new Bundle();
161 args.putString(PowerUsageDetail.EXTRA_TITLE, sipper.name);
162 args.putInt(PowerUsageDetail.EXTRA_PERCENT, (int)
163 Math.ceil(sipper.getSortValue() * 100 / mTotalPower));
164 args.putInt(PowerUsageDetail.EXTRA_GAUGE, (int)
165 Math.ceil(sipper.getSortValue() * 100 / mMaxPower));
166 args.putLong(PowerUsageDetail.EXTRA_USAGE_DURATION, mStatsPeriod);
167 args.putString(PowerUsageDetail.EXTRA_ICON_PACKAGE, sipper.defaultPackageName);
168 args.putInt(PowerUsageDetail.EXTRA_ICON_ID, sipper.iconId);
169 args.putDouble(PowerUsageDetail.EXTRA_NO_COVERAGE, sipper.noCoveragePercent);
170 if (sipper.uidObj != null) {
171 args.putInt(PowerUsageDetail.EXTRA_UID, sipper.uidObj.getUid());
173 args.putSerializable(PowerUsageDetail.EXTRA_DRAIN_TYPE, sipper.drainType);
177 switch (sipper.drainType) {
180 Uid uid = sipper.uidObj;
182 R.string.usage_type_cpu,
183 R.string.usage_type_cpu_foreground,
184 R.string.usage_type_wake_lock,
185 R.string.usage_type_gps,
186 R.string.usage_type_wifi_running,
187 R.string.usage_type_data_send,
188 R.string.usage_type_data_recv,
189 R.string.usage_type_audio,
190 R.string.usage_type_video,
192 values = new double[] {
197 sipper.wifiRunningTime,
199 sipper.tcpBytesReceived,
204 Writer result = new StringWriter();
205 PrintWriter printWriter = new PrintWriter(result);
206 mStats.dumpLocked(printWriter, "", mStatsType, uid.getUid());
207 args.putString(PowerUsageDetail.EXTRA_REPORT_DETAILS, result.toString());
209 result = new StringWriter();
210 printWriter = new PrintWriter(result);
211 mStats.dumpCheckinLocked(printWriter, mStatsType, uid.getUid());
212 args.putString(PowerUsageDetail.EXTRA_REPORT_CHECKIN_DETAILS, result.toString());
218 R.string.usage_type_on_time,
219 R.string.usage_type_no_coverage
221 values = new double[] {
223 sipper.noCoveragePercent
230 R.string.usage_type_wifi_running,
231 R.string.usage_type_cpu,
232 R.string.usage_type_cpu_foreground,
233 R.string.usage_type_wake_lock,
234 R.string.usage_type_data_send,
235 R.string.usage_type_data_recv,
237 values = new double[] {
243 sipper.tcpBytesReceived,
249 R.string.usage_type_on_time,
250 R.string.usage_type_cpu,
251 R.string.usage_type_cpu_foreground,
252 R.string.usage_type_wake_lock,
253 R.string.usage_type_data_send,
254 R.string.usage_type_data_recv,
256 values = new double[] {
262 sipper.tcpBytesReceived,
268 R.string.usage_type_on_time
270 values = new double[] {
275 args.putIntArray(PowerUsageDetail.EXTRA_DETAIL_TYPES, types);
276 args.putDoubleArray(PowerUsageDetail.EXTRA_DETAIL_VALUES, values);
277 PreferenceActivity pa = (PreferenceActivity)getActivity();
278 pa.startPreferencePanel(PowerUsageDetail.class.getName(), args,
279 R.string.details_title, null, null, 0);
281 return super.onPreferenceTreeClick(preferenceScreen, preference);
285 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
287 menu.add(0, MENU_STATS_TYPE, 0, R.string.menu_stats_total)
288 .setIcon(com.android.internal.R.drawable.ic_menu_info_details)
289 .setAlphabeticShortcut('t');
291 MenuItem refresh = menu.add(0, MENU_STATS_REFRESH, 0, R.string.menu_stats_refresh)
292 .setIcon(R.drawable.ic_menu_refresh_holo_dark)
293 .setAlphabeticShortcut('r');
294 refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
298 public boolean onOptionsItemSelected(MenuItem item) {
299 switch (item.getItemId()) {
300 case MENU_STATS_TYPE:
301 if (mStatsType == BatteryStats.STATS_SINCE_CHARGED) {
302 mStatsType = BatteryStats.STATS_SINCE_UNPLUGGED;
304 mStatsType = BatteryStats.STATS_SINCE_CHARGED;
308 case MENU_STATS_REFRESH:
317 private void addNotAvailableMessage() {
318 Preference notAvailable = new Preference(getActivity());
319 notAvailable.setTitle(R.string.power_usage_not_available);
320 mAppListGroup.addPreference(notAvailable);
323 private void refreshStats() {
324 if (mStats == null) {
333 mAppListGroup.removeAll();
335 mWifiSippers.clear();
336 mBluetoothSippers.clear();
337 mAppListGroup.setOrderingAsAdded(false);
339 BatteryHistoryPreference hist = new BatteryHistoryPreference(getActivity(), mStats);
341 mAppListGroup.addPreference(hist);
343 if (mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL) < 10) {
344 addNotAvailableMessage();
350 Collections.sort(mUsageList);
351 for (BatterySipper sipper : mUsageList) {
352 if (sipper.getSortValue() < MIN_POWER_THRESHOLD) continue;
353 final double percentOfTotal = ((sipper.getSortValue() / mTotalPower) * 100);
354 if (percentOfTotal < 1) continue;
355 PowerGaugePreference pref = new PowerGaugePreference(getActivity(), sipper.getIcon(), sipper);
356 double percentOfMax = (sipper.getSortValue() * 100) / mMaxPower;
357 sipper.percent = percentOfTotal;
358 pref.setTitle(sipper.name);
359 pref.setPercent(percentOfTotal);
360 pref.setOrder(Integer.MAX_VALUE - (int) sipper.getSortValue()); // Invert the order
361 pref.setGaugeValue(percentOfMax);
362 if (sipper.uidObj != null) {
363 pref.setKey(Integer.toString(sipper.uidObj.getUid()));
365 mAppListGroup.addPreference(pref);
366 if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) break;
368 synchronized (mRequestQueue) {
369 if (!mRequestQueue.isEmpty()) {
370 if (mRequestThread == null) {
371 mRequestThread = new Thread(this, "BatteryUsage Icon Loader");
372 mRequestThread.setPriority(Thread.MIN_PRIORITY);
373 mRequestThread.start();
375 mRequestQueue.notify();
380 private void processAppUsage() {
381 SensorManager sensorManager = (SensorManager)getActivity().getSystemService(
382 Context.SENSOR_SERVICE);
383 final int which = mStatsType;
384 final int speedSteps = mPowerProfile.getNumSpeedSteps();
385 final double[] powerCpuNormal = new double[speedSteps];
386 final long[] cpuSpeedStepTimes = new long[speedSteps];
387 for (int p = 0; p < speedSteps; p++) {
388 powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
390 final double averageCostPerByte = getAverageDataCost();
391 long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which);
392 mStatsPeriod = uSecTime;
393 SparseArray<? extends Uid> uidStats = mStats.getUidStats();
394 final int NU = uidStats.size();
395 for (int iu = 0; iu < NU; iu++) {
396 Uid u = uidStats.valueAt(iu);
398 double highestDrain = 0;
399 String packageWithHighestDrain = null;
400 //mUsageList.add(new AppUsage(u.getUid(), new double[] {power}));
401 Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
404 long wakelockTime = 0;
406 if (processStats.size() > 0) {
408 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
409 : processStats.entrySet()) {
410 if (DEBUG) Log.i(TAG, "Process name = " + ent.getKey());
411 Uid.Proc ps = ent.getValue();
412 final long userTime = ps.getUserTime(which);
413 final long systemTime = ps.getSystemTime(which);
414 final long foregroundTime = ps.getForegroundTime(which);
415 cpuFgTime += foregroundTime * 10; // convert to millis
416 final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis
417 int totalTimeAtSpeeds = 0;
418 // Get the total first
419 for (int step = 0; step < speedSteps; step++) {
420 cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which);
421 totalTimeAtSpeeds += cpuSpeedStepTimes[step];
423 if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1;
424 // Then compute the ratio of time spent at each speed
425 double processPower = 0;
426 for (int step = 0; step < speedSteps; step++) {
427 double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds;
428 processPower += ratio * tmpCpuTime * powerCpuNormal[step];
430 cpuTime += tmpCpuTime;
431 power += processPower;
432 if (packageWithHighestDrain == null
433 || packageWithHighestDrain.startsWith("*")) {
434 highestDrain = processPower;
435 packageWithHighestDrain = ent.getKey();
436 } else if (highestDrain < processPower
437 && !ent.getKey().startsWith("*")) {
438 highestDrain = processPower;
439 packageWithHighestDrain = ent.getKey();
442 if (DEBUG) Log.i(TAG, "Max drain of " + highestDrain
443 + " by " + packageWithHighestDrain);
445 if (cpuFgTime > cpuTime) {
446 if (DEBUG && cpuFgTime > cpuTime + 10000) {
447 Log.i(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
449 cpuTime = cpuFgTime; // Statistics may not have been gathered yet.
453 // Process wake lock usage
454 Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats();
455 for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry
456 : wakelockStats.entrySet()) {
457 Uid.Wakelock wakelock = wakelockEntry.getValue();
458 // Only care about partial wake locks since full wake locks
459 // are canceled when the user turns the screen off.
460 BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
462 wakelockTime += timer.getTotalTimeLocked(uSecTime, which);
465 wakelockTime /= 1000; // convert to millis
467 // Add cost of holding a wake lock
468 power += (wakelockTime
469 * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / 1000;
471 // Add cost of data traffic
472 long tcpBytesReceived = u.getTcpBytesReceived(mStatsType);
473 long tcpBytesSent = u.getTcpBytesSent(mStatsType);
474 power += (tcpBytesReceived+tcpBytesSent) * averageCostPerByte;
476 // Add cost of keeping WIFI running.
477 long wifiRunningTimeMs = u.getWifiRunningTime(uSecTime, which) / 1000;
478 mAppWifiRunning += wifiRunningTimeMs;
479 power += (wifiRunningTimeMs
480 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000;
482 // Process Sensor usage
483 Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
484 for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry
485 : sensorStats.entrySet()) {
486 Uid.Sensor sensor = sensorEntry.getValue();
487 int sensorType = sensor.getHandle();
488 BatteryStats.Timer timer = sensor.getSensorTime();
489 long sensorTime = timer.getTotalTimeLocked(uSecTime, which) / 1000;
490 double multiplier = 0;
491 switch (sensorType) {
493 multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON);
494 gpsTime = sensorTime;
497 android.hardware.Sensor sensorData =
498 sensorManager.getDefaultSensor(sensorType);
499 if (sensorData != null) {
500 multiplier = sensorData.getPower();
502 Log.i(TAG, "Got sensor " + sensorData.getName() + " with power = "
507 power += (multiplier * sensorTime) / 1000;
510 if (DEBUG) Log.i(TAG, "UID " + u.getUid() + ": power=" + power);
512 // Add the app to the list if it is consuming power
514 BatterySipper app = new BatterySipper(getActivity(), mRequestQueue, mHandler,
515 packageWithHighestDrain, DrainType.APP, 0, u,
516 new double[] {power});
517 app.cpuTime = cpuTime;
518 app.gpsTime = gpsTime;
519 app.wifiRunningTime = wifiRunningTimeMs;
520 app.cpuFgTime = cpuFgTime;
521 app.wakeLockTime = wakelockTime;
522 app.tcpBytesReceived = tcpBytesReceived;
523 app.tcpBytesSent = tcpBytesSent;
524 if (u.getUid() == Process.WIFI_UID) {
525 mWifiSippers.add(app);
526 } else if (u.getUid() == Process.BLUETOOTH_GID) {
527 mBluetoothSippers.add(app);
532 if (u.getUid() == Process.WIFI_UID) {
534 } else if (u.getUid() == Process.BLUETOOTH_GID) {
535 mBluetoothPower += power;
537 if (power > mMaxPower) mMaxPower = power;
538 mTotalPower += power;
540 if (DEBUG) Log.i(TAG, "Added power = " + power);
544 private void addPhoneUsage(long uSecNow) {
545 long phoneOnTimeMs = mStats.getPhoneOnTime(uSecNow, mStatsType) / 1000;
546 double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
547 * phoneOnTimeMs / 1000;
548 addEntry(getActivity().getString(R.string.power_phone), DrainType.PHONE, phoneOnTimeMs,
549 R.drawable.ic_settings_voice_calls, phoneOnPower);
552 private void addScreenUsage(long uSecNow) {
554 long screenOnTimeMs = mStats.getScreenOnTime(uSecNow, mStatsType) / 1000;
555 power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);
556 final double screenFullPower =
557 mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
558 for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
559 double screenBinPower = screenFullPower * (i + 0.5f)
560 / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
561 long brightnessTime = mStats.getScreenBrightnessTime(i, uSecNow, mStatsType) / 1000;
562 power += screenBinPower * brightnessTime;
564 Log.i(TAG, "Screen bin power = " + (int) screenBinPower + ", time = "
568 power /= 1000; // To seconds
569 addEntry(getActivity().getString(R.string.power_screen), DrainType.SCREEN, screenOnTimeMs,
570 R.drawable.ic_settings_display, power);
573 private void addRadioUsage(long uSecNow) {
575 final int BINS = BatteryStats.NUM_SIGNAL_STRENGTH_BINS;
576 long signalTimeMs = 0;
577 for (int i = 0; i < BINS; i++) {
578 long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, uSecNow, mStatsType) / 1000;
579 power += strengthTimeMs / 1000
580 * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i);
581 signalTimeMs += strengthTimeMs;
583 long scanningTimeMs = mStats.getPhoneSignalScanningTime(uSecNow, mStatsType) / 1000;
584 power += scanningTimeMs / 1000 * mPowerProfile.getAveragePower(
585 PowerProfile.POWER_RADIO_SCANNING);
587 addEntry(getActivity().getString(R.string.power_cell), DrainType.CELL,
588 signalTimeMs, R.drawable.ic_settings_cell_standby, power);
589 if (signalTimeMs != 0) {
590 bs.noCoveragePercent = mStats.getPhoneSignalStrengthTime(0, uSecNow, mStatsType)
591 / 1000 * 100.0 / signalTimeMs;
595 private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
596 for (int i=0; i<from.size(); i++) {
597 BatterySipper wbs = from.get(i);
598 if (DEBUG) Log.i(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime);
599 bs.cpuTime += wbs.cpuTime;
600 bs.gpsTime += wbs.gpsTime;
601 bs.wifiRunningTime += wbs.wifiRunningTime;
602 bs.cpuFgTime += wbs.cpuFgTime;
603 bs.wakeLockTime += wbs.wakeLockTime;
604 bs.tcpBytesReceived += wbs.tcpBytesReceived;
605 bs.tcpBytesSent += wbs.tcpBytesSent;
609 private void addWiFiUsage(long uSecNow) {
610 long onTimeMs = mStats.getWifiOnTime(uSecNow, mStatsType) / 1000;
611 long runningTimeMs = mStats.getGlobalWifiRunningTime(uSecNow, mStatsType) / 1000;
612 if (DEBUG) Log.i(TAG, "WIFI runningTime=" + runningTimeMs
613 + " app runningTime=" + mAppWifiRunning);
614 runningTimeMs -= mAppWifiRunning;
615 if (runningTimeMs < 0) runningTimeMs = 0;
616 double wifiPower = (onTimeMs * 0 /* TODO */
617 * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)
618 + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000;
619 if (DEBUG) Log.i(TAG, "WIFI power=" + wifiPower + " from procs=" + mWifiPower);
620 BatterySipper bs = addEntry(getActivity().getString(R.string.power_wifi), DrainType.WIFI,
621 runningTimeMs, R.drawable.ic_settings_wifi, wifiPower + mWifiPower);
622 aggregateSippers(bs, mWifiSippers, "WIFI");
625 private void addIdleUsage(long uSecNow) {
626 long idleTimeMs = (uSecNow - mStats.getScreenOnTime(uSecNow, mStatsType)) / 1000;
627 double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE))
629 addEntry(getActivity().getString(R.string.power_idle), DrainType.IDLE, idleTimeMs,
630 R.drawable.ic_settings_phone_idle, idlePower);
633 private void addBluetoothUsage(long uSecNow) {
634 long btOnTimeMs = mStats.getBluetoothOnTime(uSecNow, mStatsType) / 1000;
635 double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON)
637 int btPingCount = mStats.getBluetoothPingCount();
638 btPower += (btPingCount
639 * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) / 1000;
640 BatterySipper bs = addEntry(getActivity().getString(R.string.power_bluetooth),
641 DrainType.BLUETOOTH, btOnTimeMs, R.drawable.ic_settings_bluetooth,
642 btPower + mBluetoothPower);
643 aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
646 private double getAverageDataCost() {
647 final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
648 final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
649 final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
651 final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
653 final long mobileData = mStats.getMobileTcpBytesReceived(mStatsType) +
654 mStats.getMobileTcpBytesSent(mStatsType);
655 final long wifiData = mStats.getTotalTcpBytesReceived(mStatsType) +
656 mStats.getTotalTcpBytesSent(mStatsType) - mobileData;
657 final long radioDataUptimeMs = mStats.getRadioDataUptime() / 1000;
658 final long mobileBps = radioDataUptimeMs != 0
659 ? mobileData * 8 * 1000 / radioDataUptimeMs
662 double mobileCostPerByte = MOBILE_POWER / (mobileBps / 8);
663 double wifiCostPerByte = WIFI_POWER / (WIFI_BPS / 8);
664 if (wifiData + mobileData != 0) {
665 return (mobileCostPerByte * mobileData + wifiCostPerByte * wifiData)
666 / (mobileData + wifiData);
672 private void processMiscUsage() {
673 final int which = mStatsType;
674 long uSecTime = SystemClock.elapsedRealtime() * 1000;
675 final long uSecNow = mStats.computeBatteryRealtime(uSecTime, which);
676 final long timeSinceUnplugged = uSecNow;
678 Log.i(TAG, "Uptime since last unplugged = " + (timeSinceUnplugged / 1000));
681 addPhoneUsage(uSecNow);
682 addScreenUsage(uSecNow);
683 addWiFiUsage(uSecNow);
684 addBluetoothUsage(uSecNow);
685 addIdleUsage(uSecNow); // Not including cellular idle power
686 addRadioUsage(uSecNow);
689 private BatterySipper addEntry(String label, DrainType drainType, long time, int iconId,
691 if (power > mMaxPower) mMaxPower = power;
692 mTotalPower += power;
693 BatterySipper bs = new BatterySipper(getActivity(), mRequestQueue, mHandler,
694 label, drainType, iconId, null, new double[] {power});
701 private void load() {
703 byte[] data = mBatteryInfo.getStatistics();
704 Parcel parcel = Parcel.obtain();
705 parcel.unmarshall(data, 0, data.length);
706 parcel.setDataPosition(0);
707 mStats = com.android.internal.os.BatteryStatsImpl.CREATOR
708 .createFromParcel(parcel);
709 mStats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED);
710 } catch (RemoteException e) {
711 Log.e(TAG, "RemoteException:", e);
718 synchronized (mRequestQueue) {
719 if (mRequestQueue.isEmpty() || mAbort) {
720 mRequestThread = null;
723 bs = mRequestQueue.remove(0);
729 static final int MSG_UPDATE_NAME_ICON = 1;
731 Handler mHandler = new Handler() {
734 public void handleMessage(Message msg) {
736 case MSG_UPDATE_NAME_ICON:
737 BatterySipper bs = (BatterySipper) msg.obj;
738 PowerGaugePreference pgp =
739 (PowerGaugePreference) findPreference(
740 Integer.toString(bs.uidObj.getUid()));
742 pgp.setPowerIcon(bs.icon);
743 pgp.setPercent(bs.percent);
744 pgp.setTitle(bs.name);
748 super.handleMessage(msg);