Keep IME above starting window.

While app window is animating-in, IME window is child of aboveAppContainers.
If app requests IME on activity resume, IME will start animating-in
above Snapshot during app window animation.
However when app window animation finishes, IME's parent is no
longer aboveAppContainers, thereby making snapshot occlude
IME briefly. This results in flashing.

The fix is to keep IME in aboveAppContainers as long as startingWindow
is not removed from hierarchy.

Bug: 137534359
Test: Manual and atest ZOrderingTests AppWindowTokenTests
Change-Id: I78c939e8d1f23144162ba9eb8d834150fdd08ae8
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 6c577e1..f1a9e60 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -206,6 +206,7 @@
     boolean removed;
 
     // Information about an application starting window if displayed.
+    // Note: these are de-referenced before the starting window animates away.
     StartingData mStartingData;
     WindowState startingWindow;
     StartingSurface startingSurface;
@@ -1243,6 +1244,21 @@
         return true;
     }
 
+    /**
+     * @return {@code true} if starting window is in app's hierarchy.
+     */
+    boolean hasStartingWindow() {
+        if (startingDisplayed || mStartingData != null) {
+            return true;
+        }
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            if (getChildAt(i).mAttrs.type == TYPE_APPLICATION_STARTING) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     void addWindow(WindowState w) {
         super.addWindow(w);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a1b8760..885095f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4845,9 +4845,13 @@
         //
         // In the case where we have no IME target we assign it where it's base layer would
         // place it in the AboveAppWindowContainers.
-        if (imeTarget != null && !(imeTarget.inSplitScreenWindowingMode()
-                || imeTarget.mToken.isAppAnimating())
-                && (imeTarget.getSurfaceControl() != null)) {
+        //
+        // Keep IME window in mAboveAppWindowsContainers as long as app's starting window exists
+        // so it get's layered above the starting window.
+        if (imeTarget != null
+                && !(imeTarget.mAppToken != null && imeTarget.mAppToken.hasStartingWindow())
+                && (!(imeTarget.inSplitScreenWindowingMode() || imeTarget.mToken.isAppAnimating())
+                && (imeTarget.getSurfaceControl() != null))) {
             mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
                     // TODO: We need to use an extra level on the app surface to ensure
                     // this is always above SurfaceView but always below attached window.
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 7f35dac..d9566a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -504,6 +504,21 @@
         assertEquals(stackBounds, mToken.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
     }
 
+    @Test
+    public void testHasStartingWindow() {
+        final WindowManager.LayoutParams attrs =
+                new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING);
+        final WindowTestUtils.TestWindowState startingWindow = createWindowState(attrs, mToken);
+        mToken.startingDisplayed = true;
+        mToken.addWindow(startingWindow);
+        assertTrue("Starting window should be present", mToken.hasStartingWindow());
+        mToken.startingDisplayed = false;
+        assertTrue("Starting window should be present", mToken.hasStartingWindow());
+
+        mToken.removeChild(startingWindow);
+        assertFalse("Starting window should not be present", mToken.hasStartingWindow());
+    }
+
     private void assertHasStartingWindow(AppWindowToken atoken) {
         assertNotNull(atoken.startingSurface);
         assertNotNull(atoken.mStartingData);