am 5fdacb8a: am ee455f5a: Merge "People holding partial wake locks now get blamed for CPU usage." into gingerbread

Merge commit '5fdacb8a2818136218afdea4308ad1b10049a201'

* commit '5fdacb8a2818136218afdea4308ad1b10049a201':
  People holding partial wake locks now get blamed for CPU usage.
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a857e58..32fb108 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1586,8 +1586,10 @@
                         sb.append(prefix); sb.append("      CPU: ");
                                 formatTime(sb, userTime); sb.append("usr + ");
                                 formatTime(sb, systemTime); sb.append("krn\n");
-                        sb.append(prefix); sb.append("      "); sb.append(starts);
-                                sb.append(" proc starts");
+                        if (starts != 0) {
+                            sb.append(prefix); sb.append("      "); sb.append(starts);
+                                    sb.append(" proc starts");
+                        }
                         pw.println(sb.toString());
                         for (int e=0; e<numExcessive; e++) {
                             Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a6a031a..9ed3658 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -22,6 +22,8 @@
 import android.net.TrafficStats;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFormatException;
 import android.os.Parcelable;
@@ -79,6 +81,38 @@
 
     private final JournaledFile mFile;
 
+    static final int MSG_UPDATE_WAKELOCKS = 1;
+    static final int MSG_REPORT_POWER_CHANGE = 2;
+    static final long DELAY_UPDATE_WAKELOCKS = 15*1000;
+
+    public interface BatteryCallback {
+        public void batteryNeedsCpuUpdate();
+        public void batteryPowerChanged(boolean onBattery);
+    }
+
+    final class MyHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            BatteryCallback cb = mCallback;
+            switch (msg.what) {
+                case MSG_UPDATE_WAKELOCKS:
+                    if (cb != null) {
+                        cb.batteryNeedsCpuUpdate();
+                    }
+                    break;
+                case MSG_REPORT_POWER_CHANGE:
+                    if (cb != null) {
+                        cb.batteryPowerChanged(msg.arg1 != 0);
+                    }
+                    break;
+            }
+        }
+    }
+
+    private final MyHandler mHandler;
+
+    private BatteryCallback mCallback;
+
     /**
      * The statistics we have collected organized by uids.
      */
@@ -95,6 +129,9 @@
     final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers
             = new SparseArray<ArrayList<StopwatchTimer>>();
 
+    // Last partial timers we use for distributing CPU usage.
+    final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
+
     // These are the objects that will want to do something when the device
     // is unplugged from power.
     final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
@@ -240,6 +277,7 @@
     // For debugging
     public BatteryStatsImpl() {
         mFile = null;
+        mHandler = null;
     }
 
     public static interface Unpluggable {
@@ -739,7 +777,9 @@
      * State for keeping track of timing information.
      */
     public static final class StopwatchTimer extends Timer {
+        final Uid mUid;
         final ArrayList<StopwatchTimer> mTimerPool;
+
         int mNesting;
 
         /**
@@ -757,16 +797,24 @@
 
         long mTimeout;
 
-        StopwatchTimer(int type, ArrayList<StopwatchTimer> timerPool,
+        /**
+         * For partial wake locks, keep track of whether we are in the list
+         * to consume CPU cycles.
+         */
+        boolean mInList;
+
+        StopwatchTimer(Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
                 ArrayList<Unpluggable> unpluggables, Parcel in) {
             super(type, unpluggables, in);
+            mUid = uid;
             mTimerPool = timerPool;
             mUpdateTime = in.readLong();
         }
 
-        StopwatchTimer(int type, ArrayList<StopwatchTimer> timerPool,
+        StopwatchTimer(Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
                 ArrayList<Unpluggable> unpluggables) {
             super(type, unpluggables);
+            mUid = uid;
             mTimerPool = timerPool;
         }
         
@@ -1254,6 +1302,10 @@
             mWakeLockNesting++;
         }
         if (uid >= 0) {
+            if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
+                Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
+                mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
+            }
             getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
         }
     }
@@ -1269,10 +1321,112 @@
             }
         }
         if (uid >= 0) {
+            if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
+                Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
+                mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
+            }
             getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
         }
     }
 
+    public int startAddingCpuLocked() {
+        mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
+
+        if (mScreenOn) {
+            return 0;
+        }
+
+        final int N = mPartialTimers.size();
+        if (N == 0) {
+            mLastPartialTimers.clear();
+            return 0;
+        }
+
+        // How many timers should consume CPU?  Only want to include ones
+        // that have already been in the list.
+        for (int i=0; i<N; i++) {
+            StopwatchTimer st = mPartialTimers.get(i);
+            if (st.mInList) {
+                Uid uid = st.mUid;
+                // We don't include the system UID, because it so often
+                // holds wake locks at one request or another of an app.
+                if (uid != null && uid.mUid != Process.SYSTEM_UID) {
+                    return 50;
+                }
+            }
+        }
+
+        return 0;
+    }
+
+    public void finishAddingCpuLocked(int perc, int utime, int stime, long[] cpuSpeedTimes) {
+        final int N = mPartialTimers.size();
+        if (perc != 0) {
+            int num = 0;
+            for (int i=0; i<N; i++) {
+                StopwatchTimer st = mPartialTimers.get(i);
+                if (st.mInList) {
+                    Uid uid = st.mUid;
+                    // We don't include the system UID, because it so often
+                    // holds wake locks at one request or another of an app.
+                    if (uid != null && uid.mUid != Process.SYSTEM_UID) {
+                        num++;
+                    }
+                }
+            }
+            if (num != 0) {
+                for (int i=0; i<N; i++) {
+                    StopwatchTimer st = mPartialTimers.get(i);
+                    if (st.mInList) {
+                        int myUTime = utime/num;
+                        int mySTime = stime/num;
+                        utime -= myUTime;
+                        stime -= mySTime;
+                        num--;
+                        Uid uid = st.mUid;
+                        if (uid != null && uid.mUid != Process.SYSTEM_UID) {
+                            Uid.Proc proc = uid.getProcessStatsLocked("*wakelock*");
+                            proc.addCpuTimeLocked(myUTime, mySTime);
+                            proc.addSpeedStepTimes(cpuSpeedTimes);
+                        }
+                    }
+                }
+            }
+
+            // Just in case, collect any lost CPU time.
+            if (utime != 0 || stime != 0) {
+                Uid uid = getUidStatsLocked(Process.SYSTEM_UID);
+                if (uid != null) {
+                    Uid.Proc proc = uid.getProcessStatsLocked("*lost*");
+                    proc.addCpuTimeLocked(utime, stime);
+                    proc.addSpeedStepTimes(cpuSpeedTimes);
+                }
+            }
+        }
+
+        final int NL = mLastPartialTimers.size();
+        boolean diff = N != NL;
+        for (int i=0; i<NL && !diff; i++) {
+            diff |= mPartialTimers.get(i) != mLastPartialTimers.get(i);
+        }
+        if (!diff) {
+            for (int i=0; i<NL; i++) {
+                mPartialTimers.get(i).mInList = true;
+            }
+            return;
+        }
+
+        for (int i=0; i<NL; i++) {
+            mLastPartialTimers.get(i).mInList = false;
+        }
+        mLastPartialTimers.clear();
+        for (int i=0; i<N; i++) {
+            StopwatchTimer st = mPartialTimers.get(i);
+            st.mInList = true;
+            mLastPartialTimers.add(st);
+        }
+    }
+
     public void noteProcessDiedLocked(int uid, int pid) {
         Uid u = mUidStats.get(uid);
         if (u != null) {
@@ -1924,13 +2078,18 @@
 
         public Uid(int uid) {
             mUid = uid;
-            mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables);
-            mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK, null, mUnpluggables);
-            mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK, null, mUnpluggables);
-            mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+            mWifiTurnedOnTimer = new StopwatchTimer(Uid.this, WIFI_TURNED_ON,
                     null, mUnpluggables);
-            mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON, null, mUnpluggables);
-            mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON, null, mUnpluggables);
+            mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
+                    null, mUnpluggables);
+            mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK,
+                    null, mUnpluggables);
+            mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
+                    null, mUnpluggables);
+            mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
+                    null, mUnpluggables);
+            mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
+                    null, mUnpluggables);
         }
 
         @Override
@@ -1998,7 +2157,7 @@
             if (!mWifiTurnedOn) {
                 mWifiTurnedOn = true;
                 if (mWifiTurnedOnTimer == null) {
-                    mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON,
+                    mWifiTurnedOnTimer = new StopwatchTimer(Uid.this, WIFI_TURNED_ON,
                             null, mUnpluggables);
                 }
                 mWifiTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2018,7 +2177,7 @@
             if (!mFullWifiLockOut) {
                 mFullWifiLockOut = true;
                 if (mFullWifiLockTimer == null) {
-                    mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK,
+                    mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
                             null, mUnpluggables);
                 }
                 mFullWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2038,7 +2197,7 @@
             if (!mScanWifiLockOut) {
                 mScanWifiLockOut = true;
                 if (mScanWifiLockTimer == null) {
-                    mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK,
+                    mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK,
                             null, mUnpluggables);
                 }
                 mScanWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2058,7 +2217,7 @@
             if (!mWifiMulticastEnabled) {
                 mWifiMulticastEnabled = true;
                 if (mWifiMulticastTimer == null) {
-                    mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+                    mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
                             null, mUnpluggables);
                 }
                 mWifiMulticastTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2078,7 +2237,7 @@
             if (!mAudioTurnedOn) {
                 mAudioTurnedOn = true;
                 if (mAudioTurnedOnTimer == null) {
-                    mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON,
+                    mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
                             null, mUnpluggables);
                 }
                 mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2098,7 +2257,7 @@
             if (!mVideoTurnedOn) {
                 mVideoTurnedOn = true;
                 if (mVideoTurnedOnTimer == null) {
-                    mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON,
+                    mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
                             null, mUnpluggables);
                 }
                 mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
@@ -2458,42 +2617,42 @@
             mTcpBytesSentAtLastUnplug = in.readLong();
             mWifiTurnedOn = false;
             if (in.readInt() != 0) {
-                mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON,
+                mWifiTurnedOnTimer = new StopwatchTimer(Uid.this, WIFI_TURNED_ON,
                         null, mUnpluggables, in);
             } else {
                 mWifiTurnedOnTimer = null;
             }
             mFullWifiLockOut = false;
             if (in.readInt() != 0) {
-                mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK,
+                mFullWifiLockTimer = new StopwatchTimer(Uid.this, FULL_WIFI_LOCK,
                         null, mUnpluggables, in);
             } else {
                 mFullWifiLockTimer = null;
             }
             mScanWifiLockOut = false;
             if (in.readInt() != 0) {
-                mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK,
+                mScanWifiLockTimer = new StopwatchTimer(Uid.this, SCAN_WIFI_LOCK,
                         null, mUnpluggables, in);
             } else {
                 mScanWifiLockTimer = null;
             }
             mWifiMulticastEnabled = false;
             if (in.readInt() != 0) {
-                mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+                mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
                         null, mUnpluggables, in);
             } else {
                 mWifiMulticastTimer = null;
             }
             mAudioTurnedOn = false;
             if (in.readInt() != 0) {
-                mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON,
+                mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
                         null, mUnpluggables, in);
             } else {
                 mAudioTurnedOnTimer = null;
             }
             mVideoTurnedOn = false;
             if (in.readInt() != 0) {
-                mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON,
+                mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
                         null, mUnpluggables, in);
             } else {
                 mVideoTurnedOnTimer = null;
@@ -2540,7 +2699,7 @@
                     return null;
                 }
 
-                return new StopwatchTimer(type, pool, unpluggables, in);
+                return new StopwatchTimer(Uid.this, type, pool, unpluggables, in);
             }
 
             boolean reset() {
@@ -2616,7 +2775,7 @@
                     pool = new ArrayList<StopwatchTimer>();
                     mSensorTimers.put(mHandle, pool);
                 }
-                return new StopwatchTimer(0, pool, unpluggables, in);
+                return new StopwatchTimer(Uid.this, 0, pool, unpluggables, in);
             }
 
             boolean reset() {
@@ -3418,21 +3577,24 @@
                 case WAKE_TYPE_PARTIAL:
                     t = wl.mTimerPartial;
                     if (t == null) {
-                        t = new StopwatchTimer(WAKE_TYPE_PARTIAL, mPartialTimers, mUnpluggables);
+                        t = new StopwatchTimer(Uid.this, WAKE_TYPE_PARTIAL,
+                                mPartialTimers, mUnpluggables);
                         wl.mTimerPartial = t;
                     }
                     return t;
                 case WAKE_TYPE_FULL:
                     t = wl.mTimerFull;
                     if (t == null) {
-                        t = new StopwatchTimer(WAKE_TYPE_FULL, mFullTimers, mUnpluggables);
+                        t = new StopwatchTimer(Uid.this, WAKE_TYPE_FULL,
+                                mFullTimers, mUnpluggables);
                         wl.mTimerFull = t;
                     }
                     return t;
                 case WAKE_TYPE_WINDOW:
                     t = wl.mTimerWindow;
                     if (t == null) {
-                        t = new StopwatchTimer(WAKE_TYPE_WINDOW, mWindowTimers, mUnpluggables);
+                        t = new StopwatchTimer(Uid.this, WAKE_TYPE_WINDOW,
+                                mWindowTimers, mUnpluggables);
                         wl.mTimerWindow = t;
                     }
                     return t;
@@ -3459,7 +3621,7 @@
                 timers = new ArrayList<StopwatchTimer>();
                 mSensorTimers.put(sensor, timers);
             }
-            t = new StopwatchTimer(BatteryStats.SENSOR, timers, mUnpluggables);
+            t = new StopwatchTimer(Uid.this, BatteryStats.SENSOR, timers, mUnpluggables);
             se.mTimer = t;
             return t;
         }
@@ -3532,25 +3694,26 @@
 
     public BatteryStatsImpl(String filename) {
         mFile = new JournaledFile(new File(filename), new File(filename + ".tmp"));
+        mHandler = new MyHandler();
         mStartCount++;
-        mScreenOnTimer = new StopwatchTimer(-1, null, mUnpluggables);
+        mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
-            mScreenBrightnessTimer[i] = new StopwatchTimer(-100-i, null, mUnpluggables);
+            mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mUnpluggables);
         }
         mInputEventCounter = new Counter(mUnpluggables);
-        mPhoneOnTimer = new StopwatchTimer(-2, null, mUnpluggables);
+        mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables);
         for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
-            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(-200-i, null, mUnpluggables);
+            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null, mUnpluggables);
         }
-        mPhoneSignalScanningTimer = new StopwatchTimer(-200+1, null, mUnpluggables);
+        mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mUnpluggables);
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
-            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(-300-i, null, mUnpluggables);
+            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i, null, mUnpluggables);
         }
-        mWifiOnTimer = new StopwatchTimer(-3, null, mUnpluggables);
-        mWifiRunningTimer = new StopwatchTimer(-4, null, mUnpluggables);
-        mBluetoothOnTimer = new StopwatchTimer(-5, null, mUnpluggables);
-        mAudioOnTimer = new StopwatchTimer(-6, null, mUnpluggables);
-        mVideoOnTimer = new StopwatchTimer(-7, null, mUnpluggables);
+        mWifiOnTimer = new StopwatchTimer(null, -3, null, mUnpluggables);
+        mWifiRunningTimer = new StopwatchTimer(null, -4, null, mUnpluggables);
+        mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mUnpluggables);
+        mAudioOnTimer = new StopwatchTimer(null, -6, null, mUnpluggables);
+        mVideoOnTimer = new StopwatchTimer(null, -7, null, mUnpluggables);
         mOnBattery = mOnBatteryInternal = false;
         initTimes();
         mTrackBatteryPastUptime = 0;
@@ -3568,9 +3731,14 @@
 
     public BatteryStatsImpl(Parcel p) {
         mFile = null;
+        mHandler = null;
         readFromParcel(p);
     }
 
+    public void setCallback(BatteryCallback cb) {
+        mCallback = cb;
+    }
+
     public void setNumSpeedSteps(int steps) {
         if (sNumSpeedSteps == 0) sNumSpeedSteps = steps;
     }
@@ -3655,6 +3823,9 @@
     void setOnBattery(boolean onBattery, int oldStatus, int level) {
         synchronized(this) {
             boolean doWrite = false;
+            Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
+            m.arg1 = onBattery ? 1 : 0;
+            mHandler.sendMessage(m);
             mOnBattery = mOnBatteryInternal = onBattery;
             
             long uptime = SystemClock.uptimeMillis() * 1000;
@@ -4555,26 +4726,29 @@
         mBatteryRealtime = in.readLong();
         mBatteryLastRealtime = 0;
         mScreenOn = false;
-        mScreenOnTimer = new StopwatchTimer(-1, null, mUnpluggables, in);
+        mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables, in);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
-            mScreenBrightnessTimer[i] = new StopwatchTimer(-100-i, null, mUnpluggables, in);
+            mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i,
+                    null, mUnpluggables, in);
         }
         mInputEventCounter = new Counter(mUnpluggables, in);
         mPhoneOn = false;
-        mPhoneOnTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
+        mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
-            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(-200-i, null, mUnpluggables, in);
+            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i,
+                    null, mUnpluggables, in);
         }
-        mPhoneSignalScanningTimer = new StopwatchTimer(-200+1, null, mUnpluggables, in);
+        mPhoneSignalScanningTimer = new StopwatchTimer(null, -200+1, null, mUnpluggables, in);
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
-            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(-300-i, null, mUnpluggables, in);
+            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(null, -300-i,
+                    null, mUnpluggables, in);
         }
         mWifiOn = false;
-        mWifiOnTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
+        mWifiOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         mWifiRunning = false;
-        mWifiRunningTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
+        mWifiRunningTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         mBluetoothOn = false;
-        mBluetoothOnTimer = new StopwatchTimer(-2, null, mUnpluggables, in);
+        mBluetoothOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in);
         mUptime = in.readLong();
         mUptimeStart = in.readLong();
         mLastUptime = 0;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2ce8c08..7d8c307 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -132,7 +132,8 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
-public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor {
+public final class ActivityManagerService extends ActivityManagerNative
+        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
     static final String TAG = "ActivityManager";
     static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
@@ -751,6 +752,7 @@
     boolean mBooting = false;
     boolean mWaitingUpdate = false;
     boolean mDidUpdate = false;
+    boolean mOnBattery = false;
 
     Context mContext;
 
@@ -1382,8 +1384,10 @@
                 systemDir, "batterystats.bin").toString());
         mBatteryStatsService.getActiveStatistics().readLocked();
         mBatteryStatsService.getActiveStatistics().writeLocked();
+        mOnBattery = mBatteryStatsService.getActiveStatistics().getIsOnBattery();
+        mBatteryStatsService.getActiveStatistics().setCallback(this);
         
-        mUsageStatsService = new UsageStatsService( new File(
+        mUsageStatsService = new UsageStatsService(new File(
                 systemDir, "usagestats").toString());
 
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
@@ -1496,25 +1500,36 @@
             synchronized(bstats) {
                 synchronized(mPidsSelfLocked) {
                     if (haveNewCpuStats) {
-                        if (mBatteryStatsService.isOnBattery()) {
+                        if (mOnBattery) {
+                            int perc = bstats.startAddingCpuLocked();
+                            int totalUTime = 0;
+                            int totalSTime = 0;
                             final int N = mProcessStats.countWorkingStats();
                             for (int i=0; i<N; i++) {
                                 ProcessStats.Stats st
                                         = mProcessStats.getWorkingStats(i);
                                 ProcessRecord pr = mPidsSelfLocked.get(st.pid);
+                                int otherUTime = (st.rel_utime*perc)/100;
+                                int otherSTime = (st.rel_stime*perc)/100;
+                                totalUTime += otherUTime;
+                                totalSTime += otherSTime;
                                 if (pr != null) {
                                     BatteryStatsImpl.Uid.Proc ps = pr.batteryStats;
-                                    ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+                                    ps.addCpuTimeLocked(st.rel_utime-otherUTime,
+                                            st.rel_stime-otherSTime);
                                     ps.addSpeedStepTimes(cpuSpeedTimes);
                                 } else {
                                     BatteryStatsImpl.Uid.Proc ps =
                                             bstats.getProcessStatsLocked(st.name, st.pid);
                                     if (ps != null) {
-                                        ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+                                        ps.addCpuTimeLocked(st.rel_utime-otherUTime,
+                                                st.rel_stime-otherSTime);
                                         ps.addSpeedStepTimes(cpuSpeedTimes);
                                     }
                                 }
                             }
+                            bstats.finishAddingCpuLocked(perc, totalUTime,
+                                    totalSTime, cpuSpeedTimes);
                         }
                     }
                 }
@@ -1527,6 +1542,23 @@
         }
     }
     
+    @Override
+    public void batteryNeedsCpuUpdate() {
+        updateCpuStatsNow();
+    }
+
+    @Override
+    public void batteryPowerChanged(boolean onBattery) {
+        // When plugging in, update the CPU stats first before changing
+        // the plug state.
+        updateCpuStatsNow();
+        synchronized (this) {
+            synchronized(mPidsSelfLocked) {
+                mOnBattery = onBattery;
+            }
+        }
+    }
+
     /**
      * Initialize the application bind args. These are passed to each
      * process when the bindApplication() IPC is sent to the process. They're
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 6d1fbab..67df707 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -131,6 +131,13 @@
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
 
+        long wtime;
+        synchronized (batteryStats.getBatteryStats()) {
+            wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid,
+                    pid, SystemClock.elapsedRealtime());
+        }
+        long timeUsed = wtime - lastWakeTime;
+
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
         }
@@ -182,7 +189,9 @@
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.println(lruSeq);
         pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
-                pw.print(" lastRequestedGc=");
+                pw.print(" time used=");
+                TimeUtils.formatDuration(timeUsed, pw); pw.println("");
+        pw.print(prefix); pw.print("lastRequestedGc=");
                 TimeUtils.formatDuration(lastRequestedGc, now, pw);
                 pw.print(" lastLowMemory=");
                 TimeUtils.formatDuration(lastLowMemory, now, pw);