Make window token add/remove APIs require displayId

Window tokens can now only be on one display, so we now require clients
that want to add/remove window tokens to specify the display they would
like the token to be created on. This simplifies the token handling code
in WM and will be useful moving forward for clients that want to add
windows to external displays.

Test: Existing tests pass
Change-Id: I6b2d8d58a913b3624f1a9a7bebbb99315613f103
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a44c8aa..622eece 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -26,22 +26,27 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.logWithStack;
 
+import android.os.Debug;
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.wm.WindowManagerService.H;
 
@@ -398,6 +403,63 @@
         return super.checkCompleteDeferredRemoval();
     }
 
+    void onRemovedFromDisplay() {
+        AppWindowToken startingToken = null;
+
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
+
+        boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, voiceInteraction);
+
+        mService.mOpeningApps.remove(this);
+        waitingToShow = false;
+        if (mService.mClosingApps.contains(this)) {
+            delayed = true;
+        } else if (mService.mAppTransition.isTransitionSet()) {
+            mService.mClosingApps.add(this);
+            delayed = true;
+        }
+
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app " + this + " delayed=" + delayed
+                + " animation=" + mAppAnimator.animation + " animating=" + mAppAnimator.animating);
+
+        if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
+                + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
+
+        final TaskStack stack = mTask.mStack;
+        if (delayed && !isEmpty()) {
+            // set the token aside because it has an active animation to be finished
+            if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM,
+                    "removeAppToken make exiting: " + this);
+            stack.mExitingAppTokens.add(this);
+            mIsExiting = true;
+        } else {
+            // Make sure there is no animation running on this token, so any windows associated
+            // with it will be removed as soon as their animations are complete
+            mAppAnimator.clearAnimation();
+            mAppAnimator.animating = false;
+            removeIfPossible();
+        }
+
+        removed = true;
+        if (startingData != null) {
+            startingToken = this;
+        }
+        stopFreezingScreen(true, true);
+        if (mService.mFocusedApp == this) {
+            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
+            mService.mFocusedApp = null;
+            mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+            mService.mInputMonitor.setFocusedAppLw(null);
+        }
+
+        if (!delayed) {
+            updateReportedVisibilityLocked();
+        }
+
+        // Will only remove if startingToken non null.
+        mService.scheduleRemoveStartingWindowLocked(startingToken);
+    }
+
     void clearAnimatingFlags() {
         boolean wallpaperMightChange = false;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1960285..13099dc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -62,7 +62,6 @@
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
@@ -314,6 +313,23 @@
         return token;
     }
 
+    void removeAppToken(IBinder binder) {
+        final WindowToken token = removeWindowToken(binder);
+        if (token == null) {
+            Slog.w(TAG_WM, "removeAppToken: Attempted to remove non-existing token: " + binder);
+            return;
+        }
+
+        final AppWindowToken appToken = token.asAppWindowToken();
+
+        if (appToken == null) {
+            Slog.w(TAG_WM, "Attempted to remove non-App token: " + binder + " token=" + token);
+            return;
+        }
+
+        appToken.onRemovedFromDisplay();
+    }
+
     Display getDisplay() {
         return mDisplay;
     }
@@ -638,7 +654,7 @@
         rebuildAppWindowList();
     }
 
-    void resetAnimationBackgroundAnimator() {
+    private void resetAnimationBackgroundAnimator() {
         for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
             mTaskStackContainers.get(stackNdx).resetAnimationBackgroundAnimator();
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 26f79a9..6325cda 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -126,15 +126,6 @@
 
     private final ArrayList<Integer> mChangedStackList = new ArrayList();
 
-    private final ArrayList<WindowToken> mTmpTokensList = new ArrayList();
-
-    // Collection of binder tokens mapped to their window type we are allowed to create window
-    // tokens for but that are not current attached to any display. We need to track this here
-    // because a binder token can be added through {@link WindowManagerService#addWindowToken},
-    // but we don't know what display windows for the token will be added to until
-    // {@link WindowManagerService#addWindow} is called.
-    private final HashMap<IBinder, Integer> mUnattachedBinderTokens = new HashMap();
-
     // State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
     // instances will be replaced with an instance that writes a binary representation of all
     // commands to mSurfaceTraceFd.
@@ -340,36 +331,6 @@
         return null;
     }
 
-    /** Return the window token associated with the input binder token on the input display */
-    WindowToken getWindowToken(IBinder binder, DisplayContent dc) {
-        final WindowToken token = dc.getWindowToken(binder);
-        if (token != null) {
-            return token;
-        }
-
-        // There is no window token mapped to the binder on the display. Create and map a window
-        // token if it is currently allowed.
-        if (!mUnattachedBinderTokens.containsKey(binder)) {
-            return null;
-        }
-
-        final int type = mUnattachedBinderTokens.get(binder);
-        return new WindowToken(mService, binder, type, true, dc);
-    }
-
-    /** Returns all window tokens mapped to the input binder. */
-    ArrayList<WindowToken> getWindowTokens(IBinder binder) {
-        mTmpTokensList.clear();
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final DisplayContent dc = mChildren.get(i);
-            final WindowToken token = dc.getWindowToken(binder);
-            if (token != null) {
-                mTmpTokensList.add(token);
-            }
-        }
-        return mTmpTokensList;
-    }
-
     /**
      * Returns the app window token for the input binder if it exist in the system.
      * NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since
@@ -403,140 +364,6 @@
         return null;
     }
 
-    void addWindowToken(IBinder binder, int type) {
-        if (mUnattachedBinderTokens.containsKey(binder)) {
-            Slog.w(TAG_WM, "addWindowToken: Attempted to add existing binder token: " + binder);
-            return;
-        }
-
-        final ArrayList<WindowToken> tokens = getWindowTokens(binder);
-
-        if (!tokens.isEmpty()) {
-            Slog.w(TAG_WM, "addWindowToken: Attempted to add binder token: " + binder
-                    + " for already created window tokens: " + tokens);
-            return;
-        }
-
-        mUnattachedBinderTokens.put(binder, type);
-
-        // TODO(multi-display): By default we add this to the default display, but maybe we
-        // should provide an API for a token to be added to any display?
-        final DisplayContent dc = getDisplayContent(DEFAULT_DISPLAY);
-        final WindowToken token = new WindowToken(mService, binder, type, true, dc);
-        if (type == TYPE_WALLPAPER) {
-            dc.mWallpaperController.addWallpaperToken(token);
-        }
-    }
-
-    ArrayList<WindowToken> removeWindowToken(IBinder binder) {
-        mUnattachedBinderTokens.remove(binder);
-
-        mTmpTokensList.clear();
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final DisplayContent dc = mChildren.get(i);
-            final WindowToken token = dc.removeWindowToken(binder);
-            if (token != null) {
-                mTmpTokensList.add(token);
-            }
-        }
-        return mTmpTokensList;
-    }
-
-    /**
-     * Removed the mapping to the input binder for the system if it no longer as a window token
-     * associated with it on any display.
-     */
-    void removeWindowTokenIfPossible(IBinder binder) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final DisplayContent dc = mChildren.get(i);
-            final WindowToken token = dc.getWindowToken(binder);
-            if (token != null) {
-                return;
-            }
-        }
-
-        mUnattachedBinderTokens.remove(binder);
-    }
-
-    void removeAppToken(IBinder binder) {
-        final ArrayList<WindowToken> removedTokens = removeWindowToken(binder);
-        if (removedTokens == null || removedTokens.isEmpty()) {
-            Slog.w(TAG_WM, "removeAppToken: Attempted to remove non-existing token: " + binder);
-            return;
-        }
-
-        for (int i = removedTokens.size() - 1; i >= 0; --i) {
-            WindowToken wtoken = removedTokens.get(i);
-            AppWindowToken appToken = wtoken.asAppWindowToken();
-
-            if (appToken == null) {
-                Slog.w(TAG_WM,
-                        "Attempted to remove non-App token: " + binder + " wtoken=" + wtoken);
-                continue;
-            }
-
-            AppWindowToken startingToken = null;
-
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + appToken);
-
-            boolean delayed = appToken.setVisibility(null, false, TRANSIT_UNSET, true,
-                    appToken.voiceInteraction);
-
-            mService.mOpeningApps.remove(appToken);
-            mService.mUnknownAppVisibilityController.appRemoved(appToken);
-            appToken.waitingToShow = false;
-            if (mService.mClosingApps.contains(appToken)) {
-                delayed = true;
-            } else if (mService.mAppTransition.isTransitionSet()) {
-                mService.mClosingApps.add(appToken);
-                delayed = true;
-            }
-
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app " + appToken
-                    + " delayed=" + delayed
-                    + " animation=" + appToken.mAppAnimator.animation
-                    + " animating=" + appToken.mAppAnimator.animating);
-
-            if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
-                    + appToken + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
-
-            final TaskStack stack = appToken.mTask.mStack;
-            if (delayed && !appToken.isEmpty()) {
-                // set the token aside because it has an active animation to be finished
-                if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM,
-                        "removeAppToken make exiting: " + appToken);
-                stack.mExitingAppTokens.add(appToken);
-                appToken.mIsExiting = true;
-            } else {
-                // Make sure there is no animation running on this token, so any windows associated
-                // with it will be removed as soon as their animations are complete
-                appToken.mAppAnimator.clearAnimation();
-                appToken.mAppAnimator.animating = false;
-                appToken.removeIfPossible();
-            }
-
-            appToken.removed = true;
-            if (appToken.startingData != null) {
-                startingToken = appToken;
-            }
-            appToken.stopFreezingScreen(true, true);
-            if (mService.mFocusedApp == appToken) {
-                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + appToken);
-                mService.mFocusedApp = null;
-                mService.updateFocusedWindowLocked(
-                        UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
-                mService.mInputMonitor.setFocusedAppLw(null);
-            }
-
-            if (!delayed) {
-                appToken.updateReportedVisibilityLocked();
-            }
-
-            // Will only remove if startingToken non null.
-            mService.scheduleRemoveStartingWindowLocked(startingToken);
-        }
-    }
-
     // TODO: Users would have their own window containers under the display container?
     void switchUser() {
         final int count = mChildren.size();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2fef928..66b2cbc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1166,8 +1166,8 @@
             final boolean hasParent = parentWindow != null;
             // Use existing parent window token for child windows since they go in the same token
             // as there parent window so we can apply the same policy on them.
-            WindowToken token = mRoot.getWindowToken(
-                    hasParent ? parentWindow.mAttrs.token : attrs.token, displayContent);
+            WindowToken token = displayContent.getWindowToken(
+                    hasParent ? parentWindow.mAttrs.token : attrs.token);
             // If this is a child window, we want to apply the same type checking rules as the
             // parent window type.
             final int rootType = hasParent ? parentWindow.mAttrs.type : type;
@@ -2395,18 +2395,29 @@
     }
 
     @Override
-    public void addWindowToken(IBinder token, int type) {
+    public void addWindowToken(IBinder binder, int type, int displayId) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized(mWindowMap) {
-            mRoot.addWindowToken(token, type);
+            final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
+            WindowToken token = dc.getWindowToken(binder);
+            if (token != null) {
+                Slog.w(TAG_WM, "addWindowToken: Attempted to add binder token: " + binder
+                        + " for already created window token: " + token
+                        + " displayId=" + displayId);
+                return;
+            }
+            token = new WindowToken(this, binder, type, true, dc);
+            if (type == TYPE_WALLPAPER) {
+                dc.mWallpaperController.addWallpaperToken(token);
+            }
         }
     }
 
     @Override
-    public void removeWindowToken(IBinder token) {
+    public void removeWindowToken(IBinder binder, int displayId) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
@@ -2414,22 +2425,26 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mWindowMap) {
-                final ArrayList<WindowToken> removedTokens = mRoot.removeWindowToken(token);
-                if (removedTokens == null || removedTokens.isEmpty()) {
-                    Slog.w(TAG_WM,
-                            "removeWindowToken: Attempted to remove non-existing token: " + token);
+                final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                if (dc == null) {
+                    Slog.w(TAG_WM, "removeWindowToken: Attempted to remove token: " + binder
+                            + " for non-exiting displayId=" + displayId);
                     return;
                 }
 
-                for (int i = removedTokens.size() - 1; i >= 0; --i) {
-                    final WindowToken wtoken = removedTokens.get(i);
-                    wtoken.setExiting();
-                    if (wtoken.windowType == TYPE_WALLPAPER) {
-                        wtoken.getDisplayContent().mWallpaperController.removeWallpaperToken(wtoken);
-                    }
-
-                    mInputMonitor.updateInputWindowsLw(true /*force*/);
+                final WindowToken token = dc.removeWindowToken(binder);
+                if (token == null) {
+                    Slog.w(TAG_WM,
+                            "removeWindowToken: Attempted to remove non-existing token: " + binder);
+                    return;
                 }
+
+                token.setExiting();
+                if (token.windowType == TYPE_WALLPAPER) {
+                    dc.mWallpaperController.removeWallpaperToken(token);
+                }
+
+                mInputMonitor.updateInputWindowsLw(true /*force*/);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -3262,7 +3277,7 @@
     }
 
     @Override
-    public void removeAppToken(IBinder token) {
+    public void removeAppToken(IBinder binder, int displayId) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
@@ -3270,7 +3285,13 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized(mWindowMap) {
-                mRoot.removeAppToken(token);
+                final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                if (dc == null) {
+                    Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: " + binder
+                            + " from non-existing displayId=" + displayId);
+                    return;
+                }
+                dc.removeAppToken(binder);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -5790,33 +5811,29 @@
     private boolean mEventDispatchingEnabled;
 
     @Override
-    public void pauseKeyDispatching(IBinder _token) {
+    public void pauseKeyDispatching(IBinder binder) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "pauseKeyDispatching()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized (mWindowMap) {
-            final ArrayList<WindowToken> tokens = mRoot.getWindowTokens(_token);
-            if (tokens != null && !tokens.isEmpty()) {
-                for (int i = tokens.size() - 1; i >= 0; --i) {
-                    mInputMonitor.pauseDispatchingLw(tokens.get(i));
-                }
+            WindowToken token = mRoot.getAppWindowToken(binder);
+            if (token != null) {
+                mInputMonitor.pauseDispatchingLw(token);
             }
         }
     }
 
     @Override
-    public void resumeKeyDispatching(IBinder _token) {
+    public void resumeKeyDispatching(IBinder binder) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "resumeKeyDispatching()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized (mWindowMap) {
-            final ArrayList<WindowToken> tokens = mRoot.getWindowTokens(_token);
-            if (tokens != null && !tokens.isEmpty()) {
-                for (int i = tokens.size() - 1; i >= 0; --i) {
-                    mInputMonitor.resumeDispatchingLw(tokens.get(i));
-                }
+            WindowToken token = mRoot.getAppWindowToken(binder);
+            if (token != null) {
+                mInputMonitor.resumeDispatchingLw(token);
             }
         }
     }
@@ -8807,23 +8824,31 @@
         }
 
         @Override
-        public void addWindowToken(IBinder token, int type) {
-            WindowManagerService.this.addWindowToken(token, type);
+        public void addWindowToken(IBinder token, int type, int displayId) {
+            WindowManagerService.this.addWindowToken(token, type, displayId);
         }
 
         @Override
-        public void removeWindowToken(IBinder token, boolean removeWindows) {
+        public void removeWindowToken(IBinder binder, boolean removeWindows, int displayId) {
             synchronized(mWindowMap) {
                 if (removeWindows) {
-                    final ArrayList<WindowToken> removedTokens = mRoot.removeWindowToken(token);
-                    if (removedTokens != null && !removedTokens.isEmpty()) {
-                        for (int i = removedTokens.size() - 1; i >= 0; --i) {
-                            final WindowToken wtoken = removedTokens.get(i);
-                            wtoken.removeAllWindows();
-                        }
+                    final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                    if (dc == null) {
+                        Slog.w(TAG_WM, "removeWindowToken: Attempted to remove token: " + binder
+                                + " for non-exiting displayId=" + displayId);
+                        return;
                     }
+
+                    final WindowToken token = dc.removeWindowToken(binder);
+                    if (token == null) {
+                        Slog.w(TAG_WM, "removeWindowToken: Attempted to remove non-existing token: "
+                                + binder);
+                        return;
+                    }
+
+                    token.removeAllWindows();
                 }
-                WindowManagerService.this.removeWindowToken(token);
+                WindowManagerService.this.removeWindowToken(binder, displayId);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index cf1a98a..b821f09 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -469,7 +469,6 @@
     void removeImmediately() {
         if (mDisplayContent != null) {
             mDisplayContent.removeWindowToken(token);
-            mService.mRoot.removeWindowTokenIfPossible(token);
         }
         // Needs to occur after the token is removed from the display above to avoid attempt at
         // duplicate removal of this window container from it's parent.