Merge "DO NOT MERGE Always set/remove default routes." into gingerbread
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d66e98b..d5741fc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -365,7 +365,8 @@
         
         /**
          * The time when the service was first made active, either by someone
-         * starting or binding to it.
+         * starting or binding to it.  This
+         * is in units of {@link android.os.SystemClock#elapsedRealtime()}.
          */
         public long activeSince;
         
@@ -387,7 +388,8 @@
         
         /**
          * The time when there was last activity in the service (either
-         * explicit requests to start it or clients binding to it).
+         * explicit requests to start it or clients binding to it).  This
+         * is in units of {@link android.os.SystemClock#uptimeMillis()}.
          */
         public long lastActivityTime;
         
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a699388..a0a3bdf 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -290,6 +290,11 @@
          */
         public static abstract class Proc {
 
+            public static class ExcessiveWake {
+                public long overTime;
+                public long usedTime;
+            }
+
             /**
              * Returns the total time (in 1/100 sec) spent executing in user code.
              *
@@ -326,6 +331,10 @@
              * @see BatteryStats#getCpuSpeedSteps()
              */
             public abstract long getTimeAtCpuSpeedStep(int speedStep, int which);
+
+            public abstract int countExcessiveWakes();
+
+            public abstract ExcessiveWake getExcessiveWake(int i);
         }
 
         /**
@@ -421,6 +430,8 @@
         public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
         public static final int STATE_AUDIO_ON_FLAG = 1<<19;
         public static final int STATE_VIDEO_ON_FLAG = 1<<18;
+        public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
+        public static final int STATE_SENSOR_ON_FLAG = 1<<16;
         
         public int states;
 
@@ -470,6 +481,16 @@
             batteryVoltage = o.batteryVoltage;
             states = o.states;
         }
+
+        public boolean same(HistoryItem o) {
+            return batteryLevel == o.batteryLevel
+                    && batteryStatus == o.batteryStatus
+                    && batteryHealth == o.batteryHealth
+                    && batteryPlugType == o.batteryPlugType
+                    && batteryTemperature == o.batteryTemperature
+                    && batteryVoltage == o.batteryVoltage
+                    && states == o.states;
+        }
     }
     
     public static final class BitDescription {
@@ -633,6 +654,8 @@
         new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth"),
         new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio"),
         new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video"),
+        new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock"),
+        new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor"),
         new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
                 HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness",
                 SCREEN_BRIGHTNESS_NAMES),
@@ -1376,7 +1399,6 @@
                         pw.println(getDischargeStartLevel());
                 pw.print(prefix); pw.print("    Discharge cycle current level: ");
                         pw.println(getDischargeCurrentLevel());
-            } else {
                 pw.print(prefix); pw.println("  Device is currently plugged into power");
                 pw.print(prefix); pw.print("    Last discharge cycle start level: "); 
                         pw.println(getDischargeStartLevel());
@@ -1384,6 +1406,13 @@
                         pw.println(getDischargeCurrentLevel());
             }
             pw.println(" ");
+        } else {
+            pw.print(prefix); pw.println("  Device battery use since last full charge");
+            pw.print(prefix); pw.print("    Amount discharged (lower bound): ");
+                    pw.println(getLowDischargeAmountSinceCharge());
+            pw.print(prefix); pw.print("    Amount discharged (upper bound): ");
+                    pw.println(getHighDischargeAmountSinceCharge());
+            pw.println(" ");
         }
         
 
@@ -1524,12 +1553,16 @@
                     long userTime;
                     long systemTime;
                     int starts;
+                    int numExcessive;
 
                     userTime = ps.getUserTime(which);
                     systemTime = ps.getSystemTime(which);
                     starts = ps.getStarts(which);
+                    numExcessive = which == STATS_SINCE_CHARGED
+                            ? ps.countExcessiveWakes() : 0;
 
-                    if (userTime != 0 || systemTime != 0 || starts != 0) {
+                    if (userTime != 0 || systemTime != 0 || starts != 0
+                            || numExcessive != 0) {
                         sb.setLength(0);
                         sb.append(prefix); sb.append("    Proc ");
                                 sb.append(ent.getKey()); sb.append(":\n");
@@ -1539,6 +1572,16 @@
                         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);
+                            if (ew != null) {
+                                pw.print(prefix); pw.print("      * Killed for wake lock use: ");
+                                        pw.print(ew.usedTime); pw.print("ms over ");
+                                        pw.print(ew.overTime); pw.print("ms (");
+                                        pw.print((ew.usedTime*100)/ew.overTime);
+                                        pw.println("%)");
+                            }
+                        }
                         uidActivity = true;
                     }
                 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index d040d3f..1620778 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -22,8 +22,8 @@
 
 interface IBatteryStats {
     byte[] getStatistics();
-    void noteStartWakelock(int uid, String name, int type);
-    void noteStopWakelock(int uid, String name, int type);
+    void noteStartWakelock(int uid, int pid, String name, int type);
+    void noteStopWakelock(int uid, int pid, String name, int type);
     
     /* DO NOT CHANGE the position of noteStartSensor without updating
        SensorService.cpp */
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b3323e9..2f26135 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -27,6 +27,7 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.BatteryStats.Uid.Proc.ExcessiveWake;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
@@ -63,7 +64,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 49;
+    private static final int VERSION = 50;
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 1000;
@@ -107,6 +108,7 @@
     int mNumHistoryItems;
     HistoryItem mHistory;
     HistoryItem mHistoryEnd;
+    HistoryItem mHistoryLastEnd;
     HistoryItem mHistoryCache;
     final HistoryItem mHistoryCur = new HistoryItem();
     
@@ -451,7 +453,7 @@
          * Clear state of this timer.  Returns true if the timer is inactive
          * so can be completely dropped.
          */
-        boolean reset(boolean detachIfReset) {
+        boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
             mTotalTime = mLoadedTime = mLastTime = 0;
             mCount = mLoadedCount = mLastCount = 0;
             if (detachIfReset) {
@@ -713,8 +715,8 @@
             out.writeInt(mTrackingReportedValues ? 1 : 0);
         }
         
-        boolean reset(boolean detachIfReset) {
-            super.reset(detachIfReset);
+        boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+            super.reset(stats, detachIfReset);
             setStale();
             return true;
         }
@@ -749,7 +751,7 @@
         long mUpdateTime;
         
         /**
-         * The total time at which the timer was acquired, to determine if
+         * The total time at which the timer was acquired, to determine if it
          * was actually held for an interesting duration.
          */
         long mAcquireTime;
@@ -890,9 +892,14 @@
             return mCount;
         }
 
-        boolean reset(boolean detachIfReset) {
+        boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
             boolean canDetach = mNesting <= 0;
-            super.reset(canDetach && detachIfReset);
+            super.reset(stats, canDetach && detachIfReset);
+            if (mNesting > 0) {
+                mUpdateTime = stats.getBatteryRealtimeLocked(
+                        SystemClock.elapsedRealtime() * 1000);
+            }
+            mAcquireTime = mTotalTime;
             return canDetach;
         }
         
@@ -1113,6 +1120,26 @@
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
+
+        // If the current time is basically the same as the last time,
+        // just collapse into one record.
+        if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
+                && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) {
+            // If the current is the same as the one before, then we no
+            // longer need the entry.
+            if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
+                    && mHistoryLastEnd.same(mHistoryCur)) {
+                mHistoryLastEnd.next = null;
+                mHistoryEnd.next = mHistoryCache;
+                mHistoryCache = mHistoryEnd;
+                mHistoryEnd = mHistoryLastEnd;
+                mHistoryLastEnd = null;
+            } else {
+                mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+            }
+            return;
+        }
+
         if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
             // Once we've reached the maximum number of items, we only
             // record changes to the battery level.
@@ -1121,6 +1148,7 @@
                 return;
             }
         }
+
         addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
     }
     
@@ -1139,6 +1167,7 @@
     void addHistoryRecordLocked(HistoryItem rec) {
         mNumHistoryItems++;
         rec.next = null;
+        mHistoryLastEnd = mHistoryEnd;
         if (mHistoryEnd != null) {
             mHistoryEnd.next = rec;
             mHistoryEnd = rec;
@@ -1151,7 +1180,7 @@
         if (mHistory != null) {
             mHistoryEnd.next = mHistoryCache;
             mHistoryCache = mHistory;
-            mHistory = mHistoryEnd = null;
+            mHistory = mHistoryLastEnd = mHistoryEnd = null;
         }
         mNumHistoryItems = 0;
         mHistoryBaseTime = 0;
@@ -1209,6 +1238,83 @@
         mBluetoothPingStart = -1;
     }
 
+    int mWakeLockNesting;
+
+    public void noteStartWakeLocked(int uid, int pid, String name, int type) {
+        if (mWakeLockNesting == 0) {
+            mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+        }
+        mWakeLockNesting++;
+        if (uid >= 0) {
+            getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
+        }
+    }
+
+    public void noteStopWakeLocked(int uid, int pid, String name, int type) {
+        mWakeLockNesting--;
+        if (mWakeLockNesting == 0) {
+            mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+        }
+        if (uid >= 0) {
+            getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
+        }
+    }
+
+    public void noteProcessDiedLocked(int uid, int pid) {
+        Uid u = mUidStats.get(uid);
+        if (u != null) {
+            u.mPids.remove(pid);
+        }
+    }
+
+    public long getProcessWakeTime(int uid, int pid, long realtime) {
+        Uid u = mUidStats.get(uid);
+        if (u != null) {
+            Uid.Pid p = u.mPids.get(pid);
+            if (p != null) {
+                return p.mWakeSum + (p.mWakeStart != 0 ? (realtime - p.mWakeStart) : 0);
+            }
+        }
+        return 0;
+    }
+
+    public void reportExcessiveWakeLocked(int uid, String proc, long overTime, long usedTime) {
+        Uid u = mUidStats.get(uid);
+        if (u != null) {
+            u.reportExcessiveWakeLocked(proc, overTime, usedTime);
+        }
+    }
+
+    int mSensorNesting;
+
+    public void noteStartSensorLocked(int uid, int sensor) {
+        if (mSensorNesting == 0) {
+            mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+        }
+        mSensorNesting++;
+        getUidStatsLocked(uid).noteStartSensor(sensor);
+    }
+
+    public void noteStopSensorLocked(int uid, int sensor) {
+        mSensorNesting--;
+        if (mSensorNesting == 0) {
+            mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+        }
+        getUidStatsLocked(uid).noteStopSensor(sensor);
+    }
+
     int mGpsNesting;
     
     public void noteStartGpsLocked(int uid) {
@@ -1244,6 +1350,10 @@
             if (mScreenBrightnessBin >= 0) {
                 mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this);
             }
+
+            // Fake a wake lock, so we consider the device waked as long
+            // as the screen is on.
+            noteStartWakeLocked(-1, -1, "dummy", 0);
         }
     }
     
@@ -1258,6 +1368,8 @@
             if (mScreenBrightnessBin >= 0) {
                 mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
             }
+
+            noteStopWakeLocked(-1, -1, "dummy", 0);
         }
     }
     
@@ -1798,6 +1910,11 @@
          */
         final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>();
         
+        /**
+         * The transient wake stats we have collected for this uid's pids.
+         */
+        final SparseArray<Pid> mPids = new SparseArray<Pid>();
+
         public Uid(int uid) {
             mUid = uid;
             mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables);
@@ -2081,27 +2198,27 @@
             boolean active = false;
             
             if (mWifiTurnedOnTimer != null) {
-                active |= !mWifiTurnedOnTimer.reset(false);
+                active |= !mWifiTurnedOnTimer.reset(BatteryStatsImpl.this, false);
                 active |= mWifiTurnedOn;
             }
             if (mFullWifiLockTimer != null) {
-                active |= !mFullWifiLockTimer.reset(false);
+                active |= !mFullWifiLockTimer.reset(BatteryStatsImpl.this, false);
                 active |= mFullWifiLockOut;
             }
             if (mScanWifiLockTimer != null) {
-                active |= !mScanWifiLockTimer.reset(false);
+                active |= !mScanWifiLockTimer.reset(BatteryStatsImpl.this, false);
                 active |= mScanWifiLockOut;
             }
             if (mWifiMulticastTimer != null) {
-                active |= !mWifiMulticastTimer.reset(false);
+                active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false);
                 active |= mWifiMulticastEnabled;
             }
             if (mAudioTurnedOnTimer != null) {
-                active |= !mAudioTurnedOnTimer.reset(false);
+                active |= !mAudioTurnedOnTimer.reset(BatteryStatsImpl.this, false);
                 active |= mAudioTurnedOn;
             }
             if (mVideoTurnedOnTimer != null) {
-                active |= !mVideoTurnedOnTimer.reset(false);
+                active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
                 active |= mVideoTurnedOn;
             }
             
@@ -2146,6 +2263,14 @@
                 }
                 mProcessStats.clear();
             }
+            if (mPids.size() > 0) {
+                for (int i=0; !active && i<mPids.size(); i++) {
+                    Pid pid = mPids.valueAt(i);
+                    if (pid.mWakeStart != 0) {
+                        active = true;
+                    }
+                }
+            }
             if (mPackageStats.size() > 0) {
                 Iterator<Map.Entry<String, Pkg>> it = mPackageStats.entrySet().iterator();
                 while (it.hasNext()) {
@@ -2164,6 +2289,8 @@
                 mPackageStats.clear();
             }
             
+            mPids.clear();
+
             if (!active) {
                 if (mWifiTurnedOnTimer != null) {
                     mWifiTurnedOnTimer.detach();
@@ -2412,13 +2539,13 @@
             boolean reset() {
                 boolean wlactive = false;
                 if (mTimerFull != null) {
-                    wlactive |= !mTimerFull.reset(false);
+                    wlactive |= !mTimerFull.reset(BatteryStatsImpl.this, false);
                 }
                 if (mTimerPartial != null) {
-                    wlactive |= !mTimerPartial.reset(false);
+                    wlactive |= !mTimerPartial.reset(BatteryStatsImpl.this, false);
                 }
                 if (mTimerWindow != null) {
-                    wlactive |= !mTimerWindow.reset(false);
+                    wlactive |= !mTimerWindow.reset(BatteryStatsImpl.this, false);
                 }
                 if (!wlactive) {
                     if (mTimerFull != null) {
@@ -2486,7 +2613,7 @@
             }
 
             boolean reset() {
-                if (mTimer.reset(true)) {
+                if (mTimer.reset(BatteryStatsImpl.this, true)) {
                     mTimer = null;
                     return true;
                 }
@@ -2598,6 +2725,8 @@
 
             SamplingCounter[] mSpeedBins;
 
+            ArrayList<ExcessiveWake> mExcessiveWake;
+
             Proc() {
                 mUnpluggables.add(this);
                 mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
@@ -2624,6 +2753,58 @@
                 }
             }
             
+            public int countExcessiveWakes() {
+                return mExcessiveWake != null ? mExcessiveWake.size() : 0;
+            }
+
+            public ExcessiveWake getExcessiveWake(int i) {
+                if (mExcessiveWake != null) {
+                    return mExcessiveWake.get(i);
+                }
+                return null;
+            }
+
+            public void addExcessiveWake(long overTime, long usedTime) {
+                if (mExcessiveWake == null) {
+                    mExcessiveWake = new ArrayList<ExcessiveWake>();
+                }
+                ExcessiveWake ew = new ExcessiveWake();
+                ew.overTime = overTime;
+                ew.usedTime = usedTime;
+                mExcessiveWake.add(ew);
+            }
+
+            void writeExcessiveWakeToParcelLocked(Parcel out) {
+                if (mExcessiveWake == null) {
+                    out.writeInt(0);
+                    return;
+                }
+
+                final int N = mExcessiveWake.size();
+                out.writeInt(N);
+                for (int i=0; i<N; i++) {
+                    ExcessiveWake ew = mExcessiveWake.get(i);
+                    out.writeLong(ew.overTime);
+                    out.writeLong(ew.usedTime);
+                }
+            }
+
+            void readExcessiveWakeFromParcelLocked(Parcel in) {
+                final int N = in.readInt();
+                if (N == 0) {
+                    mExcessiveWake = null;
+                    return;
+                }
+
+                mExcessiveWake = new ArrayList<ExcessiveWake>();
+                for (int i=0; i<N; i++) {
+                    ExcessiveWake ew = new ExcessiveWake();
+                    ew.overTime = in.readLong();
+                    ew.usedTime = in.readLong();
+                    mExcessiveWake.add(ew);
+                }
+            }
+
             void writeToParcelLocked(Parcel out) {
                 out.writeLong(mUserTime);
                 out.writeLong(mSystemTime);
@@ -2648,6 +2829,8 @@
                         out.writeInt(0);
                     }
                 }
+
+                writeExcessiveWakeToParcelLocked(out);
             }
 
             void readFromParcelLocked(Parcel in) {
@@ -2676,6 +2859,8 @@
                         mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
                     }
                 }
+
+                readExcessiveWakeFromParcelLocked(in);
             }
 
             public BatteryStatsImpl getBatteryStats() {
@@ -3153,6 +3338,11 @@
             }
         }
 
+        public class Pid {
+            long mWakeSum;
+            long mWakeStart;
+        }
+
         /**
          * Retrieve the statistics object for a particular process, creating
          * if needed.
@@ -3167,6 +3357,15 @@
             return ps;
         }
 
+        public Pid getPidStatsLocked(int pid) {
+            Pid p = mPids.get(pid);
+            if (p == null) {
+                p = new Pid();
+                mPids.put(pid, p);
+            }
+            return p;
+        }
+
         /**
          * Retrieve the statistics object for a particular service, creating
          * if needed.
@@ -3259,18 +3458,36 @@
             return t;
         }
 
-        public void noteStartWakeLocked(String name, int type) {
+        public void noteStartWakeLocked(int pid, String name, int type) {
             StopwatchTimer t = getWakeTimerLocked(name, type);
             if (t != null) {
                 t.startRunningLocked(BatteryStatsImpl.this);
             }
+            if (pid >= 0) {
+                Pid p = getPidStatsLocked(pid);
+                p.mWakeStart = SystemClock.elapsedRealtime();
+            }
         }
 
-        public void noteStopWakeLocked(String name, int type) {
+        public void noteStopWakeLocked(int pid, String name, int type) {
             StopwatchTimer t = getWakeTimerLocked(name, type);
             if (t != null) {
                 t.stopRunningLocked(BatteryStatsImpl.this);
             }
+            if (pid >= 0) {
+                Pid p = mPids.get(pid);
+                if (p != null) {
+                    p.mWakeSum += SystemClock.elapsedRealtime() - p.mWakeStart;
+                    p.mWakeStart = 0;
+                }
+            }
+        }
+
+        public void reportExcessiveWakeLocked(String proc, long overTime, long usedTime) {
+            Proc p = getProcessStatsLocked(proc);
+            if (p != null) {
+                p.addExcessiveWake(overTime, usedTime);
+            }
         }
         
         public void noteStartSensor(int sensor) {
@@ -3372,6 +3589,10 @@
         return mOnBattery;
     }
 
+    public boolean isScreenOn() {
+        return mScreenOn;
+    }
+
     void initTimes() {
         mBatteryRealtime = mTrackBatteryPastUptime = 0;
         mBatteryUptime = mTrackBatteryPastRealtime = 0;
@@ -3384,24 +3605,24 @@
     public void resetAllStatsLocked() {
         mStartCount = 0;
         initTimes();
-        mScreenOnTimer.reset(false);
+        mScreenOnTimer.reset(this, false);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
-            mScreenBrightnessTimer[i].reset(false);
+            mScreenBrightnessTimer[i].reset(this, false);
         }
         mInputEventCounter.reset(false);
-        mPhoneOnTimer.reset(false);
-        mAudioOnTimer.reset(false);
-        mVideoOnTimer.reset(false);
+        mPhoneOnTimer.reset(this, false);
+        mAudioOnTimer.reset(this, false);
+        mVideoOnTimer.reset(this, false);
         for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
-            mPhoneSignalStrengthsTimer[i].reset(false);
+            mPhoneSignalStrengthsTimer[i].reset(this, false);
         }
-        mPhoneSignalScanningTimer.reset(false);
+        mPhoneSignalScanningTimer.reset(this, false);
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
-            mPhoneDataConnectionsTimer[i].reset(false);
+            mPhoneDataConnectionsTimer[i].reset(this, false);
         }
-        mWifiOnTimer.reset(false);
-        mWifiRunningTimer.reset(false);
-        mBluetoothOnTimer.reset(false);
+        mWifiOnTimer.reset(this, false);
+        mWifiRunningTimer.reset(this, false);
+        mBluetoothOnTimer.reset(this, false);
         
         for (int i=0; i<mUidStats.size(); i++) {
             if (mUidStats.valueAt(i).reset()) {
@@ -4083,6 +4304,7 @@
                 p.mUserTime = p.mLoadedUserTime = in.readLong();
                 p.mSystemTime = p.mLoadedSystemTime = in.readLong();
                 p.mStarts = p.mLoadedStarts = in.readInt();
+                p.readExcessiveWakeFromParcelLocked(in);
             }
 
             NP = in.readInt();
@@ -4271,6 +4493,7 @@
                     out.writeLong(ps.mUserTime);
                     out.writeLong(ps.mSystemTime);
                     out.writeInt(ps.mStarts);
+                    ps.writeExcessiveWakeToParcelLocked(out);
                 }
             }
 
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2fb481c..4ee89cc 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -151,6 +151,7 @@
     static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;
 
     private final int MY_UID;
+    private final int MY_PID;
 
     private boolean mDoneBooting = false;
     private boolean mBootCompleted = false;
@@ -309,7 +310,7 @@
                 long ident = Binder.clearCallingIdentity();
                 try {
                     PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
-                            MY_UID, mTag);
+                            MY_UID, MY_PID, mTag);
                     mHeld = true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -434,11 +435,11 @@
         }
     }
 
-    PowerManagerService()
-    {
+    PowerManagerService() {
         // Hack to get our uid...  should have a func for this.
         long token = Binder.clearCallingIdentity();
-        MY_UID = Binder.getCallingUid();
+        MY_UID = Process.myUid();
+        MY_PID = Process.myPid();
         Binder.restoreCallingIdentity(token);
 
         // XXX remove this when the kernel doesn't timeout wake locks
@@ -573,13 +574,13 @@
 
     private class WakeLock implements IBinder.DeathRecipient
     {
-        WakeLock(int f, IBinder b, String t, int u) {
+        WakeLock(int f, IBinder b, String t, int u, int p) {
             super();
             flags = f;
             binder = b;
             tag = t;
             uid = u == MY_UID ? Process.SYSTEM_UID : u;
-            pid = Binder.getCallingPid();
+            pid = p;
             if (u != MY_UID || (
                     !"KEEP_SCREEN_ON_FLAG".equals(tag)
                     && !"KeyInputQueue".equals(tag))) {
@@ -631,21 +632,23 @@
 
     public void acquireWakeLock(int flags, IBinder lock, String tag) {
         int uid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
         if (uid != Process.myUid()) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
         }
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mLocks) {
-                acquireWakeLockLocked(flags, lock, uid, tag);
+                acquireWakeLockLocked(flags, lock, uid, pid, tag);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
-    public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) {
+    public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) {
         int acquireUid = -1;
+        int acquirePid = -1;
         String acquireName = null;
         int acquireType = -1;
 
@@ -657,7 +660,7 @@
         WakeLock wl;
         boolean newlock;
         if (index < 0) {
-            wl = new WakeLock(flags, lock, tag, uid);
+            wl = new WakeLock(flags, lock, tag, uid, pid);
             switch (wl.flags & LOCK_MASK)
             {
                 case PowerManager.FULL_WAKE_LOCK:
@@ -730,13 +733,14 @@
         }
         if (newlock) {
             acquireUid = wl.uid;
+            acquirePid = wl.pid;
             acquireName = wl.tag;
             acquireType = wl.monitorType;
         }
 
         if (acquireType >= 0) {
             try {
-                mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType);
+                mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType);
             } catch (RemoteException e) {
                 // Ignore
             }
@@ -756,6 +760,7 @@
 
     private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
         int releaseUid;
+        int releasePid;
         String releaseName;
         int releaseType;
 
@@ -800,13 +805,14 @@
         // Unlink the lock from the binder.
         wl.binder.unlinkToDeath(wl, 0);
         releaseUid = wl.uid;
+        releasePid = wl.pid;
         releaseName = wl.tag;
         releaseType = wl.monitorType;
 
         if (releaseType >= 0) {
             long origId = Binder.clearCallingIdentity();
             try {
-                mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType);
+                mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType);
             } catch (RemoteException e) {
                 // Ignore
             } finally {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 59deef3..e259887 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -8081,12 +8081,12 @@
                     if (oldHold != newHold) {
                         try {
                             if (oldHold != null) {
-                                mBatteryStats.noteStopWakelock(oldHold.mUid,
+                                mBatteryStats.noteStopWakelock(oldHold.mUid, -1,
                                         "window",
                                         BatteryStats.WAKE_TYPE_WINDOW);
                             }
                             if (newHold != null) {
-                                mBatteryStats.noteStartWakelock(newHold.mUid,
+                                mBatteryStats.noteStartWakelock(newHold.mUid, -1,
                                         "window",
                                         BatteryStats.WAKE_TYPE_WINDOW);
                             }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index dff6a8a..e5d1025 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -199,6 +199,9 @@
     // The minimum amount of time between successive GC requests for a process.
     static final int GC_MIN_INTERVAL = 60*1000;
 
+    // The rate at which we check for apps using excessive wake locks -- 15 mins.
+    static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000;
+
     // How long we allow a receiver to run before giving up on it.
     static final int BROADCAST_TIMEOUT = 10*1000;
 
@@ -770,6 +773,11 @@
     boolean mDidAppSwitch;
     
     /**
+     * Last time (in realtime) at which we checked for wake lock usage.
+     */
+    long mLastWakeLockCheckTime;
+
+    /**
      * Set while we are wanting to sleep, to prevent any
      * activities from being started/resumed.
      */
@@ -914,6 +922,7 @@
     static final int POST_HEAVY_NOTIFICATION_MSG = 24;
     static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
     static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
+    static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
 
     AlertDialog mUidAlert;
 
@@ -1173,6 +1182,16 @@
                 } catch (RemoteException e) {
                 }
             } break;
+            case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
+                synchronized (ActivityManagerService.this) {
+                    checkExcessiveWakeLocksLocked(true);
+                    mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+                    if (mSleeping) {
+                        Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+                        mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+                    }
+                }
+            } break;
             }
         }
     };
@@ -2555,6 +2574,11 @@
 
         mProcDeaths[0]++;
         
+        BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        synchronized (stats) {
+            stats.noteProcessDiedLocked(app.info.uid, pid);
+        }
+
         // Clean up already done if the process has been re-started.
         if (app.pid == pid && app.thread != null &&
                 app.thread.asBinder() == thread.asBinder()) {
@@ -3570,6 +3594,9 @@
             }
             
             if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+                // Start looking for apps that are abusing wake locks.
+                Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+                mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
                 // Tell anyone interested that we are done booting!
                 SystemProperties.set("sys.boot_completed", "1");
                 broadcastIntentLocked(null, null,
@@ -5375,6 +5402,12 @@
             } else {
                 Slog.w(TAG, "goingToSleep with no resumed activity!");
             }
+
+            // Initialize the wake times of all processes.
+            checkExcessiveWakeLocksLocked(false);
+            mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+            Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+            mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
         }
     }
 
@@ -5424,6 +5457,7 @@
             mWindowManager.setEventDispatching(true);
             mSleeping = false;
             mMainStack.resumeTopActivityLocked(null);
+            mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
         }
     }
 
@@ -11259,6 +11293,52 @@
         }
     }
 
+    final void checkExcessiveWakeLocksLocked(boolean doKills) {
+        BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        if (mLastWakeLockCheckTime == 0) {
+            doKills = false;
+        }
+        if (stats.isScreenOn()) {
+            doKills = false;
+        }
+        final long curRealtime = SystemClock.elapsedRealtime();
+        final long timeSince = curRealtime - mLastWakeLockCheckTime;
+        mLastWakeLockCheckTime = curRealtime;
+        if (timeSince < 5*60*1000) {
+            doKills = false;
+        }
+        int i = mLruProcesses.size();
+        while (i > 0) {
+            i--;
+            ProcessRecord app = mLruProcesses.get(i);
+            if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {
+                long wtime;
+                synchronized (stats) {
+                    wtime = stats.getProcessWakeTime(app.info.uid,
+                            app.pid, curRealtime);
+                }
+                long timeUsed = wtime - app.lastWakeTime;
+                Slog.i(TAG, "Wake for " + app + ": over "
+                        + timeSince + " used " + timeUsed
+                        + " (" + ((timeUsed*100)/timeSince) + "%)");
+                // If a process has held a wake lock for more
+                // than 50% of the time during this period,
+                // that sounds pad.  Kill!
+                if (doKills && timeSince > 0
+                        && ((timeUsed*100)/timeSince) >= 50) {
+                    Slog.i(TAG, "Excessive wake lock in " + app.processName
+                            + " (pid " + app.pid + "): held " + timeUsed
+                            + " during " + timeSince);
+                    EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                            app.processName, app.setAdj, "excessive wake lock");
+                    Process.killProcessQuiet(app.pid);
+                } else {
+                    app.lastWakeTime = wtime;
+                }
+            }
+        }
+    }
+
     private final boolean updateOomAdjLocked(
         ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
         app.hiddenAdj = hiddenAdj;
@@ -11281,6 +11361,12 @@
                     // Likewise do a gc when an app is moving in to the
                     // background (such as a service stopping).
                     scheduleAppGcLocked(app);
+                    // And note its current wake lock time.
+                    BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+                    synchronized (stats) {
+                        app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
+                                app.pid, SystemClock.elapsedRealtime());
+                    }
                 }
                 app.setRawAdj = app.curRawAdj;
             }
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 37da6f7..7314e04 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -93,31 +93,31 @@
         return data;
     }
     
-    public void noteStartWakelock(int uid, String name, int type) {
+    public void noteStartWakelock(int uid, int pid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
+            mStats.noteStartWakeLocked(uid, pid, name, type);
         }
     }
 
-    public void noteStopWakelock(int uid, String name, int type) {
+    public void noteStopWakelock(int uid, int pid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
+            mStats.noteStopWakeLocked(uid, pid, name, type);
         }
     }
 
     public void noteStartSensor(int uid, int sensor) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.getUidStatsLocked(uid).noteStartSensor(sensor);
+            mStats.noteStartSensorLocked(uid, sensor);
         }
     }
     
     public void noteStopSensor(int uid, int sensor) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.getUidStatsLocked(uid).noteStopSensor(sensor);
+            mStats.noteStopSensorLocked(uid, sensor);
         }
     }
     
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 18b1acb..73e489f 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -74,6 +74,7 @@
     Bundle instrumentationArguments;// as given to us
     ComponentName instrumentationResultClass;// copy of instrumentationClass
     BroadcastRecord curReceiver;// receiver currently running in the app
+    long lastWakeTime;          // How long proc held wake lock at last check
     long lastRequestedGc;       // When we last asked the app to do a gc
     long lastLowMemory;         // When we last told the app that memory is low
     boolean reportLowMemory;    // Set to true when waiting to report low mem
@@ -158,7 +159,7 @@
         pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
                 pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
         pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime);
-                pw.print(" lruWeight="); pw.println(lruWeight);
+                pw.print(" lruWeight="); pw.print(lruWeight);
                 pw.print(" hidden="); pw.print(hidden);
                 pw.print(" empty="); pw.println(empty);
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
@@ -177,6 +178,10 @@
                 pw.print(" persistentActivities="); pw.println(persistentActivities);
         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(lastRequestedGc);
+                pw.print(" lastLowMemory="); pw.print(lastLowMemory);
+                pw.print(" reportLowMemory="); pw.println(reportLowMemory);
         if (killedBackground) {
             pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
         }
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 75365ad..35f18750 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -149,7 +149,9 @@
                     pw.print(" foregroundId="); pw.print(foregroundId);
                     pw.print(" foregroundNoti="); pw.println(foregroundNoti);
         }
-        pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now);
+        pw.print(prefix); pw.print("createTime=");
+                pw.print(createTime-SystemClock.elapsedRealtime());
+                pw.print(" lastActivity="); pw.print(lastActivity-now);
                 pw.print(" executingStart="); pw.print(executingStart-now);
                 pw.print(" restartTime="); pw.println(restartTime);
         if (startRequested || lastStartId != 0) {
@@ -213,7 +215,8 @@
         dataDir = sInfo.applicationInfo.dataDir;
         exported = sInfo.exported;
         this.restarter = restarter;
-        createTime = lastActivity = SystemClock.uptimeMillis();
+        createTime = SystemClock.elapsedRealtime();
+        lastActivity = SystemClock.uptimeMillis();
     }
 
     public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/tests/BatteryWaster/res/layout/battery_waster.xml b/tests/BatteryWaster/res/layout/battery_waster.xml
index e1cb6bf..36aa68b 100644
--- a/tests/BatteryWaster/res/layout/battery_waster.xml
+++ b/tests/BatteryWaster/res/layout/battery_waster.xml
@@ -30,6 +30,16 @@
         android:text="@string/waste_away"
         />
 
+    <CheckBox android:id="@+id/checkbox_wake"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="25dp"
+        android:layout_marginTop="25dp"
+        android:textSize="18sp"
+        android:textColor="#ffffffff"
+        android:text="@string/wake_away"
+        />
+
     <ScrollView android:id="@+id/scroll"
         android:layout_width="match_parent"
         android:layout_height="0px"
diff --git a/tests/BatteryWaster/res/values/strings.xml b/tests/BatteryWaster/res/values/strings.xml
index 46c5fa1..a3b849a 100644
--- a/tests/BatteryWaster/res/values/strings.xml
+++ b/tests/BatteryWaster/res/values/strings.xml
@@ -18,5 +18,7 @@
 
     <string name="waste_away">Discharge my battery!</string>
 
+    <string name="wake_away">Keep my device awake!</string>
+
 </resources>
 
diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
index 8ea7e00..499330f 100644
--- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
+++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
@@ -41,6 +41,8 @@
     PowerManager.WakeLock mWakeLock;
     SpinThread mThread;
 
+    boolean mWasting, mWaking;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -50,6 +52,7 @@
         setContentView(R.layout.battery_waster);
 
         findViewById(R.id.checkbox).setOnClickListener(mClickListener);
+        findViewById(R.id.checkbox_wake).setOnClickListener(mWakeClickListener);
         mLog = (TextView)findViewById(R.id.log);
 
         mDateFormat = DateFormat.getInstance();
@@ -67,9 +70,18 @@
 
     @Override
     public void onPause() {
+        super.onPause();
         stopRunning();
     }
 
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
+    }
+
     View.OnClickListener mClickListener = new View.OnClickListener() {
         public void onClick(View v) {
             CheckBox checkbox = (CheckBox)v;
@@ -81,23 +93,54 @@
         }
     };
 
+    View.OnClickListener mWakeClickListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            CheckBox checkbox = (CheckBox)v;
+            if (checkbox.isChecked()) {
+                mWaking = true;
+                updateWakeLock();
+            } else {
+                mWaking = false;
+                updateWakeLock();
+            }
+        }
+    };
+
     void startRunning() {
-        log("Start");
-        registerReceiver(mReceiver, mFilter);
-        mWakeLock.acquire();
-        if (mThread == null) {
-            mThread = new SpinThread();
-            mThread.start();
+        if (!mWasting) {
+            log("Start");
+            registerReceiver(mReceiver, mFilter);
+            mWasting = true;
+            updateWakeLock();
+            if (mThread == null) {
+                mThread = new SpinThread();
+                mThread.start();
+            }
         }
     }
 
     void stopRunning() {
-        log("Stop");
-        unregisterReceiver(mReceiver);
-        mWakeLock.release();
-        if (mThread != null) {
-            mThread.quit();
-            mThread = null;
+        if (mWasting) {
+            log("Stop");
+            unregisterReceiver(mReceiver);
+            mWasting = false;
+            updateWakeLock();
+            if (mThread != null) {
+                mThread.quit();
+                mThread = null;
+            }
+        }
+    }
+
+    void updateWakeLock() {
+        if (mWasting || mWaking) {
+            if (!mWakeLock.isHeld()) {
+                mWakeLock.acquire();
+            }
+        } else {
+            if (mWakeLock.isHeld()) {
+                mWakeLock.release();
+            }
         }
     }