More work on process tracking.

Re-arranged code to be more flexible, now track
state of services, dump ordered list of running
processes while memory was critical and low.

Also rename battery stats service from "batteryinfo" to "batterystats".

Change-Id: I0f4f0c8d443c49d255cb84d0fc917e8ec18b152e
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f523b55..62ed697 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.os.BatteryStats;
 import android.os.IBinder;
 import com.android.internal.app.IUsageStats;
 import com.android.internal.os.PkgUsageStats;
@@ -2210,7 +2211,7 @@
         pw.println();
         dumpService(pw, fd, "package", new String[] { packageName});
         pw.println();
-        dumpService(pw, fd, "batteryinfo", new String[] { packageName});
+        dumpService(pw, fd, BatteryStats.SERVICE_NAME, new String[] { packageName});
         pw.flush();
     }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fef1818..7c09e89c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -41,7 +41,10 @@
 public abstract class BatteryStats implements Parcelable {
 
     private static final boolean LOCAL_LOGV = false;
-    
+
+    /** @hide */
+    public static final String SERVICE_NAME = "batterystats";
+
     /**
      * A constant indicating a partial wake lock timer.
      */
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index ce952d1..6f740cd0 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -43,6 +43,7 @@
 import android.net.RouteInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.INetworkManagementService;
@@ -347,7 +348,7 @@
 
         if (mBandwidthControlEnabled) {
             try {
-                IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"))
+                IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME))
                         .noteNetworkStatsEnabled();
             } catch (RemoteException e) {
             }
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 21d3111..9b5f8f6 100644
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.hardware.input.InputManager;
+import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.IVibratorService;
 import android.os.PowerManager;
@@ -143,7 +144,8 @@
         mWakeLock.setReferenceCounted(true);
 
         mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
-        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+                BatteryStats.SERVICE_NAME));
 
         mVibrations = new LinkedList<Vibration>();
 
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 912c465..c558fbd 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -241,11 +241,14 @@
         if (unscheduleServiceRestartLocked(r)) {
             if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
         }
+        r.lastActivity = SystemClock.uptimeMillis();
         r.startRequested = true;
+        if (r.tracker != null) {
+            r.tracker.setStarted(true, mAm.mProcessTracker.getMemFactor(), r.lastActivity);
+        }
         r.callStart = false;
         r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                 service, neededGrants));
-        r.lastActivity = SystemClock.uptimeMillis();
         synchronized (r.stats.getBatteryStats()) {
             r.stats.startRunningLocked();
         }
@@ -261,8 +264,12 @@
             service.stats.stopRunningLocked();
         }
         service.startRequested = false;
+        if (service.tracker != null) {
+            service.tracker.setStarted(false, mAm.mProcessTracker.getMemFactor(),
+                    SystemClock.uptimeMillis());
+        }
         service.callStart = false;
-        bringDownServiceLocked(service, false);
+        bringDownServiceIfNeededLocked(service, false, false);
     }
 
     int stopServiceLocked(IApplicationThread caller, Intent service,
@@ -355,11 +362,15 @@
 
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.stopRunningLocked();
-                r.startRequested = false;
-                r.callStart = false;
             }
+            r.startRequested = false;
+            if (r.tracker != null) {
+                r.tracker.setStarted(false, mAm.mProcessTracker.getMemFactor(),
+                        SystemClock.uptimeMillis());
+            }
+            r.callStart = false;
             final long origId = Binder.clearCallingIdentity();
-            bringDownServiceLocked(r, false);
+            bringDownServiceIfNeededLocked(r, false, false);
             Binder.restoreCallingIdentity(origId);
             return true;
         }
@@ -489,6 +500,17 @@
                         + s);
             }
 
+            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
+                s.lastActivity = SystemClock.uptimeMillis();
+                if (!s.hasAutoCreateConnections()) {
+                    // This is the first binding, let the tracker know.
+                    if (s.tracker != null) {
+                        s.tracker.setBound(true, mAm.mProcessTracker.getMemFactor(),
+                                s.lastActivity);
+                    }
+                }
+            }
+
             AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
             ConnectionRecord c = new ConnectionRecord(b, activity,
                     connection, flags, clientLabel, clientIntent);
@@ -748,7 +770,12 @@
                                 sInfo.applicationInfo.uid, sInfo.packageName,
                                 sInfo.name);
                     }
-                    r = new ServiceRecord(mAm, ss, name, filter, sInfo, res);
+                    ProcessTracker.ServiceState tracker = null;
+                    if ((sInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
+                        tracker = mAm.mProcessTracker.getServiceStateLocked(sInfo.packageName,
+                                sInfo.applicationInfo.uid, sInfo.name);
+                    }
+                    r = new ServiceRecord(mAm, ss, name, filter, sInfo, res, tracker);
                     res.setService(r);
                     mServiceMap.putServiceByName(name, UserHandle.getUserId(r.appInfo.uid), r);
                     mServiceMap.putServiceByIntent(filter, UserHandle.getUserId(r.appInfo.uid), r);
@@ -798,14 +825,19 @@
         else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING "
                 + why + " of " + r.shortName);
         long now = SystemClock.uptimeMillis();
-        if (r.executeNesting == 0 && r.app != null) {
-            if (r.app.executingServices.size() == 0) {
-                Message msg = mAm.mHandler.obtainMessage(
-                        ActivityManagerService.SERVICE_TIMEOUT_MSG);
-                msg.obj = r.app;
-                mAm.mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT);
+        if (r.executeNesting == 0) {
+            if (r.tracker != null) {
+                r.tracker.setExecuting(true, mAm.mProcessTracker.getMemFactor(), now);
             }
-            r.app.executingServices.add(r);
+            if (r.app != null) {
+                if (r.app.executingServices.size() == 0) {
+                    Message msg = mAm.mHandler.obtainMessage(
+                            ActivityManagerService.SERVICE_TIMEOUT_MSG);
+                    msg.obj = r.app;
+                    mAm.mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT);
+                }
+                r.app.executingServices.add(r);
+            }
         }
         r.executeNesting++;
         r.executingStart = now;
@@ -991,7 +1023,7 @@
                     + r.appInfo.uid + " for service "
                     + r.intent.getIntent() + ": user " + r.userId + " is stopped";
             Slog.w(TAG, msg);
-            bringDownServiceLocked(r, true);
+            bringDownServiceLocked(r);
             return msg;
         }
 
@@ -1045,7 +1077,7 @@
                         + r.appInfo.uid + " for service "
                         + r.intent.getIntent() + ": process is bad";
                 Slog.w(TAG, msg);
-                bringDownServiceLocked(r, true);
+                bringDownServiceLocked(r);
                 return msg;
             }
             if (isolated) {
@@ -1167,26 +1199,29 @@
         }
     }
 
-    private final void bringDownServiceLocked(ServiceRecord r, boolean force) {
+    private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
+            boolean hasConn) {
         //Slog.i(TAG, "Bring down service:");
         //r.dump("  ");
 
         // Does it still need to run?
-        if (!force && r.startRequested) {
+        if (r.startRequested) {
             return;
         }
-        if (!force) {
-            // XXX should probably keep a count of the number of auto-create
-            // connections directly in the service.
-            for (int conni=r.connections.size()-1; conni>=0; conni--) {
-                ArrayList<ConnectionRecord> cr = r.connections.valueAt(conni);
-                for (int i=0; i<cr.size(); i++) {
-                    if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
-                        return;
-                    }
-                }
-            }
+
+        if (!knowConn) {
+            hasConn = r.hasAutoCreateConnections();
         }
+        if (hasConn) {
+            return;
+        }
+
+        bringDownServiceLocked(r);
+    }
+
+    private final void bringDownServiceLocked(ServiceRecord r) {
+        //Slog.i(TAG, "Bring down service:");
+        //r.dump("  ");
 
         // Report to all of the connections that the service is no longer
         // available.
@@ -1291,6 +1326,13 @@
         if (r.restarter instanceof ServiceRestarter) {
            ((ServiceRestarter)r.restarter).setService(null);
         }
+
+        int memFactor = mAm.mProcessTracker.getMemFactor();
+        long now = SystemClock.uptimeMillis();
+        if (r.tracker != null) {
+            r.tracker.setStarted(false, memFactor, now);
+            r.tracker.setBound(false, memFactor, now);
+        }
     }
 
     void removeConnectionLocked(
@@ -1349,7 +1391,14 @@
             }
 
             if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
-                bringDownServiceLocked(s, false);
+                boolean hasAutoCreate = s.hasAutoCreateConnections();
+                if (!hasAutoCreate) {
+                    if (s.tracker != null) {
+                        s.tracker.setBound(false, mAm.mProcessTracker.getMemFactor(),
+                                SystemClock.uptimeMillis());
+                    }
+                }
+                bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
             }
         }
     }
@@ -1422,22 +1471,28 @@
                 + ", inStopping=" + inStopping + ", app=" + r.app);
         else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName);
         r.executeNesting--;
-        if (r.executeNesting <= 0 && r.app != null) {
-            if (DEBUG_SERVICE) Slog.v(TAG,
-                    "Nesting at 0 of " + r.shortName);
-            r.app.executingServices.remove(r);
-            if (r.app.executingServices.size() == 0) {
-                if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
-                        "No more executingServices of " + r.shortName);
-                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
-            }
-            if (inStopping) {
+        if (r.executeNesting <= 0) {
+            if (r.app != null) {
                 if (DEBUG_SERVICE) Slog.v(TAG,
-                        "doneExecuting remove stopping " + r);
-                mStoppingServices.remove(r);
-                r.bindings.clear();
+                        "Nesting at 0 of " + r.shortName);
+                r.app.executingServices.remove(r);
+                if (r.app.executingServices.size() == 0) {
+                    if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,
+                            "No more executingServices of " + r.shortName);
+                    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
+                }
+                if (inStopping) {
+                    if (DEBUG_SERVICE) Slog.v(TAG,
+                            "doneExecuting remove stopping " + r);
+                    mStoppingServices.remove(r);
+                    r.bindings.clear();
+                }
+                mAm.updateOomAdjLocked(r.app);
             }
-            mAm.updateOomAdjLocked(r.app);
+            if (r.tracker != null) {
+                r.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactor(),
+                        SystemClock.uptimeMillis());
+            }
         }
     }
 
@@ -1494,7 +1549,7 @@
                 sr.isolatedProc = null;
                 mPendingServices.remove(i);
                 i--;
-                bringDownServiceLocked(sr, true);
+                bringDownServiceLocked(sr);
             }
         }
     }
@@ -1545,7 +1600,7 @@
 
         int N = services.size();
         for (int i=0; i<N; i++) {
-            bringDownServiceLocked(services.get(i), true);
+            bringDownServiceLocked(services.get(i));
         }
         return didSomething;
     }
@@ -1628,6 +1683,10 @@
                 sr.app = null;
                 sr.isolatedProc = null;
                 sr.executeNesting = 0;
+                if (sr.tracker != null) {
+                    sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactor(),
+                            SystemClock.uptimeMillis());
+                }
                 if (mStoppingServices.remove(sr)) {
                     if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
                 }
@@ -1647,9 +1706,9 @@
                             + " times, stopping: " + sr);
                     EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
                             sr.userId, sr.crashCount, sr.shortName, app.pid);
-                    bringDownServiceLocked(sr, true);
+                    bringDownServiceLocked(sr);
                 } else if (!allowRestart) {
-                    bringDownServiceLocked(sr, true);
+                    bringDownServiceLocked(sr);
                 } else {
                     boolean canceled = scheduleServiceRestartLocked(sr, true);
 
@@ -1659,9 +1718,13 @@
                     if (sr.startRequested && (sr.stopIfKilled || canceled)) {
                         if (sr.pendingStarts.size() == 0) {
                             sr.startRequested = false;
-                            if (numClients > 0) {
+                            if (sr.tracker != null) {
+                                sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactor(),
+                                        SystemClock.uptimeMillis());
+                            }
+                            if (!sr.hasAutoCreateConnections()) {
                                 // Whoops, no reason to restart!
-                                bringDownServiceLocked(sr, true);
+                                bringDownServiceLocked(sr);
                             }
                         }
                     }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2124095..21c752b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7292,7 +7292,6 @@
         if (isolated) {
             int userId = UserHandle.getUserId(uid);
             int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
-            uid = 0;
             while (true) {
                 if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
                         || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
@@ -7314,7 +7313,7 @@
             ps = stats.getProcessStatsLocked(info.uid, proc);
         }
         return new ProcessRecord(ps, thread, info, proc, uid,
-                mProcessTracker.getProcessStateLocked(info.packageName, uid, proc));
+                mProcessTracker.getProcessStateLocked(info.packageName, info.uid, proc));
     }
 
     final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
@@ -14193,7 +14192,7 @@
         // are managing to keep around is less than half the maximum we desire;
         // if we are keeping a good number around, we'll let them use whatever
         // memory they want.
-        int memFactor = ProcessTracker.STATE_MEM_FACTOR_NORMAL_ADJ;
+        int memFactor = ProcessTracker.ADJ_MEM_FACTOR_NORMAL;
         if (numCached <= ProcessList.TRIM_CACHED_APPS
                 && numEmpty <= ProcessList.TRIM_EMPTY_APPS) {
             final int numCachedAndEmpty = numCached + numEmpty;
@@ -14207,13 +14206,13 @@
             int fgTrimLevel;
             if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
-                memFactor = ProcessTracker.STATE_MEM_FACTOR_CRITIAL_ADJ;
+                memFactor = ProcessTracker.ADJ_MEM_FACTOR_CRITICAL;
             } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
-                memFactor = ProcessTracker.STATE_MEM_FACTOR_LOW_ADJ;
+                memFactor = ProcessTracker.ADJ_MEM_FACTOR_LOW;
             } else {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
-                memFactor = ProcessTracker.STATE_MEM_FACTOR_MODERATE_ADJ;
+                memFactor = ProcessTracker.ADJ_MEM_FACTOR_MODERATE;
             }
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             for (i=0; i<N; i++) {
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index f143feb..12cad7b 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -59,7 +60,7 @@
     
     public void publish(Context context) {
         mContext = context;
-        ServiceManager.addService("batteryinfo", asBinder());
+        ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
         mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps());
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout)
@@ -77,7 +78,7 @@
         if (sService != null) {
             return sService;
         }
-        IBinder b = ServiceManager.getService("batteryinfo");
+        IBinder b = ServiceManager.getService(BatteryStats.SERVICE_NAME);
         sService = asInterface(b);
         return sService;
     }
@@ -479,7 +480,7 @@
     }
     
     private void dumpHelp(PrintWriter pw) {
-        pw.println("Battery stats (batteryinfo) dump options:");
+        pw.println("Battery stats (batterystats) dump options:");
         pw.println("  [--checkin] [--unplugged] [--reset] [--write] [-h] [<package.name>]");
         pw.println("  --checkin: format output for a checkin report.");
         pw.println("  --unplugged: only output data since last unplugged.");
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
index fb8d09c..ec8a0b2 100644
--- a/services/java/com/android/server/am/ProcessTracker.java
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -24,6 +25,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 
 public final class ProcessTracker {
     public static final int STATE_NOTHING = -1;
@@ -36,16 +40,18 @@
     public static final int STATE_HOME = 6;
     public static final int STATE_PREVIOUS = 7;
     public static final int STATE_CACHED = 8;
-    public static final int STATE_MEM_FACTOR_MOD = STATE_CACHED+1;
-    public static final int STATE_MEM_FACTOR_NORMAL_ADJ = 0;
-    public static final int STATE_MEM_FACTOR_MODERATE_ADJ = STATE_MEM_FACTOR_MOD;
-    public static final int STATE_MEM_FACTOR_LOW_ADJ = STATE_MEM_FACTOR_MOD*2;
-    public static final int STATE_MEM_FACTOR_CRITIAL_ADJ = STATE_MEM_FACTOR_MOD*3;
-    public static final int STATE_MEM_FACTOR_COUNT = STATE_MEM_FACTOR_MOD*4;
-    public static final int STATE_SCREEN_ON_MOD = STATE_MEM_FACTOR_COUNT;
-    public static final int STATE_SCREEN_OFF_ADJ = 0;
-    public static final int STATE_SCREEN_ON_ADJ = STATE_SCREEN_ON_MOD;
-    public static final int STATE_COUNT = STATE_SCREEN_ON_MOD*2;
+    public static final int STATE_COUNT = STATE_CACHED+1;
+
+    public static final int ADJ_NOTHING = -1;
+    public static final int ADJ_MEM_FACTOR_NORMAL = 0;
+    public static final int ADJ_MEM_FACTOR_MODERATE = 1;
+    public static final int ADJ_MEM_FACTOR_LOW = 2;
+    public static final int ADJ_MEM_FACTOR_CRITICAL = 3;
+    public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1;
+    public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT;
+    public static final int ADJ_SCREEN_OFF = 0;
+    public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD;
+    public static final int ADJ_COUNT = ADJ_SCREEN_ON*2;
 
     static String[] STATE_NAMES = new String[] {
             "Top        ", "Foreground ", "Visible    ", "Perceptible", "Backup     ",
@@ -53,13 +59,25 @@
     };
 
     public static final class ProcessState {
-        final long[] mDurations = new long[STATE_COUNT];
+        final String mPackage;
+        final int mUid;
+        final String mName;
+
+        final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
         int mCurState = STATE_NOTHING;
         long mStartTime;
 
+        long mTmpTotalTime;
+
+        public ProcessState(String pkg, int uid, String name) {
+            mPackage = pkg;
+            mUid = uid;
+            mName = name;
+        }
+
         public void setState(int state, int memFactor, long now) {
             if (state != STATE_NOTHING) {
-                state += memFactor;
+                state += memFactor*STATE_COUNT;
             }
             if (mCurState != state) {
                 if (mCurState != STATE_NOTHING) {
@@ -72,8 +90,59 @@
     }
 
     public static final class ServiceState {
-        long mStartedDuration;
-        long mStartedTime;
+        final long[] mStartedDurations = new long[ADJ_COUNT];
+        int mStartedCount;
+        int mStartedState = STATE_NOTHING;
+        long mStartedStartTime;
+
+        final long[] mBoundDurations = new long[ADJ_COUNT];
+        int mBoundCount;
+        int mBoundState = STATE_NOTHING;
+        long mBoundStartTime;
+
+        final long[] mExecDurations = new long[ADJ_COUNT];
+        int mExecCount;
+        int mExecState = STATE_NOTHING;
+        long mExecStartTime;
+
+        public void setStarted(boolean started, int memFactor, long now) {
+            int state = started ? memFactor : STATE_NOTHING;
+            if (mStartedState != state) {
+                if (mStartedState != STATE_NOTHING) {
+                    mStartedDurations[mStartedState] += now - mStartedStartTime;
+                } else if (started) {
+                    mStartedCount++;
+                }
+                mStartedState = state;
+                mStartedStartTime = now;
+            }
+        }
+
+        public void setBound(boolean bound, int memFactor, long now) {
+            int state = bound ? memFactor : STATE_NOTHING;
+            if (mBoundState != state) {
+                if (mBoundState != STATE_NOTHING) {
+                    mBoundDurations[mBoundState] += now - mBoundStartTime;
+                } else if (bound) {
+                    mBoundCount++;
+                }
+                mBoundState = state;
+                mBoundStartTime = now;
+            }
+        }
+
+        public void setExecuting(boolean executing, int memFactor, long now) {
+            int state = executing ? memFactor : STATE_NOTHING;
+            if (mExecState != state) {
+                if (mExecState != STATE_NOTHING) {
+                    mExecDurations[mExecState] += now - mExecStartTime;
+                } else if (executing) {
+                    mExecCount++;
+                }
+                mExecState = state;
+                mExecStartTime = now;
+            }
+        }
     }
 
     public static final class PackageState {
@@ -88,7 +157,7 @@
 
     static final class State {
         final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
-        final long[] mMemFactorDurations = new long[STATE_COUNT/STATE_MEM_FACTOR_MOD];
+        final long[] mMemFactorDurations = new long[ADJ_COUNT];
         int mMemFactor = STATE_NOTHING;
         long mStartTime;
     }
@@ -114,7 +183,7 @@
         if (ps != null) {
             return ps;
         }
-        ps = new ProcessState();
+        ps = new ProcessState(packageName, uid, processName);
         as.mProcesses.put(processName, ps);
         return ps;
     }
@@ -132,15 +201,31 @@
 
     public boolean setMemFactor(int memFactor, boolean screenOn, long now) {
         if (screenOn) {
-            memFactor += STATE_SCREEN_ON_MOD;
+            memFactor += ADJ_SCREEN_ON;
         }
         if (memFactor != mState.mMemFactor) {
             if (mState.mMemFactor != STATE_NOTHING) {
-                mState.mMemFactorDurations[mState.mMemFactor/STATE_MEM_FACTOR_MOD]
-                        += now - mState.mStartTime;
+                mState.mMemFactorDurations[mState.mMemFactor] += now - mState.mStartTime;
             }
             mState.mMemFactor = memFactor;
             mState.mStartTime = now;
+            ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
+            for (int i=0; i<pmap.size(); i++) {
+                SparseArray<PackageState> uids = pmap.valueAt(i);
+                for (int j=0; j<uids.size(); j++) {
+                    PackageState pkg = uids.valueAt(j);
+                    ArrayMap<String, ServiceState> services = pkg.mServices;
+                    for (int k=0; k<services.size(); k++) {
+                        ServiceState service = services.valueAt(k);
+                        if (service.mStartedState != STATE_NOTHING) {
+                            service.setStarted(true, memFactor, now);
+                        }
+                        if (service.mBoundState != STATE_NOTHING) {
+                            service.setBound(true, memFactor, now);
+                        }
+                    }
+                }
+            }
             return true;
         }
         return false;
@@ -150,15 +235,15 @@
         return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0;
     }
 
-    private void printScreenLabel(PrintWriter pw, int offset) {
+    static private void printScreenLabel(PrintWriter pw, int offset) {
         switch (offset) {
-            case STATE_NOTHING:
+            case ADJ_NOTHING:
                 pw.print("             ");
                 break;
-            case STATE_SCREEN_OFF_ADJ:
+            case ADJ_SCREEN_OFF:
                 pw.print("Screen Off / ");
                 break;
-            case STATE_SCREEN_ON_ADJ:
+            case ADJ_SCREEN_ON:
                 pw.print("Screen On  / ");
                 break;
             default:
@@ -167,21 +252,21 @@
         }
     }
 
-    private void printMemLabel(PrintWriter pw, int offset) {
+    static private void printMemLabel(PrintWriter pw, int offset) {
         switch (offset) {
-            case STATE_NOTHING:
+            case ADJ_NOTHING:
                 pw.print("       ");
                 break;
-            case STATE_MEM_FACTOR_NORMAL_ADJ:
+            case ADJ_MEM_FACTOR_NORMAL:
                 pw.print("Norm / ");
                 break;
-            case STATE_MEM_FACTOR_MODERATE_ADJ:
+            case ADJ_MEM_FACTOR_MODERATE:
                 pw.print("Mod  / ");
                 break;
-            case STATE_MEM_FACTOR_LOW_ADJ:
+            case ADJ_MEM_FACTOR_LOW:
                 pw.print("Low  / ");
                 break;
-            case STATE_MEM_FACTOR_CRITIAL_ADJ:
+            case ADJ_MEM_FACTOR_CRITICAL:
                 pw.print("Crit / ");
                 break;
             default:
@@ -190,29 +275,191 @@
         }
     }
 
+    static void dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
+            int curState, long curStartTime, long now) {
+        long totalTime = 0;
+        int printedScreen = -1;
+        for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
+            int printedMem = -1;
+            for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
+                int state = imem+iscreen;
+                long time = durations[state];
+                String running = "";
+                if (curState == state) {
+                    time += now - curStartTime;
+                    running = " (running)";
+                }
+                if (time != 0) {
+                    pw.print(prefix);
+                    printScreenLabel(pw, printedScreen != iscreen
+                            ? iscreen : STATE_NOTHING);
+                    printedScreen = iscreen;
+                    printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+                    printedMem = imem;
+                    TimeUtils.formatDuration(time, pw); pw.println(running);
+                    totalTime += time;
+                }
+            }
+        }
+        if (totalTime != 0) {
+            pw.print(prefix);
+            printScreenLabel(pw, STATE_NOTHING);
+            pw.print("TOTAL: ");
+            TimeUtils.formatDuration(totalTime, pw);
+            pw.println();
+        }
+    }
+
+    long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates,
+                int[] procStates, long now) {
+        long totalTime = 0;
+        for (int is=0; is<screenStates.length; is++) {
+            for (int im=0; im<memStates.length; im++) {
+                for (int ip=0; ip<procStates.length; ip++) {
+                    int bucket = ((screenStates[is]+ memStates[im]) * STATE_COUNT)
+                            + procStates[ip];
+                    totalTime += proc.mDurations[bucket];
+                    if (proc.mCurState == bucket) {
+                        totalTime += now - proc.mStartTime;
+                    }
+                }
+            }
+        }
+        proc.mTmpTotalTime = totalTime;
+        return totalTime;
+    }
+
+    ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
+            int[] procStates, long now) {
+        ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>();
+        ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
+        for (int ip=0; ip<pmap.size(); ip++) {
+            SparseArray<PackageState> procs = pmap.valueAt(ip);
+            for (int iu=0; iu<procs.size(); iu++) {
+                PackageState state = procs.valueAt(iu);
+                for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
+                    if (computeProcessTimeLocked(state.mProcesses.valueAt(iproc),
+                            screenStates, memStates, procStates, now) > 0) {
+                        outProcs.add(state.mProcesses.valueAt(iproc));
+                    }
+                }
+            }
+        }
+        Collections.sort(outProcs, new Comparator<ProcessState>() {
+            @Override
+            public int compare(ProcessState lhs, ProcessState rhs) {
+                if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
+                    return -1;
+                } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
+                    return 1;
+                }
+                return 0;
+            }
+        });
+        return outProcs;
+    }
+
+    void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates,
+            int[] memStates, int[] procStates, long now) {
+        long totalTime = 0;
+        int printedScreen = -1;
+        for (int is=0; is<screenStates.length; is++) {
+            int printedMem = -1;
+            for (int im=0; im<memStates.length; im++) {
+                for (int ip=0; ip<procStates.length; ip++) {
+                    final int iscreen = screenStates[is];
+                    final int imem = memStates[im];
+                    final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
+                    long time = proc.mDurations[bucket];
+                    String running = "";
+                    if (proc.mCurState == bucket) {
+                        time += now - proc.mStartTime;
+                        running = " (running)";
+                    }
+                    totalTime += time;
+                    if (time != 0) {
+                        pw.print(prefix);
+                        if (screenStates.length > 1) {
+                            printScreenLabel(pw, printedScreen != iscreen
+                                    ? iscreen : STATE_NOTHING);
+                            printedScreen = iscreen;
+                        }
+                        if (memStates.length > 1) {
+                            printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
+                            printedMem = imem;
+                        }
+                        pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
+                        TimeUtils.formatDuration(time, pw); pw.println(running);
+                        totalTime += time;
+                    }
+                }
+            }
+        }
+        if (totalTime != 0) {
+            pw.print(prefix);
+            if (screenStates.length > 1) {
+                printScreenLabel(pw, STATE_NOTHING);
+            }
+            if (memStates.length > 1) {
+                printMemLabel(pw, STATE_NOTHING);
+            }
+            pw.print("TOTAL      : ");
+            TimeUtils.formatDuration(totalTime, pw);
+            pw.println();
+        }
+    }
+
+    void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
+            int[] screenStates, int[] memStates, int[] procStates, long now) {
+        String innerPrefix = prefix + "  ";
+        for (int i=procs.size()-1; i>=0; i--) {
+            ProcessState proc = procs.get(i);
+            pw.print(prefix);
+            pw.print(proc.mPackage);
+            pw.print(" / ");
+            UserHandle.formatUid(pw, proc.mUid);
+            pw.print(" / ");
+            pw.print(proc.mName);
+            pw.println(":");
+            dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now);
+        }
+    }
+
+    void dumpFilteredProcesses(PrintWriter pw, String header, String prefix,
+            int[] screenStates, int[] memStates, int[] procStates, long now) {
+        ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
+                procStates, now);
+        if (procs.size() > 0) {
+            pw.println();
+            pw.println(header);
+            dumpProcessList(pw, prefix, procs, screenStates, memStates, procStates, now);
+        }
+    }
+
     public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
         final long now = SystemClock.uptimeMillis();
         ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
-        pw.println("Process Stats:");
+        pw.println("Per-Package Process Stats:");
         for (int ip=0; ip<pmap.size(); ip++) {
             String procName = pmap.keyAt(ip);
             SparseArray<PackageState> procs = pmap.valueAt(ip);
             for (int iu=0; iu<procs.size(); iu++) {
                 int uid = procs.keyAt(iu);
                 PackageState state = procs.valueAt(iu);
-                pw.print("  "); pw.print(procName); pw.print(" / "); pw.print(uid); pw.println(":");
+                pw.print("  * "); pw.print(procName); pw.print(" / ");
+                        UserHandle.formatUid(pw, uid); pw.println(":");
                 for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
-                    pw.print("    Process ");
+                    pw.print("      Process ");
                     pw.print(state.mProcesses.keyAt(iproc));
                     pw.println(":");
                     long totalTime = 0;
                     ProcessState proc = state.mProcesses.valueAt(iproc);
                     int printedScreen = -1;
-                    for (int iscreen=0; iscreen<STATE_COUNT; iscreen+=STATE_SCREEN_ON_MOD) {
+                    for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
                         int printedMem = -1;
-                        for (int imem=0; imem<STATE_MEM_FACTOR_COUNT; imem+=STATE_MEM_FACTOR_MOD) {
+                        for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
                             for (int is=0; is<STATE_NAMES.length; is++) {
-                                int bucket = is+imem+iscreen;
+                                int bucket = is+(STATE_COUNT*(imem+iscreen));
                                 long time = proc.mDurations[bucket];
                                 String running = "";
                                 if (proc.mCurState == bucket) {
@@ -220,7 +467,7 @@
                                     running = " (running)";
                                 }
                                 if (time != 0) {
-                                    pw.print("      ");
+                                    pw.print("        ");
                                     printScreenLabel(pw, printedScreen != iscreen
                                             ? iscreen : STATE_NOTHING);
                                     printedScreen = iscreen;
@@ -234,7 +481,7 @@
                         }
                     }
                     if (totalTime != 0) {
-                        pw.print("      ");
+                        pw.print("        ");
                         printScreenLabel(pw, STATE_NOTHING);
                         printMemLabel(pw, STATE_NOTHING);
                         pw.print("TOTAL      : ");
@@ -243,53 +490,46 @@
                     }
                 }
                 for (int isvc=0; isvc<state.mServices.size(); isvc++) {
-                    pw.print("    Service ");
+                    pw.print("      Service ");
                     pw.print(state.mServices.keyAt(isvc));
                     pw.println(":");
                     ServiceState svc = state.mServices.valueAt(isvc);
-                    long time = svc.mStartedDuration;
-                    if (svc.mStartedTime >= 0) {
-                        time += now - svc.mStartedTime;
+                    if (svc.mStartedCount != 0) {
+                        pw.print("        Started op count "); pw.print(svc.mStartedCount);
+                        pw.println(":");
+                        dumpSingleTime(pw, "          ", svc.mStartedDurations, svc.mStartedState,
+                                svc.mStartedStartTime, now);
                     }
-                    if (time != 0) {
-                        pw.print("    Started: ");
-                        TimeUtils.formatDuration(time, pw); pw.println();
+                    if (svc.mBoundCount != 0) {
+                        pw.print("        Bound op count "); pw.print(svc.mBoundCount);
+                        pw.println(":");
+                        dumpSingleTime(pw, "          ", svc.mBoundDurations, svc.mBoundState,
+                                svc.mBoundStartTime, now);
+                    }
+                    if (svc.mExecCount != 0) {
+                        pw.print("        Executing op count "); pw.print(svc.mExecCount);
+                        pw.println(":");
+                        dumpSingleTime(pw, "          ", svc.mExecDurations, svc.mExecState,
+                                svc.mExecStartTime, now);
                     }
                 }
             }
         }
+        dumpFilteredProcesses(pw, "Processes running while critical mem:", "  ",
+                new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+                new int[] {ADJ_MEM_FACTOR_CRITICAL},
+                new int[] {STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE,
+                        STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
+                now);
+        dumpFilteredProcesses(pw, "Processes running while low mem:", "  ",
+                new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+                new int[] {ADJ_MEM_FACTOR_LOW},
+                new int[] {STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE,
+                        STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
+                now);
         pw.println();
         pw.println("Run time Stats:");
-        long totalTime = 0;
-        int printedScreen = -1;
-        for (int iscreen=0; iscreen<STATE_COUNT; iscreen+=STATE_SCREEN_ON_MOD) {
-            int printedMem = -1;
-            for (int imem=0; imem<STATE_MEM_FACTOR_COUNT; imem+=STATE_MEM_FACTOR_MOD) {
-                int bucket = imem+iscreen;
-                long time = mState.mMemFactorDurations[bucket/STATE_MEM_FACTOR_MOD];
-                String running = "";
-                if (mState.mMemFactor == bucket) {
-                    time += now - mState.mStartTime;
-                    running = " (running)";
-                }
-                if (time != 0) {
-                    pw.print("  ");
-                    printScreenLabel(pw, printedScreen != iscreen
-                            ? iscreen : STATE_NOTHING);
-                    printedScreen = iscreen;
-                    printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING);
-                    printedMem = imem;
-                    TimeUtils.formatDuration(time, pw); pw.println(running);
-                    totalTime += time;
-                }
-            }
-        }
-        if (totalTime != 0) {
-            pw.print("  ");
-            printScreenLabel(pw, STATE_NOTHING);
-            pw.print("TOTAL: ");
-            TimeUtils.formatDuration(totalTime, pw);
-            pw.println();
-        }
+        dumpSingleTime(pw, "  ", mState.mMemFactorDurations, mState.mMemFactor,
+                mState.mStartTime, now);
     }
 }
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 45e248b..5000940 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -73,6 +73,7 @@
     final String dataDir;   // where activity data should go
     final boolean exported; // from ServiceInfo.exported
     final Runnable restarter; // used to schedule retries of starting the service
+    final ProcessTracker.ServiceState tracker; // tracking service execution, may be null
     final long createTime;  // when this service was created
     final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
             = new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
@@ -281,7 +282,8 @@
 
     ServiceRecord(ActivityManagerService ams,
             BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
-            Intent.FilterComparison intent, ServiceInfo sInfo, Runnable restarter) {
+            Intent.FilterComparison intent, ServiceInfo sInfo, Runnable restarter,
+            ProcessTracker.ServiceState tracker) {
         this.ams = ams;
         this.stats = servStats;
         this.name = name;
@@ -297,6 +299,7 @@
         dataDir = sInfo.applicationInfo.dataDir;
         exported = sInfo.exported;
         this.restarter = restarter;
+        this.tracker = tracker;
         createTime = SystemClock.elapsedRealtime();
         lastActivity = SystemClock.uptimeMillis();
         userId = UserHandle.getUserId(appInfo.uid);
@@ -319,6 +322,20 @@
         return a;
     }
 
+    public boolean hasAutoCreateConnections() {
+        // XXX should probably keep a count of the number of auto-create
+        // connections directly in the service.
+        for (int conni=connections.size()-1; conni>=0; conni--) {
+            ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
+            for (int i=0; i<cr.size(); i++) {
+                if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     public void resetRestartCounter() {
         restartCount = 0;
         restartDelay = 0;
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 8c88cab..4791ec0 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -41,6 +41,7 @@
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -456,7 +457,8 @@
                 Context.APP_OPS_SERVICE));
 
         // Battery statistics service to be notified when GPS turns on or off
-        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+                BatteryStats.SERVICE_NAME));
 
         mProperties = new Properties();
         try {
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 9f3a0d3..1040ab19 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -54,6 +54,7 @@
 import android.net.wifi.WpsResult.Status;
 import android.net.wifi.p2p.WifiP2pManager;
 import android.net.wifi.p2p.WifiP2pService;
+import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -542,7 +543,8 @@
         mInterfaceName = wlanInterface;
 
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
-        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+                BatteryStats.SERVICE_NAME));
 
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         mNwService = INetworkManagementService.Stub.asInterface(b);