Merge "Add new proc state constants and delivery."
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5fa874b..c79768e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -228,6 +228,39 @@
     /** @hide User operation call: given user id is the current user, can't be stopped. */
     public static final int USER_OP_IS_CURRENT = -2;
 
+    /** @hide Process is a persistent system process. */
+    public static final int PROCESS_STATE_PERSISTENT = 0;
+
+    /** @hide Process is a persistent system process and is doing UI. */
+    public static final int PROCESS_STATE_PERSISTENT_UI = 1;
+
+    /** @hide Process is hosting the current top activity. */
+    public static final int PROCESS_STATE_TOP = 2;
+
+    /** @hide Process is important to the user, and something they are aware of. */
+    public static final int PROCESS_STATE_IMPORTANT_PERCEPTIBLE = 3;
+
+    /** @hide Process is important to the user, but not something they are aware of. */
+    public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
+
+    /** @hide Process is in the background running a receiver. */
+    public static final int PROCESS_STATE_RECEIVER = 5;
+
+    /** @hide Process is in the background running a backup/restore operation. */
+    public static final int PROCESS_STATE_BACKUP = 6;
+
+    /** @hide Process is in the background running a service. */
+    public static final int PROCESS_STATE_SERVICE = 7;
+
+    /** @hide Process is in the background but hosts the home activity. */
+    public static final int PROCESS_STATE_HOME = 8;
+
+    /** @hide Process is in the background but hosts the last shown activity. */
+    public static final int PROCESS_STATE_LAST_ACTIVITY = 9;
+
+    /** @hide Process is being cached for later use. */
+    public static final int PROCESS_STATE_CACHED = 10;
+
     /*package*/ ActivityManager(Context context, Handler handler) {
         mContext = context;
         mHandler = handler;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b24aeb0..222ad69 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -547,6 +547,8 @@
         // Formatting for checkin service - update version if row format changes
         private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;
 
+        private int mLastProcessState = -1;
+
         private void updatePendingConfiguration(Configuration config) {
             synchronized (mResourcesManager) {
                 if (mPendingConfiguration == null ||
@@ -582,7 +584,9 @@
             queueOrSendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
         }
 
-        public final void scheduleResumeActivity(IBinder token, boolean isForward) {
+        public final void scheduleResumeActivity(IBinder token, int processState,
+                boolean isForward) {
+            updateProcessState(processState, false);
             queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
         }
 
@@ -597,9 +601,12 @@
         // activity itself back to the activity manager. (matters more with ipc)
         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                 ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-                Bundle state, List<ResultInfo> pendingResults,
+                int procState, Bundle state, List<ResultInfo> pendingResults,
                 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                 String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+
+            updateProcessState(procState, false);
+
             ActivityClientRecord r = new ActivityClientRecord();
 
             r.token = token;
@@ -647,7 +654,8 @@
 
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
                 CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
-                boolean sync, int sendingUser) {
+                boolean sync, int sendingUser, int processState) {
+            updateProcessState(processState, false);
             ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                     sync, false, mAppThread.asBinder(), sendingUser);
             r.info = info;
@@ -675,7 +683,8 @@
         }
 
         public final void scheduleCreateService(IBinder token,
-                ServiceInfo info, CompatibilityInfo compatInfo) {
+                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
+            updateProcessState(processState, false);
             CreateServiceData s = new CreateServiceData();
             s.token = token;
             s.info = info;
@@ -685,7 +694,8 @@
         }
 
         public final void scheduleBindService(IBinder token, Intent intent,
-                boolean rebind) {
+                boolean rebind, int processState) {
+            updateProcessState(processState, false);
             BindServiceData s = new BindServiceData();
             s.token = token;
             s.intent = intent;
@@ -810,7 +820,8 @@
         // applies transaction ordering per object for such calls.
         public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                 int resultCode, String dataStr, Bundle extras, boolean ordered,
-                boolean sticky, int sendingUser) throws RemoteException {
+                boolean sticky, int sendingUser, int processState) throws RemoteException {
+            updateProcessState(processState, false);
             receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                     sticky, sendingUser);
         }
@@ -1218,6 +1229,24 @@
         public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
             queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
         }
+
+        public void setProcessState(int state) {
+            updateProcessState(state, true);
+        }
+
+        public void updateProcessState(int processState, boolean fromIpc) {
+            synchronized (this) {
+                if (mLastProcessState != processState) {
+                    mLastProcessState = processState;
+
+                    // Update Dalvik state here based on ActivityManager.PROCESS_STATE_* constants.
+                    if (false) {
+                        Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState
+                                + (fromIpc ? " (from ipc": ""));
+                    }
+                }
+            }
+        }
     }
 
     private class H extends Handler {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index cc495aa..6f18e84 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -111,8 +111,9 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             IBinder b = data.readStrongBinder();
+            int procState = data.readInt();
             boolean isForward = data.readInt() != 0;
-            scheduleResumeActivity(b, isForward);
+            scheduleResumeActivity(b, procState, isForward);
             return true;
         }
         
@@ -134,6 +135,7 @@
             ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
             Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
             CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+            int procState = data.readInt();
             Bundle state = data.readBundle();
             List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
             List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
@@ -143,8 +145,8 @@
             ParcelFileDescriptor profileFd = data.readInt() != 0
                     ? data.readFileDescriptor() : null;
             boolean autoStopProfiler = data.readInt() != 0;
-            scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, state, ri, pi,
-                    notResumed, isForward, profileName, profileFd, autoStopProfiler);
+            scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, procState, state,
+                    ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler);
             return true;
         }
         
@@ -194,8 +196,9 @@
             Bundle resultExtras = data.readBundle();
             boolean sync = data.readInt() != 0;
             int sendingUser = data.readInt();
+            int processState = data.readInt();
             scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
-                    resultExtras, sync, sendingUser);
+                    resultExtras, sync, sendingUser, processState);
             return true;
         }
 
@@ -204,7 +207,8 @@
             IBinder token = data.readStrongBinder();
             ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data);
             CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
-            scheduleCreateService(token, info, compatInfo);
+            int processState = data.readInt();
+            scheduleCreateService(token, info, compatInfo, processState);
             return true;
         }
 
@@ -213,7 +217,8 @@
             IBinder token = data.readStrongBinder();
             Intent intent = Intent.CREATOR.createFromParcel(data);
             boolean rebind = data.readInt() != 0;
-            scheduleBindService(token, intent, rebind);
+            int processState = data.readInt();
+            scheduleBindService(token, intent, rebind, processState);
             return true;
         }
 
@@ -384,8 +389,9 @@
             boolean ordered = data.readInt() != 0;
             boolean sticky = data.readInt() != 0;
             int sendingUser = data.readInt();
+            int processState = data.readInt();
             scheduleRegisteredReceiver(receiver, intent,
-                    resultCode, dataStr, extras, ordered, sticky, sendingUser);
+                    resultCode, dataStr, extras, ordered, sticky, sendingUser, processState);
             return true;
         }
 
@@ -613,6 +619,15 @@
             reply.writeNoException();
             return true;
         }
+
+        case SET_PROCESS_STATE_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            int state = data.readInt();
+            setProcessState(state);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -682,11 +697,12 @@
         data.recycle();
     }
 
-    public final void scheduleResumeActivity(IBinder token, boolean isForward)
+    public final void scheduleResumeActivity(IBinder token, int procState, boolean isForward)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
+        data.writeInt(procState);
         data.writeInt(isForward ? 1 : 0);
         mRemote.transact(SCHEDULE_RESUME_ACTIVITY_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
@@ -706,7 +722,7 @@
 
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-            Bundle state, List<ResultInfo> pendingResults,
+            int procState, Bundle state, List<ResultInfo> pendingResults,
     		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
     		String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
     		throws RemoteException {
@@ -718,6 +734,7 @@
         info.writeToParcel(data, 0);
         curConfig.writeToParcel(data, 0);
         compatInfo.writeToParcel(data, 0);
+        data.writeInt(procState);
         data.writeBundle(state);
         data.writeTypedList(pendingResults);
         data.writeTypedList(pendingNewIntents);
@@ -783,7 +800,7 @@
     
     public final void scheduleReceiver(Intent intent, ActivityInfo info,
             CompatibilityInfo compatInfo, int resultCode, String resultData,
-            Bundle map, boolean sync, int sendingUser) throws RemoteException {
+            Bundle map, boolean sync, int sendingUser, int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         intent.writeToParcel(data, 0);
@@ -794,6 +811,7 @@
         data.writeBundle(map);
         data.writeInt(sync ? 1 : 0);
         data.writeInt(sendingUser);
+        data.writeInt(processState);
         mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -823,24 +841,26 @@
     }
     
     public final void scheduleCreateService(IBinder token, ServiceInfo info,
-            CompatibilityInfo compatInfo) throws RemoteException {
+            CompatibilityInfo compatInfo, int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
         info.writeToParcel(data, 0);
         compatInfo.writeToParcel(data, 0);
+        data.writeInt(processState);
         mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
 
-    public final void scheduleBindService(IBinder token, Intent intent, boolean rebind)
-            throws RemoteException {
+    public final void scheduleBindService(IBinder token, Intent intent, boolean rebind,
+            int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
         intent.writeToParcel(data, 0);
         data.writeInt(rebind ? 1 : 0);
+        data.writeInt(processState);
         mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -1023,7 +1043,7 @@
 
     public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
             int resultCode, String dataStr, Bundle extras, boolean ordered,
-            boolean sticky, int sendingUser) throws RemoteException {
+            boolean sticky, int sendingUser, int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(receiver.asBinder());
@@ -1034,6 +1054,7 @@
         data.writeInt(ordered ? 1 : 0);
         data.writeInt(sticky ? 1 : 0);
         data.writeInt(sendingUser);
+        data.writeInt(processState);
         mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -1238,4 +1259,13 @@
         mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
+
+    @Override
+    public void setProcessState(int state) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeInt(state);
+        mRemote.transact(SET_PROCESS_STATE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 286566d..8e882df 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -50,11 +50,12 @@
             int configChanges) throws RemoteException;
     void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
     void scheduleSleeping(IBinder token, boolean sleeping) throws RemoteException;
-    void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException;
+    void scheduleResumeActivity(IBinder token, int procState, boolean isForward)
+            throws RemoteException;
     void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
     void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-            Bundle state, List<ResultInfo> pendingResults,
+            int procState, Bundle state, List<ResultInfo> pendingResults,
     		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
     		String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
     		throws RemoteException;
@@ -66,7 +67,7 @@
             int configChanges) throws RemoteException;
     void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
             int resultCode, String data, Bundle extras, boolean sync,
-            int sendingUser) throws RemoteException;
+            int sendingUser, int processState) throws RemoteException;
     static final int BACKUP_MODE_INCREMENTAL = 0;
     static final int BACKUP_MODE_FULL = 1;
     static final int BACKUP_MODE_RESTORE = 2;
@@ -76,9 +77,9 @@
     void scheduleDestroyBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo)
             throws RemoteException;
     void scheduleCreateService(IBinder token, ServiceInfo info,
-            CompatibilityInfo compatInfo) throws RemoteException;
+            CompatibilityInfo compatInfo, int processState) throws RemoteException;
     void scheduleBindService(IBinder token,
-            Intent intent, boolean rebind) throws RemoteException;
+            Intent intent, boolean rebind, int processState) throws RemoteException;
     void scheduleUnbindService(IBinder token,
             Intent intent) throws RemoteException;
     void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
@@ -108,7 +109,7 @@
             throws RemoteException;
     void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
             int resultCode, String data, Bundle extras, boolean ordered,
-            boolean sticky, int sendingUser) throws RemoteException;
+            boolean sticky, int sendingUser, int processState) throws RemoteException;
     void scheduleLowMemory() throws RemoteException;
     void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
     void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType)
@@ -135,6 +136,7 @@
             throws RemoteException;
     void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
             throws RemoteException;
+    void setProcessState(int state) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -186,4 +188,5 @@
     int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
     int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
     int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
+    int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
 }
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 10ec9c9..5d72102 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -852,7 +852,9 @@
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
                 bumpServiceExecutingLocked(r, "bind");
-                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
+                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
+                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
+                        r.app.repProcState);
                 if (!rebind) {
                     i.requested = true;
                 }
@@ -1127,8 +1129,10 @@
                 r.stats.startLaunchedLocked();
             }
             mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
+            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
             app.thread.scheduleCreateService(r, r.serviceInfo,
-                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
+                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
+                    app.repProcState);
             r.postNotification();
             created = true;
         } finally {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 65006e5..c41c23e 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10789,30 +10789,87 @@
             } else {
                 oomAdj = Integer.toString(r.setAdj);
             }
-            String schedGroup;
+            char schedGroup;
             switch (r.setSchedGroup) {
                 case Process.THREAD_GROUP_BG_NONINTERACTIVE:
-                    schedGroup = "B";
+                    schedGroup = 'B';
                     break;
                 case Process.THREAD_GROUP_DEFAULT:
-                    schedGroup = "F";
+                    schedGroup = 'F';
                     break;
                 default:
-                    schedGroup = Integer.toString(r.setSchedGroup);
+                    schedGroup = '?';
                     break;
             }
-            String foreground;
+            char foreground;
             if (r.foregroundActivities) {
-                foreground = "A";
+                foreground = 'A';
             } else if (r.foregroundServices) {
-                foreground = "S";
+                foreground = 'S';
             } else {
-                foreground = " ";
+                foreground = ' ';
             }
-            pw.println(String.format("%s%s #%2d: adj=%s/%s%s trm=%2d %s (%s)",
-                    prefix, (r.persistent ? persistentLabel : normalLabel),
-                    (origList.size()-1)-list.get(i).second, oomAdj, schedGroup,
-                    foreground, r.trimMemoryLevel, r.toShortString(), r.adjType));
+            String procState;
+            switch (r.curProcState) {
+                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_PERCEPTIBLE:
+                    procState = "IP";
+                    break;
+                case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+                    procState = "IB";
+                    break;
+                case ActivityManager.PROCESS_STATE_RECEIVER:
+                    procState = "R ";
+                    break;
+                case ActivityManager.PROCESS_STATE_BACKUP:
+                    procState = "BU";
+                    break;
+                case ActivityManager.PROCESS_STATE_SERVICE:
+                    procState = "S ";
+                    break;
+                case ActivityManager.PROCESS_STATE_HOME:
+                    procState = "H ";
+                    break;
+                case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
+                    procState = "LA";
+                    break;
+                case ActivityManager.PROCESS_STATE_CACHED:
+                    procState = "C ";
+                    break;
+                default:
+                    procState = "??";
+                    break;
+            }
+            pw.print(prefix);
+            pw.print(r.persistent ? persistentLabel : normalLabel);
+            pw.print(" #");
+            int num = (origList.size()-1)-list.get(i).second;
+            if (num < 10) pw.print(' ');
+            pw.print(num);
+            pw.print(": ");
+            pw.print(oomAdj);
+            pw.print(' ');
+            pw.print(schedGroup);
+            pw.print('/');
+            pw.print(foreground);
+            pw.print('/');
+            pw.print(procState);
+            pw.print(" trm:");
+            if (r.trimMemoryLevel < 10) pw.print(' ');
+            pw.print(r.trimMemoryLevel);
+            pw.print(' ');
+            pw.print(r.toShortString());
+            pw.print(" (");
+            pw.print(r.adjType);
+            pw.println(')');
             if (r.adjSource != null || r.adjTarget != null) {
                 pw.print(prefix);
                 pw.print("    ");
@@ -13374,6 +13431,7 @@
         if (app.thread == null) {
             app.adjSeq = mAdjSeq;
             app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+            app.curProcState = ActivityManager.PROCESS_STATE_CACHED;
             return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
         }
 
@@ -13396,6 +13454,7 @@
             app.foregroundActivities = false;
             app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
+            app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
             // System process can do UI, and when they do we want to have
             // them trim their memory after the user leaves the UI.  To
             // facilitate this, here we need to determine whether or not it
@@ -13415,6 +13474,9 @@
                     }
                 }
             }
+            if (!app.systemNoUi) {
+                app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+            }
             return (app.curAdj=app.maxAdj);
         }
 
@@ -13426,6 +13488,7 @@
         // important to least, and assign an appropriate OOM adjustment.
         int adj;
         int schedGroup;
+        int procState;
         boolean foregroundActivities = false;
         boolean interesting = false;
         BroadcastQueue queue;
@@ -13437,12 +13500,14 @@
             foregroundActivities = true;
             interesting = true;
             app.hasActivities = true;
+            procState = ActivityManager.PROCESS_STATE_TOP;
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "instrumentation";
             interesting = true;
+            procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
         } else if ((queue = isReceivingBroadcast(app)) != null) {
             // An app that is currently receiving a broadcast also
             // counts as being in the foreground for OOM killer purposes.
@@ -13452,12 +13517,14 @@
             schedGroup = (queue == mFgBroadcastQueue)
                     ? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
             app.adjType = "broadcast";
+            procState = ActivityManager.PROCESS_STATE_RECEIVER;
         } else if (app.executingServices.size() > 0) {
             // An app that is currently executing a service callback also
             // counts as being in the foreground.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "exec-service";
+            procState = ActivityManager.PROCESS_STATE_SERVICE;
         } else {
             // Assume process is cached (has activities); we will correct
             // later if this is not the case.
@@ -13465,6 +13532,7 @@
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
             app.cached = true;
             app.adjType = "cch-act";
+            procState = ActivityManager.PROCESS_STATE_CACHED;
         }
 
         boolean hasStoppingActivities = false;
@@ -13479,6 +13547,9 @@
                         adj = ProcessList.VISIBLE_APP_ADJ;
                         app.adjType = "visible";
                     }
+                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
+                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                    }
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.cached = false;
                     app.hasActivities = true;
@@ -13489,12 +13560,18 @@
                         adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                         app.adjType = "pausing";
                     }
+                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
+                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                    }
                     app.cached = false;
                     foregroundActivities = true;
                 } else if (r.state == ActivityState.STOPPING) {
                     // We will apply the actual adjustment later, because
                     // we want to allow this process to immediately go through
                     // any memory trimming that is in effect.
+                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                    }
                     app.cached = false;
                     foregroundActivities = true;
                     hasStoppingActivities = true;
@@ -13522,12 +13599,14 @@
             if (app.foregroundServices) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
                 app.cached = false;
                 app.adjType = "fg-service";
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             } else if (app.forcingToForeground != null) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
                 app.cached = false;
                 app.adjType = "force-fg";
                 app.adjSource = app.forcingToForeground;
@@ -13547,24 +13626,33 @@
             app.adjType = "heavy";
         }
 
-        if (adj > ProcessList.HOME_APP_ADJ && app == mHomeProcess) {
-            // This process is hosting what we currently consider to be the
-            // home app, so we don't want to let it go into the background.
-            adj = ProcessList.HOME_APP_ADJ;
-            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.cached = false;
-            app.adjType = "home";
+        if (app == mHomeProcess) {
+            if (adj > ProcessList.HOME_APP_ADJ) {
+                // This process is hosting what we currently consider to be the
+                // home app, so we don't want to let it go into the background.
+                adj = ProcessList.HOME_APP_ADJ;
+                schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+                app.cached = false;
+                app.adjType = "home";
+            }
+            if (procState > ActivityManager.PROCESS_STATE_HOME) {
+                procState = ActivityManager.PROCESS_STATE_HOME;
+            }
         }
 
-        if (adj > ProcessList.PREVIOUS_APP_ADJ && app == mPreviousProcess
-                && app.activities.size() > 0) {
-            // This was the previous process that showed UI to the user.
-            // We want to try to keep it around more aggressively, to give
-            // a good experience around switching between two apps.
-            adj = ProcessList.PREVIOUS_APP_ADJ;
-            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.cached = false;
-            app.adjType = "previous";
+        if (app == mPreviousProcess && app.activities.size() > 0) {
+            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+                // This was the previous process that showed UI to the user.
+                // We want to try to keep it around more aggressively, to give
+                // a good experience around switching between two apps.
+                adj = ProcessList.PREVIOUS_APP_ADJ;
+                schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+                app.cached = false;
+                app.adjType = "previous";
+            }
+            if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+                procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+            }
         }
 
         if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
@@ -13583,9 +13671,15 @@
             if (adj > ProcessList.BACKUP_APP_ADJ) {
                 if (DEBUG_BACKUP) Slog.v(TAG, "oom BACKUP_APP_ADJ for " + app);
                 adj = ProcessList.BACKUP_APP_ADJ;
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                }
                 app.adjType = "backup";
                 app.cached = false;
             }
+            if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
+                procState = ActivityManager.PROCESS_STATE_BACKUP;
+            }
         }
 
         if (app.services.size() != 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
@@ -13598,6 +13692,9 @@
                 ServiceRecord s = jt.next();
                 if (s.startRequested) {
                     app.hasStartedServices = true;
+                    if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
+                        procState = ActivityManager.PROCESS_STATE_SERVICE;
+                    }
                     if (app.hasShownUi && app != mHomeProcess) {
                         // If this process has shown some UI, let it immediately
                         // go to the LRU list because it may be pretty heavy with
@@ -13646,7 +13743,6 @@
                         }
                         if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                             ProcessRecord client = cr.binding.client;
-                            int clientAdj = adj;
                             int myCachedAdj = cachedAdj;
                             if (myCachedAdj > client.cachedAdj) {
                                 if (client.cachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
@@ -13671,8 +13767,9 @@
                                     myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
                                 }
                             }
-                            clientAdj = computeOomAdjLocked(client, myCachedAdj,
+                            int clientAdj = computeOomAdjLocked(client, myCachedAdj,
                                     myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
+                            int clientProcState = client.curProcState;
                             String adjType = null;
                             if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
                                 // Not doing bind OOM management, so treat
@@ -13687,6 +13784,7 @@
                                     }
                                     app.cached = false;
                                     clientAdj = adj;
+                                    clientProcState = procState;
                                 } else {
                                     if (now >= (s.lastActivity
                                             + ActiveServices.MAX_SERVICE_INACTIVITY)) {
@@ -13710,7 +13808,7 @@
                                     // LRU list as a cached process (with activities),
                                     // we don't want the service it is connected to
                                     // to go into the empty LRU and quickly get killed,
-                                    // because I'll we'll do is just end up restarting
+                                    // because all we'll do is just end up restarting
                                     // the service.
                                     app.hasClientActivities |= client.hasActivities;
                                 }
@@ -13751,6 +13849,25 @@
                                     adjType = "service";
                                 }
                             }
+                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                }
+                                if (clientProcState <
+                                        ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
+                                    clientProcState =
+                                            ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                                }
+                            } else {
+                                if (clientProcState <
+                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                                    clientProcState =
+                                            ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                                }
+                            }
+                            if (procState > clientProcState) {
+                                procState = clientProcState;
+                            }
                             if (adjType != null) {
                                 app.adjType = adjType;
                                 app.adjTypeCode = ActivityManager.RunningAppProcessInfo
@@ -13759,11 +13876,6 @@
                                 app.adjSourceOom = clientAdj;
                                 app.adjTarget = s.name;
                             }
-                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                            }
                         }
                         final ActivityRecord a = cr.activity;
                         if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
@@ -13861,6 +13973,9 @@
                     app.adjSourceOom = clientAdj;
                     app.adjTarget = cpr.name;
                 }
+                if (procState > client.curProcState) {
+                    procState = client.curProcState;
+                }
                 if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                 }
@@ -13877,6 +13992,9 @@
                     app.adjType = "provider";
                     app.adjTarget = cpr.name;
                 }
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
+                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                }
             }
         }
 
@@ -13935,6 +14053,8 @@
             }
         }
 
+        app.curProcState = procState;
+
         int importance = app.memImportance;
         if (importance == 0 || adj != app.curAdj || schedGroup != app.curSchedGroup) {
             app.curAdj = adj;
@@ -14288,7 +14408,8 @@
     }
 
     private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
-            int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll, long now) {
+            int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll,
+            boolean doingProcessState, long now) {
         app.cachedAdj = cachedAdj;
         app.clientCachedAdj = clientCachedAdj;
         app.emptyAdj = emptyAdj;
@@ -14391,6 +14512,20 @@
                 }
             }
         }
+        if (app.repProcState != app.curProcState) {
+            app.repProcState = app.curProcState;
+            if (!doingProcessState && app.thread != null) {
+                try {
+                    if (false) {
+                        //RuntimeException h = new RuntimeException("here");
+                        Slog.i(TAG, "Sending new process state " + app.repProcState
+                                + " to " + app /*, h*/);
+                    }
+                    app.thread.setProcessState(app.repProcState);
+                } catch (RemoteException e) {
+                }
+            }
+        }
         return success;
     }
 
@@ -14399,6 +14534,10 @@
     }
 
     final boolean updateOomAdjLocked(ProcessRecord app) {
+        return updateOomAdjLocked(app, false);
+    }
+
+    final boolean updateOomAdjLocked(ProcessRecord app, boolean doingProcessState) {
         final ActivityRecord TOP_ACT = resumedAppLocked();
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
         int curAdj = app.curAdj;
@@ -14408,7 +14547,7 @@
         mAdjSeq++;
 
         boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj,
-                app.emptyAdj, TOP_APP, false, SystemClock.uptimeMillis());
+                app.emptyAdj, TOP_APP, false, doingProcessState, SystemClock.uptimeMillis());
         final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
             && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
         if (nowCached != wasCached) {
@@ -14490,7 +14629,7 @@
             //Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj);
             app.setAdjChanged = false;
             updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP,
-                    true, now);
+                    true, false, now);
             changed |= app.setAdjChanged;
             if (!app.killedBackground) {
                 if (app.curRawAdj == curCachedAdj && app.hasActivities) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index be03ee3..a154b9c 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -811,21 +811,7 @@
                     destroyActivityLocked(r, true, false, "stop-config");
                     mStackSupervisor.resumeTopActivitiesLocked();
                 } else {
-                    // Now that this process has stopped, we may want to consider
-                    // it to be the previous app to try to keep around in case
-                    // the user wants to return to it.
-                    ProcessRecord fgApp = null;
-                    if (mResumedActivity != null) {
-                        fgApp = mResumedActivity.app;
-                    } else if (mPausingActivity != null) {
-                        fgApp = mPausingActivity.app;
-                    }
-                    if (r.app != null && fgApp != null && r.app != fgApp
-                            && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
-                            && r.app != mService.mHomeProcess) {
-                        mService.mPreviousProcess = r.app;
-                        mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
-                    }
+                    mStackSupervisor.updatePreviousProcessLocked(r);
                 }
             }
         }
@@ -1513,7 +1499,8 @@
                 next.sleeping = false;
                 mService.showAskCompatModeDialogLocked(next);
                 next.app.pendingUiClean = true;
-                next.app.thread.scheduleResumeActivity(next.appToken,
+                next.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
+                next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                         mService.isNextTransitionForward());
 
                 mStackSupervisor.checkReadyForSleepLocked();
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index d39798d..8d1a665 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -929,10 +929,11 @@
                     }
                 }
             }
+            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info,
-                    new Configuration(mService.mConfiguration),
-                    r.compat, r.icicle, results, newIntents, !andResume,
+                    new Configuration(mService.mConfiguration), r.compat,
+                    app.repProcState, r.icicle, results, newIntents, !andResume,
                     mService.isNextTransitionForward(), profileFile, profileFd,
                     profileAutoStop);
 
@@ -1893,6 +1894,37 @@
         return didSomething;
     }
 
+    void updatePreviousProcessLocked(ActivityRecord r) {
+        // Now that this process has stopped, we may want to consider
+        // it to be the previous app to try to keep around in case
+        // the user wants to return to it.
+
+        // First, found out what is currently the foreground app, so that
+        // we don't blow away the previous app if this activity is being
+        // hosted by the process that is actually still the foreground.
+        ProcessRecord fgApp = null;
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mStacks.get(stackNdx);
+            if (isFrontStack(stack)) {
+                if (stack.mResumedActivity != null) {
+                    fgApp = stack.mResumedActivity.app;
+                } else if (stack.mPausingActivity != null) {
+                    fgApp = stack.mPausingActivity.app;
+                }
+                break;
+            }
+        }
+
+        // Now set this one as the previous process, only if that really
+        // makes sense to.
+        if (r.app != null && fgApp != null && r.app != fgApp
+                && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+                && r.app != mService.mHomeProcess) {
+            mService.mPreviousProcess = r.app;
+            mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
+        }
+    }
+
     boolean resumeTopActivitiesLocked() {
         return resumeTopActivitiesLocked(null, null, null);
     }
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 5238bd1..c0140e4 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -217,6 +217,7 @@
         r.receiver = app.thread.asBinder();
         r.curApp = app;
         app.curReceiver = r;
+        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
         mService.updateLruProcessLocked(app, true);
 
         // Tell the application to launch this receiver.
@@ -230,7 +231,8 @@
             mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                     mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
-                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId);
+                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
+                    app.repProcState);
             if (DEBUG_BROADCAST)  Slog.v(TAG,
                     "Process cur broadcast " + r + " DELIVERED for app " + app);
             started = true;
@@ -371,7 +373,7 @@
             // If we have an app thread, do the call through that so it is
             // correctly ordered with other one-way calls.
             app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
-                    data, extras, ordered, sticky, sendingUser);
+                    data, extras, ordered, sticky, sendingUser, app.repProcState);
         } else {
             receiver.performReceive(intent, resultCode, data, extras, ordered,
                     sticky, sendingUser);
@@ -437,7 +439,7 @@
                     // are already core system stuff so don't matter for this.
                     r.curApp = filter.receiverList.app;
                     filter.receiverList.app.curReceiver = r;
-                    mService.updateOomAdjLocked();
+                    mService.updateOomAdjLocked(r.curApp, true);
                 }
             }
             try {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 21a6ff0..cb9a76d 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -76,6 +76,8 @@
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
     int memImportance;          // Importance constant computed from curAdj
+    int curProcState = -1;      // Currently computed process state: ActivityManager.PROCESS_STATE_*
+    int repProcState = -1;      // Last reported process state
     boolean serviceb;           // Process currently is on the service B list
     boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
@@ -225,6 +227,8 @@
                 pw.print(" setSchedGroup="); pw.print(setSchedGroup);
                 pw.print(" systemNoUi="); pw.print(systemNoUi);
                 pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
+        pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
+                pw.print(" repProcState="); pw.println(repProcState);
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.print(lruSeq);
                 pw.print(" lastPssTime="); pw.println(lastPssTime);
@@ -473,6 +477,12 @@
         return setAdj;
     }
 
+    public void forceProcessStateUpTo(int newState) {
+        if (repProcState > newState) {
+            curProcState = repProcState = newState;
+        }
+    }
+
     public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now,
             ProcessList plist) {
         int state = this == TOP_APP ? ProcessTracker.STATE_TOP