From 4ede3e0d0a9e76c701db19e073d2d1ff487d2a64 Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Thu, 12 Jan 2017 11:52:31 -0800 Subject: [PATCH] Add unit tests for 180 degree rotation These tests if an app window token reports resize when device is rotated from landscape to seascape. There is also some additional plumbing to be able to perform a rotation in unit test. Also dynamic stacks are now allowed to influence the orientation of the device. Test: bit FrameworksServicesTests:com.android.server.wm.AppWindowTokenTests Bug: 33607506 Change-Id: I7b23e2de48d56c9fe485eae6a165378dbbbd08bb --- core/java/android/app/ActivityManager.java | 6 +- .../server/am/ActivityStackSupervisor.java | 1 - .../android/server/am/ActivityStarter.java | 6 +- .../server/wm/RootWindowContainer.java | 2 +- .../server/wm/WindowManagerService.java | 7 +- .../tests/servicestests/AndroidManifest.xml | 1 + .../server/wm/AppWindowTokenTests.java | 66 +++++++++++++++++++ .../server/wm/TestWindowManagerPolicy.java | 15 ++++- .../android/server/wm/WindowTestsBase.java | 34 +++++++++- 9 files changed, 129 insertions(+), 9 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 3170d0d97af08..f1f6e7b09767e 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -628,6 +628,10 @@ public class ActivityManager { return stackId >= FIRST_STATIC_STACK_ID && stackId <= LAST_STATIC_STACK_ID; } + public static boolean isDynamicStack(int stackId) { + return stackId >= FIRST_DYNAMIC_STACK_ID; + } + /** * Returns true if the activities contained in the input stack display a shadow around * their border. @@ -825,7 +829,7 @@ public class ActivityManager { /** Returns true if the input stack and its content can affect the device orientation. */ public static boolean canSpecifyOrientation(int stackId) { return stackId == HOME_STACK_ID || stackId == RECENTS_STACK_ID - || stackId == FULLSCREEN_WORKSPACE_STACK_ID; + || stackId == FULLSCREEN_WORKSPACE_STACK_ID || isDynamicStack(stackId); } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8cf0708e22aa1..af52bba2cffe7 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1984,7 +1984,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) { return null; } - // TODO(multi-display): Allow creating stacks on secondary displays. return createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop); } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index f401863acf421..be268f404fdac 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -32,7 +32,7 @@ import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.RECENTS_STACK_ID; -import static android.app.ActivityManager.StackId.isStaticStack; +import static android.app.ActivityManager.StackId.isDynamicStack; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -1923,7 +1923,7 @@ class ActivityStarter { final boolean canUseFocusedStack = focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID || (focusedStackId == DOCKED_STACK_ID && r.canGoInDockedStack()) || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeableOrForced()) - || !isStaticStack(focusedStackId); + || isDynamicStack(focusedStackId); if (canUseFocusedStack && (!newTask || mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, @@ -1935,7 +1935,7 @@ class ActivityStarter { final ArrayList homeDisplayStacks = mSupervisor.mHomeStack.mStacks; for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) { stack = homeDisplayStacks.get(stackNdx); - if (!ActivityManager.StackId.isStaticStack(stack.mStackId)) { + if (isDynamicStack(stack.mStackId)) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: Setting focused stack=" + stack); return stack; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6ac172b26e241..0cc6c701a93ef 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -228,7 +228,7 @@ class RootWindowContainer extends WindowContainer { mService.configureDisplayPolicyLocked(dc); // TODO(multi-display): Create an input channel for each display with touch capability. - if (displayId == DEFAULT_DISPLAY) { + if (displayId == DEFAULT_DISPLAY && mService.canDispatchPointerEvents()) { dc.mTapDetector = new TaskTapPointerEventListener( mService, dc); mService.registerPointerEventListener(dc.mTapDetector); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 195d4c31f5665..655e3b4adb301 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3404,6 +3404,11 @@ public class WindowManagerService extends IWindowManager.Stub mPointerEventDispatcher.unregisterInputEventListener(listener); } + /** Check if the service is set to dispatch pointer events. */ + boolean canDispatchPointerEvents() { + return mPointerEventDispatcher != null; + } + // Called by window manager policy. Not exposed externally. @Override public int getLidState() { @@ -4983,7 +4988,7 @@ public class WindowManagerService extends IWindowManager.Stub int keyboardPresence = 0; int navigationPresence = 0; final InputDevice[] devices = mInputManager.getInputDevices(); - final int len = devices.length; + final int len = devices != null ? devices.length : 0; for (int i = 0; i < len; i++) { InputDevice device = devices[i]; if (!device.isVirtual()) { diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 1c92e452d03cb..1393615b5623b 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -46,6 +46,7 @@ + diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 06837d38d69c0..1d9875f308e99 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -22,7 +22,11 @@ import org.junit.runner.RunWith; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.view.Surface; +import android.view.WindowManager; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -83,4 +87,66 @@ public class AppWindowTokenTests extends WindowTestsBase { final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2"); assertEquals(window2, token.findMainWindow()); } + + @Test + public void testLandscapeSeascapeRotationByApp() throws Exception { + // Some plumbing to get the service ready for rotation updates. + sWm.mDisplayReady = true; + sWm.mDisplayEnabled = true; + + // Create an app window with token on a display. + final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final TestAppWindowToken appWindowToken = new TestAppWindowToken(sDisplayContent); + task.addChild(appWindowToken, 0); + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( + TYPE_BASE_APPLICATION); + attrs.setTitle("AppWindow"); + final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken); + appWindowToken.addWindow(appWindow); + + // Set initial orientation and update. + appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + sWm.updateOrientationFromAppTokens(sDisplayContent.getOverrideConfiguration(), null, + sDisplayContent.getDisplayId()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, sWm.mLastOrientation); + appWindow.resizeReported = false; + + // Update the orientation to perform 180 degree rotation and check that resize was reported. + appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + sWm.updateOrientationFromAppTokens(sDisplayContent.getOverrideConfiguration(), null, + sDisplayContent.getDisplayId()); + sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); + assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, sWm.mLastOrientation); + assertTrue(appWindow.resizeReported); + } + + @Test + public void testLandscapeSeascapeRotationByPolicy() throws Exception { + // Some plumbing to get the service ready for rotation updates. + sWm.mDisplayReady = true; + sWm.mDisplayEnabled = true; + + // Create an app window with token on a display. + final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final TestAppWindowToken appWindowToken = new TestAppWindowToken(sDisplayContent); + task.addChild(appWindowToken, 0); + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( + TYPE_BASE_APPLICATION); + attrs.setTitle("AppWindow"); + final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken); + appWindowToken.addWindow(appWindow); + + // Set initial orientation and update. + ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = Surface.ROTATION_90; + sWm.updateRotation(false, false); + appWindow.resizeReported = false; + + // Update the rotation to perform 180 degree rotation and check that resize was reported. + ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = Surface.ROTATION_270; + sWm.updateRotation(false, false); + sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); + assertTrue(appWindow.resizeReported); + } } 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 12e7a15d2cbb3..c0c8fb0494156 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -65,6 +65,7 @@ import android.content.Context; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Rect; +import android.hardware.display.DisplayManagerInternal; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -75,10 +76,12 @@ import android.view.KeyEvent; import android.view.WindowManager; import android.view.WindowManagerPolicy; import android.view.animation.Animation; +import android.os.PowerManagerInternal; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; import com.android.server.input.InputManagerService; +import com.android.server.LocalServices; import java.io.PrintWriter; @@ -87,10 +90,20 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { private static WindowManagerService sWm = null; + int rotationToReport = 0; + 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 // register a bunch of local services again. + if (LocalServices.getService(DisplayManagerInternal.class) == null) { + LocalServices.addService(DisplayManagerInternal.class, + mock(DisplayManagerInternal.class)); + } + if (LocalServices.getService(PowerManagerInternal.class) == null) { + LocalServices.addService(PowerManagerInternal.class, + mock(PowerManagerInternal.class)); + } sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false, false, new TestWindowManagerPolicy()); } @@ -543,7 +556,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public int rotationForOrientationLw(int orientation, int lastRotation) { - return 0; + return rotationToReport; } @Override 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 466bd6700f16d..fa4b79ffda6f7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -177,7 +177,7 @@ class WindowTestsBase { } } - /* Used so we can gain access to some protected members of the {@link AppWindowToken} class */ + /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ class TestAppWindowToken extends AppWindowToken { TestAppWindowToken(DisplayContent dc) { @@ -213,4 +213,36 @@ class WindowTestsBase { false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */); } } + + /** Used to track resize reports. */ + class TestWindowState extends WindowState { + boolean resizeReported; + + TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) { + super(sWm, mMockSession, mIWindow, token, null, OP_NONE, 0, attrs, 0, 0); + } + + @Override + void reportResized() { + super.reportResized(); + resizeReported = true; + } + + @Override + public boolean isGoneForLayoutLw() { + return false; + } + + @Override + void updateResizingWindowIfNeeded() { + // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive + // the system that it can actually update the window. + boolean hadSurface = mHasSurface; + mHasSurface = true; + + super.updateResizingWindowIfNeeded(); + + mHasSurface = hadSurface; + } + } }