Various battery info things:

- Now track wake locks in battery history.
- Now track sensors in battery history.
- Some filtering of sensory data.
- Fixes to some data that wasn't cleared when resetting battery stats.
- Print amount discharged since last charge.

And the big part -- keep track of wake locks held per process,
and kill processes that hold wake locks too much while they are in
the background.  This includes information in the battery stats
about the process being killed, which will be available to the
developer if the app is reported.

Change-Id: I97202e94d00aafe0526ba2db74a03212e7539c54
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,