OSDN Git Service

9fafc5685e06389596c455fb5a4917d125149d5e
[android-x86/packages-apps-Settings.git] / src / com / android / settings / fuelgauge / PowerUsageSummary.java
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.settings.fuelgauge;
18
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;
25
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;
50
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;
57 import java.util.Map;
58
59 /**
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.
62  */
63 public class PowerUsageSummary extends PreferenceFragment implements Runnable {
64
65     private static final boolean DEBUG = false;
66
67     private static final String TAG = "PowerUsageSummary";
68
69     private static final int MENU_STATS_TYPE = Menu.FIRST;
70     private static final int MENU_STATS_REFRESH = Menu.FIRST + 1;
71
72     private static BatteryStatsImpl sStatsXfer;
73
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>();
79
80     private PreferenceGroup mAppListGroup;
81
82     private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
83
84     private static final int MIN_POWER_THRESHOLD = 5;
85     private static final int MAX_ITEMS_TO_LIST = 10;
86
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;
93
94     // How much the apps together have left WIFI running.
95     private long mAppWifiRunning;
96
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;
101     
102     @Override
103     public void onCreate(Bundle icicle) {
104         super.onCreate(icicle);
105
106         if (icicle != null) {
107             mStats = sStatsXfer;
108         }
109
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);
116     }
117
118     @Override
119     public void onResume() {
120         super.onResume();
121         mAbort = false;
122         refreshStats();
123     }
124
125     @Override
126     public void onPause() {
127         synchronized (mRequestQueue) {
128             mAbort = true;
129         }
130         mHandler.removeMessages(MSG_UPDATE_NAME_ICON);
131         super.onPause();
132     }
133
134     @Override
135     public void onDestroy() {
136         super.onDestroy();
137         if (getActivity().isChangingConfigurations()) {
138             sStatsXfer = mStats;
139         }
140     }
141
142     @Override
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);
154         }
155         if (!(preference instanceof PowerGaugePreference)) {
156             return false;
157         }
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());
172         }
173         args.putSerializable(PowerUsageDetail.EXTRA_DRAIN_TYPE, sipper.drainType);
174
175         int[] types;
176         double[] values;
177         switch (sipper.drainType) {
178             case APP:
179             {
180                 Uid uid = sipper.uidObj;
181                 types = new int[] {
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,
191                 };
192                 values = new double[] {
193                     sipper.cpuTime,
194                     sipper.cpuFgTime,
195                     sipper.wakeLockTime,
196                     sipper.gpsTime,
197                     sipper.wifiRunningTime,
198                     sipper.tcpBytesSent,
199                     sipper.tcpBytesReceived,
200                     0,
201                     0
202                 };
203
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());
208                 
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());
213             }
214             break;
215             case CELL:
216             {
217                 types = new int[] {
218                     R.string.usage_type_on_time,
219                     R.string.usage_type_no_coverage
220                 };
221                 values = new double[] {
222                     sipper.usageTime,
223                     sipper.noCoveragePercent
224                 };
225             }
226             break;
227             case WIFI:
228             {
229                 types = new int[] {
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,
236                 };
237                 values = new double[] {
238                     sipper.usageTime,
239                     sipper.cpuTime,
240                     sipper.cpuFgTime,
241                     sipper.wakeLockTime,
242                     sipper.tcpBytesSent,
243                     sipper.tcpBytesReceived,
244                 };
245             } break;
246             case BLUETOOTH:
247             {
248                 types = new int[] {
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,
255                 };
256                 values = new double[] {
257                     sipper.usageTime,
258                     sipper.cpuTime,
259                     sipper.cpuFgTime,
260                     sipper.wakeLockTime,
261                     sipper.tcpBytesSent,
262                     sipper.tcpBytesReceived,
263                 };
264             } break;
265             default:
266             {
267                 types = new int[] {
268                     R.string.usage_type_on_time
269                 };
270                 values = new double[] {
271                     sipper.usageTime
272                 };
273             }
274         }
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);
280
281         return super.onPreferenceTreeClick(preferenceScreen, preference);
282     }
283
284     @Override
285     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
286         if (DEBUG) {
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');
290         }
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);
295     }
296
297     @Override
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;
303                 } else {
304                     mStatsType = BatteryStats.STATS_SINCE_CHARGED;
305                 }
306                 refreshStats();
307                 return true;
308             case MENU_STATS_REFRESH:
309                 mStats = null;
310                 refreshStats();
311                 return true;
312             default:
313                 return false;
314         }
315     }
316
317     private void addNotAvailableMessage() {
318         Preference notAvailable = new Preference(getActivity());
319         notAvailable.setTitle(R.string.power_usage_not_available);
320         mAppListGroup.addPreference(notAvailable);
321     }
322
323     private void refreshStats() {
324         if (mStats == null) {
325             load();
326         }
327         mMaxPower = 0;
328         mTotalPower = 0;
329         mWifiPower = 0;
330         mBluetoothPower = 0;
331         mAppWifiRunning = 0;
332
333         mAppListGroup.removeAll();
334         mUsageList.clear();
335         mWifiSippers.clear();
336         mBluetoothSippers.clear();
337         mAppListGroup.setOrderingAsAdded(false);
338
339         BatteryHistoryPreference hist = new BatteryHistoryPreference(getActivity(), mStats);
340         hist.setOrder(-1);
341         mAppListGroup.addPreference(hist);
342         
343         if (mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL) < 10) {
344             addNotAvailableMessage();
345             return;
346         }
347         processAppUsage();
348         processMiscUsage();
349
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()));
364             }
365             mAppListGroup.addPreference(pref);
366             if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) break;
367         }
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();
374                 }
375                 mRequestQueue.notify();
376             }
377         }
378     }
379
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);
389         }
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);
397             double power = 0;
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();
402             long cpuTime = 0;
403             long cpuFgTime = 0;
404             long wakelockTime = 0;
405             long gpsTime = 0;
406             if (processStats.size() > 0) {
407                 // Process CPU time
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];
422                     }
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];
429                     }
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();
440                     }
441                 }
442                 if (DEBUG) Log.i(TAG, "Max drain of " + highestDrain 
443                         + " by " + packageWithHighestDrain);
444             }
445             if (cpuFgTime > cpuTime) {
446                 if (DEBUG && cpuFgTime > cpuTime + 10000) {
447                     Log.i(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
448                 }
449                 cpuTime = cpuFgTime; // Statistics may not have been gathered yet.
450             }
451             power /= 1000;
452
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);
461                 if (timer != null) {
462                     wakelockTime += timer.getTotalTimeLocked(uSecTime, which);
463                 }
464             }
465             wakelockTime /= 1000; // convert to millis
466
467             // Add cost of holding a wake lock
468             power += (wakelockTime
469                     * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / 1000;
470             
471             // Add cost of data traffic
472             long tcpBytesReceived = u.getTcpBytesReceived(mStatsType);
473             long tcpBytesSent = u.getTcpBytesSent(mStatsType);
474             power += (tcpBytesReceived+tcpBytesSent) * averageCostPerByte;
475
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;
481
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) {
492                     case Uid.Sensor.GPS:
493                         multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON);
494                         gpsTime = sensorTime;
495                         break;
496                     default:
497                         android.hardware.Sensor sensorData =
498                                 sensorManager.getDefaultSensor(sensorType);
499                         if (sensorData != null) {
500                             multiplier = sensorData.getPower();
501                             if (DEBUG) {
502                                 Log.i(TAG, "Got sensor " + sensorData.getName() + " with power = "
503                                         + multiplier);
504                             }
505                         }
506                 }
507                 power += (multiplier * sensorTime) / 1000;
508             }
509
510             if (DEBUG) Log.i(TAG, "UID " + u.getUid() + ": power=" + power);
511
512             // Add the app to the list if it is consuming power
513             if (power != 0) {
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);
528                 } else {
529                     mUsageList.add(app);
530                 }
531             }
532             if (u.getUid() == Process.WIFI_UID) {
533                 mWifiPower += power;
534             } else if (u.getUid() == Process.BLUETOOTH_GID) {
535                 mBluetoothPower += power;
536             } else {
537                 if (power > mMaxPower) mMaxPower = power;
538                 mTotalPower += power;
539             }
540             if (DEBUG) Log.i(TAG, "Added power = " + power);
541         }
542     }
543
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);
550     }
551
552     private void addScreenUsage(long uSecNow) {
553         double power = 0;
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;
563             if (DEBUG) {
564                 Log.i(TAG, "Screen bin power = " + (int) screenBinPower + ", time = "
565                         + brightnessTime);
566             }
567         }
568         power /= 1000; // To seconds
569         addEntry(getActivity().getString(R.string.power_screen), DrainType.SCREEN, screenOnTimeMs,
570                 R.drawable.ic_settings_display, power);
571     }
572
573     private void addRadioUsage(long uSecNow) {
574         double power = 0;
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;
582         }
583         long scanningTimeMs = mStats.getPhoneSignalScanningTime(uSecNow, mStatsType) / 1000;
584         power += scanningTimeMs / 1000 * mPowerProfile.getAveragePower(
585                 PowerProfile.POWER_RADIO_SCANNING);
586         BatterySipper bs =
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;
592         }
593     }
594
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;
606         }
607     }
608
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");
623     }
624
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))
628                 / 1000;
629         addEntry(getActivity().getString(R.string.power_idle), DrainType.IDLE, idleTimeMs,
630                 R.drawable.ic_settings_phone_idle, idlePower);
631     }
632
633     private void addBluetoothUsage(long uSecNow) {
634         long btOnTimeMs = mStats.getBluetoothOnTime(uSecNow, mStatsType) / 1000;
635         double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON)
636                 / 1000;
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");
644     }
645
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)
650                 / 3600;
651         final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
652                 / 3600;
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
660                 : MOBILE_BPS;
661
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);
667         } else {
668             return 0;
669         }
670     }
671
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;
677         if (DEBUG) {
678             Log.i(TAG, "Uptime since last unplugged = " + (timeSinceUnplugged / 1000));
679         }
680
681         addPhoneUsage(uSecNow);
682         addScreenUsage(uSecNow);
683         addWiFiUsage(uSecNow);
684         addBluetoothUsage(uSecNow);
685         addIdleUsage(uSecNow); // Not including cellular idle power
686         addRadioUsage(uSecNow);
687     }
688
689     private BatterySipper addEntry(String label, DrainType drainType, long time, int iconId,
690             double power) {
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});
695         bs.usageTime = time;
696         bs.iconId = iconId;
697         mUsageList.add(bs);
698         return bs;
699     }
700
701     private void load() {
702         try {
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);
712         }
713     }
714
715     public void run() {
716         while (true) {
717             BatterySipper bs;
718             synchronized (mRequestQueue) {
719                 if (mRequestQueue.isEmpty() || mAbort) {
720                     mRequestThread = null;
721                     return;
722                 }
723                 bs = mRequestQueue.remove(0);
724             }
725             bs.getNameIcon();
726         }
727     }
728
729     static final int MSG_UPDATE_NAME_ICON = 1;
730
731     Handler mHandler = new Handler() {
732
733         @Override
734         public void handleMessage(Message msg) {
735             switch (msg.what) {
736                 case MSG_UPDATE_NAME_ICON:
737                     BatterySipper bs = (BatterySipper) msg.obj;
738                     PowerGaugePreference pgp = 
739                             (PowerGaugePreference) findPreference(
740                                     Integer.toString(bs.uidObj.getUid()));
741                     if (pgp != null) {
742                         pgp.setPowerIcon(bs.icon);
743                         pgp.setPercent(bs.percent);
744                         pgp.setTitle(bs.name);
745                     }
746                     break;
747             }
748             super.handleMessage(msg);
749         }
750     };
751 }