Merge "Treat IME processes as hosting activities"
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 81a886a..134ffa9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -242,6 +242,16 @@
     public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080;
 
     /**
+     * @hide Flag for {@link #bindService}: Treat the binding as hosting
+     * an activity, an unbinding as the activity going in the background.
+     * That is, when unbinding, the process when empty will go on the activity
+     * LRU list instead of the regular one, keeping it around more aggressively
+     * than it otherwise would be.  This is intended for use with IMEs to try
+     * to keep IME processes around for faster keyboard switching.
+     */
+    public static final int BIND_TREAT_LIKE_ACTIVITY = 0x08000000;
+
+    /**
      * @hide An idea that is not yet implemented.
      * Flag for {@link #bindService}: If binding from an activity, consider
      * this service to be visible like the binding activity is.  That is,
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 6827b3f..af8a6e7 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1225,7 +1225,8 @@
         mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
                 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
         if (bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE
-                | Context.BIND_NOT_VISIBLE | Context.BIND_SHOWING_UI)) {
+                | Context.BIND_NOT_VISIBLE | Context.BIND_NOT_FOREGROUND
+                | Context.BIND_SHOWING_UI)) {
             mLastBindTime = SystemClock.uptimeMillis();
             mHaveConnection = true;
             mCurId = info.getId();
@@ -1783,7 +1784,8 @@
             mInputShown = true;
             if (mHaveConnection && !mVisibleBound) {
                 bindCurrentInputMethodService(
-                        mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE);
+                        mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE
+                                | Context.BIND_TREAT_LIKE_ACTIVITY);
                 mVisibleBound = true;
             }
             res = true;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a845127..1345cfd 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -667,8 +667,7 @@
             // what they are, so we can report this elsewhere for
             // others to know why certain services are running.
             try {
-                clientIntent = (PendingIntent)service.getParcelableExtra(
-                        Intent.EXTRA_CLIENT_INTENT);
+                clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT);
             } catch (RuntimeException e) {
             }
             if (clientIntent != null) {
@@ -682,6 +681,11 @@
             }
         }
 
+        if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+            mAm.enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                    "BIND_TREAT_LIKE_ACTIVITY");
+        }
+
         final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
 
         ServiceLookupResult res =
@@ -755,8 +759,12 @@
             }
 
             if (s.app != null) {
+                if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+                    s.app.treatLikeActivity = true;
+                }
                 // This could have made the service more important.
-                mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities, b.client);
+                mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
+                        || s.app.treatLikeActivity, b.client);
                 mAm.updateOomAdjLocked(s.app);
             }
 
@@ -858,6 +866,12 @@
 
                 if (r.binding.service.app != null) {
                     // This could have made the service less important.
+                    if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+                        r.binding.service.app.treatLikeActivity = true;
+                        mAm.updateLruProcessLocked(r.binding.service.app,
+                                r.binding.service.app.hasClientActivities
+                                || r.binding.service.app.treatLikeActivity, null);
+                    }
                     mAm.updateOomAdjLocked(r.binding.service.app);
                 }
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b8ec3ab..5eaa3a1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2314,11 +2314,12 @@
 
     final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
             ProcessRecord client) {
-        final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities;
+        final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
+                || app.treatLikeActivity;
         final boolean hasService = false; // not impl yet. app.services.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
+            // The process has activities, so we are only allowing activity-based adjustments
+            // to 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;
@@ -12571,6 +12572,7 @@
         updateProcessForegroundLocked(app, false, false);
         app.foregroundActivities = false;
         app.hasShownUi = false;
+        app.treatLikeActivity = false;
         app.hasAboveClient = false;
 
         mServices.killServicesLocked(app, allowRestart);
@@ -14862,6 +14864,9 @@
                             app.adjTarget = s.name;
                         }
                     }
+                    if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+                        app.treatLikeActivity = true;
+                    }
                     final ActivityRecord a = cr.activity;
                     if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
                         if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
@@ -14994,10 +14999,17 @@
             }
         }
 
-        if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY && app.hasClientActivities) {
-            // This is a cached process, but with client activities.  Mark it so.
-            procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
-            app.adjType = "cch-client-act";
+        if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+            if (app.hasClientActivities) {
+                // This is a cached process, but with client activities.  Mark it so.
+                procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+                app.adjType = "cch-client-act";
+            } else if (app.treatLikeActivity) {
+                // This is a cached process, but somebody wants us to treat it like it has
+                // an activity, okay!
+                procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+                app.adjType = "cch-as-act";
+            }
         }
 
         if (adj == ProcessList.SERVICE_ADJ) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 10574ed..d04a6b2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -95,6 +95,7 @@
     boolean hasShownUi;         // Has UI been shown in this process since it was started?
     boolean pendingUiClean;     // Want to clean up resources from showing UI?
     boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower
+    boolean treatLikeActivity;  // Bound using BIND_TREAT_LIKE_ACTIVITY
     boolean bad;                // True if disabled in the bad process list
     boolean killedByAm;         // True when proc has been killed by activity manager, not for RAM
     boolean procStateChanged;   // Keep track of whether we changed 'setAdj'.
@@ -251,10 +252,11 @@
                 pw.print(" lastStateTime=");
                 TimeUtils.formatDuration(lastStateTime, now, pw);
                 pw.println();
-        if (hasShownUi || pendingUiClean || hasAboveClient) {
+        if (hasShownUi || pendingUiClean || hasAboveClient || treatLikeActivity) {
             pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
                     pw.print(" pendingUiClean="); pw.print(pendingUiClean);
-                    pw.print(" hasAboveClient="); pw.println(hasAboveClient);
+                    pw.print(" hasAboveClient="); pw.print(hasAboveClient);
+                    pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
         }
         if (setIsForeground || foregroundServices || forcingToForeground != null) {
             pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground);