diff options
6 files changed, 284 insertions, 112 deletions
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 7e29a3aa811e..88b7a11f02fd 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -390,12 +390,13 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { } } - final boolean inPositioning = (mService.mTaskPositioner != null); + final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked(); if (inPositioning) { if (DEBUG_TASK_POSITIONING) { Log.d(TAG_WM, "Inserting window handle for repositioning"); } - final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle; + final InputWindowHandle dragWindowHandle = + mService.mTaskPositioningController.getDragWindowHandleLocked(); if (dragWindowHandle != null) { addInputWindowHandle(dragWindowHandle); } else { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 63eea104c7e7..192d6c84e190 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -368,7 +368,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { long ident = Binder.clearCallingIdentity(); try { - return mService.startMovingTask(window, startX, startY); + return mService.mTaskPositioningController.startMovingTask(window, startX, startY); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 87d0a4016634..ca9f3a933f70 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -16,15 +16,11 @@ package com.android.server.wm; -import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; -import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.RESIZE_MODE_USER; import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.dipToPixel; @@ -44,7 +40,6 @@ import android.util.Slog; import android.view.BatchedInputEventReceiver; import android.view.Choreographer; import android.view.Display; -import android.view.DisplayInfo; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java new file mode 100644 index 000000000000..bb5706ceb0ec --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.annotation.Nullable; +import android.app.IActivityManager; +import android.os.RemoteException; +import android.util.Slog; +import android.view.Display; +import android.view.IWindow; +import com.android.internal.annotations.GuardedBy; +import com.android.server.input.InputManagerService; +import com.android.server.input.InputWindowHandle; + +/** + * Controller for task positioning by drag. + */ +class TaskPositioningController { + private final WindowManagerService mService; + private final InputManagerService mInputManager; + private final InputMonitor mInputMonitor; + private final IActivityManager mActivityManager; + + @GuardedBy("WindowManagerSerivce.mWindowMap") + private @Nullable TaskPositioner mTaskPositioner; + + boolean isPositioningLocked() { + return mTaskPositioner != null; + } + + InputWindowHandle getDragWindowHandleLocked() { + return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null; + } + + TaskPositioningController(WindowManagerService service, InputManagerService inputManager, + InputMonitor inputMonitor, IActivityManager activityManager) { + mService = service; + mInputMonitor = inputMonitor; + mInputManager = inputManager; + mActivityManager = activityManager; + } + + boolean startMovingTask(IWindow window, float startX, float startY) { + WindowState win = null; + synchronized (mService.mWindowMap) { + win = mService.windowForClientLocked(null, window, false); + // win shouldn't be null here, pass it down to startPositioningLocked + // to get warning if it's null. + if (!startPositioningLocked( + win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) { + return false; + } + } + try { + mActivityManager.setFocusedTask(win.getTask().mTaskId); + } catch(RemoteException e) {} + return true; + } + + void handleTapOutsideTask(DisplayContent displayContent, int x, int y) { + int taskId = -1; + synchronized (mService.mWindowMap) { + final Task task = displayContent.findTaskForResizePoint(x, y); + if (task != null) { + if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/, + task.preserveOrientationOnResize(), x, y)) { + return; + } + taskId = task.mTaskId; + } else { + taskId = displayContent.taskIdFromPoint(x, y); + } + } + if (taskId >= 0) { + try { + mActivityManager.setFocusedTask(taskId); + } catch(RemoteException e) {} + } + } + + private boolean startPositioningLocked(WindowState win, boolean resize, + boolean preserveOrientation, float startX, float startY) { + if (DEBUG_TASK_POSITIONING) + Slog.d(TAG_WM, "startPositioningLocked: " + + "win=" + win + ", resize=" + resize + ", preserveOrientation=" + + preserveOrientation + ", {" + startX + ", " + startY + "}"); + + if (win == null || win.getAppToken() == null) { + Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win); + return false; + } + if (win.mInputChannel == null) { + Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, " + + " probably being removed"); + return false; + } + + final DisplayContent displayContent = win.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win); + return false; + } + + Display display = displayContent.getDisplay(); + mTaskPositioner = new TaskPositioner(mService); + mTaskPositioner.register(displayContent); + mInputMonitor.updateInputWindowsLw(true /*force*/); + + // We need to grab the touch focus so that the touch events during the + // resizing/scrolling are not sent to the app. 'win' is the main window + // of the app, it may not have focus since there might be other windows + // on top (eg. a dialog window). + WindowState transferFocusFromWin = win; + if (mService.mCurrentFocus != null && mService.mCurrentFocus != win + && mService.mCurrentFocus.mAppToken == win.mAppToken) { + transferFocusFromWin = mService.mCurrentFocus; + } + if (!mInputManager.transferTouchFocus( + transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) { + Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus"); + mTaskPositioner.unregister(); + mTaskPositioner = null; + mInputMonitor.updateInputWindowsLw(true /*force*/); + return false; + } + + mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY); + return true; + } + + void finishPositioning() { + if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning"); + + synchronized (mService.mWindowMap) { + if (mTaskPositioner != null) { + mTaskPositioner.unregister(); + mTaskPositioner = null; + mInputMonitor.updateInputWindowsLw(true /*force*/); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b43c3116ea5a..8c9948e1ee49 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -755,7 +755,7 @@ public class WindowManagerService extends IWindowManager.Stub // Whether or not a layout can cause a wake up when theater mode is enabled. boolean mAllowTheaterModeWakeFromLayout; - TaskPositioner mTaskPositioner; + final TaskPositioningController mTaskPositioningController; final DragDropController mDragDropController; // For frozen screen animations. @@ -1088,6 +1088,8 @@ public class WindowManagerService extends IWindowManager.Stub mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); + mTaskPositioningController = + new TaskPositioningController(this, mInputManager, mInputMonitor, mActivityManager); mDragDropController = new DragDropController(this, mH.getLooper()); LocalServices.addService(WindowManagerInternal.class, new LocalService()); @@ -4442,107 +4444,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - boolean startMovingTask(IWindow window, float startX, float startY) { - WindowState win = null; - synchronized (mWindowMap) { - win = windowForClientLocked(null, window, false); - // win shouldn't be null here, pass it down to startPositioningLocked - // to get warning if it's null. - if (!startPositioningLocked( - win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) { - return false; - } - } - try { - mActivityManager.setFocusedTask(win.getTask().mTaskId); - } catch(RemoteException e) {} - return true; - } - - private void handleTapOutsideTask(DisplayContent displayContent, int x, int y) { - int taskId = -1; - synchronized (mWindowMap) { - final Task task = displayContent.findTaskForResizePoint(x, y); - if (task != null) { - if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/, - task.preserveOrientationOnResize(), x, y)) { - return; - } - taskId = task.mTaskId; - } else { - taskId = displayContent.taskIdFromPoint(x, y); - } - } - if (taskId >= 0) { - try { - mActivityManager.setFocusedTask(taskId); - } catch(RemoteException e) {} - } - } - - private boolean startPositioningLocked(WindowState win, boolean resize, - boolean preserveOrientation, float startX, float startY) { - if (DEBUG_TASK_POSITIONING) - Slog.d(TAG_WM, "startPositioningLocked: " - + "win=" + win + ", resize=" + resize + ", preserveOrientation=" - + preserveOrientation + ", {" + startX + ", " + startY + "}"); - - if (win == null || win.getAppToken() == null) { - Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win); - return false; - } - if (win.mInputChannel == null) { - Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, " - + " probably being removed"); - return false; - } - - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent == null) { - Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win); - return false; - } - - Display display = displayContent.getDisplay(); - mTaskPositioner = new TaskPositioner(this); - mTaskPositioner.register(displayContent); - mInputMonitor.updateInputWindowsLw(true /*force*/); - - // We need to grab the touch focus so that the touch events during the - // resizing/scrolling are not sent to the app. 'win' is the main window - // of the app, it may not have focus since there might be other windows - // on top (eg. a dialog window). - WindowState transferFocusFromWin = win; - if (mCurrentFocus != null && mCurrentFocus != win - && mCurrentFocus.mAppToken == win.mAppToken) { - transferFocusFromWin = mCurrentFocus; - } - if (!mInputManager.transferTouchFocus( - transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) { - Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus"); - mTaskPositioner.unregister(); - mTaskPositioner = null; - mInputMonitor.updateInputWindowsLw(true /*force*/); - return false; - } - - mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY); - return true; - } - - private void finishPositioning() { - if (DEBUG_TASK_POSITIONING) { - Slog.d(TAG_WM, "finishPositioning"); - } - synchronized (mWindowMap) { - if (mTaskPositioner != null) { - mTaskPositioner.unregister(); - mTaskPositioner = null; - mInputMonitor.updateInputWindowsLw(true /*force*/); - } - } - } - // ------------------------------------------------------------- // Input Events and Focus Management // ------------------------------------------------------------- @@ -5005,12 +4906,13 @@ public class WindowManagerService extends IWindowManager.Stub } case TAP_OUTSIDE_TASK: { - handleTapOutsideTask((DisplayContent)msg.obj, msg.arg1, msg.arg2); + mTaskPositioningController.handleTapOutsideTask( + (DisplayContent)msg.obj, msg.arg1, msg.arg2); } break; case FINISH_TASK_POSITIONING: { - finishPositioning(); + mTaskPositioningController.finishPositioning(); } break; diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java new file mode 100644 index 000000000000..89447a912c21 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.InputChannel; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +/** + * Tests for the {@link TaskPositioningController} class. + * + * atest com.android.server.wm.TaskPositioningControllerTests + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +@Presubmit +public class TaskPositioningControllerTests extends WindowTestsBase { + private static final int TIMEOUT_MS = 1000; + private TaskPositioningController mTarget; + private WindowState mWindow; + + @Before + public void setUp() throws Exception { + super.setUp(); + + assertNotNull(sWm.mTaskPositioningController); + mTarget = sWm.mTaskPositioningController; + + when(sWm.mInputManager.transferTouchFocus( + any(InputChannel.class), + any(InputChannel.class))).thenReturn(true); + + mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window"); + mWindow.mInputChannel = new InputChannel(); + synchronized (sWm.mWindowMap) { + sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); + } + } + + @Test + public void testStartAndFinishPositioning() throws Exception { + synchronized (sWm.mWindowMap) { + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0)); + + synchronized (sWm.mWindowMap) { + assertTrue(mTarget.isPositioningLocked()); + assertNotNull(mTarget.getDragWindowHandleLocked()); + } + + assertTrue(sWm.mH.runWithScissors(() -> { + mTarget.finishPositioning(); + }, TIMEOUT_MS)); + + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + @Test + public void testHandleTapOutsideTask() throws Exception { + synchronized (sWm.mWindowMap) { + + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + final DisplayContent content = mock(DisplayContent.class); + when(content.findTaskForResizePoint(anyInt(), anyInt())).thenReturn(mWindow.getTask()); + assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow()); + + mTarget.handleTapOutsideTask(content, 0, 0); + + synchronized (sWm.mWindowMap) { + assertTrue(mTarget.isPositioningLocked()); + assertNotNull(mTarget.getDragWindowHandleLocked()); + } + + assertTrue(sWm.mH.runWithScissors(() -> { + mTarget.finishPositioning(); + }, TIMEOUT_MS)); + + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } +} |