Improve process tracking.

We now keep track of when each process is running, batched
by the current memory status of the device.  In addition,
the stats are organized by package first, and then processes
associated with each package inside of that.  Finally, we
also keep track of the overall time spent in each memory
status.

This should start to actually get us to some information
we can reach some conclusions about.  The total time spent
in each memory status gives us some indication of how much
we are running while memory is low; the new package organization
batched by memory status lets us see what packages have
what processes running when memory is low.

Change-Id: I389d62d39d115a846126cf354e4c20070d8f1180
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
index 4eb71c7..fb8d09c 100644
--- a/services/java/com/android/server/am/ProcessTracker.java
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -36,7 +36,15 @@
     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_SCREEN_ON_MOD = STATE_CACHED+1;
+    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;
 
     static String[] STATE_NAMES = new String[] {
@@ -45,21 +53,44 @@
     };
 
     public static final class ProcessState {
-        final long[] mTimes = new long[STATE_COUNT];
+        final long[] mDurations = new long[STATE_COUNT];
         int mCurState = STATE_NOTHING;
         long mStartTime;
 
-        public void setState(int state, long now) {
-            if (mCurState != STATE_NOTHING) {
-                mTimes[mCurState] += now - mStartTime;
+        public void setState(int state, int memFactor, long now) {
+            if (state != STATE_NOTHING) {
+                state += memFactor;
             }
-            mCurState = state;
-            mStartTime = now;
+            if (mCurState != state) {
+                if (mCurState != STATE_NOTHING) {
+                    mDurations[mCurState] += now - mStartTime;
+                }
+                mCurState = state;
+                mStartTime = now;
+            }
+        }
+    }
+
+    public static final class ServiceState {
+        long mStartedDuration;
+        long mStartedTime;
+    }
+
+    public static final class PackageState {
+        final ArrayMap<String, ProcessState> mProcesses = new ArrayMap<String, ProcessState>();
+        final ArrayMap<String, ServiceState> mServices = new ArrayMap<String, ServiceState>();
+        final int mUid;
+
+        public PackageState(int uid) {
+            mUid = uid;
         }
     }
 
     static final class State {
-        final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
+        final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
+        final long[] mMemFactorDurations = new long[STATE_COUNT/STATE_MEM_FACTOR_MOD];
+        int mMemFactor = STATE_NOTHING;
+        long mStartTime;
     }
 
     final State mState = new State();
@@ -67,44 +98,198 @@
     public ProcessTracker() {
     }
 
-    public ProcessState getStateLocked(String name, int uid) {
-        ProcessState ps = mState.mProcesses.get(name, uid);
+    private PackageState getPackageStateLocked(String packageName, int uid) {
+        PackageState as = mState.mPackages.get(packageName, uid);
+        if (as != null) {
+            return as;
+        }
+        as = new PackageState(uid);
+        mState.mPackages.put(packageName, uid, as);
+        return as;
+    }
+
+    public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
+        final PackageState as = getPackageStateLocked(packageName, uid);
+        ProcessState ps = as.mProcesses.get(processName);
         if (ps != null) {
             return ps;
         }
         ps = new ProcessState();
-        mState.mProcesses.put(name, uid, ps);
+        as.mProcesses.put(processName, ps);
         return ps;
     }
 
+    public ServiceState getServiceStateLocked(String packageName, int uid, String className) {
+        final PackageState as = getPackageStateLocked(packageName, uid);
+        ServiceState ss = as.mServices.get(className);
+        if (ss != null) {
+            return ss;
+        }
+        ss = new ServiceState();
+        as.mServices.put(className, ss);
+        return ss;
+    }
+
+    public boolean setMemFactor(int memFactor, boolean screenOn, long now) {
+        if (screenOn) {
+            memFactor += STATE_SCREEN_ON_MOD;
+        }
+        if (memFactor != mState.mMemFactor) {
+            if (mState.mMemFactor != STATE_NOTHING) {
+                mState.mMemFactorDurations[mState.mMemFactor/STATE_MEM_FACTOR_MOD]
+                        += now - mState.mStartTime;
+            }
+            mState.mMemFactor = memFactor;
+            mState.mStartTime = now;
+            return true;
+        }
+        return false;
+    }
+
+    public int getMemFactor() {
+        return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0;
+    }
+
+    private void printScreenLabel(PrintWriter pw, int offset) {
+        switch (offset) {
+            case STATE_NOTHING:
+                pw.print("             ");
+                break;
+            case STATE_SCREEN_OFF_ADJ:
+                pw.print("Screen Off / ");
+                break;
+            case STATE_SCREEN_ON_ADJ:
+                pw.print("Screen On  / ");
+                break;
+            default:
+                pw.print("?????????? / ");
+                break;
+        }
+    }
+
+    private void printMemLabel(PrintWriter pw, int offset) {
+        switch (offset) {
+            case STATE_NOTHING:
+                pw.print("       ");
+                break;
+            case STATE_MEM_FACTOR_NORMAL_ADJ:
+                pw.print("Norm / ");
+                break;
+            case STATE_MEM_FACTOR_MODERATE_ADJ:
+                pw.print("Mod  / ");
+                break;
+            case STATE_MEM_FACTOR_LOW_ADJ:
+                pw.print("Low  / ");
+                break;
+            case STATE_MEM_FACTOR_CRITIAL_ADJ:
+                pw.print("Crit / ");
+                break;
+            default:
+                pw.print("???? / ");
+                break;
+        }
+    }
+
     public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
         final long now = SystemClock.uptimeMillis();
-        ArrayMap<String, SparseArray<ProcessState>> pmap = mState.mProcesses.getMap();
+        ArrayMap<String, SparseArray<PackageState>> pmap = mState.mPackages.getMap();
+        pw.println("Process Stats:");
         for (int ip=0; ip<pmap.size(); ip++) {
             String procName = pmap.keyAt(ip);
-            SparseArray<ProcessState> procs = pmap.valueAt(ip);
+            SparseArray<PackageState> procs = pmap.valueAt(ip);
             for (int iu=0; iu<procs.size(); iu++) {
                 int uid = procs.keyAt(iu);
-                ProcessState state = procs.valueAt(iu);
+                PackageState state = procs.valueAt(iu);
                 pw.print("  "); pw.print(procName); pw.print(" / "); pw.print(uid); pw.println(":");
-                long totalTime = 0;
-                for (int is=0; is<STATE_NAMES.length; is++) {
-                    long time = state.mTimes[is];
-                    if (state.mCurState == is) {
-                        time += now - state.mStartTime;
+                for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
+                    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) {
+                        int printedMem = -1;
+                        for (int imem=0; imem<STATE_MEM_FACTOR_COUNT; imem+=STATE_MEM_FACTOR_MOD) {
+                            for (int is=0; is<STATE_NAMES.length; is++) {
+                                int bucket = is+imem+iscreen;
+                                long time = proc.mDurations[bucket];
+                                String running = "";
+                                if (proc.mCurState == bucket) {
+                                    time += now - proc.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;
+                                    pw.print(STATE_NAMES[is]); pw.print(": ");
+                                    TimeUtils.formatDuration(time, pw); pw.println(running);
+                                    totalTime += time;
+                                }
+                            }
+                        }
+                    }
+                    if (totalTime != 0) {
+                        pw.print("      ");
+                        printScreenLabel(pw, STATE_NOTHING);
+                        printMemLabel(pw, STATE_NOTHING);
+                        pw.print("TOTAL      : ");
+                        TimeUtils.formatDuration(totalTime, pw);
+                        pw.println();
+                    }
+                }
+                for (int isvc=0; isvc<state.mServices.size(); isvc++) {
+                    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 (time != 0) {
-                        pw.print("    "); pw.print(STATE_NAMES[is]); pw.print(": ");
+                        pw.print("    Started: ");
                         TimeUtils.formatDuration(time, pw); pw.println();
-                        totalTime += time;
                     }
                 }
-                if (totalTime != 0) {
-                    pw.print("    TOTAL      : ");
-                    TimeUtils.formatDuration(totalTime, pw);
-                    pw.println();
+            }
+        }
+        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();
+        }
     }
 }