OSDN Git Service

Add unit tests for 180 degree rotation
authorAndrii Kulian <akulian@google.com>
Thu, 12 Jan 2017 19:52:31 +0000 (11:52 -0800)
committerAndrii Kulian <akulian@google.com>
Thu, 12 Jan 2017 20:29:54 +0000 (12:29 -0800)
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
services/core/java/com/android/server/am/ActivityStackSupervisor.java
services/core/java/com/android/server/am/ActivityStarter.java
services/core/java/com/android/server/wm/RootWindowContainer.java
services/core/java/com/android/server/wm/WindowManagerService.java
services/tests/servicestests/AndroidManifest.xml
services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java

index 3170d0d..f1f6e7b 100644 (file)
@@ -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);
         }
     }
 
index 8cf0708..af52bba 100644 (file)
@@ -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);
     }
 
index f401863..be268f4 100644 (file)
@@ -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<ActivityStack> 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;
index 6ac172b..0cc6c70 100644 (file)
@@ -228,7 +228,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
             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);
index 195d4c3..655e3b4 100644 (file)
@@ -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()) {
index 1c92e45..1393615 100644 (file)
@@ -46,6 +46,7 @@
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
 
     <application>
         <uses-library android:name="android.test.runner" />
index 06837d3..1d9875f 100644 (file)
@@ -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);
+    }
 }
index 12e7a15..c0c8fb0 100644 (file)
@@ -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
index 466bd67..fa4b79f 100644 (file)
@@ -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;
+        }
+    }
 }