diff options
9 files changed, 129 insertions, 9 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 3170d0d97af0..f1f6e7b09767 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 527e91830ccf..4fe893979c6f 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 81a5d3beefbd..2634385e49dc 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; @@ -1926,7 +1926,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, @@ -1938,7 +1938,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; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6ac172b26e24..0cc6c701a93e 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<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); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 381521d5c81d..0c8c10ba2668 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() { @@ -4980,7 +4985,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 1c92e452d03c..1393615b5623 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -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" /> 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 06837d38d69c..1d9875f308e9 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 12e7a15d2cbb..c0c8fb049415 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 53e5a2a5995a..44d5055d5eb6 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -182,7 +182,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) { @@ -280,4 +280,36 @@ class WindowTestsBase { return mBinder; } } + + /** 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; + } + } } |