Remove starting window when activity is preempted.

Activities launch starting windows before they are resumed. If another
activity is started after a first activity has launched its starting
window then it was possible that the starting window will never be
removed. An earlier fix, ag/368411, solved this by posting a delayed
message that would remove orphaned starting windows after 10 seconds.

This fix immediately removes starting windows that have been orphaned
through the above sequence.

A few code cleanups are also included in this CL.

Fixes bug 11029212.

Change-Id: I7a9befca92888aefe4000b90716c57c2aa572634
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 49f29fe..5776181 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -143,6 +143,7 @@
 
     private boolean inHistory;  // are we in the history stack?
     final ActivityStackSupervisor mStackSupervisor;
+    boolean mStartingWindowShown = false;
 
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 2e5dedf..f879971 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -61,7 +61,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -121,8 +120,7 @@
     // convertToTranslucent().
     static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
 
-    static final boolean SCREENSHOT_FORCE_565 = ActivityManager
-            .isLowRamDeviceStatic() ? true : false;
+    static final boolean SCREENSHOT_FORCE_565 = ActivityManager.isLowRamDeviceStatic();
 
     enum ActivityState {
         INITIALIZING,
@@ -398,8 +396,9 @@
         // be simplified once we stop storing tasks with empty mActivities lists.
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                return activities.get(activityNdx);
+            final int topActivityNdx = activities.size() - 1;
+            if (topActivityNdx >= 0) {
+                return activities.get(topActivityNdx);
             }
         }
         return null;
@@ -436,25 +435,6 @@
         return null;
     }
 
-    boolean containsApp(ProcessRecord app) {
-        if (app == null) {
-            return false;
-        }
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.finishing) {
-                    continue;
-                }
-                if (r.app == app) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     final boolean updateLRUListLocked(ActivityRecord r) {
         final boolean hadit = mLRUActivities.remove(r);
         mLRUActivities.add(r);
@@ -1231,6 +1211,32 @@
         }
     }
 
+    /** If any activities below the top running one are in the INITIALIZING state and they have a
+     * starting window displayed then remove that starting window. It is possible that the activity
+     * in this state will never resumed in which case that starting window will be orphaned. */
+    void cancelInitializingActivities() {
+        final ActivityRecord topActivity = topRunningActivityLocked(null);
+        boolean aboveTop = true;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (aboveTop) {
+                    if (r == topActivity) {
+                        aboveTop = false;
+                    }
+                    continue;
+                }
+
+                if (r.state == ActivityState.INITIALIZING && r.mStartingWindowShown) {
+                    if (DEBUG_VISBILITY) Slog.w(TAG, "Found orphaned starting window " + r);
+                    r.mStartingWindowShown = false;
+                    mWindowManager.removeAppStartingWindow(r.appToken);
+                }
+            }
+        }
+    }
+
     /**
      * Ensure that the top activity in the stack is resumed.
      *
@@ -1247,6 +1253,8 @@
     final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
         if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
 
+        cancelInitializingActivities();
+
         // Find the first activity that is not finishing.
         ActivityRecord next = topRunningActivityLocked(null);
 
@@ -1811,6 +1819,7 @@
                                 r.info.applicationInfo), r.nonLocalizedLabel,
                         r.labelRes, r.icon, r.logo, r.windowFlags,
                         prev != null ? prev.appToken : null, showStartingIcon);
+                r.mStartingWindowShown = true;
             }
         } else {
             // If this is the first activity, don't do any fancy animations,
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 22be807..fbd610c 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -256,9 +256,6 @@
     /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
     static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
 
-    /** Amount of time (in milliseconds) to delay before declaring a starting window leaked. */
-    static final int STARTING_WINDOW_TIMEOUT_DURATION = 10000;
-
     /**
      * If true, the window manager will do its own custom freezing and general
      * management of the screen during rotation.
@@ -2268,8 +2265,6 @@
                 token.appWindowToken.startingWindow = win;
                 if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken
                         + " startingWindow=" + win);
-                Message m = mH.obtainMessage(H.REMOVE_STARTING_TIMEOUT, token.appWindowToken);
-                mH.sendMessageDelayed(m, STARTING_WINDOW_TIMEOUT_DURATION);
             }
 
             boolean imMayMove = true;
@@ -2372,7 +2367,6 @@
     public void removeWindowLocked(Session session, WindowState win) {
         if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
             if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
-            removeStartingWindowTimeout(win.mAppToken);
         }
 
         if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v(
@@ -2516,7 +2510,6 @@
         if (atoken != null) {
             if (atoken.startingWindow == win) {
                 if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Nulling startingWindow " + win);
-                removeStartingWindowTimeout(atoken);
                 atoken.startingWindow = null;
             } else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
                 // If this is the last window and we had requested a starting
@@ -3983,7 +3976,6 @@
                         if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) {
                             Slog.v(TAG, "Removing starting window: " + startingWindow);
                         }
-                        removeStartingWindowTimeout(ttoken);
                         startingWindow.getWindowList().remove(startingWindow);
                         mWindowsChanged = true;
                         if (DEBUG_ADD_REMOVE) Slog.v(TAG,
@@ -4130,6 +4122,15 @@
         }
     }
 
+    public void removeAppStartingWindow(IBinder token) {
+        synchronized (mWindowMap) {
+            AppWindowToken wtoken = mTokenMap.get(token).appWindowToken;
+            if (wtoken.startingWindow != null) {
+                scheduleRemoveStartingWindow(wtoken);
+            }
+        }
+    }
+
     @Override
     public void setAppWillBeHidden(IBinder token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
@@ -4551,21 +4552,11 @@
         scheduleRemoveStartingWindow(startingToken);
     }
 
-    void removeStartingWindowTimeout(AppWindowToken wtoken) {
-        if (wtoken != null) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG, Debug.getCallers(1) +
-                    ": Remove starting window timeout " + wtoken + (wtoken != null ?
-                    " startingWindow=" + wtoken.startingWindow : ""));
-            mH.removeMessages(H.REMOVE_STARTING_TIMEOUT, wtoken);
-        }
-    }
-
     void scheduleRemoveStartingWindow(AppWindowToken wtoken) {
         if (wtoken != null && wtoken.startingWindow != null) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG, Debug.getCallers(1) +
                     ": Schedule remove starting " + wtoken + (wtoken != null ?
                     " startingWindow=" + wtoken.startingWindow : ""));
-            removeStartingWindowTimeout(wtoken);
             Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken);
             mH.sendMessage(m);
         }
@@ -7072,8 +7063,6 @@
         public static final int TAP_OUTSIDE_STACK = 31;
         public static final int NOTIFY_ACTIVITY_DRAWN = 32;
 
-        public static final int REMOVE_STARTING_TIMEOUT = 33;
-
         @Override
         public void handleMessage(Message msg) {
             if (DEBUG_WINDOW_TRACE) {
@@ -7172,7 +7161,6 @@
                                             "Aborted starting " + wtoken
                                             + ": removed=" + wtoken.removed
                                             + " startingData=" + wtoken.startingData);
-                                    removeStartingWindowTimeout(wtoken);
                                     wtoken.startingWindow = null;
                                     wtoken.startingData = null;
                                     abort = true;
@@ -7197,11 +7185,6 @@
                     }
                 } break;
 
-                case REMOVE_STARTING_TIMEOUT: {
-                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
-                    Slog.e(TAG, "Starting window " + wtoken + " timed out");
-                    // Fall through.
-                }
                 case REMOVE_STARTING: {
                     final AppWindowToken wtoken = (AppWindowToken)msg.obj;
                     IBinder token = null;