Various performance and other work.

- IME service now switches between visible and perceptible depending on
  whether it is being showm, allowing us to more aggressively free its
  memory when not shown.

- The activity display time is no longer delayed by the activity
  transition animation.

- New -R (repeat) option for launching activities with the am command.

- Improved some documentation on Loader to be clear about some methods
  that apps should not normally call.

- FrameworkPerf test now allows you to select individual tests to run.

Change-Id: Id1f73de66dc93d63212183958a72119ad174318b
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 17ea03b..ddac35c 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -161,6 +161,16 @@
     private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
             new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
 
+    // Used to bring IME service up to visible adjustment while it is being shown.
+    final ServiceConnection mVisibleConnection = new ServiceConnection() {
+        @Override public void onServiceConnected(ComponentName name, IBinder service) {
+        }
+
+        @Override public void onServiceDisconnected(ComponentName name) {
+        }
+    };
+    boolean mVisibleBound = false;
+
     // Ongoing notification
     private NotificationManager mNotificationManager;
     private KeyguardManager mKeyguardManager;
@@ -893,7 +903,8 @@
                 com.android.internal.R.string.input_method_binding_label);
         mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
                 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
-        if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {
+        if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE
+                | Context.BIND_NOT_VISIBLE)) {
             mLastBindTime = SystemClock.uptimeMillis();
             mHaveConnection = true;
             mCurId = info.getId();
@@ -975,6 +986,11 @@
     }
 
     void unbindCurrentMethodLocked(boolean reportToClient) {
+        if (mVisibleBound) {
+            mContext.unbindService(mVisibleConnection);
+            mVisibleBound = false;
+        }
+
         if (mHaveConnection) {
             mContext.unbindService(this);
             mHaveConnection = false;
@@ -1366,6 +1382,10 @@
                     MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
                     resultReceiver));
             mInputShown = true;
+            if (mHaveConnection && !mVisibleBound) {
+                mContext.bindService(mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE);
+                mVisibleBound = true;
+            }
             res = true;
         } else if (mHaveConnection && SystemClock.uptimeMillis()
                 >= (mLastBindTime+TIME_TO_RECONNECT)) {
@@ -1377,7 +1397,8 @@
                     SystemClock.uptimeMillis()-mLastBindTime,1);
             Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
             mContext.unbindService(this);
-            mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE);
+            mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE
+                    | Context.BIND_NOT_VISIBLE);
         }
 
         return res;
@@ -1436,6 +1457,10 @@
         } else {
             res = false;
         }
+        if (mHaveConnection && mVisibleBound) {
+            mContext.unbindService(mVisibleConnection);
+            mVisibleBound = false;
+        }
         mInputShown = false;
         mShowRequested = false;
         mShowExplicitlyRequested = false;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 04bbc11..f8a7d6a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -13241,10 +13241,17 @@
                                         if ((cr.flags&(Context.BIND_ABOVE_CLIENT
                                                 |Context.BIND_IMPORTANT)) != 0) {
                                             adj = clientAdj;
-                                        } else if (clientAdj >= ProcessList.VISIBLE_APP_ADJ) {
+                                        } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
+                                                && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+                                                && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                            adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                                        } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
                                             adj = clientAdj;
                                         } else {
-                                            adj = ProcessList.VISIBLE_APP_ADJ;
+                                            app.pendingUiClean = true;
+                                            if (adj > ProcessList.VISIBLE_APP_ADJ) {
+                                                adj = ProcessList.VISIBLE_APP_ADJ;
+                                            }
                                         }
                                         if (!client.hidden) {
                                             app.hidden = false;
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index ce45bfb1..00e6cb2 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -523,7 +523,7 @@
         }
     }
     
-    public void windowsVisible() {
+    public void windowsDrawn() {
         synchronized(service) {
             if (launchTime != 0) {
                 final long curTime = SystemClock.uptimeMillis();
@@ -555,6 +555,11 @@
                 stack.mInitialStartTime = 0;
             }
             startTime = 0;
+        }
+    }
+
+    public void windowsVisible() {
+        synchronized(service) {
             stack.reportActivityVisibleLocked(this);
             if (ActivityManagerService.DEBUG_SWITCH) Log.v(
                     ActivityManagerService.TAG, "windowsVisible(): " + this);
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 61c96bb..0e3d20a 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -77,6 +77,9 @@
     // Last visibility state we reported to the app token.
     boolean reportedVisible;
 
+    // Last drawn state we reported to the app token.
+    boolean reportedDrawn;
+
     // Set to true when the token has been removed from the window mgr.
     boolean removed;
 
@@ -277,6 +280,7 @@
 
         int numInteresting = 0;
         int numVisible = 0;
+        int numDrawn = 0;
         boolean nowGone = true;
 
         if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Update reported visibility: " + this);
@@ -307,6 +311,7 @@
             }
             numInteresting++;
             if (win.isDrawnLw()) {
+                numDrawn++;
                 if (!win.isAnimating()) {
                     numVisible++;
                 }
@@ -316,9 +321,27 @@
             }
         }
 
+        boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting;
         boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
+        if (!nowGone) {
+            // If the app is not yet gone, then it can only become visible/drawn.
+            if (!nowDrawn) {
+                nowDrawn = reportedDrawn;
+            }
+            if (!nowVisible) {
+                nowVisible = reportedVisible;
+            }
+        }
         if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "VIS " + this + ": interesting="
                 + numInteresting + " visible=" + numVisible);
+        if (nowDrawn != reportedDrawn) {
+            if (nowDrawn) {
+                Message m = service.mH.obtainMessage(
+                        H.REPORT_APPLICATION_TOKEN_DRAWN, this);
+                service.mH.sendMessage(m);
+            }
+            reportedDrawn = nowDrawn;
+        }
         if (nowVisible != reportedVisible) {
             if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(
                     WindowManagerService.TAG, "Visibility changed in " + this
@@ -360,6 +383,7 @@
         pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
                 pw.print(" clientHidden="); pw.print(clientHidden);
                 pw.print(" willBeHidden="); pw.print(willBeHidden);
+                pw.print(" reportedDrawn="); pw.print(reportedDrawn);
                 pw.print(" reportedVisible="); pw.println(reportedVisible);
         if (paused || freezingScreen) {
             pw.print(prefix); pw.print("paused="); pw.print(paused);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 08797dd..2cd3062 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -6386,6 +6386,7 @@
         public static final int REMOVE_STARTING = 6;
         public static final int FINISHED_STARTING = 7;
         public static final int REPORT_APPLICATION_TOKEN_WINDOWS = 8;
+        public static final int REPORT_APPLICATION_TOKEN_DRAWN = 9;
         public static final int WINDOW_FREEZE_TIMEOUT = 11;
         public static final int HOLD_SCREEN_CHANGED = 12;
         public static final int APP_TRANSITION_TIMEOUT = 13;
@@ -6599,6 +6600,17 @@
                     }
                 } break;
 
+                case REPORT_APPLICATION_TOKEN_DRAWN: {
+                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
+
+                    try {
+                        if (DEBUG_VISIBILITY) Slog.v(
+                                TAG, "Reporting drawn in " + wtoken);
+                        wtoken.appToken.windowsDrawn();
+                    } catch (RemoteException ex) {
+                    }
+                } break;
+
                 case REPORT_APPLICATION_TOKEN_WINDOWS: {
                     final AppWindowToken wtoken = (AppWindowToken)msg.obj;