OSDN Git Service

Implement issue #10691475: Kill cached processes if about to...
authorDianne Hackborn <hackbod@google.com>
Thu, 19 Sep 2013 20:34:35 +0000 (13:34 -0700)
committerDianne Hackborn <hackbod@google.com>
Thu, 19 Sep 2013 21:35:53 +0000 (14:35 -0700)
...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

12 files changed:
core/java/android/app/ActivityThread.java
core/java/android/app/ApplicationThreadNative.java
core/java/android/app/IApplicationThread.java
core/java/com/android/internal/app/ProcessStats.java
services/java/com/android/server/AlarmManagerService.java
services/java/com/android/server/am/ActiveServices.java
services/java/com/android/server/am/ActivityManagerService.java
services/java/com/android/server/am/ActivityStack.java
services/java/com/android/server/am/ActivityStackSupervisor.java
services/java/com/android/server/am/BroadcastQueue.java
services/java/com/android/server/am/ProcessList.java
services/java/com/android/server/am/ProcessRecord.java

index 209514a..3e20f1f 100644 (file)
@@ -855,10 +855,6 @@ public final class ActivityThread {
             }
         }
 
-        public void getMemoryInfo(Debug.MemoryInfo outInfo) {
-            Debug.getMemoryInfo(outInfo);
-        }
-
         public void dispatchPackageBroadcast(int cmd, String[] packages) {
             queueOrSendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd);
         }
@@ -895,30 +891,23 @@ public final class ActivityThread {
         }
 
         @Override
-        public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin,
+        public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin,
                 boolean dumpInfo, boolean dumpDalvik, String[] args) {
             FileOutputStream fout = new FileOutputStream(fd);
             PrintWriter pw = new FastPrintWriter(fout);
             try {
-                return dumpMemInfo(pw, checkin, dumpInfo, dumpDalvik);
+                dumpMemInfo(pw, mem, checkin, dumpInfo, dumpDalvik);
             } finally {
                 pw.flush();
             }
         }
 
-        private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean dumpInfo,
-                boolean dumpDalvik) {
+        private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
+                boolean dumpInfo, boolean dumpDalvik) {
             long nativeMax = Debug.getNativeHeapSize() / 1024;
             long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
             long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
 
-            Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
-            Debug.getMemoryInfo(memInfo);
-
-            if (!dumpInfo) {
-                return memInfo;
-            }
-
             Runtime runtime = Runtime.getRuntime();
 
             long dalvikMax = runtime.totalMemory() / 1024;
@@ -1043,7 +1032,7 @@ public final class ActivityThread {
                 }
                 pw.println();
 
-                return memInfo;
+                return;
             }
 
             // otherwise, show human-readable format
@@ -1168,8 +1157,6 @@ public final class ActivityThread {
                 pw.println(" Asset Allocations");
                 pw.print(assetAlloc);
             }
-
-            return memInfo;
         }
 
         @Override
index a4e80e5..876bf78 100644 (file)
@@ -450,16 +450,6 @@ public abstract class ApplicationThreadNative extends Binder
             return true;
         }
 
-        case GET_MEMORY_INFO_TRANSACTION:
-        {
-            data.enforceInterface(IApplicationThread.descriptor);
-            Debug.MemoryInfo mi = new Debug.MemoryInfo();
-            getMemoryInfo(mi);
-            reply.writeNoException();
-            mi.writeToParcel(reply, 0);
-            return true;
-        }
-
         case DISPATCH_PACKAGE_BROADCAST_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -530,14 +520,14 @@ public abstract class ApplicationThreadNative extends Binder
         {
             data.enforceInterface(IApplicationThread.descriptor);
             ParcelFileDescriptor fd = data.readFileDescriptor();
+            Debug.MemoryInfo mi = Debug.MemoryInfo.CREATOR.createFromParcel(data);
             boolean checkin = data.readInt() != 0;
             boolean dumpInfo = data.readInt() != 0;
             boolean dumpDalvik = data.readInt() != 0;
             String[] args = data.readStringArray();
-            Debug.MemoryInfo mi = null;
             if (fd != null) {
                 try {
-                    mi = dumpMemInfo(fd.getFileDescriptor(), checkin, dumpInfo, dumpDalvik, args);
+                    dumpMemInfo(fd.getFileDescriptor(), mi, checkin, dumpInfo, dumpDalvik, args);
                 } finally {
                     try {
                         fd.close();
@@ -547,7 +537,6 @@ public abstract class ApplicationThreadNative extends Binder
                 }
             }
             reply.writeNoException();
-            mi.writeToParcel(reply, 0);
             return true;
         }
 
@@ -1108,17 +1097,6 @@ class ApplicationThreadProxy implements IApplicationThread {
         data.recycle();
     }
     
-    public void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IApplicationThread.descriptor);
-        mRemote.transact(GET_MEMORY_INFO_TRANSACTION, data, reply, 0);
-        reply.readException();
-        outInfo.readFromParcel(reply);
-        data.recycle();
-        reply.recycle();
-    }
-    
     public void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -1194,23 +1172,21 @@ class ApplicationThreadProxy implements IApplicationThread {
                 IBinder.FLAG_ONEWAY);
     }
 
-    public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean dumpInfo,
-            boolean dumpDalvik, String[] args) throws RemoteException {
+    public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin,
+            boolean dumpInfo, boolean dumpDalvik, String[] args) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeFileDescriptor(fd);
+        mem.writeToParcel(data, 0);
         data.writeInt(checkin ? 1 : 0);
         data.writeInt(dumpInfo ? 1 : 0);
         data.writeInt(dumpDalvik ? 1 : 0);
         data.writeStringArray(args);
         mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0);
         reply.readException();
-        Debug.MemoryInfo info = new Debug.MemoryInfo();
-        info.readFromParcel(reply);
         data.recycle();
         reply.recycle();
-        return info;
     }
 
     public void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException {
index 058b975..43a5fbd 100644 (file)
@@ -118,7 +118,6 @@ public interface IApplicationThread extends IInterface {
     void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
             throws RemoteException;
     void setSchedulingGroup(int group) throws RemoteException;
-    void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException;
     static final int PACKAGE_REMOVED = 0;
     static final int EXTERNAL_STORAGE_UNAVAILABLE = 1;
     void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException;
@@ -128,7 +127,7 @@ public interface IApplicationThread extends IInterface {
     void setCoreSettings(Bundle coreSettings) throws RemoteException;
     void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException;
     void scheduleTrimMemory(int level) throws RemoteException;
-    Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean dumpInfo,
+    void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, boolean dumpInfo,
             boolean dumpDalvik, String[] args) throws RemoteException;
     void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
     void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException;
@@ -171,7 +170,7 @@ public interface IApplicationThread extends IInterface {
     int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
     int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
     int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
-    int GET_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
+
     int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
     int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
     int SCHEDULE_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
index 1475e2c..a281f7c 100644 (file)
@@ -166,7 +166,7 @@ public final class ProcessStats implements Parcelable {
     static final String CSV_SEP = "\t";
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 12;
+    private static final int PARCEL_VERSION = 13;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535453;
 
@@ -646,6 +646,13 @@ public final class ProcessStats implements Parcelable {
             pw.print(prefix); pw.print("Killed for excessive CPU use: ");
                     pw.print(proc.mNumExcessiveCpu); pw.println(" times");
         }
+        if (proc.mNumCachedKill != 0) {
+            pw.print(prefix); pw.print("Killed from cached state: ");
+                    pw.print(proc.mNumCachedKill); pw.print(" times from pss ");
+                    printSizeValue(pw, proc.mMinCachedKillPss * 1024); pw.print("-");
+                    printSizeValue(pw, proc.mAvgCachedKillPss * 1024); pw.print("-");
+                    printSizeValue(pw, proc.mMaxCachedKillPss * 1024); pw.println();
+        }
     }
 
     static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
@@ -2033,7 +2040,8 @@ public final class ProcessStats implements Parcelable {
                         dumpAllProcessPssCheckin(pw, proc);
                         pw.println();
                     }
-                    if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) {
+                    if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0
+                            || proc.mNumCachedKill > 0) {
                         pw.print("pkgkills,");
                         pw.print(pkgName);
                         pw.print(",");
@@ -2044,6 +2052,14 @@ public final class ProcessStats implements Parcelable {
                         pw.print(proc.mNumExcessiveWake);
                         pw.print(",");
                         pw.print(proc.mNumExcessiveCpu);
+                        pw.print(",");
+                        pw.print(proc.mNumCachedKill);
+                        pw.print(",");
+                        pw.print(proc.mMinCachedKillPss);
+                        pw.print(":");
+                        pw.print(proc.mAvgCachedKillPss);
+                        pw.print(":");
+                        pw.print(proc.mMaxCachedKillPss);
                         pw.println();
                     }
                 }
@@ -2090,7 +2106,8 @@ public final class ProcessStats implements Parcelable {
                     dumpAllProcessPssCheckin(pw, procState);
                     pw.println();
                 }
-                if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0) {
+                if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0
+                        || procState.mNumCachedKill > 0) {
                     pw.print("kills,");
                     pw.print(procName);
                     pw.print(",");
@@ -2099,6 +2116,14 @@ public final class ProcessStats implements Parcelable {
                     pw.print(procState.mNumExcessiveWake);
                     pw.print(",");
                     pw.print(procState.mNumExcessiveCpu);
+                    pw.print(",");
+                    pw.print(procState.mNumCachedKill);
+                    pw.print(",");
+                    pw.print(procState.mMinCachedKillPss);
+                    pw.print(":");
+                    pw.print(procState.mAvgCachedKillPss);
+                    pw.print(":");
+                    pw.print(procState.mMaxCachedKillPss);
                     pw.println();
                 }
             }
@@ -2135,6 +2160,11 @@ public final class ProcessStats implements Parcelable {
         int mNumExcessiveWake;
         int mNumExcessiveCpu;
 
+        int mNumCachedKill;
+        long mMinCachedKillPss;
+        long mAvgCachedKillPss;
+        long mMaxCachedKillPss;
+
         boolean mMultiPackage;
         boolean mDead;
 
@@ -2200,6 +2230,10 @@ public final class ProcessStats implements Parcelable {
             }
             pnew.mNumExcessiveWake = mNumExcessiveWake;
             pnew.mNumExcessiveCpu = mNumExcessiveCpu;
+            pnew.mNumCachedKill = mNumCachedKill;
+            pnew.mMinCachedKillPss = mMinCachedKillPss;
+            pnew.mAvgCachedKillPss = mAvgCachedKillPss;
+            pnew.mMaxCachedKillPss = mMaxCachedKillPss;
             pnew.mActive = mActive;
             pnew.mNumStartedServices = mNumStartedServices;
             return pnew;
@@ -2226,6 +2260,10 @@ public final class ProcessStats implements Parcelable {
             }
             mNumExcessiveWake += other.mNumExcessiveWake;
             mNumExcessiveCpu += other.mNumExcessiveCpu;
+            if (other.mNumCachedKill > 0) {
+                addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss,
+                        other.mAvgCachedKillPss, other.mMaxCachedKillPss);
+            }
         }
 
         void resetSafely(long now) {
@@ -2238,6 +2276,8 @@ public final class ProcessStats implements Parcelable {
             mPssTableSize = 0;
             mNumExcessiveWake = 0;
             mNumExcessiveCpu = 0;
+            mNumCachedKill = 0;
+            mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
         }
 
         void makeDead() {
@@ -2268,6 +2308,12 @@ public final class ProcessStats implements Parcelable {
             }
             out.writeInt(mNumExcessiveWake);
             out.writeInt(mNumExcessiveCpu);
+            out.writeInt(mNumCachedKill);
+            if (mNumCachedKill > 0) {
+                out.writeLong(mMinCachedKillPss);
+                out.writeLong(mAvgCachedKillPss);
+                out.writeLong(mMaxCachedKillPss);
+            }
         }
 
         boolean readFromParcel(Parcel in, boolean fully) {
@@ -2289,6 +2335,14 @@ public final class ProcessStats implements Parcelable {
             mPssTableSize = mPssTable != null ? mPssTable.length : 0;
             mNumExcessiveWake = in.readInt();
             mNumExcessiveCpu = in.readInt();
+            mNumCachedKill = in.readInt();
+            if (mNumCachedKill > 0) {
+                mMinCachedKillPss = in.readLong();
+                mAvgCachedKillPss = in.readLong();
+                mMaxCachedKillPss = in.readLong();
+            } else {
+                mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
+            }
             return true;
         }
 
@@ -2502,6 +2556,37 @@ public final class ProcessStats implements Parcelable {
             }
         }
 
+        private void addCachedKill(int num, long minPss, long avgPss, long maxPss) {
+            if (mNumCachedKill <= 0) {
+                mNumCachedKill = num;
+                mMinCachedKillPss = minPss;
+                mAvgCachedKillPss = avgPss;
+                mMaxCachedKillPss = maxPss;
+            } else {
+                if (minPss < mMinCachedKillPss) {
+                    mMinCachedKillPss = minPss;
+                }
+                if (maxPss > mMaxCachedKillPss) {
+                    mMaxCachedKillPss = maxPss;
+                }
+                mAvgCachedKillPss = (long)( ((mAvgCachedKillPss*(double)mNumCachedKill) + avgPss)
+                        / (mNumCachedKill+num) );
+                mNumCachedKill += num;
+            }
+        }
+
+        public void reportCachedKill(ArrayMap<String, ProcessState> pkgList, long pss) {
+            ensureNotDead();
+            mCommonProcess.addCachedKill(1, pss, pss, pss);
+            if (!mCommonProcess.mMultiPackage) {
+                return;
+            }
+
+            for (int ip=pkgList.size()-1; ip>=0; ip--) {
+                pullFixedProc(pkgList, ip).addCachedKill(1, pss, pss, pss);
+            }
+        }
+
         ProcessState pullFixedProc(String pkgName) {
             if (mMultiPackage) {
                 // The array map is still pointing to a common process state
index 98b5f66..bb6c4e6 100644 (file)
@@ -470,7 +470,8 @@ class AlarmManagerService extends IAlarmManager.Stub {
         
         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);
index fea7623..4379c70 100644 (file)
@@ -311,7 +311,7 @@ public final class ActiveServices {
         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 @@ public final class ActiveServices {
                     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 @@ public final class ActiveServices {
         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 @@ public final class ActiveServices {
         // 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 @@ public final class ActiveServices {
 
         app.services.add(r);
         bumpServiceExecutingLocked(r, execInFg, "create");
-        mAm.updateLruProcessLocked(app, true);
+        mAm.updateLruProcessLocked(app, true, false);
 
         boolean created = false;
         try {
index f08b5b9..7a480dc 100644 (file)
@@ -499,6 +499,11 @@ public final class ActivityManagerService extends ActivityManagerNative
     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 @@ public final class ActivityManagerService extends ActivityManagerNative
                     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 @@ public final class ActivityManagerService extends ActivityManagerNative
                                 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 @@ public final class ActivityManagerService extends ActivityManagerNative
                 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 @@ public final class ActivityManagerService extends ActivityManagerNative
         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;
+        if (app.activities.size() > 0) {
+            // Don't want to touch dependent processes that are hosting activities.
+            return index;
+        }
 
-        app.lruSeq = mLruSeq;
+        int lrui = mLruProcesses.lastIndexOf(app);
+        if (lrui < 0) {
+            throw new IllegalStateException("Adding dependent process " + app
+                    + " not on LRU list!");
+        }
 
-        // 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;
-        }
-
-        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;
+        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--;
             }
-            i--;
+            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);
         }
-        if (i < 0) {
-            mLruProcesses.add(0, app);
+
+        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 @@ public final class ActivityManagerService extends ActivityManagerNative
             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 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
     }
 
-    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 @@ public final class ActivityManagerService extends ActivityManagerNative
             }
         }
         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 @@ public final class ActivityManagerService extends ActivityManagerNative
     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 @@ public final class ActivityManagerService extends ActivityManagerNative
             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 @@ public final class ActivityManagerService extends ActivityManagerNative
             boolean restarting, boolean allowRestart) {
         cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
         if (!restarting) {
-            mLruProcesses.remove(app);
+            removeLruProcessLocked(app);
         }
 
         if (mProfileProc == app) {
@@ -4212,7 +4260,7 @@ public final class ActivityManagerService extends ActivityManagerNative
         // 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 @@ public final class ActivityManagerService extends ActivityManagerNative
             }
             killUnneededProcessLocked(app, reason);
             handleAppDiedLocked(app, true, allowRestart);
-            mLruProcesses.remove(app);
+            removeLruProcessLocked(app);
 
             if (app.persistent && !app.isolated) {
                 if (!callerWillRestart) {
@@ -4712,7 +4760,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                     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 @@ public final class ActivityManagerService extends ActivityManagerNative
                         // 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 @@ public final class ActivityManagerService extends ActivityManagerNative
                         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 @@ public final class ActivityManagerService extends ActivityManagerNative
     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 @@ public final class ActivityManagerService extends ActivityManagerNative
             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 @@ public final class ActivityManagerService extends ActivityManagerNative
                 } 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 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
 
         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 @@ public final class ActivityManagerService extends ActivityManagerNative
             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 @@ public final class ActivityManagerService extends ActivityManagerNative
     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 @@ public final class ActivityManagerService extends ActivityManagerNative
             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 @@ public final class ActivityManagerService extends ActivityManagerNative
                 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 @@ public final class ActivityManagerService extends ActivityManagerNative
                 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 @@ public final class ActivityManagerService extends ActivityManagerNative
     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 @@ public final class ActivityManagerService extends ActivityManagerNative
                     : 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 @@ public final class ActivityManagerService extends ActivityManagerNative
         }
         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) {
index d22a9f2..3bdd01a 100644 (file)
@@ -69,7 +69,6 @@ import android.os.Handler;
 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 @@ final class ActivityStack {
             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 @@ final class ActivityStack {
             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 @@ final class ActivityStack {
                             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();
                 }
             }
index 9b1db7f..6ff5dcb 100644 (file)
@@ -912,7 +912,7 @@ public final class ActivityStackSupervisor {
         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 @@ public final class ActivityStackSupervisor {
             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 @@ public final class ActivityStackSupervisor {
         }
 
         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,
index b35ca79..b2e3ce7 100644 (file)
@@ -220,7 +220,7 @@ public final class BroadcastQueue {
         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 final class BroadcastQueue {
     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 @@ public final class BroadcastQueue {
         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 @@ public final class BroadcastQueue {
 
             // 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 @@ public final class BroadcastQueue {
                     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.
index 54593aa..ef7f523 100644 (file)
@@ -173,6 +173,8 @@ final class ProcessList {
 
     private final long mTotalMemMb;
 
+    private long mCachedRestoreLevel;
+
     private boolean mHaveDisplaySize;
 
     ProcessList() {
@@ -243,6 +245,11 @@ final class ProcessList {
             }
         }
 
+        // 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,6 +330,70 @@ final class ProcessList {
         }
     }
 
+    public static String makeProcStateString(int curProcState) {
+        String procState;
+        switch (curProcState) {
+            case -1:
+                procState = "N ";
+                break;
+            case ActivityManager.PROCESS_STATE_PERSISTENT:
+                procState = "P ";
+                break;
+            case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
+                procState = "PU";
+                break;
+            case ActivityManager.PROCESS_STATE_TOP:
+                procState = "T ";
+                break;
+            case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+                procState = "IF";
+                break;
+            case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+                procState = "IB";
+                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 = "HO";
+                break;
+            case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
+                procState = "LA";
+                break;
+            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 = "??";
+                break;
+        }
+        return procState;
+    }
+
+    public static void appendRamKb(StringBuilder sb, long ramKb) {
+        for (int j=0, fact=10; j<6; j++, fact*=10) {
+            if (ramKb < fact) {
+                sb.append(' ');
+            }
+        }
+        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;
 
@@ -336,10 +407,13 @@ final class ProcessList {
     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;
+    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 = 5*60*1000;
+    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;
@@ -377,12 +451,12 @@ final class ProcessList {
         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_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
@@ -411,70 +485,6 @@ final class ProcessList {
         return sProcStateToProcMem[procState1] != sProcStateToProcMem[procState2];
     }
 
-    public static String makeProcStateString(int curProcState) {
-        String procState;
-        switch (curProcState) {
-            case -1:
-                procState = "N ";
-                break;
-            case ActivityManager.PROCESS_STATE_PERSISTENT:
-                procState = "P ";
-                break;
-            case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
-                procState = "PU";
-                break;
-            case ActivityManager.PROCESS_STATE_TOP:
-                procState = "T ";
-                break;
-            case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
-                procState = "IF";
-                break;
-            case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
-                procState = "IB";
-                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 = "HO";
-                break;
-            case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
-                procState = "LA";
-                break;
-            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 = "??";
-                break;
-        }
-        return procState;
-    }
-
-    public static void appendRamKb(StringBuilder sb, long ramKb) {
-        for (int j=0, fact=10; j<6; j++, fact*=10) {
-            if (ramKb < fact) {
-                sb.append(' ');
-            }
-        }
-        sb.append(ramKb);
-    }
-
     public static long computeNextPssTime(int procState, boolean first, boolean sleeping,
             long now) {
         final long[] table = sleeping
@@ -496,6 +506,14 @@ final class ProcessList {
         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 {
index 7a456f7..c5491ef 100644 (file)
@@ -62,12 +62,12 @@ final class ProcessRecord {
     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 @@ final class ProcessRecord {
                 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 @@ final class ProcessRecord {
                 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);