Starting window tests, yay!

Test: bit FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
Fixes: 34364463
Fixes: 34361417
Change-Id: Ie1b8debc894e5cad8fe517912a1991a38661dfaa
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 0436139..27e0f29 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -56,7 +56,7 @@
     private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
 
     private final IApplicationToken mToken;
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Handler mHandler;
 
     private final Runnable mOnWindowsDrawn = () -> {
         if (mListener == null) {
@@ -186,6 +186,7 @@
             int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
             WindowManagerService service) {
         super(listener, service);
+        mHandler = new Handler(service.mH.getLooper());
         mToken = token;
         synchronized(mWindowMap) {
             AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 26accc3..2af4163 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -18,12 +18,10 @@
 
 import org.junit.Test;
 
-import android.os.Binder;
-import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.IApplicationToken;
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -74,6 +72,67 @@
         controller.removeContainer(sDisplayContent.getDisplayId());
         // Assert orientation is unspecified to after container is removed.
         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
+
+        // Reset display frozen state
+        sWm.mDisplayFrozen = false;
+    }
+
+    private void assertHasStartingWindow(AppWindowToken atoken) {
+        assertNotNull(atoken.startingSurface);
+        assertNotNull(atoken.startingData);
+        assertNotNull(atoken.startingWindow);
+    }
+
+    private void assertNoStartingWindow(AppWindowToken atoken) {
+        assertNull(atoken.startingSurface);
+        assertNull(atoken.startingWindow);
+        assertNull(atoken.startingData);
+    }
+
+    @Test
+    public void testCreateRemoveStartingWindow() throws Exception {
+        final TestAppWindowContainerController controller = createAppWindowController();
+        controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
+        waitUntilHandlerIdle();
+        final AppWindowToken atoken = controller.getAppWindowToken();
+        assertHasStartingWindow(atoken);
+        controller.removeStartingWindow();
+        waitUntilHandlerIdle();
+        assertNoStartingWindow(atoken);
+    }
+
+    @Test
+    public void testTransferStartingWindow() throws Exception {
+        final TestAppWindowContainerController controller1 = createAppWindowController();
+        final TestAppWindowContainerController controller2 = createAppWindowController();
+        controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
+        waitUntilHandlerIdle();
+        controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
+                true, true, false);
+        waitUntilHandlerIdle();
+        assertNoStartingWindow(controller1.getAppWindowToken());
+        assertHasStartingWindow(controller2.getAppWindowToken());
+    }
+
+    @Test
+    public void testTransferStartingWindowWhileCreating() throws Exception {
+        final TestAppWindowContainerController controller1 = createAppWindowController();
+        final TestAppWindowContainerController controller2 = createAppWindowController();
+        sPolicy.setRunnableWhenAddingSplashScreen(() -> {
+
+            // Surprise, ...! Transfer window in the middle of the creation flow.
+            controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
+                    true, true, false);
+        });
+        controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
+        waitUntilHandlerIdle();
+        assertNoStartingWindow(controller1.getAppWindowToken());
+        assertHasStartingWindow(controller2.getAppWindowToken());
     }
 
     private TestAppWindowContainerController createAppWindowController() {
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 269b719..ec429a0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -24,6 +24,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
@@ -74,6 +75,7 @@
 import android.view.IWindowManager;
 import android.view.KeyEvent;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy;
 import android.view.animation.Animation;
 import android.os.PowerManagerInternal;
@@ -92,6 +94,8 @@
 
     int rotationToReport = 0;
 
+    private Runnable mRunnableWhenAddingSplashScreen;
+
     static synchronized WindowManagerService getWindowManagerService(Context context) {
         if (sWm == null) {
             // We only want to do this once for the test process as we don't want WM to try to
@@ -318,11 +322,36 @@
         return false;
     }
 
+    /**
+     * Sets a runnable to run when adding a splash screen which gets executed after the window has
+     * been added but before returning the surface.
+     */
+    void setRunnableWhenAddingSplashScreen(Runnable r) {
+        mRunnableWhenAddingSplashScreen = r;
+    }
+
     @Override
     public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
             CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
             int logo, int windowFlags, Configuration overrideConfig, int displayId) {
-        return null;
+        final com.android.server.wm.WindowState window;
+        final AppWindowToken atoken;
+        synchronized (sWm.mWindowMap) {
+            atoken = WindowTestsBase.sDisplayContent.getAppWindowToken(appToken);
+            window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
+                    "Starting window");
+            atoken.startingWindow = window;
+        }
+        if (mRunnableWhenAddingSplashScreen != null) {
+            mRunnableWhenAddingSplashScreen.run();
+            mRunnableWhenAddingSplashScreen = null;
+        }
+        return () -> {
+            synchronized (sWm.mWindowMap) {
+                atoken.removeChild(window);
+                atoken.startingWindow = null;
+            }
+        };
     }
 
     @Override
@@ -482,7 +511,7 @@
 
     @Override
     public boolean isScreenOn() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 6129198..772bfb4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -45,20 +45,18 @@
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
-public class UnknownAppVisibilityControllerTest {
+public class UnknownAppVisibilityControllerTest extends WindowTestsBase {
 
     private WindowManagerService mWm;
-    private @Mock ActivityManagerInternal mAm;
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+        super.setUp();
         final Context context = InstrumentationRegistry.getTargetContext();
-        LocalServices.addService(ActivityManagerInternal.class, mAm);
         doAnswer((InvocationOnMock invocationOnMock) -> {
             invocationOnMock.getArgumentAt(0, Runnable.class).run();
             return null;
-        }).when(mAm).notifyKeyguardFlagsChanged(any());
+        }).when(sMockAm).notifyKeyguardFlagsChanged(any());
         mWm = TestWindowManagerPolicy.getWindowManagerService(context);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 813d263..7c25e43 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -16,15 +16,17 @@
 
 package com.android.server.wm;
 
+import android.app.ActivityManagerInternal;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.view.IApplicationToken;
 import org.junit.Assert;
 import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 
-import android.app.ActivityManager;
-import android.app.ActivityManager.TaskSnapshot;
 import android.content.Context;
 import android.os.IBinder;
 import android.support.test.InstrumentationRegistry;
@@ -50,13 +52,17 @@
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static org.mockito.Mockito.mock;
 
+import com.android.server.AttributeCache;
+import com.android.server.LocalServices;
+
 /**
  * Common base class for window manager unit test classes.
  */
 class WindowTestsBase {
     static WindowManagerService sWm = null;
-    private final IWindow mIWindow = new TestIWindow();
-    private final Session mMockSession = mock(Session.class);
+    static TestWindowManagerPolicy sPolicy = null;
+    private final static IWindow sIWindow = new TestIWindow();
+    private final static Session sMockSession = mock(Session.class);
     static int sNextStackId = FIRST_DYNAMIC_STACK_ID;
     private static int sNextTaskId = 0;
 
@@ -72,19 +78,27 @@
     static WindowState sAppWindow;
     static WindowState sChildAppWindowAbove;
     static WindowState sChildAppWindowBelow;
+    static @Mock ActivityManagerInternal sMockAm;
 
     @Before
     public void setUp() throws Exception {
         if (sOneTimeSetupDone) {
+            Mockito.reset(sMockAm);
             return;
         }
         sOneTimeSetupDone = true;
+        MockitoAnnotations.initMocks(this);
         final Context context = InstrumentationRegistry.getTargetContext();
+        LocalServices.addService(ActivityManagerInternal.class, sMockAm);
+        AttributeCache.init(context);
         sWm = TestWindowManagerPolicy.getWindowManagerService(context);
+        sPolicy = (TestWindowManagerPolicy) sWm.mPolicy;
         sLayersController = new WindowLayersController(sWm);
         sDisplayContent = new DisplayContent(context.getDisplay(), sWm, sLayersController,
                 new WallpaperController(sWm));
         sWm.mRoot.addChild(sDisplayContent, 0);
+        sWm.mDisplayEnabled = true;
+        sWm.mDisplayReady = true;
 
         // Set-up some common windows.
         sWallpaperWindow = createWindow(null, TYPE_WALLPAPER, sDisplayContent, "wallpaperWindow");
@@ -108,7 +122,14 @@
         Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
     }
 
-    private WindowToken createWindowToken(DisplayContent dc, int type) {
+    /**
+     * Waits until the main handler for WM has processed all messages.
+     */
+    void waitUntilHandlerIdle() {
+        sWm.mH.runWithScissors(() -> { }, 0);
+    }
+
+    private static WindowToken createWindowToken(DisplayContent dc, int type) {
         if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
             return new TestWindowToken(type, dc);
         }
@@ -120,7 +141,7 @@
         return token;
     }
 
-    WindowState createWindow(WindowState parent, int type, String name) {
+    static WindowState createWindow(WindowState parent, int type, String name) {
         return (parent == null)
                 ? createWindow(parent, type, sDisplayContent, name)
                 : createWindow(parent, type, parent.mToken, name);
@@ -132,16 +153,16 @@
         return createWindow(null, type, token, name);
     }
 
-    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
+    static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
         final WindowToken token = createWindowToken(dc, type);
         return createWindow(parent, type, token, name);
     }
 
-    WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
+    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
         attrs.setTitle(name);
 
-        final WindowState w = new WindowState(sWm, mMockSession, mIWindow, token, parent, OP_NONE,
+        final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
                 0, attrs, 0, 0);
         // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
         // adding it to the token...
@@ -150,14 +171,14 @@
     }
 
     /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
-    TaskStack createTaskStackOnDisplay(DisplayContent dc) {
+    static TaskStack createTaskStackOnDisplay(DisplayContent dc) {
         final int stackId = sNextStackId++;
         dc.addStackToDisplay(stackId, true);
         return sWm.mStackIdToStack.get(stackId);
     }
 
     /**Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
-    Task createTaskInStack(TaskStack stack, int userId) {
+    static Task createTaskInStack(TaskStack stack, int userId) {
         final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0,
                 false, null);
         stack.addTask(newTask, POSITION_TOP);
@@ -165,7 +186,7 @@
     }
 
     /* Used so we can gain access to some protected members of the {@link WindowToken} class */
-    class TestWindowToken extends WindowToken {
+    static class TestWindowToken extends WindowToken {
 
         TestWindowToken(int type, DisplayContent dc) {
             this(type, dc, false /* persistOnEmpty */);
@@ -185,7 +206,7 @@
     }
 
     /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
-    class TestAppWindowToken extends AppWindowToken {
+    static class TestAppWindowToken extends AppWindowToken {
 
         TestAppWindowToken(DisplayContent dc) {
             super(sWm, null, false, dc);
@@ -279,6 +300,10 @@
                     0 /* inputDispatchingTimeoutNanos */, sWm);
             mToken = token;
         }
+
+        AppWindowToken getAppWindowToken() {
+            return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder());
+        }
     }
 
     class TestIApplicationToken implements IApplicationToken {
@@ -295,7 +320,7 @@
         boolean resizeReported;
 
         TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) {
-            super(sWm, mMockSession, mIWindow, token, null, OP_NONE, 0, attrs, 0, 0);
+            super(sWm, sMockSession, sIWindow, token, null, OP_NONE, 0, attrs, 0, 0);
         }
 
         @Override