Implement issue #10691475: Kill cached processes if about to...

...be uncached and too large

When the device is in a low RAM state, when we go to pull a cached
process out to use for some background operation, we can now kill
the current process if we consider its size to be too large.

Note that the current implementation for killing processes is to
just use the same killUnneededProcessLocked() method that we already
have for other things like too many cached processes.  This is a
little wrong here, though, because in this case we are at the
point where the caller is actually looking for a process to use.
This current code is not actually removing or cleaning up the
process, so we still need to return the now killed ProcessRecord
and let things fall out from there, which typically means the caller
trying to make an IPC on it and failing and falling into its "oh
no the process died unexpectedly" path.  All code using this
*should* be able to handle this correctly, anyway, since processes
really can be killed at any time.

At some point we may to make this implementation cleaner, where it
actually tears down the process right in the call and returns a
null ProcessRecord.  That is very dangerous however (we'd need to
go through all paths into this to make sure they are going to be
okay with process state changing on them like that), and I'm not
sure it is really worthwhile.  This intention is that killing
processes like this is unusual, due to processes being too large,
and anyway as I wrote all of our incoming code paths must already
be able to handle the process being killed at this point and one
could argue this is just another way to excercise those code paths.
Really, the main negative to this is that we will often have spam
in the log with exceptions about processes dying unexpectedly.
If that is the only issue, we could just add some conditions to
quiet that up at in this case.

We don't want to compute the size of the process each time we try
to evaluate it here (it takes 10s or ms to do so), so there is now
a new field associated with the process to give us the last pss
size we computed for it while it was in the cached state.

To be able to have better cached pss data when we now need it, the
timing for computing process pss has been tuned to use a much
shorter delay for the situations when the process has first switch
into a new state.  This may result in us having a fair amount more
pss data overall, which is good, as long as it doesn't cause us to
be computing pss excessively and burning cpu.

Procstats now also has new state to keep track of the number of
times each process has been killed by this new system, along with
the min, avg, max pss of all the times it has happened.  This has
slightly changed the checkin format to include this additional data
at the end of pkgkills/prockills lines.

Other changes here:

- Fixed a problem where GPU RAM was not being seen when dumping
  the full RAM details of a process.  This was because in that
  case the system would ask the process to compute its own MemInfo,
  which it returned, but the process doesn't have permission to
  access the files containing the GPU RAM data.  So now the system
  always computes the MemInfo and hands it to the app.

- Improved broadcast delays to not apply the delay if the next receiver
  of the broadcast is going to run in the same process as the last
  one.  A situation I was seeing was an application that had two
  receivers, one of which started a service; we are better off letting
  the second receiver run while the service is running.

- Changed the alarm manager's TIME_TICK broadcast to be a foreground
  broadcast.  This really should have been anyway (it is supposed to
  go out even minute, on the minute, very accurately, for UI elements
  to update), and is even more important now that we are doing more
  things to delay background broadcasts.

- Reworked how we maintain the LRU process list.  It is now divided
  into the two parts, the top always containing the processes holding
  activities.  This better matches the semantics we want (always try
  to keep those around modulated by the LRU order we interleave with
  other cached processes), and we now know whether a process is being
  moved on the LRU list because of an activity operation so we can
  only change the order of these activity processes when user operations
  happen.  Further, this just makes that common code path a lot simpler
  and gets rid of all the old complexity that doesn't make sense any
  more.

Change-Id: I04933ec3931b96db70b2b6ac109c071698e124eb
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 98b5f66..bb6c4e6 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -470,7 +470,8 @@
         
         mTimeTickSender = PendingIntent.getBroadcastAsUser(context, 0,
                 new Intent(Intent.ACTION_TIME_TICK).addFlags(
-                        Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0,
+                        Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                        | Intent.FLAG_RECEIVER_FOREGROUND), 0,
                         UserHandle.ALL);
         Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index fea7623..4379c70 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -311,7 +311,7 @@
         final ServiceMap smap = getServiceMap(r.userId);
         boolean addToStarting = false;
         if (!callerFg && r.app == null && mAm.mStartedUsers.get(r.userId) != null) {
-            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid);
+            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
             if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
                 // If this is not coming from a foreground caller, then we may want
                 // to delay the start if there are already other background services
@@ -562,7 +562,7 @@
                     if (r.isForeground) {
                         r.isForeground = false;
                         if (r.app != null) {
-                            mAm.updateLruProcessLocked(r.app, false);
+                            mAm.updateLruProcessLocked(r.app, false, false);
                             updateServiceForegroundLocked(r.app, true);
                         }
                     }
@@ -1241,9 +1241,9 @@
         ProcessRecord app;
 
         if (!isolated) {
-            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
-            if (DEBUG_MU)
-                Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
+            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
+            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+                        + " app=" + app);
             if (app != null && app.thread != null) {
                 try {
                     app.addPackage(r.appInfo.packageName, mAm.mProcessStats);
@@ -1270,7 +1270,7 @@
         // to be executed when the app comes up.
         if (app == null) {
             if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
-                    "service", r.name, false, isolated)) == null) {
+                    "service", r.name, false, isolated, false)) == null) {
                 String msg = "Unable to launch app "
                         + r.appInfo.packageName + "/"
                         + r.appInfo.uid + " for service "
@@ -1322,7 +1322,7 @@
 
         app.services.add(r);
         bumpServiceExecutingLocked(r, execInFg, "create");
-        mAm.updateLruProcessLocked(app, true);
+        mAm.updateLruProcessLocked(app, true, false);
 
         boolean created = false;
         try {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f08b5b9..7a480dc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -499,6 +499,11 @@
     final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
 
     /**
+     * Where in mLruProcesses that the processes hosting activities start.
+     */
+    int mLruProcessActivityStart = 0;
+
+    /**
      * List of processes that should gc as soon as things are idle.
      */
     final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
@@ -1638,7 +1643,7 @@
                     int pid;
                     synchronized (ActivityManagerService.this) {
                         if (i >= mPendingPssProcesses.size()) {
-                            if (DEBUG_PSS) Slog.i(TAG, "Collected PSS of " + num + " of " + i
+                            if (DEBUG_PSS) Slog.d(TAG, "Collected PSS of " + num + " of " + i
                                     + " processes in " + (SystemClock.uptimeMillis()-start) + "ms");
                             mPendingPssProcesses.clear();
                             return;
@@ -1661,10 +1666,16 @@
                                 num++;
                                 proc.lastPssTime = SystemClock.uptimeMillis();
                                 proc.baseProcessTracker.addPss(pss, tmp[0], true);
+                                if (DEBUG_PSS) Slog.d(TAG, "PSS of " + proc.toShortString()
+                                        + ": " + pss + " lastPss=" + proc.lastPss
+                                        + " state=" + ProcessList.makeProcStateString(procState));
                                 if (proc.initialIdlePss == 0) {
                                     proc.initialIdlePss = pss;
                                 }
                                 proc.lastPss = pss;
+                                if (procState >= ActivityManager.PROCESS_STATE_HOME) {
+                                    proc.lastCachedPss = pss;
+                                }
                             }
                         }
                     }
@@ -1704,7 +1715,7 @@
                 synchronized (mSelf.mPidsSelfLocked) {
                     mSelf.mPidsSelfLocked.put(app.pid, app);
                 }
-                mSelf.updateLruProcessLocked(app, true);
+                mSelf.updateLruProcessLocked(app, true, false);
             }
         } catch (PackageManager.NameNotFoundException e) {
             throw new RuntimeException(
@@ -2219,52 +2230,76 @@
         mHandler.sendMessage(msg);
     }
 
-    private final void updateLruProcessInternalLocked(ProcessRecord app, int bestPos) {
-        // put it on the LRU to keep track of when it should be exited.
-        int lrui = mLruProcesses.indexOf(app);
-        if (lrui >= 0) mLruProcesses.remove(lrui);
+    private final int updateLruProcessInternalLocked(ProcessRecord app, long now, int index) {
+        app.lastActivityTime = now;
 
-        int i = mLruProcesses.size()-1;
-        int skipTop = 0;
-
-        app.lruSeq = mLruSeq;
-
-        // compute the new weight for this process.
-        app.lastActivityTime = SystemClock.uptimeMillis();
         if (app.activities.size() > 0) {
-            // If this process has activities, we more strongly want to keep
-            // it around.
-            app.lruWeight = app.lastActivityTime;
-        } else if (app.pubProviders.size() > 0) {
-            // If this process contains content providers, we want to keep
-            // it a little more strongly.
-            app.lruWeight = app.lastActivityTime - ProcessList.CONTENT_APP_IDLE_OFFSET;
-            // Also don't let it kick out the first few "real" cached processes.
-            skipTop = ProcessList.MIN_CACHED_APPS;
-        } else {
-            // If this process doesn't have activities, we less strongly
-            // want to keep it around, and generally want to avoid getting
-            // in front of any very recently used activities.
-            app.lruWeight = app.lastActivityTime - ProcessList.EMPTY_APP_IDLE_OFFSET;
-            // Also don't let it kick out the first few "real" cached processes.
-            skipTop = ProcessList.MIN_CACHED_APPS;
+            // Don't want to touch dependent processes that are hosting activities.
+            return index;
         }
 
-        while (i >= 0) {
-            ProcessRecord p = mLruProcesses.get(i);
-            // If this app shouldn't be in front of the first N background
-            // apps, then skip over that many that are currently cached.
-            if (skipTop > 0 && p.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
-                skipTop--;
-            }
-            if (p.lruWeight <= app.lruWeight || i < bestPos) {
-                mLruProcesses.add(i+1, app);
-                break;
-            }
-            i--;
+        int lrui = mLruProcesses.lastIndexOf(app);
+        if (lrui < 0) {
+            throw new IllegalStateException("Adding dependent process " + app
+                    + " not on LRU list!");
         }
-        if (i < 0) {
-            mLruProcesses.add(0, app);
+
+        if (lrui >= mLruProcessActivityStart) {
+            // Don't want to touch dependent processes that are hosting activities.
+            return index;
+        }
+
+        mLruProcesses.remove(lrui);
+        if (index > 0) {
+            index--;
+        }
+        mLruProcesses.add(index, app);
+        return index;
+    }
+
+    final void removeLruProcessLocked(ProcessRecord app) {
+        int lrui = mLruProcesses.lastIndexOf(app);
+        if (lrui >= 0) {
+            if (lrui <= mLruProcessActivityStart) {
+                mLruProcessActivityStart--;
+            }
+            mLruProcesses.remove(lrui);
+        }
+    }
+
+    final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj, boolean activityChange) {
+        final boolean hasActivity = app.activities.size() > 0;
+        if (!activityChange && hasActivity) {
+            // The process has activties, so we are only going to allow activity-based
+            // adjustments move it.  It should be kept in the front of the list with other
+            // processes that have activities, and we don't want those to change their
+            // order except due to activity operations.
+            return;
+        }
+
+        mLruSeq++;
+        final long now = SystemClock.uptimeMillis();
+        app.lastActivityTime = now;
+
+        int lrui = mLruProcesses.lastIndexOf(app);
+
+        if (lrui >= 0) {
+            if (lrui < mLruProcessActivityStart) {
+                mLruProcessActivityStart--;
+            }
+            mLruProcesses.remove(lrui);
+        }
+
+        int nextIndex;
+        if (!hasActivity) {
+            // Process doesn't have activities, it goes to the top of the non-activity area.
+            mLruProcesses.add(mLruProcessActivityStart, app);
+            nextIndex = mLruProcessActivityStart-1;
+            mLruProcessActivityStart++;
+        } else {
+            // Process does have activities, put it at the very tipsy-top.
+            mLruProcesses.add(app);
+            nextIndex = mLruProcessActivityStart;
         }
 
         // If the app is currently using a content provider or service,
@@ -2274,20 +2309,15 @@
             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);
+                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex);
             }
         }
         for (int j=app.conProviders.size()-1; j>=0; j--) {
             ContentProviderRecord cpr = app.conProviders.get(j).provider;
             if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
-                updateLruProcessInternalLocked(cpr.proc, i+1);
+                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex);
             }
         }
-    }
-
-    final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj) {
-        mLruSeq++;
-        updateLruProcessInternalLocked(app, 0);
 
         //Slog.i(TAG, "Putting proc to front: " + app.processName);
         if (oomAdj) {
@@ -2295,14 +2325,12 @@
         }
     }
 
-    final ProcessRecord getProcessRecordLocked(
-            String processName, int uid) {
+    final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
         if (uid == Process.SYSTEM_UID) {
             // The system gets to run in any process.  If there are multiple
             // processes with the same uid, just pick the first (this
             // should never happen).
-            SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(
-                    processName);
+            SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
             if (procs == null) return null;
             final int N = procs.size();
             for (int i = 0; i < N; i++) {
@@ -2310,6 +2338,26 @@
             }
         }
         ProcessRecord proc = mProcessNames.get(processName, uid);
+        if (false && proc != null && !keepIfLarge
+                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
+                && proc.lastCachedPss >= 4000) {
+            // Turn this condition on to cause killing to happen regularly, for testing.
+            if (proc.baseProcessTracker != null) {
+                proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
+            }
+            killUnneededProcessLocked(proc, Long.toString(proc.lastCachedPss)
+                    + "k from cached");
+        } else if (proc != null && !keepIfLarge && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+            if (DEBUG_PSS) Slog.d(TAG, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
+            if (proc.lastCachedPss >= mProcessList.getCachedRestoreThreshold()) {
+                if (proc.baseProcessTracker != null) {
+                    proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
+                }
+                killUnneededProcessLocked(proc, Long.toString(proc.lastCachedPss)
+                        + "k from cached");
+            }
+        }
         return proc;
     }
 
@@ -2333,10 +2381,10 @@
     final ProcessRecord startProcessLocked(String processName,
             ApplicationInfo info, boolean knownToBeDead, int intentFlags,
             String hostingType, ComponentName hostingName, boolean allowWhileBooting,
-            boolean isolated) {
+            boolean isolated, boolean keepIfLarge) {
         ProcessRecord app;
         if (!isolated) {
-            app = getProcessRecordLocked(processName, info.uid);
+            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
         } else {
             // If this is an isolated process, it can't re-use an existing process.
             app = null;
@@ -2647,7 +2695,7 @@
             aInfo = new ActivityInfo(aInfo);
             aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
             ProcessRecord app = getProcessRecordLocked(aInfo.processName,
-                    aInfo.applicationInfo.uid);
+                    aInfo.applicationInfo.uid, true);
             if (app == null || app.instrumentationClass == null) {
                 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                 mStackSupervisor.startHomeActivity(intent, aInfo);
@@ -3364,7 +3412,7 @@
             boolean restarting, boolean allowRestart) {
         cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
         if (!restarting) {
-            mLruProcesses.remove(app);
+            removeLruProcessLocked(app);
         }
 
         if (mProfileProc == app) {
@@ -4212,7 +4260,7 @@
         // Only the system server can kill an application
         if (callerUid == Process.SYSTEM_UID) {
             synchronized (this) {
-                ProcessRecord app = getProcessRecordLocked(processName, uid);
+                ProcessRecord app = getProcessRecordLocked(processName, uid, true);
                 if (app != null && app.thread != null) {
                     try {
                         app.thread.scheduleSuicide();
@@ -4517,7 +4565,7 @@
             }
             killUnneededProcessLocked(app, reason);
             handleAppDiedLocked(app, true, allowRestart);
-            mLruProcesses.remove(app);
+            removeLruProcessLocked(app);
 
             if (app.persistent && !app.isolated) {
                 if (!callerWillRestart) {
@@ -4712,7 +4760,7 @@
                     isRestrictedBackupMode || !normalMode, app.persistent,
                     new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
                     mCoreSettingsObserver.getCoreSettingsLocked());
-            updateLruProcessLocked(app, false);
+            updateLruProcessLocked(app, false, false);
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
         } catch (Exception e) {
             // todo: Yikes!  What should we do?  For now we will try to
@@ -7175,7 +7223,7 @@
                         // make sure to count it as being accessed and thus
                         // back up on the LRU list.  This is good because
                         // content providers are often expensive to start.
-                        updateLruProcessLocked(cpr.proc, false);
+                        updateLruProcessLocked(cpr.proc, false, false);
                     }
                 }
 
@@ -7325,7 +7373,7 @@
                         ProcessRecord proc = startProcessLocked(cpi.processName,
                                 cpr.appInfo, false, 0, "content provider",
                                 new ComponentName(cpi.applicationInfo.packageName,
-                                        cpi.name), false, false);
+                                        cpi.name), false, false, false);
                         if (proc == null) {
                             Slog.w(TAG, "Unable to launch app "
                                     + cpi.applicationInfo.packageName + "/"
@@ -7734,7 +7782,7 @@
     final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
         ProcessRecord app;
         if (!isolated) {
-            app = getProcessRecordLocked(info.processName, info.uid);
+            app = getProcessRecordLocked(info.processName, info.uid, true);
         } else {
             app = null;
         }
@@ -7745,7 +7793,7 @@
             if (isolated) {
                 mIsolatedProcesses.put(app.uid, app);
             }
-            updateLruProcessLocked(app, true);
+            updateLruProcessLocked(app, true, false);
         }
 
         // This package really, really can not be stopped.
@@ -8613,6 +8661,8 @@
                 } else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME) {
                     proc.notCachedSinceIdle = true;
                     proc.initialIdlePss = 0;
+                    proc.nextPssTime = ProcessList.computeNextPssTime(proc.curProcState, true,
+                            mSleeping, now);
                 }
             }
 
@@ -10230,13 +10280,16 @@
         }
 
         if (mLruProcesses.size() > 0) {
-            boolean printed = dumpProcessOomList(pw, this, mLruProcesses, "    ",
-                    "Proc", "PERS", false, dumpPackage, needSep,
-                    "  Process LRU list (sorted by oom_adj):");
-            if (printed) {
-                needSep = true;
-                printedAnything = true;
+            if (needSep) {
+                pw.println();
             }
+            pw.print("  Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
+                    pw.print(" total, non-activities at ");
+                    pw.print(mLruProcesses.size()-mLruProcessActivityStart);
+                    pw.println("):");
+            dumpProcessOomList(pw, this, mLruProcesses, "    ", "Proc", "PERS", false, dumpPackage);
+            needSep = true;
+            printedAnything = true;
         }
 
         if (dumpAll || dumpPackage != null) {
@@ -10607,14 +10660,15 @@
             printOomLevel(pw, "CACHED_APP_MAX_ADJ", ProcessList.CACHED_APP_MAX_ADJ);
 
             if (needSep) pw.println();
-            needSep = true;
-            pw.println("  Process OOM control:");
-            dumpProcessOomList(pw, this, mLruProcesses, "    ",
-                    "Proc", "PERS", true, null, false, null);
+            pw.print("  Process OOM control ("); pw.print(mLruProcesses.size());
+                    pw.print(" total, non-activities at ");
+                    pw.print(mLruProcesses.size()-mLruProcessActivityStart);
+                    pw.println("):");
+            dumpProcessOomList(pw, this, mLruProcesses, "    ", "Proc", "PERS", true, null);
             needSep = true;
         }
 
-        needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, null);
+        dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, null);
 
         pw.println();
         pw.println("  mHomeProcess: " + mHomeProcess);
@@ -11034,7 +11088,7 @@
     private static final boolean dumpProcessOomList(PrintWriter pw,
             ActivityManagerService service, List<ProcessRecord> origList,
             String prefix, String normalLabel, String persistentLabel,
-            boolean inclDetails, String dumpPackage, boolean needSep, String header) {
+            boolean inclDetails, String dumpPackage) {
 
         ArrayList<Pair<ProcessRecord, Integer>> list
                 = new ArrayList<Pair<ProcessRecord, Integer>>(origList.size());
@@ -11050,13 +11104,6 @@
             return false;
         }
 
-        if (header != null) {
-            if (needSep) {
-                pw.println();
-            }
-            pw.println(header);
-        }
-
         Comparator<Pair<ProcessRecord, Integer>> comparator
                 = new Comparator<Pair<ProcessRecord, Integer>>() {
             @Override
@@ -11156,6 +11203,12 @@
                 pw.print(" set="); pw.println(r.setAdj);
                 pw.print(prefix);
                 pw.print("    ");
+                pw.print("state: cur="); pw.print(ProcessList.makeProcStateString(r.curProcState));
+                pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState));
+                pw.print(" lastPss="); pw.print(r.lastPss);
+                pw.print(" lastCachedPss="); pw.println(r.lastCachedPss);
+                pw.print(prefix);
+                pw.print("    ");
                 pw.print("keeping="); pw.print(r.keeping);
                 pw.print(" cached="); pw.print(r.cached);
                 pw.print(" empty="); pw.print(r.empty);
@@ -11505,27 +11558,25 @@
                 if (!isCheckinRequest && dumpDetails) {
                     pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
                 }
+                if (mi == null) {
+                    mi = new Debug.MemoryInfo();
+                }
+                if (dumpDetails || (!brief && !oomOnly)) {
+                    Debug.getMemoryInfo(pid, mi);
+                } else {
+                    mi.dalvikPss = (int)Debug.getPss(pid, tmpLong);
+                    mi.dalvikPrivateDirty = (int)tmpLong[0];
+                }
                 if (dumpDetails) {
                     try {
                         pw.flush();
-                        mi = null;
-                        mi = thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs);
+                        thread.dumpMemInfo(fd, mi, isCheckinRequest, true, dumpDalvik, innerArgs);
                     } catch (RemoteException e) {
                         if (!isCheckinRequest) {
                             pw.println("Got RemoteException!");
                             pw.flush();
                         }
                     }
-                } else {
-                    if (mi == null) {
-                        mi = new Debug.MemoryInfo();
-                    }
-                    if (!brief && !oomOnly) {
-                        Debug.getMemoryInfo(pid, mi);
-                    } else {
-                        mi.dalvikPss = (int)Debug.getPss(pid, tmpLong);
-                        mi.dalvikPrivateDirty = (int)tmpLong[0];
-                    }
                 }
 
                 final long myTotalPss = mi.getTotalPss();
@@ -11821,7 +11872,7 @@
     private final void cleanUpApplicationRecordLocked(ProcessRecord app,
             boolean restarting, boolean allowRestart, int index) {
         if (index >= 0) {
-            mLruProcesses.remove(index);
+            removeLruProcessLocked(app);
         }
 
         mProcessesToGc.remove(app);
@@ -12278,7 +12329,7 @@
                     : new ComponentName("android", "FullBackupAgent");
             // startProcessLocked() returns existing proc's record if it's already running
             ProcessRecord proc = startProcessLocked(app.processName, app,
-                    false, 0, "backup", hostingName, false, false);
+                    false, 0, "backup", hostingName, false, false, false);
             if (proc == null) {
                 Slog.e(TAG, "Unable to start backup agent process " + r);
                 return false;
@@ -14711,17 +14762,21 @@
         }
         if (app.setProcState < 0 || ProcessList.procStatesDifferForMem(app.curProcState,
                 app.setProcState)) {
-            if (DEBUG_PSS) Slog.d(TAG, "Process state change from " + app.setProcState
-                    + " to " + app.curProcState + ": " + app);
             app.lastStateTime = now;
             app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
                     mSleeping, now);
+            if (DEBUG_PSS) Slog.d(TAG, "Process state change from "
+                    + ProcessList.makeProcStateString(app.setProcState) + " to "
+                    + ProcessList.makeProcStateString(app.curProcState) + " next pss in "
+                    + (app.nextPssTime-now) + ": " + app);
         } else {
             if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
                     && now > (app.lastStateTime+ProcessList.PSS_MIN_TIME_FROM_STATE_CHANGE))) {
                 requestPssLocked(app, app.setProcState);
                 app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, false,
                         mSleeping, now);
+            } else if (false && DEBUG_PSS) {
+                Slog.d(TAG, "Not requesting PSS of " + app + ": next=" + (app.nextPssTime-now));
             }
         }
         if (app.setProcState != app.curProcState) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index d22a9f2..3bdd01a 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -69,7 +69,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -1342,7 +1341,7 @@
             if (next.app != null && next.app.thread != null) {
                 // No reason to do full oom adj update here; we'll let that
                 // happen whenever it needs to later.
-                mService.updateLruProcessLocked(next.app, false);
+                mService.updateLruProcessLocked(next.app, false, true);
             }
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return true;
@@ -1470,7 +1469,7 @@
             mResumedActivity = next;
             next.task.touchActiveTime();
             mService.addRecentTaskLocked(next.task);
-            mService.updateLruProcessLocked(next.app, true);
+            mService.updateLruProcessLocked(next.app, true, true);
             updateLRUListLocked(next);
 
             // Have the window manager re-evaluate the orientation of
@@ -2707,7 +2706,8 @@
                             ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
                 }
                 if (r.app.activities.isEmpty()) {
-                    // No longer have activities, so update oom adj.
+                    // No longer have activities, so update LRU list and oom adj.
+                    mService.updateLruProcessLocked(r.app, false, false);
                     mService.updateOomAdjLocked();
                 }
             }
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 9b1db7f..6ff5dcb 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -912,7 +912,7 @@
         if (idx < 0) {
             app.activities.add(r);
         }
-        mService.updateLruProcessLocked(app, true);
+        mService.updateLruProcessLocked(app, true, true);
 
         final ActivityStack stack = r.task.stack;
         try {
@@ -1052,7 +1052,7 @@
             boolean andResume, boolean checkConfig) {
         // Is this activity's application already running?
         ProcessRecord app = mService.getProcessRecordLocked(r.processName,
-                r.info.applicationInfo.uid);
+                r.info.applicationInfo.uid, true);
 
         r.task.stack.setLaunchTime(r);
 
@@ -1071,7 +1071,7 @@
         }
 
         mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
-                "activity", r.intent.getComponent(), false, false);
+                "activity", r.intent.getComponent(), false, false, true);
     }
 
     final int startActivityLocked(IApplicationThread caller,
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index b35ca79..b2e3ce7 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -220,7 +220,7 @@
         r.curApp = app;
         app.curReceiver = r;
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
-        mService.updateLruProcessLocked(app, true);
+        mService.updateLruProcessLocked(app, true, false);
 
         // Tell the application to launch this receiver.
         r.intent.setComponent(r.curComponent);
@@ -334,6 +334,7 @@
     public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
             String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
         final int state = r.state;
+        final ActivityInfo receiver = r.curReceiver;
         r.state = BroadcastRecord.IDLE;
         if (state == BroadcastRecord.IDLE) {
             Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
@@ -363,15 +364,27 @@
         if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
                 && r.queue.mOrderedBroadcasts.size() > 0
                 && r.queue.mOrderedBroadcasts.get(0) == r) {
-            // In this case, we are ready to process the next receiver for the current broadcast,
-            // but are on a queue that would like to wait for services to finish before moving
-            // on.  If there are background services currently starting, then we will go into a
-            // special state where we hold off on continuing this broadcast until they are done.
-            if (mService.mServices.hasBackgroundServices(r.userId)) {
-                Slog.i(ActivityManagerService.TAG, "Delay finish: "
-                        + r.curComponent.flattenToShortString());
-                r.state = BroadcastRecord.WAITING_SERVICES;
-                return false;
+            ActivityInfo nextReceiver;
+            if (r.nextReceiver < r.receivers.size()) {
+                Object obj = r.receivers.get(r.nextReceiver);
+                nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
+            } else {
+                nextReceiver = null;
+            }
+            // Don't do this if the next receive is in the same process as the current one.
+            if (receiver == null || nextReceiver == null
+                    || receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
+                    || !receiver.processName.equals(nextReceiver.processName)) {
+                // In this case, we are ready to process the next receiver for the current broadcast,
+                // but are on a queue that would like to wait for services to finish before moving
+                // on.  If there are background services currently starting, then we will go into a
+                // special state where we hold off on continuing this broadcast until they are done.
+                if (mService.mServices.hasBackgroundServices(r.userId)) {
+                    Slog.i(ActivityManagerService.TAG, "Delay finish: "
+                            + r.curComponent.flattenToShortString());
+                    r.state = BroadcastRecord.WAITING_SERVICES;
+                    return false;
+                }
             }
         }
 
@@ -833,7 +846,7 @@
 
             // Is this receiver's application already running?
             ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
-                    info.activityInfo.applicationInfo.uid);
+                    info.activityInfo.applicationInfo.uid, false);
             if (app != null && app.thread != null) {
                 try {
                     app.addPackage(info.activityInfo.packageName, mService.mProcessStats);
@@ -871,7 +884,7 @@
                     info.activityInfo.applicationInfo, true,
                     r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                     "broadcast", r.curComponent,
-                    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false))
+                    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                             == null) {
                 // Ah, this recipient is unavailable.  Finish it if necessary,
                 // and mark the broadcast record as ready for the next.
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index 54593aa..ef7f523 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -173,6 +173,8 @@
 
     private final long mTotalMemMb;
 
+    private long mCachedRestoreLevel;
+
     private boolean mHaveDisplaySize;
 
     ProcessList() {
@@ -243,6 +245,11 @@
             }
         }
 
+        // The maximum size we will restore a process from cached to background, when under
+        // memory duress, is 1/3 the size we have reserved for kernel caches and other overhead
+        // before killing background processes.
+        mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;
+
         for (int i=0; i<mOomAdj.length; i++) {
             if (i > 0) {
                 adjString.append(',');
@@ -323,94 +330,6 @@
         }
     }
 
-    // The minimum amount of time after a state change it is safe ro collect PSS.
-    public static final int PSS_MIN_TIME_FROM_STATE_CHANGE = 15*1000;
-
-    // The maximum amount of time we want to go between PSS collections.
-    public static final int PSS_MAX_INTERVAL = 30*60*1000;
-
-    // The minimum amount of time between successive PSS requests for *all* processes.
-    public static final int PSS_ALL_INTERVAL = 10*60*1000;
-
-    // The minimum amount of time between successive PSS requests for a process.
-    private static final int PSS_SHORT_INTERVAL = 2*60*1000;
-
-    // The amount of time until PSS when a process first becomes top.
-    private static final int PSS_FIRST_TOP_INTERVAL = 15*1000;
-
-    // The amount of time until PSS when a process first becomes cached.
-    private static final int PSS_FIRST_CACHED_INTERVAL = 5*60*1000;
-
-    // The amount of time until PSS when an important process stays in the same state.
-    private static final int PSS_SAME_IMPORTANT_INTERVAL = 15*60*1000;
-
-    // The amount of time until PSS when a service process stays in the same state.
-    private static final int PSS_SAME_SERVICE_INTERVAL = 20*60*1000;
-
-    // The amount of time until PSS when a cached process stays in the same state.
-    private static final int PSS_SAME_CACHED_INTERVAL = 30*60*1000;
-
-    public static final int PROC_MEM_PERSISTENT = 0;
-    public static final int PROC_MEM_TOP = 1;
-    public static final int PROC_MEM_IMPORTANT = 2;
-    public static final int PROC_MEM_SERVICE = 3;
-    public static final int PROC_MEM_CACHED = 4;
-
-    private static final int[] sProcStateToProcMem = new int[] {
-        PROC_MEM_PERSISTENT,            // ActivityManager.PROCESS_STATE_PERSISTENT
-        PROC_MEM_PERSISTENT,            // ActivityManager.PROCESS_STATE_PERSISTENT_UI
-        PROC_MEM_TOP,                   // ActivityManager.PROCESS_STATE_TOP
-        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
-        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_BACKUP
-        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
-        PROC_MEM_SERVICE,               // ActivityManager.PROCESS_STATE_SERVICE
-        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_RECEIVER
-        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_HOME
-        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
-        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
-        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
-        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_CACHED_EMPTY
-    };
-
-    private static final long[] sFirstAwakePssTimes = new long[] {
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT_UI
-        PSS_FIRST_TOP_INTERVAL,         // ActivityManager.PROCESS_STATE_TOP
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_BACKUP
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_SERVICE
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_RECEIVER
-        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_HOME
-        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
-        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
-        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
-        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_CACHED_EMPTY
-    };
-
-    private static final long[] sSameAwakePssTimes = new long[] {
-        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT
-        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT_UI
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_TOP
-        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
-        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_BACKUP
-        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
-        PSS_SAME_SERVICE_INTERVAL,      // ActivityManager.PROCESS_STATE_SERVICE
-        PSS_SAME_SERVICE_INTERVAL,      // ActivityManager.PROCESS_STATE_RECEIVER
-        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_HOME
-        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
-        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
-        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
-        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_EMPTY
-    };
-
-    public static boolean procStatesDifferForMem(int procState1, int procState2) {
-        return sProcStateToProcMem[procState1] != sProcStateToProcMem[procState2];
-    }
-
     public static String makeProcStateString(int curProcState) {
         String procState;
         switch (curProcState) {
@@ -475,6 +394,97 @@
         sb.append(ramKb);
     }
 
+    // The minimum amount of time after a state change it is safe ro collect PSS.
+    public static final int PSS_MIN_TIME_FROM_STATE_CHANGE = 15*1000;
+
+    // The maximum amount of time we want to go between PSS collections.
+    public static final int PSS_MAX_INTERVAL = 30*60*1000;
+
+    // The minimum amount of time between successive PSS requests for *all* processes.
+    public static final int PSS_ALL_INTERVAL = 10*60*1000;
+
+    // The minimum amount of time between successive PSS requests for a process.
+    private static final int PSS_SHORT_INTERVAL = 2*60*1000;
+
+    // The amount of time until PSS when a process first becomes top.
+    private static final int PSS_FIRST_TOP_INTERVAL = 10*1000;
+
+    // The amount of time until PSS when a process first goes into the background.
+    private static final int PSS_FIRST_BACKGROUND_INTERVAL = 20*1000;
+
+    // The amount of time until PSS when a process first becomes cached.
+    private static final int PSS_FIRST_CACHED_INTERVAL = 30*1000;
+
+    // The amount of time until PSS when an important process stays in the same state.
+    private static final int PSS_SAME_IMPORTANT_INTERVAL = 15*60*1000;
+
+    // The amount of time until PSS when a service process stays in the same state.
+    private static final int PSS_SAME_SERVICE_INTERVAL = 20*60*1000;
+
+    // The amount of time until PSS when a cached process stays in the same state.
+    private static final int PSS_SAME_CACHED_INTERVAL = 30*60*1000;
+
+    public static final int PROC_MEM_PERSISTENT = 0;
+    public static final int PROC_MEM_TOP = 1;
+    public static final int PROC_MEM_IMPORTANT = 2;
+    public static final int PROC_MEM_SERVICE = 3;
+    public static final int PROC_MEM_CACHED = 4;
+
+    private static final int[] sProcStateToProcMem = new int[] {
+        PROC_MEM_PERSISTENT,            // ActivityManager.PROCESS_STATE_PERSISTENT
+        PROC_MEM_PERSISTENT,            // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+        PROC_MEM_TOP,                   // ActivityManager.PROCESS_STATE_TOP
+        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_BACKUP
+        PROC_MEM_IMPORTANT,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+        PROC_MEM_SERVICE,               // ActivityManager.PROCESS_STATE_SERVICE
+        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_RECEIVER
+        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_HOME
+        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+        PROC_MEM_CACHED,                // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+    };
+
+    private static final long[] sFirstAwakePssTimes = new long[] {
+        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT
+        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+        PSS_FIRST_TOP_INTERVAL,         // ActivityManager.PROCESS_STATE_TOP
+        PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+        PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+        PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_BACKUP
+        PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+        PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_SERVICE
+        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_RECEIVER
+        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_HOME
+        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+        PSS_FIRST_CACHED_INTERVAL,      // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+    };
+
+    private static final long[] sSameAwakePssTimes = new long[] {
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_TOP
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_BACKUP
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+        PSS_SAME_SERVICE_INTERVAL,      // ActivityManager.PROCESS_STATE_SERVICE
+        PSS_SAME_SERVICE_INTERVAL,      // ActivityManager.PROCESS_STATE_RECEIVER
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_HOME
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+    };
+
+    public static boolean procStatesDifferForMem(int procState1, int procState2) {
+        return sProcStateToProcMem[procState1] != sProcStateToProcMem[procState2];
+    }
+
     public static long computeNextPssTime(int procState, boolean first, boolean sleeping,
             long now) {
         final long[] table = sleeping
@@ -496,6 +506,14 @@
         return mOomMinFree[mOomAdj.length-1] * 1024;
     }
 
+    /**
+     * Return the maximum pss size in kb that we consider a process acceptable to
+     * restore from its cached state for running in the background when RAM is low.
+     */
+    long getCachedRestoreThreshold() {
+        return mCachedRestoreLevel;
+    }
+
     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 7a456f7..c5491ef 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -62,12 +62,12 @@
     int pid;                    // The process of this application; 0 if none
     boolean starting;           // True if the process is being started
     long lastActivityTime;      // For managing the LRU list
-    long lruWeight;             // Weight for ordering in LRU list
     long lastPssTime;           // Last time we retrieved PSS data
     long nextPssTime;           // Next time we want to request PSS data
     long lastStateTime;         // Last time setProcState changed
     long initialIdlePss;        // Initial memory pss of process for idle maintenance.
     long lastPss;               // Last computed memory pss.
+    long lastCachedPss;         // Last computed pss when in cached state.
     int maxAdj;                 // Maximum OOM adjustment for this process
     int curRawAdj;              // Current OOM unlimited adjustment for this process
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
@@ -214,15 +214,22 @@
                 pw.println(starting);
         pw.print(prefix); pw.print("lastActivityTime=");
                 TimeUtils.formatDuration(lastActivityTime, now, pw);
-                pw.print(" lruWeight="); pw.println(lruWeight);
+                pw.print(" lastPssTime=");
+                TimeUtils.formatDuration(lastPssTime, now, pw);
+                pw.print(" nextPssTime=");
+                TimeUtils.formatDuration(nextPssTime, now, pw);
+                pw.println();
+        pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
+                pw.print(" lruSeq="); pw.print(lruSeq);
+                pw.print(" lastPss="); pw.print(lastPss);
+                pw.print(" lastCachedPss="); pw.println(lastCachedPss);
         pw.print(prefix); pw.print("serviceb="); pw.print(serviceb);
                 pw.print(" keeping="); pw.print(keeping);
                 pw.print(" cached="); pw.print(cached);
                 pw.print(" empty="); pw.println(empty);
         if (notCachedSinceIdle) {
             pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(notCachedSinceIdle);
-                    pw.print(" initialIdlePss="); pw.print(initialIdlePss);
-                    pw.print(" lastPss="); pw.println(lastPss);
+                    pw.print(" initialIdlePss="); pw.println(initialIdlePss);
         }
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
                 pw.print(" curRaw="); pw.print(curRawAdj);
@@ -240,13 +247,6 @@
                 pw.print(" lastStateTime=");
                 TimeUtils.formatDuration(lastStateTime, now, pw);
                 pw.println();
-        pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
-                pw.print(" lruSeq="); pw.print(lruSeq);
-                pw.print(" lastPssTime=");
-                TimeUtils.formatDuration(lastPssTime, now, pw);
-                pw.print(" nextPssTime=");
-                TimeUtils.formatDuration(nextPssTime, now, pw);
-                pw.println();
         if (hasShownUi || pendingUiClean || hasAboveClient) {
             pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
                     pw.print(" pendingUiClean="); pw.print(pendingUiClean);