Clean up starting window to prepare for saved surfaces

- Move all starting window logic to AppWindowContainerController
- Use startingView to hold any kind of contents for startingWindow
- Remove some conflicting code which looks very old and doesn't
apply anymore.

Test: Make sure starting window still works.

Bug: 31339431
Change-Id: I018dd013ab7e64a44932b6d54ae9bb4a47f315d3
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fa1d991..0a312f0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -251,9 +251,9 @@
     static final boolean DEBUG_INPUT = false;
     static final boolean DEBUG_KEYGUARD = false;
     static final boolean DEBUG_LAYOUT = false;
-    static final boolean DEBUG_STARTING_WINDOW = false;
+    static final boolean DEBUG_SPLASH_SCREEN = false;
     static final boolean DEBUG_WAKEUP = false;
-    static final boolean SHOW_STARTING_ANIMATIONS = true;
+    static final boolean SHOW_SPLASH_SCREENS = true;
 
     // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
     // No longer recommended for desk docks;
@@ -2794,10 +2794,10 @@
 
     /** {@inheritDoc} */
     @Override
-    public View addStartingWindow(IBinder appToken, String packageName, int theme,
-            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
-            int icon, int logo, int windowFlags, Configuration overrideConfig) {
-        if (!SHOW_STARTING_ANIMATIONS) {
+    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
+            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
+            int logo, int windowFlags, Configuration overrideConfig) {
+        if (!SHOW_SPLASH_SCREENS) {
             return null;
         }
         if (packageName == null) {
@@ -2809,7 +2809,7 @@
 
         try {
             Context context = mContext;
-            if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow " + packageName
+            if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName
                     + ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
                     + Integer.toHexString(theme));
             if (theme != context.getThemeResId() || labelRes != 0) {
@@ -2822,8 +2822,8 @@
             }
 
             if (overrideConfig != null && !overrideConfig.equals(EMPTY)) {
-                if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: creating context based"
-                        + " on overrideConfig" + overrideConfig + " for starting window");
+                if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based"
+                        + " on overrideConfig" + overrideConfig + " for splash screen");
                 final Context overrideContext = context.createConfigurationContext(overrideConfig);
                 overrideContext.setTheme(theme);
                 final TypedArray typedArray = overrideContext.obtainStyledAttributes(
@@ -2833,7 +2833,7 @@
                     // We want to use the windowBackground for the override context if it is
                     // available, otherwise we use the default one to make sure a themed starting
                     // window is displayed for the app.
-                    if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: apply overrideConfig"
+                    if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig"
                             + overrideConfig + " to starting window resId=" + resId);
                     context = overrideContext;
                 }
@@ -2895,19 +2895,19 @@
                 params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
             }
 
-            params.setTitle("Starting " + packageName);
+            params.setTitle("Splash Screen " + packageName);
 
             wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
             view = win.getDecorView();
 
-            if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Adding starting window for "
+            if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
                 + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));
 
             wm.addView(view, params);
 
             // Only return the view if it was successfully added to the
             // window manager... which we can tell by it having a parent.
-            return view.getParent() != null ? view : null;
+            return view.getParent() != null ? new SplashScreenSurface(view) : null;
         } catch (WindowManager.BadTokenException e) {
             // ignore
             Log.w(TAG, appToken + " already running, starting window not displayed. " +
@@ -2929,13 +2929,13 @@
 
     /** {@inheritDoc} */
     @Override
-    public void removeStartingWindow(IBinder appToken, View window) {
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing starting window for " + appToken + ": "
-                + window + " Callers=" + Debug.getCallers(4));
+    public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
+        if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + appToken + ": "
+                + surface + " Callers=" + Debug.getCallers(4));
 
-        if (window != null) {
+        if (surface != null) {
             WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-            wm.removeView(window);
+            wm.removeView(((SplashScreenSurface) surface).view);
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java
new file mode 100644
index 0000000..d421291
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.policy;
+
+import android.view.View;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+import com.android.internal.policy.DecorView;
+import com.android.internal.policy.PhoneWindow;
+
+/**
+ * Holds the contents of a splash screen starting window, i.e. the {@link DecorView} of a
+ * {@link PhoneWindow}. This is just a wrapper such that we can return it from
+ * {@link WindowManagerPolicy#addSplashScreen}.
+ */
+class SplashScreenSurface implements StartingSurface {
+
+    final View view;
+
+    SplashScreenSurface(View view) {
+        this.view = view;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 35004c2..cab39b5 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -27,21 +27,21 @@
 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.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.ADD_STARTING;
-
-import android.graphics.Bitmap;
-import android.os.Trace;
-import com.android.server.AttributeCache;
 
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
 import android.os.Binder;
 import android.os.Debug;
+import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
+import android.os.Looper;
+import android.os.Trace;
 import android.util.Slog;
 import android.view.IApplicationToken;
+import android.view.WindowManagerPolicy.StartingSurface;
 
+import com.android.server.AttributeCache;
 /**
  * Controller for the app window token container. This is created by activity manager to link
  * activity records to the app window token container they use in window manager.
@@ -52,6 +52,7 @@
         extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
 
     private final IApplicationToken mToken;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     private final Runnable mOnWindowsDrawn = () -> {
         if (mListener == null) {
@@ -80,6 +81,94 @@
         mListener.onWindowsGone();
     };
 
+    private final Runnable mAddStartingWindow = () -> {
+        final StartingData startingData;
+        final Configuration mergedOverrideConfiguration;
+
+        synchronized (mWindowMap) {
+            startingData = mContainer.startingData;
+            mergedOverrideConfiguration = mContainer.getMergedOverrideConfiguration();
+        }
+
+        if (startingData == null) {
+            // Animation has been canceled... do nothing.
+            return;
+        }
+
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
+                + this + ": pkg=" + mContainer.startingData.pkg);
+
+        StartingSurface contents = null;
+        try {
+            contents = mService.mPolicy.addSplashScreen(mContainer.token, startingData.pkg,
+                    startingData.theme, startingData.compatInfo, startingData.nonLocalizedLabel,
+                    startingData.labelRes, startingData.icon, startingData.logo,
+                    startingData.windowFlags, mergedOverrideConfiguration);
+        } catch (Exception e) {
+            Slog.w(TAG_WM, "Exception when adding starting window", e);
+        }
+        if (contents != null) {
+            boolean abort = false;
+
+            synchronized(mWindowMap) {
+                if (mContainer.removed || mContainer.startingData == null) {
+                    // If the window was successfully added, then
+                    // we need to remove it.
+                    if (mContainer.startingWindow != null) {
+                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+                                "Aborted starting " + mContainer
+                                        + ": removed=" + mContainer.removed
+                                        + " startingData=" + mContainer.startingData);
+                        mContainer.startingWindow = null;
+                        mContainer.startingData = null;
+                        abort = true;
+                    }
+                } else {
+                    mContainer.startingSurface = contents;
+                }
+                if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
+                        "Added starting " + mContainer
+                                + ": startingWindow="
+                                + mContainer.startingWindow + " startingView="
+                                + mContainer.startingSurface);
+            }
+
+            if (abort) {
+                try {
+                    mService.mPolicy.removeSplashScreen(mContainer.token, contents);
+                } catch (Exception e) {
+                    Slog.w(TAG_WM, "Exception when removing starting window", e);
+                }
+            }
+        }
+    };
+
+    private final Runnable mRemoveStartingWindow = () -> {
+        IBinder token = null;
+        StartingSurface contents = null;
+        synchronized (mWindowMap) {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
+                    + mContainer + ": startingWindow="
+                    + mContainer.startingWindow + " startingView="
+                    + mContainer.startingSurface);
+            if (mContainer.startingWindow != null) {
+                contents = mContainer.startingSurface;
+                token = mContainer.token;
+                mContainer.startingData = null;
+                mContainer.startingSurface = null;
+                mContainer.startingWindow = null;
+                mContainer.startingDisplayed = false;
+            }
+        }
+        if (contents != null) {
+            try {
+                mService.mPolicy.removeSplashScreen(token, contents);
+            } catch (Exception e) {
+                Slog.w(TAG_WM, "Exception when removing starting window", e);
+            }
+        }
+    };
+
     public AppWindowContainerController(IApplicationToken token,
             AppWindowContainerListener listener, int taskId, int index, int requestedOrientation,
             boolean fullscreen, boolean showForAllUsers, int configChanges,
@@ -393,19 +482,42 @@
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
             mContainer.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
                     labelRes, icon, logo, windowFlags);
-            final Message m = mService.mH.obtainMessage(ADD_STARTING, mContainer);
-            // Note: we really want to do sendMessageAtFrontOfQueue() because we
-            // want to process the message ASAP, before any other queued
-            // messages.
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
-            mService.mH.sendMessageAtFrontOfQueue(m);
+            scheduleAddStartingWindow();
         }
         return true;
     }
 
+    void scheduleAddStartingWindow() {
+
+        // Note: we really want to do sendMessageAtFrontOfQueue() because we
+        // want to process the message ASAP, before any other queued
+        // messages.
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
+        mHandler.postAtFrontOfQueue(mAddStartingWindow);
+    }
+
     public void removeStartingWindow() {
         synchronized (mWindowMap) {
-            mService.scheduleRemoveStartingWindowLocked(mContainer);
+            if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
+                // Already scheduled.
+                return;
+            }
+
+            if (mContainer.startingWindow == null) {
+                if (mContainer.startingData != null) {
+                    // Starting window has not been added yet, but it is scheduled to be added.
+                    // Go ahead and cancel the request.
+                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+                            "Clearing startingData for token=" + mContainer);
+                    mContainer.startingData = null;
+                }
+                return;
+            }
+
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1)
+                    + ": Schedule remove starting " + mContainer
+                    + " startingWindow=" + mContainer.startingWindow);
+            mHandler.post(mRemoveStartingWindow);
         }
     }
 
@@ -508,15 +620,15 @@
 
 
     void reportWindowsDrawn() {
-        mService.mH.post(mOnWindowsDrawn);
+        mHandler.post(mOnWindowsDrawn);
     }
 
     void reportWindowsVisible() {
-        mService.mH.post(mOnWindowsVisible);
+        mHandler.post(mOnWindowsVisible);
     }
 
     void reportWindowsGone() {
-        mService.mH.post(mOnWindowsGone);
+        mHandler.post(mOnWindowsGone);
     }
 
     /** Calls directly into activity manager so window manager lock shouldn't held. */
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 0a48758..f4fa220 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -29,9 +29,9 @@
 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_ADD_REMOVE;
 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;
@@ -47,22 +47,21 @@
 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.internal.util.ToBooleanFunction;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.wm.WindowManagerService.H;
-
 import android.annotation.NonNull;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Debug;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.SystemClock;
 import android.util.Slog;
 import android.view.IApplicationToken;
-import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+import com.android.internal.util.ToBooleanFunction;
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
@@ -138,7 +137,7 @@
     // Information about an application starting window if displayed.
     StartingData startingData;
     WindowState startingWindow;
-    View startingView;
+    StartingSurface startingSurface;
     boolean startingDisplayed;
     boolean startingMoved;
     boolean firstWindowDrawn;
@@ -213,8 +212,9 @@
             // it from behind the starting window, so there is no need for it to also be doing its
             // own stuff.
             winAnimator.clearAnimation();
-            winAnimator.mService.mFinishedStarting.add(this);
-            winAnimator.mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+            if (getController() != null) {
+                getController().removeStartingWindow();
+            }
         }
         updateReportedVisibilityLocked();
     }
@@ -439,8 +439,6 @@
     }
 
     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, mVoiceInteraction);
@@ -461,6 +459,10 @@
         if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                 + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
 
+        if (startingData != null && getController() != null) {
+            getController().removeStartingWindow();
+        }
+
         final TaskStack stack = mTask.mStack;
         if (delayed && !isEmpty()) {
             // set the token aside because it has an active animation to be finished
@@ -477,9 +479,6 @@
         }
 
         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);
@@ -491,9 +490,6 @@
         if (!delayed) {
             updateReportedVisibilityLocked();
         }
-
-        // Will only remove if startingToken non null.
-        mService.scheduleRemoveStartingWindowLocked(startingToken);
     }
 
     void clearAnimatingFlags() {
@@ -557,7 +553,9 @@
         mAppStopped = true;
         destroySurfaces();
         // Remove any starting window that was added for this app if they are still around.
-        mTask.mService.scheduleRemoveStartingWindowLocked(this);
+        if (getController() != null) {
+            getController().removeStartingWindow();
+        }
     }
 
     /**
@@ -667,16 +665,20 @@
         // TODO: Something smells about the code below...Is there a better way?
         if (startingWindow == win) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
-            mService.scheduleRemoveStartingWindowLocked(this);
+            if (getController() != null) {
+                getController().removeStartingWindow();
+            }
         } else if (mChildren.size() == 0 && startingData != null) {
             // If this is the last window and we had requested a starting transition window,
             // well there is no point now.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow");
             startingData = null;
-        } else if (mChildren.size() == 1 && startingView != null) {
+        } else if (mChildren.size() == 1 && startingSurface != null) {
             // If this is the last window except for a starting transition window,
             // we need to get rid of the starting transition.
-            mService.scheduleRemoveStartingWindowLocked(this);
+            if (getController() != null) {
+                getController().removeStartingWindow();
+            }
         }
     }
 
@@ -1015,7 +1017,7 @@
         }
 
         final WindowState tStartingWindow = fromToken.startingWindow;
-        if (tStartingWindow != null && fromToken.startingView != null) {
+        if (tStartingWindow != null && fromToken.startingSurface != null) {
             // In this case, the starting icon has already been displayed, so start
             // letting windows get shown immediately without any more transitions.
             mService.mSkipAppTransitionAnimation = true;
@@ -1027,13 +1029,13 @@
 
             // Transfer the starting window over to the new token.
             startingData = fromToken.startingData;
-            startingView = fromToken.startingView;
+            startingSurface = fromToken.startingSurface;
             startingDisplayed = fromToken.startingDisplayed;
             fromToken.startingDisplayed = false;
             startingWindow = tStartingWindow;
             reportedVisible = fromToken.reportedVisible;
             fromToken.startingData = null;
-            fromToken.startingView = null;
+            fromToken.startingSurface = null;
             fromToken.startingWindow = null;
             fromToken.startingMoved = true;
             tStartingWindow.mToken = this;
@@ -1080,10 +1082,9 @@
             startingData = fromToken.startingData;
             fromToken.startingData = null;
             fromToken.startingMoved = true;
-            final Message m = mService.mH.obtainMessage(H.ADD_STARTING, this);
-            // Note: we really want to do sendMessageAtFrontOfQueue() because we want to process the
-            // message ASAP, before any other queued messages.
-            mService.mH.sendMessageAtFrontOfQueue(m);
+            if (getController() != null) {
+                getController().scheduleAddStartingWindow();
+            }
             return true;
         }
 
@@ -1421,10 +1422,10 @@
                     pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
                     pw.print(" mIsExiting="); pw.println(mIsExiting);
         }
-        if (startingWindow != null || startingView != null
+        if (startingWindow != null || startingSurface != null
                 || startingDisplayed || startingMoved) {
             pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
-                    pw.print(" startingView="); pw.print(startingView);
+                    pw.print(" startingSurface="); pw.print(startingSurface);
                     pw.print(" startingDisplayed="); pw.print(startingDisplayed);
                     pw.print(" startingMoved="); pw.println(startingMoved);
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index dc06d12..775d8a0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -498,7 +498,10 @@
                     if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
                             "RECOVER DESTROY", false);
                     winAnimator.destroySurface();
-                    mService.scheduleRemoveStartingWindowLocked(winAnimator.mWin.mAppToken);
+                    if (winAnimator.mWin.mAppToken != null
+                            && winAnimator.mWin.mAppToken.getController() != null) {
+                        winAnimator.mWin.mAppToken.getController().removeStartingWindow();
+                    }
                 }
 
                 try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 38cb543..4e25935 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2851,33 +2851,6 @@
         }
     }
 
-    void scheduleRemoveStartingWindowLocked(AppWindowToken wtoken) {
-        if (wtoken == null) {
-            return;
-        }
-        if (mH.hasMessages(H.REMOVE_STARTING, wtoken)) {
-            // Already scheduled.
-            return;
-        }
-
-        if (wtoken.startingWindow == null) {
-            if (wtoken.startingData != null) {
-                // Starting window has not been added yet, but it is scheduled to be added.
-                // Go ahead and cancel the request.
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                        "Clearing startingData for token=" + wtoken);
-                wtoken.startingData = null;
-            }
-            return;
-        }
-
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1) +
-                ": Schedule remove starting " + wtoken + (wtoken != null ?
-                " startingWindow=" + wtoken.startingWindow : ""));
-        Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken);
-        mH.sendMessage(m);
-    }
-
     public void moveTaskToTop(int taskId) {
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -5578,9 +5551,6 @@
         public static final int REPORT_FOCUS_CHANGE = 2;
         public static final int REPORT_LOSING_FOCUS = 3;
         public static final int DO_TRAVERSAL = 4;
-        public static final int ADD_STARTING = 5;
-        public static final int REMOVE_STARTING = 6;
-        public static final int FINISHED_STARTING = 7;
         public static final int WINDOW_FREEZE_TIMEOUT = 11;
 
         public static final int APP_TRANSITION_TIMEOUT = 13;
@@ -5722,126 +5692,6 @@
                     }
                 } break;
 
-                case ADD_STARTING: {
-                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
-                    final StartingData sd = wtoken.startingData;
-
-                    if (sd == null) {
-                        // Animation has been canceled... do nothing.
-                        return;
-                    }
-
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
-                            + wtoken + ": pkg=" + sd.pkg);
-
-                    View view = null;
-                    try {
-                        view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
-                            sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
-                            sd.windowFlags, wtoken.getMergedOverrideConfiguration());
-                    } catch (Exception e) {
-                        Slog.w(TAG_WM, "Exception when adding starting window", e);
-                    }
-
-                    if (view != null) {
-                        boolean abort = false;
-
-                        synchronized(mWindowMap) {
-                            if (wtoken.removed || wtoken.startingData == null) {
-                                // If the window was successfully added, then
-                                // we need to remove it.
-                                if (wtoken.startingWindow != null) {
-                                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                                            "Aborted starting " + wtoken
-                                            + ": removed=" + wtoken.removed
-                                            + " startingData=" + wtoken.startingData);
-                                    wtoken.startingWindow = null;
-                                    wtoken.startingData = null;
-                                    abort = true;
-                                }
-                            } else {
-                                wtoken.startingView = view;
-                            }
-                            if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
-                                    "Added starting " + wtoken
-                                    + ": startingWindow="
-                                    + wtoken.startingWindow + " startingView="
-                                    + wtoken.startingView);
-                        }
-
-                        if (abort) {
-                            try {
-                                mPolicy.removeStartingWindow(wtoken.token, view);
-                            } catch (Exception e) {
-                                Slog.w(TAG_WM, "Exception when removing starting window", e);
-                            }
-                        }
-                    }
-                } break;
-
-                case REMOVE_STARTING: {
-                    final AppWindowToken wtoken = (AppWindowToken)msg.obj;
-                    IBinder token = null;
-                    View view = null;
-                    synchronized (mWindowMap) {
-                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
-                                + wtoken + ": startingWindow="
-                                + wtoken.startingWindow + " startingView="
-                                + wtoken.startingView);
-                        if (wtoken.startingWindow != null) {
-                            view = wtoken.startingView;
-                            token = wtoken.token;
-                            wtoken.startingData = null;
-                            wtoken.startingView = null;
-                            wtoken.startingWindow = null;
-                            wtoken.startingDisplayed = false;
-                        }
-                    }
-                    if (view != null) {
-                        try {
-                            mPolicy.removeStartingWindow(token, view);
-                        } catch (Exception e) {
-                            Slog.w(TAG_WM, "Exception when removing starting window", e);
-                        }
-                    }
-                } break;
-
-                case FINISHED_STARTING: {
-                    IBinder token = null;
-                    View view = null;
-                    while (true) {
-                        synchronized (mWindowMap) {
-                            final int N = mFinishedStarting.size();
-                            if (N <= 0) {
-                                break;
-                            }
-                            AppWindowToken wtoken = mFinishedStarting.remove(N-1);
-
-                            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                                    "Finished starting " + wtoken
-                                    + ": startingWindow=" + wtoken.startingWindow
-                                    + " startingView=" + wtoken.startingView);
-
-                            if (wtoken.startingWindow == null) {
-                                continue;
-                            }
-
-                            view = wtoken.startingView;
-                            token = wtoken.token;
-                            wtoken.startingData = null;
-                            wtoken.startingView = null;
-                            wtoken.startingWindow = null;
-                            wtoken.startingDisplayed = false;
-                        }
-
-                        try {
-                            mPolicy.removeStartingWindow(token, view);
-                        } catch (Exception e) {
-                            Slog.w(TAG_WM, "Exception when removing starting window", e);
-                        }
-                    }
-                } break;
-
                 case WINDOW_FREEZE_TIMEOUT: {
                     // TODO(multidisplay): Can non-default displays rotate?
                     synchronized (mWindowMap) {
@@ -7348,21 +7198,6 @@
     private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) {
         pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)");
         mRoot.dumpTokens(pw, dumpAll);
-        if (!mFinishedStarting.isEmpty()) {
-            pw.println();
-            pw.println("  Finishing start of application tokens:");
-            for (int i=mFinishedStarting.size()-1; i>=0; i--) {
-                WindowToken token = mFinishedStarting.get(i);
-                pw.print("  Finished Starting #"); pw.print(i);
-                        pw.print(' '); pw.print(token);
-                if (dumpAll) {
-                    pw.println(':');
-                    token.dump(pw, "    ");
-                } else {
-                    pw.println();
-                }
-            }
-        }
         if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) {
             pw.println();
             if (mOpeningApps.size() > 0) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e5ed18d..19ef44c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -461,16 +461,7 @@
         mStackClip = STACK_CLIP_BEFORE_ANIM;
         mWin.checkPolicyVisibilityChange();
         mTransformation.clear();
-        if (mDrawState == HAS_DRAWN
-                && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
-                && mWin.mAppToken != null
-                && mWin.mAppToken.firstWindowDrawn
-                && mWin.mAppToken.startingData != null) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting "
-                    + mWin.mToken + ": first real window done animating");
-            mService.mFinishedStarting.add(mWin.mAppToken);
-            mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
-        } else if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
+        if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
             // Upon completion of a not-visible to visible status bar animation a relayout is
             // required.
             if (displayContent != null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 1aabd5e..4df1001 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -441,8 +441,9 @@
             wtoken.deferClearAllDrawn = false;
             // Ensure that apps that are mid-starting are also scheduled to have their
             // starting windows removed after the animation is complete
-            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
-                mService.scheduleRemoveStartingWindowLocked(wtoken);
+            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
+                    && wtoken.getController() != null) {
+                wtoken.getController().removeStartingWindow();
             }
             mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 1853a65..c4fd722 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -16,29 +16,6 @@
 
 package com.android.server.wm;
 
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.policy.IShortcutService;
-import com.android.server.input.InputManagerService;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.Animation;
-
-import java.io.PrintWriter;
-
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
@@ -62,8 +39,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
@@ -81,9 +58,30 @@
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-
 import static org.mockito.Mockito.mock;
 
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IShortcutService;
+import com.android.server.input.InputManagerService;
+
+import java.io.PrintWriter;
+
 class TestWindowManagerPolicy implements WindowManagerPolicy {
     private static final String TAG = "TestWindowManagerPolicy";
 
@@ -308,14 +306,14 @@
     }
 
     @Override
-    public View addStartingWindow(IBinder appToken, String packageName, int theme,
+    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
             CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
             int logo, int windowFlags, Configuration overrideConfig) {
         return null;
     }
 
     @Override
-    public void removeStartingWindow(IBinder appToken, View window) {
+    public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
 
     }