Switch proc stats to use new process state constants.

These new constants are a better mapping to the kind of
information that procstats is wanting to collect about
processes.  In doing this, the process states are tweaked
to have a bit more information that we care about for
procstats.

This changes the format of the data printed by procstats,
so the checkin version is bumped to 2.  The structure is
the same, however the codes for process states have all
changed.  The new codes are, in order of precedence:

p -- persistent system process.
t -- top activity; actually any visible activity.
f -- important foreground process (ime, wallpaper, etc).
b -- important background process
u -- performing backup operation.
w -- heavy-weight process (currently not used).
s -- background process running a service.
r -- process running a receiver.
h -- process hosting home/launcher app when not on top.
l -- process hosting the last app the user was in.
a -- cached process hosting a previous activity.
c -- cached process hosting a client activity.
e -- cached process that is empty.

In addition, we are now collecting uss along with pss
data for each process, so the pss checkin entries now
have three new values at the end of the min/avg/max uss
values of that process.

With this switch to using process state constants more
fundamentally, I realized that they could actually be
used by the core oom adj code to make it a lot cleaner.
So that change has been made, that code has changed quite
radically, and lost a lot of its secondary states and flags
that it used to use in its computation, now relying on
primarily the oom_adj and proc state values for the process.

This also cleaned up a few problems -- for example for
purposes of determing the memory level of the device, if a
long-running service dropped into the cached oom_adj level,
it would start being counted as a cached process and thus
make us think that the memory state is better than it is.
Now we do this based on the proc state, which always stays
as a service regardless of what is happening like this, giving
as a more consistent view of the memory state of the device.

Making proc state a more fundamentally part of the oom adj
computation means that the values can also be more carefully
tuned in semantic meaning so the value assigned to a process
doesn't tend to change unless the semantics of the process
has really significantly changed.

For example, a process will be assigned the service state
regardless of whether that services is executing operations
in the foreground, running normally, or has been dropped to
the lru list for pruning.  The top state is used for everything
related to activities visible to the user: when actually on
top, visible but not on top, currently pausing, etc.

There is a new Context.BIND_SHOWING_UI added for when system
services bind to apps, to explicitly indicate that the app
is showing UI for the system.  This gives us a better metric
to determine when it is showing UI, and thus when it needs
to do a memory trim when it is no longer in that state.  Without
this, services could get in bad states of continually trimming.

Finally, more HashSet containers have been changed to ArraySet,
reducing the temporary iterators created for iterating over
them.

Change-Id: I1724113f42abe7862e8aecb6faae5a7620245e89
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 1c1b002..c0a5214 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1210,7 +1210,7 @@
         mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
                 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
         if (bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE
-                | Context.BIND_NOT_VISIBLE)) {
+                | Context.BIND_NOT_VISIBLE | Context.BIND_SHOWING_UI)) {
             mLastBindTime = SystemClock.uptimeMillis();
             mHaveConnection = true;
             mCurId = info.getId();
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 6823f136..9a73909 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -885,7 +885,8 @@
                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
                     0, null, new UserHandle(serviceUserId)));
-            if (!mContext.bindServiceAsUser(intent, newConn, Context.BIND_AUTO_CREATE,
+            if (!mContext.bindServiceAsUser(intent, newConn,
+                    Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI,
                     new UserHandle(serviceUserId))) {
                 String msg = "Unable to bind service: "
                         + componentName;
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 5d72102..795e142 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -421,7 +421,8 @@
 
     private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
         boolean anyForeground = false;
-        for (ServiceRecord sr : proc.services) {
+        for (int i=proc.services.size()-1; i>=0; i--) {
+            ServiceRecord sr = proc.services.valueAt(i);
             if (sr.isForeground) {
                 anyForeground = true;
                 break;
@@ -1670,78 +1671,72 @@
         }
 
         // Clean up any connections this application has to other services.
-        if (app.connections.size() > 0) {
-            Iterator<ConnectionRecord> it = app.connections.iterator();
-            while (it.hasNext()) {
-                ConnectionRecord r = it.next();
-                removeConnectionLocked(r, app, null);
-            }
+        for (int i=app.connections.size()-1; i>=0; i--) {
+            ConnectionRecord r = app.connections.valueAt(i);
+            removeConnectionLocked(r, app, null);
         }
         app.connections.clear();
 
-        if (app.services.size() != 0) {
+        for (int i=app.services.size()-1; i>=0; i--) {
             // Any services running in the application need to be placed
             // back in the pending list.
-            Iterator<ServiceRecord> it = app.services.iterator();
-            while (it.hasNext()) {
-                ServiceRecord sr = it.next();
-                synchronized (sr.stats.getBatteryStats()) {
-                    sr.stats.stopLaunchedLocked();
-                }
-                sr.app = null;
-                sr.isolatedProc = null;
-                sr.executeNesting = 0;
-                if (sr.tracker != null) {
-                    sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
-                            SystemClock.uptimeMillis());
-                }
-                if (mStoppingServices.remove(sr)) {
-                    if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
-                }
+            ServiceRecord sr = app.services.valueAt(i);
+            synchronized (sr.stats.getBatteryStats()) {
+                sr.stats.stopLaunchedLocked();
+            }
+            sr.app = null;
+            sr.isolatedProc = null;
+            sr.executeNesting = 0;
+            if (sr.tracker != null) {
+                sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
+                        SystemClock.uptimeMillis());
+            }
+            if (mStoppingServices.remove(sr)) {
+                if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+            }
 
-                final int numClients = sr.bindings.size();
-                for (int bindingi=numClients-1; bindingi>=0; bindingi--) {
-                    IntentBindRecord b = sr.bindings.valueAt(bindingi);
-                    if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
-                            + ": shouldUnbind=" + b.hasBound);
-                    b.binder = null;
-                    b.requested = b.received = b.hasBound = false;
-                }
+            final int numClients = sr.bindings.size();
+            for (int bindingi=numClients-1; bindingi>=0; bindingi--) {
+                IntentBindRecord b = sr.bindings.valueAt(bindingi);
+                if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
+                        + ": shouldUnbind=" + b.hasBound);
+                b.binder = null;
+                b.requested = b.received = b.hasBound = false;
+            }
 
-                if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
-                        &ApplicationInfo.FLAG_PERSISTENT) == 0) {
-                    Slog.w(TAG, "Service crashed " + sr.crashCount
-                            + " times, stopping: " + sr);
-                    EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
-                            sr.userId, sr.crashCount, sr.shortName, app.pid);
-                    bringDownServiceLocked(sr);
-                } else if (!allowRestart) {
-                    bringDownServiceLocked(sr);
-                } else {
-                    boolean canceled = scheduleServiceRestartLocked(sr, true);
+            if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
+                    &ApplicationInfo.FLAG_PERSISTENT) == 0) {
+                Slog.w(TAG, "Service crashed " + sr.crashCount
+                        + " times, stopping: " + sr);
+                EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
+                        sr.userId, sr.crashCount, sr.shortName, app.pid);
+                bringDownServiceLocked(sr);
+            } else if (!allowRestart) {
+                bringDownServiceLocked(sr);
+            } else {
+                boolean canceled = scheduleServiceRestartLocked(sr, true);
 
-                    // Should the service remain running?  Note that in the
-                    // extreme case of so many attempts to deliver a command
-                    // that it failed we also will stop it here.
-                    if (sr.startRequested && (sr.stopIfKilled || canceled)) {
-                        if (sr.pendingStarts.size() == 0) {
-                            sr.startRequested = false;
-                            if (sr.tracker != null) {
-                                sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
-                                        SystemClock.uptimeMillis());
-                            }
-                            if (!sr.hasAutoCreateConnections()) {
-                                // Whoops, no reason to restart!
-                                bringDownServiceLocked(sr);
-                            }
+                // Should the service remain running?  Note that in the
+                // extreme case of so many attempts to deliver a command
+                // that it failed we also will stop it here.
+                if (sr.startRequested && (sr.stopIfKilled || canceled)) {
+                    if (sr.pendingStarts.size() == 0) {
+                        sr.startRequested = false;
+                        if (sr.tracker != null) {
+                            sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
+                                    SystemClock.uptimeMillis());
+                        }
+                        if (!sr.hasAutoCreateConnections()) {
+                            // Whoops, no reason to restart!
+                            bringDownServiceLocked(sr);
                         }
                     }
                 }
             }
+        }
 
-            if (!allowRestart) {
-                app.services.clear();
-            }
+        if (!allowRestart) {
+            app.services.clear();
         }
 
         // Make sure we have no more records on the stopping list.
@@ -1880,11 +1875,10 @@
                 return;
             }
             long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT;
-            Iterator<ServiceRecord> it = proc.executingServices.iterator();
             ServiceRecord timeout = null;
             long nextTime = 0;
-            while (it.hasNext()) {
-                ServiceRecord sr = it.next();
+            for (int i=proc.executingServices.size()-1; i>=0; i--) {
+                ServiceRecord sr = proc.executingServices.valueAt(i);
                 if (sr.executingStart < maxTime) {
                     timeout = sr;
                     break;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c41c23e..591b391 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1505,6 +1505,7 @@
             case COLLECT_PSS_BG_MSG: {
                 int i=0;
                 long start = SystemClock.uptimeMillis();
+                long[] tmp = new long[1];
                 do {
                     ProcessRecord proc;
                     int oomAdj;
@@ -1528,10 +1529,10 @@
                         i++;
                     }
                     if (proc != null) {
-                        long pss = Debug.getPss(pid);
+                        long pss = Debug.getPss(pid, tmp);
                         synchronized (ActivityManagerService.this) {
                             if (proc.thread != null && proc.setAdj == oomAdj && proc.pid == pid) {
-                                proc.baseProcessTracker.addPss(pss, true);
+                                proc.baseProcessTracker.addPss(pss, tmp[0], true);
                             }
                         }
                     }
@@ -2157,13 +2158,12 @@
 
         // If the app is currently using a content provider or service,
         // bump those processes as well.
-        if (app.connections.size() > 0) {
-            for (ConnectionRecord cr : app.connections) {
-                if (cr.binding != null && cr.binding.service != null
-                        && cr.binding.service.app != null
-                        && cr.binding.service.app.lruSeq != mLruSeq) {
-                    updateLruProcessInternalLocked(cr.binding.service.app, i+1);
-                }
+        for (int j=app.connections.size()-1; j>=0; j--) {
+            ConnectionRecord cr = app.connections.valueAt(j);
+            if (cr.binding != null && cr.binding.service != null
+                    && cr.binding.service.app != null
+                    && cr.binding.service.app.lruSeq != mLruSeq) {
+                updateLruProcessInternalLocked(cr.binding.service.app, i+1);
             }
         }
         for (int j=app.conProviders.size()-1; j>=0; j--) {
@@ -4010,7 +4010,8 @@
                 synchronized (this) {
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
-                        proc.baseProcessTracker.addPss(infos[i].getTotalPss(), false);
+                        proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
+                                infos[i].getTotalUss(), false);
                     }
                 }
             }
@@ -4031,12 +4032,13 @@
                     oomAdj = proc != null ? proc.setAdj : 0;
                 }
             }
-            pss[i] = Debug.getPss(pids[i]);
+            long[] tmpUss = new long[1];
+            pss[i] = Debug.getPss(pids[i], tmpUss);
             if (proc != null) {
                 synchronized (this) {
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
-                        proc.baseProcessTracker.addPss(pss[i], false);
+                        proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false);
                     }
                 }
             }
@@ -8802,14 +8804,11 @@
         }
 
         // Bump up the crash count of any services currently running in the proc.
-        if (app.services.size() != 0) {
+        for (int i=app.services.size()-1; i>=0; i--) {
             // Any services running in the application need to be placed
             // back in the pending list.
-            Iterator<ServiceRecord> it = app.services.iterator();
-            while (it.hasNext()) {
-                ServiceRecord sr = it.next();
-                sr.crashCount++;
-            }
+            ServiceRecord sr = app.services.valueAt(i);
+            sr.crashCount++;
         }
 
         // If the crashing process is what we consider to be the "home process" and it has been
@@ -10269,8 +10268,8 @@
             pw.print("    FOREGROUND_APP_ADJ: "); pw.println(ProcessList.FOREGROUND_APP_ADJ);
             pw.print("    VISIBLE_APP_ADJ: "); pw.println(ProcessList.VISIBLE_APP_ADJ);
             pw.print("    PERCEPTIBLE_APP_ADJ: "); pw.println(ProcessList.PERCEPTIBLE_APP_ADJ);
-            pw.print("    HEAVY_WEIGHT_APP_ADJ: "); pw.println(ProcessList.HEAVY_WEIGHT_APP_ADJ);
             pw.print("    BACKUP_APP_ADJ: "); pw.println(ProcessList.BACKUP_APP_ADJ);
+            pw.print("    HEAVY_WEIGHT_APP_ADJ: "); pw.println(ProcessList.HEAVY_WEIGHT_APP_ADJ);
             pw.print("    SERVICE_ADJ: "); pw.println(ProcessList.SERVICE_ADJ);
             pw.print("    HOME_APP_ADJ: "); pw.println(ProcessList.HOME_APP_ADJ);
             pw.print("    PREVIOUS_APP_ADJ: "); pw.println(ProcessList.PREVIOUS_APP_ADJ);
@@ -10772,10 +10771,10 @@
                 oomAdj = buildOomTag("home ", null, r.setAdj, ProcessList.HOME_APP_ADJ);
             } else if (r.setAdj >= ProcessList.SERVICE_ADJ) {
                 oomAdj = buildOomTag("svc  ", null, r.setAdj, ProcessList.SERVICE_ADJ);
-            } else if (r.setAdj >= ProcessList.BACKUP_APP_ADJ) {
-                oomAdj = buildOomTag("bkup ", null, r.setAdj, ProcessList.BACKUP_APP_ADJ);
             } else if (r.setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
                 oomAdj = buildOomTag("hvy  ", null, r.setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ);
+            } else if (r.setAdj >= ProcessList.BACKUP_APP_ADJ) {
+                oomAdj = buildOomTag("bkup ", null, r.setAdj, ProcessList.BACKUP_APP_ADJ);
             } else if (r.setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
                 oomAdj = buildOomTag("prcp ", null, r.setAdj, ProcessList.PERCEPTIBLE_APP_ADJ);
             } else if (r.setAdj >= ProcessList.VISIBLE_APP_ADJ) {
@@ -10820,29 +10819,38 @@
                 case ActivityManager.PROCESS_STATE_TOP:
                     procState = "T ";
                     break;
-                case ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE:
-                    procState = "IP";
+                case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+                    procState = "IF";
                     break;
                 case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
                     procState = "IB";
                     break;
-                case ActivityManager.PROCESS_STATE_RECEIVER:
-                    procState = "R ";
-                    break;
                 case ActivityManager.PROCESS_STATE_BACKUP:
                     procState = "BU";
                     break;
+                case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
+                    procState = "HW";
+                    break;
                 case ActivityManager.PROCESS_STATE_SERVICE:
                     procState = "S ";
                     break;
+                case ActivityManager.PROCESS_STATE_RECEIVER:
+                    procState = "R ";
+                    break;
                 case ActivityManager.PROCESS_STATE_HOME:
-                    procState = "H ";
+                    procState = "HO";
                     break;
                 case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
                     procState = "LA";
                     break;
-                case ActivityManager.PROCESS_STATE_CACHED:
-                    procState = "C ";
+                case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                    procState = "CA";
+                    break;
+                case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                    procState = "Ca";
+                    break;
+                case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
+                    procState = "CE";
                     break;
                 default:
                     procState = "??";
@@ -10895,9 +10903,6 @@
                 pw.print(prefix);
                 pw.print("    ");
                 pw.print("oom: max="); pw.print(r.maxAdj);
-                pw.print(" cached="); pw.print(r.cachedAdj);
-                pw.print(" client="); pw.print(r.clientCachedAdj);
-                pw.print(" empty="); pw.print(r.emptyAdj);
                 pw.print(" curRaw="); pw.print(r.curRawAdj);
                 pw.print(" setRaw="); pw.print(r.setRawAdj);
                 pw.print(" cur="); pw.print(r.curAdj);
@@ -11139,21 +11144,24 @@
 
     static final int[] DUMP_MEM_OOM_ADJ = new int[] {
             ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ,
-            ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
-            ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
+            ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ,
+            ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
+            ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
             ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MAX_ADJ
     };
     static final String[] DUMP_MEM_OOM_LABEL = new String[] {
             "System", "Persistent", "Foreground",
-            "Visible", "Perceptible", "Heavy Weight",
-            "Backup", "A Services", "Home", "Previous",
-            "B Services", "Cached"
+            "Visible", "Perceptible",
+            "Heavy Weight", "Backup",
+            "A Services", "Home",
+            "Previous", "B Services", "Cached"
     };
     static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] {
             "sys", "pers", "fore",
-            "vis", "percept", "heavy",
-            "backup", "servicea", "home", "prev",
-            "serviceb", "cached"
+            "vis", "percept",
+            "heavy", "backup",
+            "servicea", "home",
+            "prev", "serviceb", "cached"
     };
 
     final void dumpApplicationMemoryUsage(FileDescriptor fd,
@@ -11225,6 +11233,7 @@
         long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
         ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
                 new ArrayList[DUMP_MEM_OOM_LABEL.length];
+        final long[] tmpLong = new long[1];
 
         long totalPss = 0;
         long cachedPss = 0;
@@ -11264,16 +11273,18 @@
                     if (!brief && !oomOnly) {
                         Debug.getMemoryInfo(pid, mi);
                     } else {
-                        mi.dalvikPss = (int)Debug.getPss(pid);
+                        mi.dalvikPss = (int)Debug.getPss(pid, tmpLong);
+                        mi.dalvikPrivateDirty = (int)tmpLong[0];
                     }
                 }
 
                 final long myTotalPss = mi.getTotalPss();
+                final long myTotalUss = mi.getTotalUss();
 
                 synchronized (this) {
                     if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
                         // Record this for posterity if the process has been stable.
-                        r.baseProcessTracker.addPss(myTotalPss, true);
+                        r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true);
                     }
                 }
 
@@ -11642,14 +11653,11 @@
         skipCurrentReceiverLocked(app);
 
         // Unregister any receivers.
-        if (app.receivers.size() > 0) {
-            Iterator<ReceiverList> it = app.receivers.iterator();
-            while (it.hasNext()) {
-                removeReceiverLocked(it.next());
-            }
-            app.receivers.clear();
+        for (int i=app.receivers.size()-1; i>=0; i--) {
+            removeReceiverLocked(app.receivers.valueAt(i));
         }
-        
+        app.receivers.clear();
+
         // If the app is undergoing backup, tell the backup manager about it
         if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
             if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG, "App "
@@ -13409,29 +13417,17 @@
         return null;
     }
 
-    private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, int clientCachedAdj,
-            int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
+    private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
+            boolean doingAll, long now) {
         if (mAdjSeq == app.adjSeq) {
-            // This adjustment has already been computed.  If we are calling
-            // from the top, we may have already computed our adjustment with
-            // an earlier cached adjustment that isn't really for us... if
-            // so, use the new cached adjustment.
-            if (!recursed && app.cached) {
-                if (app.hasActivities) {
-                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = cachedAdj;
-                } else if (app.hasClientActivities) {
-                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = clientCachedAdj;
-                } else {
-                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = emptyAdj;
-                }
-            }
+            // This adjustment has already been computed.
             return app.curRawAdj;
         }
 
         if (app.thread == null) {
             app.adjSeq = mAdjSeq;
             app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.curProcState = ActivityManager.PROCESS_STATE_CACHED;
+            app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
             return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
         }
 
@@ -13449,7 +13445,7 @@
             // below foreground, so it is not worth doing work for it.
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
-            app.curRawAdj = app.nonStoppingAdj = app.maxAdj;
+            app.curRawAdj = app.maxAdj;
             app.hasActivities = false;
             app.foregroundActivities = false;
             app.keeping = true;
@@ -13507,7 +13503,7 @@
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "instrumentation";
             interesting = true;
-            procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+            procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
         } else if ((queue = isReceivingBroadcast(app)) != null) {
             // An app that is currently receiving a broadcast also
             // counts as being in the foreground for OOM killer purposes.
@@ -13526,33 +13522,39 @@
             app.adjType = "exec-service";
             procState = ActivityManager.PROCESS_STATE_SERVICE;
         } else {
-            // Assume process is cached (has activities); we will correct
-            // later if this is not the case.
-            adj = cachedAdj;
+            // As far as we know the process is empty.  We may change our mind later.
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+            // At this point we don't actually know the adjustment.  Use the cached adj
+            // value that the caller wants us to.
+            adj = cachedAdj;
+            procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
             app.cached = true;
-            app.adjType = "cch-act";
-            procState = ActivityManager.PROCESS_STATE_CACHED;
+            app.empty = true;
+            app.adjType = "cch-empty";
         }
 
-        boolean hasStoppingActivities = false;
-
         // Examine all activities if not already foreground.
         if (!foregroundActivities && activitiesSize > 0) {
             for (int j = 0; j < activitiesSize; j++) {
                 final ActivityRecord r = app.activities.get(j);
+                if (r.app != app) {
+                    Slog.w(TAG, "Wtf, activity " + r + " in proc activity list not using proc "
+                            + app + "?!?");
+                    continue;
+                }
+                app.hasActivities = true;
                 if (r.visible) {
                     // App has a visible activity; only upgrade adjustment.
                     if (adj > ProcessList.VISIBLE_APP_ADJ) {
                         adj = ProcessList.VISIBLE_APP_ADJ;
                         app.adjType = "visible";
                     }
-                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
-                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                    if (procState > ActivityManager.PROCESS_STATE_TOP) {
+                        procState = ActivityManager.PROCESS_STATE_TOP;
                     }
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.cached = false;
-                    app.hasActivities = true;
+                    app.empty = false;
                     foregroundActivities = true;
                     break;
                 } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
@@ -13560,38 +13562,39 @@
                         adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                         app.adjType = "pausing";
                     }
-                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
-                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                    if (procState > ActivityManager.PROCESS_STATE_TOP) {
+                        procState = ActivityManager.PROCESS_STATE_TOP;
                     }
+                    schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.cached = false;
+                    app.empty = false;
                     foregroundActivities = true;
                 } else if (r.state == ActivityState.STOPPING) {
-                    // We will apply the actual adjustment later, because
-                    // we want to allow this process to immediately go through
-                    // any memory trimming that is in effect.
-                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
-                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                    if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                        app.adjType = "stopping";
+                    }
+                    // For the process state, we will at this point consider the
+                    // process to be cached.  It will be cached either as an activity
+                    // or empty depending on whether the activity is finishing.  We do
+                    // this so that we can treat the process as cached for purposes of
+                    // memory trimming (determing current memory level, trim command to
+                    // send to process) since there can be an arbitrary number of stopping
+                    // processes and they should soon all go into the cached state.
+                    if (!r.finishing) {
+                        if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                            procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+                        }
                     }
                     app.cached = false;
+                    app.empty = false;
                     foregroundActivities = true;
-                    hasStoppingActivities = true;
+                } else {
+                    if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                        procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+                        app.adjType = "cch-act";
+                    }
                 }
-                if (r.app == app) {
-                    app.hasActivities = true;
-                }
-            }
-        }
-
-        if (adj == cachedAdj && !app.hasActivities) {
-            if (app.hasClientActivities) {
-                adj = clientCachedAdj;
-                app.adjType = "cch-client-act";
-            } else {
-                // Whoops, this process is completely empty as far as we know
-                // at this point.
-                adj = emptyAdj;
-                app.empty = true;
-                app.adjType = "cch-empty";
             }
         }
 
@@ -13599,14 +13602,14 @@
             if (app.foregroundServices) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
                 app.cached = false;
                 app.adjType = "fg-service";
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             } else if (app.forcingToForeground != null) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
                 app.cached = false;
                 app.adjType = "force-fg";
                 app.adjSource = app.forcingToForeground;
@@ -13618,12 +13621,17 @@
             interesting = true;
         }
 
-        if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ && app == mHeavyWeightProcess) {
-            // We don't want to kill the current heavy-weight process.
-            adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
-            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.cached = false;
-            app.adjType = "heavy";
+        if (app == mHeavyWeightProcess) {
+            if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+                // We don't want to kill the current heavy-weight process.
+                adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
+                schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+                app.cached = false;
+                app.adjType = "heavy";
+            }
+            if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+                procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
+            }
         }
 
         if (app == mHomeProcess) {
@@ -13663,7 +13671,7 @@
         // this gives us a baseline and makes sure we don't get into an
         // infinite recursion.
         app.adjSeq = mAdjSeq;
-        app.curRawAdj = app.nonStoppingAdj = adj;
+        app.curRawAdj = adj;
         app.hasStartedServices = false;
 
         if (mBackupTarget != null && app == mBackupTarget.app) {
@@ -13682,243 +13690,220 @@
             }
         }
 
-        if (app.services.size() != 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
-            final long now = SystemClock.uptimeMillis();
-            // This process is more important if the top activity is
-            // bound to the service.
-            Iterator<ServiceRecord> jt = app.services.iterator();
-            while (jt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) {
-                ServiceRecord s = jt.next();
-                if (s.startRequested) {
-                    app.hasStartedServices = true;
-                    if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
-                        procState = ActivityManager.PROCESS_STATE_SERVICE;
-                    }
-                    if (app.hasShownUi && app != mHomeProcess) {
-                        // If this process has shown some UI, let it immediately
-                        // go to the LRU list because it may be pretty heavy with
-                        // UI stuff.  We'll tag it with a label just to help
-                        // debug and understand what is going on.
-                        if (adj > ProcessList.SERVICE_ADJ) {
-                            app.adjType = "cch-started-ui-services";
-                        }
-                    } else {
-                        if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
-                            // This service has seen some activity within
-                            // recent memory, so we will keep its process ahead
-                            // of the background processes.
-                            if (adj > ProcessList.SERVICE_ADJ) {
-                                adj = ProcessList.SERVICE_ADJ;
-                                app.adjType = "started-services";
-                                app.cached = false;
-                            }
-                        }
-                        // If we have let the service slide into the background
-                        // state, still have some text describing what it is doing
-                        // even though the service no longer has an impact.
-                        if (adj > ProcessList.SERVICE_ADJ) {
-                            app.adjType = "cch-started-services";
-                        }
-                    }
-                    // Don't kill this process because it is doing work; it
-                    // has said it is doing work.
-                    app.keeping = true;
+        for (int is = app.services.size()-1;
+                is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                        || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                        || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+                is--) {
+            ServiceRecord s = app.services.valueAt(is);
+            if (s.startRequested) {
+                app.hasStartedServices = true;
+                if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
+                    procState = ActivityManager.PROCESS_STATE_SERVICE;
                 }
-                for (int conni = s.connections.size()-1;
-                        conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                                || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
-                        conni--) {
-                    ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
-                    for (int i = 0;
-                            i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
-                                    || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
-                            i++) {
-                        // XXX should compute this based on the max of
-                        // all connected clients.
-                        ConnectionRecord cr = clist.get(i);
-                        if (cr.binding.client == app) {
-                            // Binding to ourself is not interesting.
-                            continue;
+                if (app.hasShownUi && app != mHomeProcess) {
+                    // If this process has shown some UI, let it immediately
+                    // go to the LRU list because it may be pretty heavy with
+                    // UI stuff.  We'll tag it with a label just to help
+                    // debug and understand what is going on.
+                    if (adj > ProcessList.SERVICE_ADJ) {
+                        app.adjType = "cch-started-ui-services";
+                    }
+                } else {
+                    if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
+                        // This service has seen some activity within
+                        // recent memory, so we will keep its process ahead
+                        // of the background processes.
+                        if (adj > ProcessList.SERVICE_ADJ) {
+                            adj = ProcessList.SERVICE_ADJ;
+                            app.adjType = "started-services";
+                            app.cached = false;
                         }
-                        if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
-                            ProcessRecord client = cr.binding.client;
-                            int myCachedAdj = cachedAdj;
-                            if (myCachedAdj > client.cachedAdj) {
-                                if (client.cachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
-                                    myCachedAdj = client.cachedAdj;
-                                } else {
-                                    myCachedAdj = ProcessList.VISIBLE_APP_ADJ;
-                                }
-                            }
-                            int myClientCachedAdj = clientCachedAdj;
-                            if (myClientCachedAdj > client.clientCachedAdj) {
-                                if (client.clientCachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
-                                    myClientCachedAdj = client.clientCachedAdj;
-                                } else {
-                                    myClientCachedAdj = ProcessList.VISIBLE_APP_ADJ;
-                                }
-                            }
-                            int myEmptyAdj = emptyAdj;
-                            if (myEmptyAdj > client.emptyAdj) {
-                                if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) {
-                                    myEmptyAdj = client.emptyAdj;
-                                } else {
-                                    myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
-                                }
-                            }
-                            int clientAdj = computeOomAdjLocked(client, myCachedAdj,
-                                    myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
-                            int clientProcState = client.curProcState;
-                            String adjType = null;
-                            if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
-                                // Not doing bind OOM management, so treat
-                                // this guy more like a started service.
-                                if (app.hasShownUi && app != mHomeProcess) {
-                                    // If this process has shown some UI, let it immediately
-                                    // go to the LRU list because it may be pretty heavy with
-                                    // UI stuff.  We'll tag it with a label just to help
-                                    // debug and understand what is going on.
-                                    if (adj > clientAdj) {
-                                        adjType = "cch-bound-ui-services";
-                                    }
-                                    app.cached = false;
-                                    clientAdj = adj;
-                                    clientProcState = procState;
-                                } else {
-                                    if (now >= (s.lastActivity
-                                            + ActiveServices.MAX_SERVICE_INACTIVITY)) {
-                                        // This service has not seen activity within
-                                        // recent memory, so allow it to drop to the
-                                        // LRU list if there is no other reason to keep
-                                        // it around.  We'll also tag it with a label just
-                                        // to help debug and undertand what is going on.
-                                        if (adj > clientAdj) {
-                                            adjType = "cch-bound-services";
-                                        }
-                                        clientAdj = adj;
-                                    }
-                                }
-                            } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                                if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) {
-                                    // If this connection is keeping the service
-                                    // created, then we want to try to better follow
-                                    // its memory management semantics for activities.
-                                    // That is, if it is sitting in the background
-                                    // LRU list as a cached process (with activities),
-                                    // we don't want the service it is connected to
-                                    // to go into the empty LRU and quickly get killed,
-                                    // because all we'll do is just end up restarting
-                                    // the service.
-                                    app.hasClientActivities |= client.hasActivities;
-                                }
-                            }
-                            if (adj > clientAdj) {
-                                // If this process has recently shown UI, and
-                                // the process that is binding to it is less
-                                // important than being visible, then we don't
-                                // care about the binding as much as we care
-                                // about letting this process get into the LRU
-                                // list to be killed and restarted if needed for
-                                // memory.
-                                if (app.hasShownUi && app != mHomeProcess
-                                        && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                    adjType = "cch-bound-ui-services";
-                                } else {
-                                    if ((cr.flags&(Context.BIND_ABOVE_CLIENT
-                                            |Context.BIND_IMPORTANT)) != 0) {
-                                        adj = clientAdj;
-                                    } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
-                                            && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
-                                            && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                                    } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
-                                        adj = clientAdj;
-                                    } else {
-                                        app.pendingUiClean = true;
-                                        if (adj > ProcessList.VISIBLE_APP_ADJ) {
-                                            adj = ProcessList.VISIBLE_APP_ADJ;
-                                        }
-                                    }
-                                    if (!client.cached) {
-                                        app.cached = false;
-                                    }
-                                    if (client.keeping) {
-                                        app.keeping = true;
-                                    }
-                                    adjType = "service";
-                                }
-                            }
-                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                                if (clientProcState <
-                                        ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
-                                    clientProcState =
-                                            ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
-                                }
-                            } else {
-                                if (clientProcState <
-                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
-                                    clientProcState =
-                                            ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
-                                }
-                            }
-                            if (procState > clientProcState) {
-                                procState = clientProcState;
-                            }
-                            if (adjType != null) {
-                                app.adjType = adjType;
-                                app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                        .REASON_SERVICE_IN_USE;
-                                app.adjSource = cr.binding.client;
-                                app.adjSourceOom = clientAdj;
-                                app.adjTarget = s.name;
-                            }
-                        }
-                        final ActivityRecord a = cr.activity;
-                        if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
-                            if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
-                                    (a.visible || a.state == ActivityState.RESUMED
-                                     || a.state == ActivityState.PAUSING)) {
-                                adj = ProcessList.FOREGROUND_APP_ADJ;
-                                if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                                app.cached = false;
-                                app.adjType = "service";
-                                app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                        .REASON_SERVICE_IN_USE;
-                                app.adjSource = a;
-                                app.adjSourceOom = adj;
-                                app.adjTarget = s.name;
-                            }
-                        }
+                    }
+                    // If we have let the service slide into the background
+                    // state, still have some text describing what it is doing
+                    // even though the service no longer has an impact.
+                    if (adj > ProcessList.SERVICE_ADJ) {
+                        app.adjType = "cch-started-services";
                     }
                 }
+                // Don't kill this process because it is doing work; it
+                // has said it is doing work.
+                app.keeping = true;
             }
-            
-            // Finally, if this process has active services running in it, we
-            // would like to avoid killing it unless it would prevent the current
-            // application from running.  By default we put the process in
-            // with the rest of the background processes; as we scan through
-            // its services we may bump it up from there.
-            if (adj > cachedAdj) {
-                adj = cachedAdj;
-                app.cached = false;
-                app.adjType = "cch-services";
+            for (int conni = s.connections.size()-1;
+                    conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                            || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                            || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+                    conni--) {
+                ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
+                for (int i = 0;
+                        i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
+                                || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                                || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+                        i++) {
+                    // XXX should compute this based on the max of
+                    // all connected clients.
+                    ConnectionRecord cr = clist.get(i);
+                    if (cr.binding.client == app) {
+                        // Binding to ourself is not interesting.
+                        continue;
+                    }
+                    if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
+                        ProcessRecord client = cr.binding.client;
+                        int clientAdj = computeOomAdjLocked(client, cachedAdj,
+                                TOP_APP, doingAll, now);
+                        int clientProcState = client.curProcState;
+                        String adjType = null;
+                        if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+                            // Not doing bind OOM management, so treat
+                            // this guy more like a started service.
+                            if (app.hasShownUi && app != mHomeProcess) {
+                                // If this process has shown some UI, let it immediately
+                                // go to the LRU list because it may be pretty heavy with
+                                // UI stuff.  We'll tag it with a label just to help
+                                // debug and understand what is going on.
+                                if (adj > clientAdj) {
+                                    adjType = "cch-bound-ui-services";
+                                }
+                                app.cached = false;
+                                clientAdj = adj;
+                                clientProcState = procState;
+                            } else {
+                                if (now >= (s.lastActivity
+                                        + ActiveServices.MAX_SERVICE_INACTIVITY)) {
+                                    // This service has not seen activity within
+                                    // recent memory, so allow it to drop to the
+                                    // LRU list if there is no other reason to keep
+                                    // it around.  We'll also tag it with a label just
+                                    // to help debug and undertand what is going on.
+                                    if (adj > clientAdj) {
+                                        adjType = "cch-bound-services";
+                                    }
+                                    clientAdj = adj;
+                                }
+                            }
+                        } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+                            if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) {
+                                // If this connection is keeping the service
+                                // created, then we want to try to better follow
+                                // its memory management semantics for activities.
+                                // That is, if it is sitting in the background
+                                // LRU list as a cached process (with activities),
+                                // we don't want the service it is connected to
+                                // to go into the empty LRU and quickly get killed,
+                                // because all we'll do is just end up restarting
+                                // the service.
+                                if (client.hasActivities) {
+                                    if (procState >
+                                            ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT) {
+                                        procState =
+                                                ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+                                        app.adjType = "cch-client-act";
+                                    }
+                                }
+                                app.hasClientActivities |= client.hasActivities;
+                            }
+                        }
+                        if (adj > clientAdj) {
+                            // If this process has recently shown UI, and
+                            // the process that is binding to it is less
+                            // important than being visible, then we don't
+                            // care about the binding as much as we care
+                            // about letting this process get into the LRU
+                            // list to be killed and restarted if needed for
+                            // memory.
+                            if (app.hasShownUi && app != mHomeProcess
+                                    && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                adjType = "cch-bound-ui-services";
+                            } else {
+                                if ((cr.flags&(Context.BIND_ABOVE_CLIENT
+                                        |Context.BIND_IMPORTANT)) != 0) {
+                                    adj = clientAdj;
+                                } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
+                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                    adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                                } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
+                                    adj = clientAdj;
+                                } else {
+                                    if (adj > ProcessList.VISIBLE_APP_ADJ) {
+                                        adj = ProcessList.VISIBLE_APP_ADJ;
+                                    }
+                                }
+                                if (!client.cached) {
+                                    app.cached = false;
+                                }
+                                if (client.keeping) {
+                                    app.keeping = true;
+                                }
+                                adjType = "service";
+                            }
+                        }
+                        if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                            if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+                                schedGroup = Process.THREAD_GROUP_DEFAULT;
+                            }
+                            if (clientProcState <
+                                    ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                                clientProcState =
+                                        ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                            }
+                        } else {
+                            if (clientProcState <
+                                    ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                                clientProcState =
+                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                            }
+                        }
+                        if (procState > clientProcState) {
+                            procState = clientProcState;
+                        }
+                        if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                                && (cr.flags&Context.BIND_SHOWING_UI) != 0) {
+                            app.pendingUiClean = true;
+                        }
+                        if (adjType != null) {
+                            app.adjType = adjType;
+                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE;
+                            app.adjSource = cr.binding.client;
+                            app.adjSourceOom = clientAdj;
+                            app.adjTarget = s.name;
+                        }
+                    }
+                    final ActivityRecord a = cr.activity;
+                    if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+                        if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
+                                (a.visible || a.state == ActivityState.RESUMED
+                                 || a.state == ActivityState.PAUSING)) {
+                            adj = ProcessList.FOREGROUND_APP_ADJ;
+                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                schedGroup = Process.THREAD_GROUP_DEFAULT;
+                            }
+                            app.cached = false;
+                            app.adjType = "service";
+                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE;
+                            app.adjSource = a;
+                            app.adjSourceOom = adj;
+                            app.adjTarget = s.name;
+                        }
+                    }
+                }
             }
         }
 
         for (int provi = app.pubProviders.size()-1;
                 provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                        || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                        || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                        || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
                 provi--) {
             ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
             for (int i = cpr.connections.size()-1;
                     i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                            || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                            || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                            || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
                     i--) {
                 ContentProviderConnection conn = cpr.connections.get(i);
                 ProcessRecord client = conn.client;
@@ -13926,32 +13911,7 @@
                     // Being our own client is not interesting.
                     continue;
                 }
-                int myCachedAdj = cachedAdj;
-                if (myCachedAdj > client.cachedAdj) {
-                    if (client.cachedAdj > ProcessList.FOREGROUND_APP_ADJ) {
-                        myCachedAdj = client.cachedAdj;
-                    } else {
-                        myCachedAdj = ProcessList.FOREGROUND_APP_ADJ;
-                    }
-                }
-                int myClientCachedAdj = clientCachedAdj;
-                if (myClientCachedAdj > client.clientCachedAdj) {
-                    if (client.clientCachedAdj >= ProcessList.FOREGROUND_APP_ADJ) {
-                        myClientCachedAdj = client.clientCachedAdj;
-                    } else {
-                        myClientCachedAdj = ProcessList.FOREGROUND_APP_ADJ;
-                    }
-                }
-                int myEmptyAdj = emptyAdj;
-                if (myEmptyAdj > client.emptyAdj) {
-                    if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) {
-                        myEmptyAdj = client.emptyAdj;
-                    } else {
-                        myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ;
-                    }
-                }
-                int clientAdj = computeOomAdjLocked(client, myCachedAdj,
-                        myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
+                int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
                 if (adj > clientAdj) {
                     if (app.hasShownUi && app != mHomeProcess
                             && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -13961,12 +13921,8 @@
                                 ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
                         app.adjType = "provider";
                     }
-                    if (!client.cached) {
-                        app.cached = false;
-                    }
-                    if (client.keeping) {
-                        app.keeping = true;
-                    }
+                    app.cached &= client.cached;
+                    app.keeping |= client.keeping;
                     app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                             .REASON_PROVIDER_IN_USE;
                     app.adjSource = client;
@@ -13974,7 +13930,10 @@
                     app.adjTarget = cpr.name;
                 }
                 if (procState > client.curProcState) {
-                    procState = client.curProcState;
+                    procState = client.curProcState >
+                            ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+                            ? client.curProcState
+                            : ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
                 }
                 if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -13992,8 +13951,8 @@
                     app.adjType = "provider";
                     app.adjTarget = cpr.name;
                 }
-                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
-                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
                 }
             }
         }
@@ -14010,16 +13969,6 @@
             app.serviceb = false;
         }
 
-        app.nonStoppingAdj = adj;
-
-        if (hasStoppingActivities) {
-            // Only upgrade adjustment.
-            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.adjType = "stopping";
-            }
-        }
-
         app.curRawAdj = adj;
         
         //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
@@ -14034,24 +13983,12 @@
             app.keeping = true;
         }
 
-        if (app.hasAboveClient) {
-            // If this process has bound to any services with BIND_ABOVE_CLIENT,
-            // then we need to drop its adjustment to be lower than the service's
-            // in order to honor the request.  We want to drop it by one adjustment
-            // level...  but there is special meaning applied to various levels so
-            // we will skip some of them.
-            if (adj < ProcessList.FOREGROUND_APP_ADJ) {
-                // System process will not get dropped, ever
-            } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
-                adj = ProcessList.VISIBLE_APP_ADJ;
-            } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-            } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
-                adj = ProcessList.CACHED_APP_MIN_ADJ;
-            } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
-                adj++;
-            }
-        }
+        // Do final modification to adj.  Everything we do between here and applying
+        // the final setAdj must be done in this function, because we will also use
+        // it when computing the final cached adj later.  Note that we don't need to
+        // worry about this for max adj above, since max adj will always be used to
+        // keep it out of the cached vaues.
+        adj = app.modifyRawOomAdj(adj);
 
         app.curProcState = procState;
 
@@ -14407,23 +14344,10 @@
         }
     }
 
-    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
-            int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll,
-            boolean doingProcessState, long now) {
-        app.cachedAdj = cachedAdj;
-        app.clientCachedAdj = clientCachedAdj;
-        app.emptyAdj = emptyAdj;
-
-        if (app.thread == null) {
-            return false;
-        }
-
-        final boolean wasKeeping = app.keeping;
-
+    private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping,
+            ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {
         boolean success = true;
 
-        computeOomAdjLocked(app, cachedAdj, clientCachedAdj, emptyAdj, TOP_APP, false, doingAll);
-
         if (app.curRawAdj != app.setRawAdj) {
             if (wasKeeping && !app.keeping) {
                 // This app is no longer something we want to keep.  Note
@@ -14467,11 +14391,6 @@
                     requestPssLocked(app, now, true);
                 }
                 app.setAdj = app.curAdj;
-                app.setAdjChanged = true;
-                if (!doingAll) {
-                    app.setProcessTrackerState(TOP_APP, mProcessTracker.getMemFactorLocked(),
-                            now, mProcessList);
-                }
             } else {
                 success = false;
                 Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
@@ -14514,7 +14433,7 @@
         }
         if (app.repProcState != app.curProcState) {
             app.repProcState = app.curProcState;
-            if (!doingProcessState && app.thread != null) {
+            if (!reportingProcessState && app.thread != null) {
                 try {
                     if (false) {
                         //RuntimeException h = new RuntimeException("here");
@@ -14526,9 +14445,33 @@
                 }
             }
         }
+        if (app.setProcState != app.curProcState) {
+            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                    "Proc state change of " + app.processName
+                    + " to " + app.curProcState);
+            app.setProcState = app.curProcState;
+            app.procStateChanged = true;
+            if (!doingAll) {
+                app.setProcessTrackerState(mProcessTracker.getMemFactorLocked(), now);
+            }
+        }
         return success;
     }
 
+    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
+            ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {
+        if (app.thread == null) {
+            return false;
+        }
+
+        final boolean wasKeeping = app.keeping;
+
+        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
+
+        return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll,
+                reportingProcessState, now);
+    }
+
     private final ActivityRecord resumedAppLocked() {
         return mStackSupervisor.resumedAppLocked();
     }
@@ -14540,17 +14483,19 @@
     final boolean updateOomAdjLocked(ProcessRecord app, boolean doingProcessState) {
         final ActivityRecord TOP_ACT = resumedAppLocked();
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
-        int curAdj = app.curAdj;
-        final boolean wasCached = curAdj >= ProcessList.CACHED_APP_MIN_ADJ
-            && curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
+        final boolean wasCached = app.cached;
 
         mAdjSeq++;
 
-        boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj,
-                app.emptyAdj, TOP_APP, false, doingProcessState, SystemClock.uptimeMillis());
-        final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
-            && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
-        if (nowCached != wasCached) {
+        // This is the desired cached adjusment we want to tell it to use.
+        // If our app is currently cached, we know it, and that is it.  Otherwise,
+        // we don't know it yet, and it needs to now be cached we will then
+        // need to do a complete oom adj.
+        final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ
+                ? app.curRawAdj : ProcessList.UNKNOWN_ADJ;
+        boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false, doingProcessState,
+                SystemClock.uptimeMillis());
+        if (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ) {
             // Changed to/from cached state, so apps after it in the LRU
             // list may also be changed.
             updateOomAdjLocked();
@@ -14563,6 +14508,7 @@
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
         final long now = SystemClock.uptimeMillis();
         final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
+        final int N = mLruProcesses.size();
 
         if (false) {
             RuntimeException e = new RuntimeException();
@@ -14591,7 +14537,7 @@
         // them.
         int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
                 - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
-        int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedHiddenProcs;
+        int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
         if (numEmptyProcs > cachedProcessLimit) {
             // If there are more empty processes than our limit on cached
             // processes, then use the cached process limit for the factor.
@@ -14616,80 +14562,97 @@
 
         // First update the OOM adjustment for each of the
         // application processes based on their current state.
-        int i = mLruProcesses.size();
         int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
         int nextCachedAdj = curCachedAdj+1;
         int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
         int nextEmptyAdj = curEmptyAdj+2;
         int curClientCachedAdj = curEmptyAdj;
-        boolean changed = false;
-        while (i > 0) {
-            i--;
+        for (int i=0; i<N; i++) {
             ProcessRecord app = mLruProcesses.get(i);
-            //Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj);
-            app.setAdjChanged = false;
-            updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP,
-                    true, false, now);
-            changed |= app.setAdjChanged;
-            if (!app.killedBackground) {
-                if (app.curRawAdj == curCachedAdj && app.hasActivities) {
-                    // This process was assigned as a cached process...  step the
-                    // cached level.
-                    mNumCachedHiddenProcs++;
-                    if (curCachedAdj != nextCachedAdj) {
-                        stepCached++;
-                        if (stepCached >= cachedFactor) {
-                            stepCached = 0;
-                            curCachedAdj = nextCachedAdj;
-                            nextCachedAdj += 2;
-                            if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                            }
-                            if (curClientCachedAdj <= curCachedAdj) {
-                                curClientCachedAdj = curCachedAdj + 1;
-                                if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                    curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+            if (!app.killedBackground && app.thread != null) {
+                app.procStateChanged = false;
+                final boolean wasKeeping = app.keeping;
+                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
+
+                // If we haven't yet assigned the final cached adj
+                // to the process, do that now.
+                if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
+                    switch (app.curProcState) {
+                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                            // This process is a cached process holding activities...
+                            // assign it the next cached value for that type, and then
+                            // step that cached level.
+                            app.curRawAdj = curCachedAdj;
+                            app.curAdj = app.modifyRawOomAdj(curCachedAdj);
+                            if (curCachedAdj != nextCachedAdj) {
+                                stepCached++;
+                                if (stepCached >= cachedFactor) {
+                                    stepCached = 0;
+                                    curCachedAdj = nextCachedAdj;
+                                    nextCachedAdj += 2;
+                                    if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                        nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                    }
+                                    if (curClientCachedAdj <= curCachedAdj) {
+                                        curClientCachedAdj = curCachedAdj + 1;
+                                        if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                            curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                        }
+                                    }
                                 }
                             }
-                        }
-                    }
-                    numCached++;
-                    if (numCached > cachedProcessLimit) {
-                        Slog.i(TAG, "No longer want " + app.processName
-                                + " (pid " + app.pid + "): cached #" + numCached);
-                        EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
-                                app.processName, app.setAdj, "too many background");
-                        app.killedBackground = true;
-                        Process.killProcessQuiet(app.pid);
-                    }
-                } else if (app.curRawAdj == curCachedAdj && app.hasClientActivities) {
-                    // This process has a client that has activities.  We will have
-                    // given it the current cached adj; here we will just leave it
-                    // without stepping the cached adj.
-                    curClientCachedAdj++;
-                    if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                        curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                    }
-                } else {
-                    if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curCachedAdj) {
-                        // This process was assigned as an empty process...  step the
-                        // empty level.
-                        if (curEmptyAdj != nextEmptyAdj) {
-                            stepEmpty++;
-                            if (stepEmpty >= emptyFactor) {
-                                stepEmpty = 0;
-                                curEmptyAdj = nextEmptyAdj;
-                                nextEmptyAdj += 2;
-                                if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                    nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                            break;
+                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                            // Special case for cached client processes...  just step
+                            // down from after regular cached processes.
+                            app.curRawAdj = curClientCachedAdj;
+                            app.curAdj = app.modifyRawOomAdj(curClientCachedAdj);
+                            curClientCachedAdj++;
+                            if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                            }
+                            break;
+                        default:
+                            // For everything else, assign next empty cached process
+                            // level and bump that up.  Note that this means that
+                            // long-running services that have dropped down to the
+                            // cached level will be treated as empty (since their process
+                            // state is still as a service), which is what we want.
+                            app.curRawAdj = curEmptyAdj;
+                            app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+                            if (curEmptyAdj != nextEmptyAdj) {
+                                stepEmpty++;
+                                if (stepEmpty >= emptyFactor) {
+                                    stepEmpty = 0;
+                                    curEmptyAdj = nextEmptyAdj;
+                                    nextEmptyAdj += 2;
+                                    if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                        nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                    }
                                 }
                             }
-                        }
-                    } else if (app.curRawAdj < ProcessList.CACHED_APP_MIN_ADJ) {
-                        mNumNonCachedProcs++;
+                            break;
                     }
-                    if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
-                            && !app.hasClientActivities) {
+                }
+
+                applyOomAdjLocked(app, wasKeeping, TOP_APP, true, false, now);
+
+                // Count the number of process types.
+                switch (app.curProcState) {
+                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                        mNumCachedHiddenProcs++;
+                        numCached++;
+                        if (numCached > cachedProcessLimit) {
+                            Slog.i(TAG, "No longer want " + app.processName
+                                    + " (pid " + app.pid + "): cached #" + numCached);
+                            EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
+                                    app.processName, app.setAdj, "too many background");
+                            app.killedBackground = true;
+                            Process.killProcessQuiet(app.pid);
+                        }
+                        break;
+                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
                         if (numEmpty > ProcessList.TRIM_EMPTY_APPS
                                 && app.lastActivityTime < oldTime) {
                             Slog.i(TAG, "No longer want " + app.processName
@@ -14711,8 +14674,12 @@
                                 Process.killProcessQuiet(app.pid);
                             }
                         }
-                    }
+                        break;
+                    default:
+                        mNumNonCachedProcs++;
+                        break;
                 }
+
                 if (app.isolated && app.services.size() <= 0) {
                     // If this is an isolated process, and there are no
                     // services running in it, then the process is no longer
@@ -14727,8 +14694,8 @@
                     app.killedBackground = true;
                     Process.killProcessQuiet(app.pid);
                 }
-                if (app.nonStoppingAdj >= ProcessList.HOME_APP_ADJ
-                        && app.nonStoppingAdj != ProcessList.SERVICE_B_ADJ
+
+                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                         && !app.killedBackground) {
                     numTrimming++;
                 }
@@ -14743,11 +14710,10 @@
         // 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.ADJ_MEM_FACTOR_NORMAL;
+        boolean allChanged;
         if (numCached <= ProcessList.TRIM_CACHED_APPS
                 && numEmpty <= ProcessList.TRIM_EMPTY_APPS) {
             final int numCachedAndEmpty = numCached + numEmpty;
-            final int N = mLruProcesses.size();
             int factor = numTrimming/3;
             int minFactor = 2;
             if (mHomeProcess != null) minFactor++;
@@ -14755,6 +14721,7 @@
             if (factor < minFactor) factor = minFactor;
             int step = 0;
             int fgTrimLevel;
+            int memFactor;
             if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
                 memFactor = ProcessTracker.ADJ_MEM_FACTOR_CRITICAL;
@@ -14766,13 +14733,19 @@
                 memFactor = ProcessTracker.ADJ_MEM_FACTOR_MODERATE;
             }
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
-            for (i=0; i<N; i++) {
+            allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now);
+            for (int i=N-1; i>=0; i--) {
                 ProcessRecord app = mLruProcesses.get(i);
-                if (app.nonStoppingAdj >= ProcessList.HOME_APP_ADJ
-                        && app.nonStoppingAdj != ProcessList.SERVICE_B_ADJ
+                if (allChanged || app.procStateChanged) {
+                    app.setProcessTrackerState(memFactor, now);
+                }
+                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                         && !app.killedBackground) {
                     if (app.trimMemoryLevel < curLevel && app.thread != null) {
                         try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                    "Trimming memory of " + app.processName
+                                    + " to " + curLevel);
                             app.thread.scheduleTrimMemory(curLevel);
                         } catch (RemoteException e) {
                         }
@@ -14803,10 +14776,13 @@
                                 break;
                         }
                     }
-                } else if (app.nonStoppingAdj == ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+                } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
                     if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
                             && app.thread != null) {
                         try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                    "Trimming memory of heavy-weight " + app.processName
+                                    + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                             app.thread.scheduleTrimMemory(
                                     ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                         } catch (RemoteException e) {
@@ -14814,14 +14790,17 @@
                     }
                     app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
                 } else {
-                    if ((app.nonStoppingAdj > ProcessList.VISIBLE_APP_ADJ || app.systemNoUi)
-                            && app.pendingUiClean) {
+                    if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                            || app.systemNoUi) && app.pendingUiClean) {
                         // If this application is now in the background and it
                         // had done UI, then give it the special trim level to
                         // have it free UI resources.
                         final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
                         if (app.trimMemoryLevel < level && app.thread != null) {
                             try {
+                                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                        "Trimming memory of bg-ui " + app.processName
+                                        + " to " + level);
                                 app.thread.scheduleTrimMemory(level);
                             } catch (RemoteException e) {
                             }
@@ -14830,6 +14809,9 @@
                     }
                     if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
                         try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                    "Trimming memory of fg " + app.processName
+                                    + " to " + fgTrimLevel);
                             app.thread.scheduleTrimMemory(fgTrimLevel);
                         } catch (RemoteException e) {
                         }
@@ -14838,14 +14820,21 @@
                 }
             }
         } else {
-            final int N = mLruProcesses.size();
-            for (i=0; i<N; i++) {
+            allChanged = mProcessTracker.setMemFactorLocked(
+                    ProcessTracker.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now);
+            for (int i=N-1; i>=0; i--) {
                 ProcessRecord app = mLruProcesses.get(i);
-                if ((app.nonStoppingAdj > ProcessList.VISIBLE_APP_ADJ || app.systemNoUi)
-                        && app.pendingUiClean) {
+                if (allChanged || app.procStateChanged) {
+                    app.setProcessTrackerState(ProcessTracker.ADJ_MEM_FACTOR_NORMAL, now);
+                }
+                if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                        || app.systemNoUi) && app.pendingUiClean) {
                     if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
                             && app.thread != null) {
                         try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                    "Trimming memory of ui hidden " + app.processName
+                                    + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
                             app.thread.scheduleTrimMemory(
                                     ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
                         } catch (RemoteException e) {
@@ -14863,16 +14852,6 @@
             mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");
         }
 
-        boolean allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now);
-        if (changed || allChanged) {
-            memFactor = mProcessTracker.getMemFactorLocked();
-            for (i=mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
-                if (allChanged || app.setAdjChanged) {
-                    app.setProcessTrackerState(TOP_APP, memFactor, now, mProcessList);
-                }
-            }
-        }
         if (allChanged) {
             requestPssAllProcsLocked(now, false);
         }
diff --git a/services/java/com/android/server/am/IntentBindRecord.java b/services/java/com/android/server/am/IntentBindRecord.java
index 6c84b9f..21cf266 100644
--- a/services/java/com/android/server/am/IntentBindRecord.java
+++ b/services/java/com/android/server/am/IntentBindRecord.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
+import android.util.ArrayMap;
 
 import java.io.PrintWriter;
 import java.util.HashMap;
@@ -33,8 +34,8 @@
     /** The intent that is bound.*/
     final Intent.FilterComparison intent; // 
     /** All apps that have bound to this Intent. */
-    final HashMap<ProcessRecord, AppBindRecord> apps
-            = new HashMap<ProcessRecord, AppBindRecord>();
+    final ArrayMap<ProcessRecord, AppBindRecord> apps
+            = new ArrayMap<ProcessRecord, AppBindRecord>();
     /** Binder published from service. */
     IBinder binder;
     /** Set when we have initiated a request for this binder. */
@@ -62,15 +63,12 @@
                 pw.print(" received="); pw.print(received);
                 pw.print(" hasBound="); pw.print(hasBound);
                 pw.print(" doRebind="); pw.println(doRebind);
-        if (apps.size() > 0) {
-            Iterator<AppBindRecord> it = apps.values().iterator();
-            while (it.hasNext()) {
-                AppBindRecord a = it.next();
-                pw.print(prefix); pw.print("* Client AppBindRecord{");
-                        pw.print(Integer.toHexString(System.identityHashCode(a)));
-                        pw.print(' '); pw.print(a.client); pw.println('}');
-                a.dumpInIntentBind(pw, prefix + "  ");
-            }
+        for (int i=0; i<apps.size(); i++) {
+            AppBindRecord a = apps.valueAt(i);
+            pw.print(prefix); pw.print("* Client AppBindRecord{");
+                    pw.print(Integer.toHexString(System.identityHashCode(a)));
+                    pw.print(' '); pw.print(a.client); pw.println('}');
+            a.dumpInIntentBind(pw, prefix + "  ");
         }
     }
 
@@ -81,12 +79,11 @@
 
     int collectFlags() {
         int flags = 0;
-        if (apps.size() > 0) {
-            for (AppBindRecord app : apps.values()) {
-                if (app.connections.size() > 0) {
-                    for (ConnectionRecord conn : app.connections) {
-                        flags |= conn.flags;
-                    }
+        for (int i=apps.size()-1; i>=0; i--) {
+            AppBindRecord app = apps.valueAt(i);
+            if (app.connections.size() > 0) {
+                for (ConnectionRecord conn : app.connections) {
+                    flags |= conn.flags;
                 }
             }
         }
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index 6a72e44f..8b0466f 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -36,6 +36,11 @@
 
     // OOM adjustments for processes in various states:
 
+    // Adjustment used in certain places where we don't know it yet.
+    // (Generally this is something that is going to be cached, but we
+    // don't know the exact value in the cached range to assign yet.)
+    static final int UNKNOWN_ADJ = 16;
+
     // This is a process only hosting activities that are not visible,
     // so it can be killed without any disruption.
     static final int CACHED_APP_MAX_ADJ = 15;
@@ -62,14 +67,14 @@
     // have much of an impact as far as the user is concerned.
     static final int SERVICE_ADJ = 5;
 
-    // This is a process currently hosting a backup operation.  Killing it
-    // is not entirely fatal but is generally a bad idea.
-    static final int BACKUP_APP_ADJ = 4;
-
     // This is a process with a heavy-weight application.  It is in the
     // background, but we want to try to avoid killing it.  Value set in
     // system/rootdir/init.rc on startup.
-    static final int HEAVY_WEIGHT_APP_ADJ = 3;
+    static final int HEAVY_WEIGHT_APP_ADJ = 4;
+
+    // This is a process currently hosting a backup operation.  Killing it
+    // is not entirely fatal but is generally a bad idea.
+    static final int BACKUP_APP_ADJ = 3;
 
     // This is a process only hosting components that are perceptible to the
     // user, and we really want to avoid killing them, but they are not
@@ -163,34 +168,11 @@
 
     private boolean mHaveDisplaySize;
 
-    private final int[] mAdjToTrackedState = new int[CACHED_APP_MAX_ADJ+1];
-
     ProcessList() {
         MemInfoReader minfo = new MemInfoReader();
         minfo.readMemInfo();
         mTotalMemMb = minfo.getTotalSize()/(1024*1024);
         updateOomLevels(0, 0, false);
-        for (int i=0; i<=CACHED_APP_MAX_ADJ; i++) {
-            if (i <= FOREGROUND_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_FOREGROUND;
-            } else if (i <= VISIBLE_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_VISIBLE;
-            } else if (i <= PERCEPTIBLE_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_PERCEPTIBLE;
-            } else if (i <= BACKUP_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_BACKUP;
-            } else if (i <= SERVICE_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE;
-            } else if (i <= HOME_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_HOME;
-            } else if (i <= PREVIOUS_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_PREVIOUS;
-            } else if (i <= SERVICE_B_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE;
-            } else {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_CACHED;
-            }
-        }
     }
 
     void applyDisplaySize(WindowManagerService wm) {
@@ -256,11 +238,6 @@
         return mOomMinFree[mOomAdj.length-1] * 1024;
     }
 
-    int adjToTrackedState(int adj) {
-        return adj >= FOREGROUND_APP_ADJ
-                ? mAdjToTrackedState[adj] : ProcessTracker.STATE_PERSISTENT;
-    }
-
     private void writeFile(String path, String data) {
         FileOutputStream fos = null;
         try {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index cb9a76d..5c1b7f24 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.util.ArraySet;
 import com.android.internal.os.BatteryStatsImpl;
 
 import android.app.ActivityManager;
@@ -64,12 +65,8 @@
     long lruWeight;             // Weight for ordering in LRU list
     long lastPssTime;           // Last time we requested PSS data
     int maxAdj;                 // Maximum OOM adjustment for this process
-    int cachedAdj;              // If cached, this is the adjustment to use
-    int clientCachedAdj;        // If empty but cached client, this is the adjustment to use
-    int emptyAdj;               // If empty, this is the adjustment to use
     int curRawAdj;              // Current OOM unlimited adjustment for this process
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
-    int nonStoppingAdj;         // Adjustment not counting any stopping activities
     int curAdj;                 // Current OOM adjustment for this process
     int setAdj;                 // Last set OOM adjustment for this process
     int curSchedGroup;          // Currently desired scheduling class
@@ -78,6 +75,7 @@
     int memImportance;          // Importance constant computed from curAdj
     int curProcState = -1;      // Currently computed process state: ActivityManager.PROCESS_STATE_*
     int repProcState = -1;      // Last reported process state
+    int setProcState = -1;      // Last set process state in process tracker
     boolean serviceb;           // Process currently is on the service B list
     boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
@@ -92,7 +90,7 @@
     boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower
     boolean bad;                // True if disabled in the bad process list
     boolean killedBackground;   // True when proc has been killed due to too many bg
-    boolean setAdjChanged;      // Keep track of whether we changed 'setAdj'.
+    boolean procStateChanged;   // Keep track of whether we changed 'setAdj'.
     String waitingToKill;       // Process is waiting to be killed when in the bg; reason
     IBinder forcingToForeground;// Token that is forcing this process to be foreground
     int adjSeq;                 // Sequence id for identifying oom_adj assignment cycles
@@ -125,15 +123,15 @@
     // contains HistoryRecord objects
     final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
     // all ServiceRecord running in this process
-    final HashSet<ServiceRecord> services = new HashSet<ServiceRecord>();
+    final ArraySet<ServiceRecord> services = new ArraySet<ServiceRecord>();
     // services that are currently executing code (need to remain foreground).
-    final HashSet<ServiceRecord> executingServices
-             = new HashSet<ServiceRecord>();
+    final ArraySet<ServiceRecord> executingServices
+             = new ArraySet<ServiceRecord>();
     // All ConnectionRecord this process holds
-    final HashSet<ConnectionRecord> connections
-            = new HashSet<ConnectionRecord>();  
+    final ArraySet<ConnectionRecord> connections
+            = new ArraySet<ConnectionRecord>();
     // all IIntentReceivers that are registered from this process.
-    final HashSet<ReceiverList> receivers = new HashSet<ReceiverList>();
+    final ArraySet<ReceiverList> receivers = new ArraySet<ReceiverList>();
     // class (String) -> ContentProviderRecord
     final ArrayMap<String, ContentProviderRecord> pubProviders
             = new ArrayMap<String, ContentProviderRecord>();
@@ -215,12 +213,8 @@
                 pw.print(" cached="); pw.print(cached);
                 pw.print(" empty="); pw.println(empty);
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
-                pw.print(" cached="); pw.print(cachedAdj);
-                pw.print(" client="); pw.print(clientCachedAdj);
-                pw.print(" empty="); pw.print(emptyAdj);
                 pw.print(" curRaw="); pw.print(curRawAdj);
                 pw.print(" setRaw="); pw.print(setRawAdj);
-                pw.print(" nonStopping="); pw.print(nonStoppingAdj);
                 pw.print(" cur="); pw.print(curAdj);
                 pw.print(" set="); pw.println(setAdj);
         pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
@@ -228,7 +222,8 @@
                 pw.print(" systemNoUi="); pw.print(systemNoUi);
                 pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
         pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
-                pw.print(" repProcState="); pw.println(repProcState);
+                pw.print(" repProcState="); pw.print(repProcState);
+                pw.print(" setProcState="); pw.println(setProcState);
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.print(lruSeq);
                 pw.print(" lastPssTime="); pw.println(lastPssTime);
@@ -301,20 +296,20 @@
         }
         if (services.size() > 0) {
             pw.print(prefix); pw.println("Services:");
-            for (ServiceRecord sr : services) {
-                pw.print(prefix); pw.print("  - "); pw.println(sr);
+            for (int i=0; i<services.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(services.valueAt(i));
             }
         }
         if (executingServices.size() > 0) {
             pw.print(prefix); pw.println("Executing Services:");
-            for (ServiceRecord sr : executingServices) {
-                pw.print(prefix); pw.print("  - "); pw.println(sr);
+            for (int i=0; i<executingServices.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(executingServices.valueAt(i));
             }
         }
         if (connections.size() > 0) {
             pw.print(prefix); pw.println("Connections:");
-            for (ConnectionRecord cr : connections) {
-                pw.print(prefix); pw.print("  - "); pw.println(cr);
+            for (int i=0; i<connections.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(connections.valueAt(i));
             }
         }
         if (pubProviders.size() > 0) {
@@ -335,8 +330,8 @@
         }
         if (receivers.size() > 0) {
             pw.print(prefix); pw.println("Receivers:");
-            for (ReceiverList rl : receivers) {
-                pw.print(prefix); pw.print("  - "); pw.println(rl);
+            for (int i=0; i<receivers.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(receivers.valueAt(i));
             }
         }
     }
@@ -353,8 +348,7 @@
         baseProcessTracker = tracker;
         pkgList.put(_info.packageName, tracker);
         thread = _thread;
-        maxAdj = ProcessList.CACHED_APP_MAX_ADJ;
-        cachedAdj = clientCachedAdj = emptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
+        maxAdj = ProcessList.UNKNOWN_ADJ;
         curRawAdj = setRawAdj = -100;
         curAdj = setAdj = -100;
         persistent = false;
@@ -400,16 +394,37 @@
 
     void updateHasAboveClientLocked() {
         hasAboveClient = false;
-        if (connections.size() > 0) {
-            for (ConnectionRecord cr : connections) {
-                if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) {
-                    hasAboveClient = true;
-                    break;
-                }
+        for (int i=connections.size()-1; i>=0; i--) {
+            ConnectionRecord cr = connections.valueAt(i);
+            if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+                hasAboveClient = true;
+                break;
             }
         }
     }
 
+    int modifyRawOomAdj(int adj) {
+        if (hasAboveClient) {
+            // If this process has bound to any services with BIND_ABOVE_CLIENT,
+            // then we need to drop its adjustment to be lower than the service's
+            // in order to honor the request.  We want to drop it by one adjustment
+            // level...  but there is special meaning applied to various levels so
+            // we will skip some of them.
+            if (adj < ProcessList.FOREGROUND_APP_ADJ) {
+                // System process will not get dropped, ever
+            } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
+                adj = ProcessList.VISIBLE_APP_ADJ;
+            } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+            } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
+                adj = ProcessList.CACHED_APP_MIN_ADJ;
+            } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
+                adj++;
+            }
+        }
+        return adj;
+    }
+
     public String toShortString() {
         if (shortStringName != null) {
             return shortStringName;
@@ -483,11 +498,8 @@
         }
     }
 
-    public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now,
-            ProcessList plist) {
-        int state = this == TOP_APP ? ProcessTracker.STATE_TOP
-                : plist.adjToTrackedState(getSetAdjWithServices());
-        baseProcessTracker.setState(state, memFactor, now, pkgList);
+    public void setProcessTrackerState(int memFactor, long now) {
+        baseProcessTracker.setState(repProcState, memFactor, now, pkgList);
     }
 
     /*
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
index 0ea93e8..ef032ba 100644
--- a/services/java/com/android/server/am/ProcessTracker.java
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -52,26 +52,34 @@
     public static final int STATE_NOTHING = -1;
     public static final int STATE_PERSISTENT = 0;
     public static final int STATE_TOP = 1;
-    public static final int STATE_FOREGROUND = 2;
-    public static final int STATE_VISIBLE = 3;
-    public static final int STATE_PERCEPTIBLE = 4;
-    public static final int STATE_BACKUP = 5;
+    public static final int STATE_IMPORTANT_FOREGROUND = 2;
+    public static final int STATE_IMPORTANT_BACKGROUND = 3;
+    public static final int STATE_BACKUP = 4;
+    public static final int STATE_HEAVY_WEIGHT = 5;
     public static final int STATE_SERVICE = 6;
-    public static final int STATE_HOME = 7;
-    public static final int STATE_PREVIOUS = 8;
-    public static final int STATE_CACHED = 9;
-    public static final int STATE_COUNT = STATE_CACHED+1;
+    public static final int STATE_RECEIVER = 7;
+    public static final int STATE_HOME = 8;
+    public static final int STATE_LAST_ACTIVITY = 9;
+    public static final int STATE_CACHED_ACTIVITY = 10;
+    public static final int STATE_CACHED_ACTIVITY_CLIENT = 11;
+    public static final int STATE_CACHED_EMPTY = 12;
+    public static final int STATE_COUNT = STATE_CACHED_EMPTY+1;
 
-    static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, STATE_TOP,
-            STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE, STATE_BACKUP,
-            STATE_SERVICE, STATE_HOME, STATE_PREVIOUS, STATE_CACHED
+    static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT,
+            STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND,
+            STATE_BACKUP, STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_RECEIVER,
+            STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
+            STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
     };
 
     public static final int PSS_SAMPLE_COUNT = 0;
     public static final int PSS_MINIMUM = 1;
     public static final int PSS_AVERAGE = 2;
     public static final int PSS_MAXIMUM = 3;
-    public static final int PSS_COUNT = PSS_MAXIMUM+1;
+    public static final int PSS_USS_MINIMUM = 4;
+    public static final int PSS_USS_AVERAGE = 5;
+    public static final int PSS_USS_MAXIMUM = 6;
+    public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
 
     public static final int ADJ_NOTHING = -1;
     public static final int ADJ_MEM_FACTOR_NORMAL = 0;
@@ -106,8 +114,9 @@
     static int OFFSET_INDEX_MASK = 0xffff;
 
     static final String[] STATE_NAMES = new String[] {
-            "Persistent ", "Top        ", "Foreground ", "Visible    ", "Perceptible",
-            "Backup     ", "Service    ", "Home       ", "Previous   ", "Cached     "
+            "Persistent", "Top       ", "Imp Fg    ", "Imp Bg    ",
+            "Backup    ", "Heavy Wght", "Service   ", "Receiver  ", "Home      ",
+            "Last Act  ", "Cch Actvty", "Cch Client", "Cch Empty "
     };
 
     static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
@@ -119,8 +128,9 @@
     };
 
     static final String[] STATE_NAMES_CSV = new String[] {
-            "pers", "top", "fore", "vis", "percept",
-            "backup", "service", "home", "prev", "cached"
+            "pers", "top", "impfg", "impbg", "backup", "heavy",
+            "service", "receiver", "home", "lastact",
+            "cch-activity", "cch-aclient", "cch-empty"
     };
 
     static final String[] ADJ_SCREEN_TAGS = new String[] {
@@ -132,8 +142,26 @@
     };
 
     static final String[] STATE_TAGS = new String[] {
-            "y", "t", "f", "v", "r",
-            "b", "s", "h", "p", "c"
+            "p", "t", "f", "b", "u", "w",
+            "s", "r", "h", "l", "a", "c", "e"
+    };
+
+    // Map from process states to the states we track.
+    static final int[] PROCESS_STATE_TO_STATE = new int[] {
+            STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
+            STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+            STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
+            STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+            STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+            STATE_BACKUP,                   // ActivityManager.PROCESS_STATE_BACKUP
+            STATE_HEAVY_WEIGHT,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+            STATE_SERVICE,                  // ActivityManager.PROCESS_STATE_SERVICE
+            STATE_RECEIVER,                 // ActivityManager.PROCESS_STATE_RECEIVER
+            STATE_HOME,                     // ActivityManager.PROCESS_STATE_HOME
+            STATE_LAST_ACTIVITY,            // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+            STATE_CACHED_ACTIVITY,          // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+            STATE_CACHED_ACTIVITY_CLIENT,   // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+            STATE_CACHED_EMPTY,             // ActivityManager.PROCESS_STATE_CACHED_EMPTY
     };
 
     static final String CSV_SEP = "\t";
@@ -322,10 +350,20 @@
             return true;
         }
 
+        /**
+         * Update the current state of the given list of processes.
+         *
+         * @param state Current ActivityManager.PROCESS_STATE_*
+         * @param memFactor Current mem factor constant.
+         * @param now Current time.
+         * @param pkgList Processes to update.
+         */
         public void setState(int state, int memFactor, long now,
                 ArrayMap<String, ProcessTracker.ProcessState> pkgList) {
-            if (state != STATE_NOTHING) {
-                state += memFactor*STATE_COUNT;
+            if (state < 0) {
+                state = STATE_NOTHING;
+            } else {
+                state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
             }
 
             // First update the common process.
@@ -370,7 +408,7 @@
             mStartTime = now;
         }
 
-        public void addPss(long pss, boolean always) {
+        public void addPss(long pss, long uss, boolean always) {
             if (!always) {
                 if (mLastPssState == mCurState && SystemClock.uptimeMillis()
                         < (mLastPssTime+(30*1000))) {
@@ -399,16 +437,27 @@
                     longs[idx+PSS_MINIMUM] = pss;
                     longs[idx+PSS_AVERAGE] = pss;
                     longs[idx+PSS_MAXIMUM] = pss;
+                    longs[idx+PSS_USS_MINIMUM] = uss;
+                    longs[idx+PSS_USS_AVERAGE] = uss;
+                    longs[idx+PSS_USS_MAXIMUM] = uss;
                 } else {
                     longs[idx+PSS_SAMPLE_COUNT] = count+1;
                     if (longs[idx+PSS_MINIMUM] > pss) {
                         longs[idx+PSS_MINIMUM] = pss;
                     }
-                    longs[idx+PSS_AVERAGE] = (long)( ((longs[idx+PSS_AVERAGE]*(double)count)+pss)
-                            / (count+1) );
+                    longs[idx+PSS_AVERAGE] = (long)(
+                            ((longs[idx+PSS_AVERAGE]*(double)count)+pss) / (count+1) );
                     if (longs[idx+PSS_MAXIMUM] < pss) {
                         longs[idx+PSS_MAXIMUM] = pss;
                     }
+                    if (longs[idx+PSS_USS_MINIMUM] > uss) {
+                        longs[idx+PSS_USS_MINIMUM] = uss;
+                    }
+                    longs[idx+PSS_USS_AVERAGE] = (long)(
+                            ((longs[idx+PSS_USS_AVERAGE]*(double)count)+uss) / (count+1) );
+                    if (longs[idx+PSS_USS_MAXIMUM] < uss) {
+                        longs[idx+PSS_USS_MAXIMUM] = uss;
+                    }
                 }
             }
         }
@@ -480,6 +529,21 @@
             int idx = State.binarySearch(mPssTable, mPssTableSize, state);
             return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
         }
+
+        long getPssUssMinimum(int state) {
+            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
+        }
+
+        long getPssUssAverage(int state) {
+            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
+        }
+
+        long getPssUssMaximum(int state) {
+            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
+        }
     }
 
     public static final class ServiceState {
@@ -589,10 +653,13 @@
 
     static final class State {
         // Current version of the parcel format.
-        private static final int PARCEL_VERSION = 3;
+        private static final int PARCEL_VERSION = 6;
         // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
         private static final int MAGIC = 0x50535453;
 
+        static final int FLAG_COMPLETE = 1<<0;
+        static final int FLAG_SHUTDOWN = 1<<1;
+
         final File mBaseDir;
         final ProcessTracker mProcessTracker;
         AtomicFile mFile;
@@ -603,6 +670,7 @@
         long mTimePeriodStartRealtime;
         long mTimePeriodEndRealtime;
         boolean mRunning;
+        int mFlags;
 
         final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
         final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
@@ -690,6 +758,7 @@
             Arrays.fill(mMemFactorDurations, 0);
             mMemFactor = STATE_NOTHING;
             mStartTime = 0;
+            mReadError = null;
         }
 
         private void buildTimePeriodStartClockStr() {
@@ -724,7 +793,7 @@
             }
         }
 
-        void readLocked() {
+        boolean readLocked() {
             try {
                 FileInputStream stream = mFile.openRead();
 
@@ -770,11 +839,14 @@
                             }
                         }
                     }
+                    return false;
                 }
             } catch (Throwable e) {
-                mReadError = "error reading: " + e;
+                mReadError = "caught exception: " + e;
                 Slog.e(TAG, "Error reading process statistics", e);
+                return false;
             }
+            return true;
         }
 
         private void writeStateLocked(boolean sync, final boolean commit) {
@@ -848,6 +920,7 @@
             out.writeLong(mTimePeriodStartClock);
             out.writeLong(mTimePeriodStartRealtime);
             out.writeLong(mTimePeriodEndRealtime);
+            out.writeInt(mFlags);
 
             out.writeInt(mLongs.size());
             out.writeInt(mNextLong);
@@ -920,7 +993,7 @@
         private boolean readCheckedInt(Parcel in, int val, String what) {
             int got;
             if ((got=in.readInt()) != val) {
-                mReadError = "bad " + ": " + got;
+                mReadError = "bad " + what + ": " + got;
                 return false;
             }
             return true;
@@ -956,6 +1029,7 @@
             buildTimePeriodStartClockStr();
             mTimePeriodStartRealtime = in.readLong();
             mTimePeriodEndRealtime = in.readLong();
+            mFlags = in.readInt();
 
             final int NLONGS = in.readInt();
             final int NEXTLONG = in.readInt();
@@ -1365,8 +1439,9 @@
 
         void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) {
             dumpFilteredSummaryLocked(pw, null, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
-                    new int[] { STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
-                            STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS },
+                    new int[] { STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
+                            STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT,
+                            STATE_SERVICE, STATE_RECEIVER, STATE_HOME, STATE_LAST_ACTIVITY },
                     now, reqPackage);
             pw.println();
             pw.println("Run time Stats:");
@@ -1379,6 +1454,9 @@
             TimeUtils.formatDuration(
                     (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
                             - mTimePeriodStartRealtime, pw);
+            if ((mFlags&FLAG_COMPLETE) != 0) pw.print(" (complete)");
+            else if ((mFlags&FLAG_SHUTDOWN) != 0) pw.print(" (shutdown)");
+            else pw.print(" (partial)");
             pw.println();
         }
 
@@ -1452,10 +1530,13 @@
         void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
             final long now = SystemClock.uptimeMillis();
             ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
-            pw.println("vers,1");
+            pw.println("vers,2");
             pw.print("period,"); pw.print(mTimePeriodStartClockStr);
             pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
             pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+            if ((mFlags&FLAG_COMPLETE) != 0) pw.print(",complete");
+            else if ((mFlags&FLAG_SHUTDOWN) != 0) pw.print(",shutdown");
+            else pw.print(",partial");
             pw.println();
             for (int ip=0; ip<pkgMap.size(); ip++) {
                 String pkgName = pkgMap.keyAt(ip);
@@ -1629,6 +1710,7 @@
         if (now > (mState.mLastWriteTime+WRITE_PERIOD)) {
             if (SystemClock.elapsedRealtime() > (mState.mTimePeriodStartRealtime+COMMIT_PERIOD)) {
                 mCommitPending = true;
+                mState.mFlags |= State.FLAG_COMPLETE;
             }
             return true;
         }
@@ -1637,6 +1719,7 @@
 
     public void shutdownLocked() {
         Slog.w(TAG, "Writing process stats before shutdown...");
+        mState.mFlags |= State.FLAG_SHUTDOWN;
         writeStateSyncLocked();
         mShuttingDown = true;
     }
@@ -1841,6 +1924,9 @@
         long minPss;
         long avgPss;
         long maxPss;
+        long minUss;
+        long avgUss;
+        long maxUss;
 
         ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
             screenStates = _screenStates;
@@ -1857,6 +1943,12 @@
                 printSizeValue(pw, avgPss * 1024);
                 pw.print("-");
                 printSizeValue(pw, maxPss * 1024);
+                pw.print("/");
+                printSizeValue(pw, minUss * 1024);
+                pw.print("-");
+                printSizeValue(pw, avgUss * 1024);
+                pw.print("-");
+                printSizeValue(pw, maxUss * 1024);
                 if (full) {
                     pw.print(" over ");
                     pw.print(numPss);
@@ -1868,7 +1960,8 @@
 
     static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) {
         data.totalTime = 0;
-        data.numPss = data.minPss = data.avgPss = data.maxPss = 0;
+        data.numPss = data.minPss = data.avgPss = data.maxPss =
+                data.minUss = data.avgUss = data.maxUss = 0;
         for (int is=0; is<data.screenStates.length; is++) {
             for (int im=0; im<data.memStates.length; im++) {
                 for (int ip=0; ip<data.procStates.length; ip++) {
@@ -1880,10 +1973,16 @@
                         long minPss = proc.getPssMinimum(bucket);
                         long avgPss = proc.getPssAverage(bucket);
                         long maxPss = proc.getPssMaximum(bucket);
+                        long minUss = proc.getPssUssMinimum(bucket);
+                        long avgUss = proc.getPssUssAverage(bucket);
+                        long maxUss = proc.getPssUssMaximum(bucket);
                         if (data.numPss == 0) {
                             data.minPss = minPss;
                             data.avgPss = avgPss;
                             data.maxPss = maxPss;
+                            data.minUss = minUss;
+                            data.avgUss = avgUss;
+                            data.maxUss = maxUss;
                         } else {
                             if (minPss < data.minPss) {
                                 data.minPss = minPss;
@@ -1893,6 +1992,14 @@
                             if (maxPss > data.maxPss) {
                                 data.maxPss = maxPss;
                             }
+                            if (minUss < data.minUss) {
+                                data.minUss = minUss;
+                            }
+                            data.avgUss = (long)( ((data.avgUss*(double)data.numPss)
+                                    + (avgUss*(double)samples)) / (data.numPss+samples) );
+                            if (maxUss > data.maxUss) {
+                                data.maxUss = maxUss;
+                            }
                         }
                         data.numPss += samples;
                     }
@@ -1989,7 +2096,7 @@
                     if (count > 0) {
                         if (!printedHeader) {
                             pw.print(prefix);
-                            pw.print("PSS (");
+                            pw.print("PSS/USS (");
                             pw.print(proc.mPssTableSize);
                             pw.println(" entries):");
                             printedHeader = true;
@@ -2013,6 +2120,12 @@
                         printSizeValue(pw, proc.getPssAverage(bucket) * 1024);
                         pw.print(" ");
                         printSizeValue(pw, proc.getPssMaximum(bucket) * 1024);
+                        pw.print(" / ");
+                        printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024);
+                        pw.print(" ");
+                        printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024);
+                        pw.print(" ");
+                        printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024);
                         pw.println();
                     }
                 }
@@ -2148,21 +2261,28 @@
             dumpProcessSummaryDetails(pw, proc, prefix, "         TOTAL: ", screenStates, memStates,
                     procStates, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "    Persistent: ", screenStates, memStates,
-                    new int[] {STATE_PERSISTENT}, now, true);
+                    new int[] { STATE_PERSISTENT }, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "           Top: ", screenStates, memStates,
                     new int[] {STATE_TOP}, now, true);
-            dumpProcessSummaryDetails(pw, proc, prefix, "    Foreground: ", screenStates, memStates,
-                    new int[] {STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE}, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "        Imp Fg: ", screenStates, memStates,
+                    new int[] { STATE_IMPORTANT_FOREGROUND }, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "        Imp Bg: ", screenStates, memStates,
+                    new int[] {STATE_IMPORTANT_BACKGROUND}, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "        Backup: ", screenStates, memStates,
                     new int[] {STATE_BACKUP}, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "     Heavy Wgt: ", screenStates, memStates,
+                    new int[] {STATE_HEAVY_WEIGHT}, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "       Service: ", screenStates, memStates,
                     new int[] {STATE_SERVICE}, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "      Receiver: ", screenStates, memStates,
+                    new int[] {STATE_RECEIVER}, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "          Home: ", screenStates, memStates,
                     new int[] {STATE_HOME}, now, true);
-            dumpProcessSummaryDetails(pw, proc, prefix, "      Previous: ", screenStates, memStates,
-                    new int[] {STATE_PREVIOUS}, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "      Last Act: ", screenStates, memStates,
+                    new int[] {STATE_LAST_ACTIVITY}, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "      (Cached): ", screenStates, memStates,
-                    new int[] {STATE_CACHED}, now, true);
+                    new int[] {STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_ACTIVITY_CLIENT,
+                            STATE_CACHED_EMPTY}, now, true);
         }
     }
 
@@ -2193,9 +2313,9 @@
         if (result < 1) {
             value = String.format("%.2f", result);
         } else if (result < 10) {
-            value = String.format("%.2f", result);
+            value = String.format("%.1f", result);
         } else if (result < 100) {
-            value = String.format("%.2f", result);
+            value = String.format("%.0f", result);
         } else {
             value = String.format("%.0f", result);
         }
@@ -2300,6 +2420,9 @@
             long min = proc.mState.getLong(off, PSS_MINIMUM);
             long avg = proc.mState.getLong(off, PSS_AVERAGE);
             long max = proc.mState.getLong(off, PSS_MAXIMUM);
+            long umin = proc.mState.getLong(off, PSS_USS_MINIMUM);
+            long uavg = proc.mState.getLong(off, PSS_USS_AVERAGE);
+            long umax = proc.mState.getLong(off, PSS_USS_MAXIMUM);
             pw.print(',');
             printProcStateTag(pw, type);
             pw.print(':');
@@ -2310,6 +2433,12 @@
             pw.print(avg);
             pw.print(':');
             pw.print(max);
+            pw.print(':');
+            pw.print(umin);
+            pw.print(':');
+            pw.print(uavg);
+            pw.print(':');
+            pw.print(umax);
         }
     }
 
@@ -2391,9 +2520,10 @@
         int[] csvMemStats = new int[] {ADJ_MEM_FACTOR_CRITICAL};
         boolean csvSepProcStats = true;
         int[] csvProcStats = new int[] {
-                STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
-                STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
-                STATE_PREVIOUS, STATE_CACHED };
+                STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
+                STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, STATE_SERVICE,
+                STATE_RECEIVER, STATE_HOME, STATE_LAST_ACTIVITY,
+                STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY };
         if (args != null) {
             for (int i=0; i<args.length; i++) {
                 String arg = args[i];
@@ -2457,6 +2587,7 @@
                 } else if ("--current".equals(arg)) {
                     currentOnly = true;
                 } else if ("--commit".equals(arg)) {
+                    mState.mFlags |= State.FLAG_COMPLETE;
                     mState.writeStateLocked(true, true);
                     pw.println("Process stats committed.");
                     return;
@@ -2557,6 +2688,13 @@
                         if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
                         try {
                             State state = new State(files.get(i));
+                            if (state.mReadError != null) {
+                                pw.print("Failure reading "); pw.print(files.get(i));
+                                pw.print("; "); pw.println(state.mReadError);
+                                if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
+                                (new File(files.get(i))).delete();
+                                continue;
+                            }
                             String fileStr = state.mFile.getBaseFile().getPath();
                             boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
                             if (isCheckin || isCompact) {
@@ -2588,7 +2726,6 @@
                             pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
                             e.printStackTrace(pw);
                         }
-                        if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
                     }
                 }
             } finally {