Loading services/core/java/com/android/server/wm/DragState.java +28 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.server.wm; import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; Loading Loading @@ -441,8 +444,9 @@ class DragState { Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")"); } final boolean containsAppExtras = containsApplicationExtras(mDataDescription); mService.mRoot.forAllWindows(w -> { sendDragStartedLocked(w, touchX, touchY, mDataDescription, mData); sendDragStartedLocked(w, touchX, touchY, containsAppExtras); }, false /* traverseTopToBottom */); } Loading @@ -455,9 +459,9 @@ class DragState { * process, so it's safe for the caller to call recycle() on the event afterwards. */ private void sendDragStartedLocked(WindowState newWin, float touchX, float touchY, ClipDescription desc, ClipData data) { boolean containsAppExtras) { final boolean interceptsGlobalDrag = targetInterceptsGlobalDrag(newWin); if (mDragInProgress && isValidDropTarget(newWin, interceptsGlobalDrag)) { if (mDragInProgress && isValidDropTarget(newWin, containsAppExtras, interceptsGlobalDrag)) { DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED, touchX, touchY, interceptsGlobalDrag, false /* includeDragSurface */, null /* dragAndDropPermission */); Loading @@ -476,10 +480,28 @@ class DragState { } } private boolean isValidDropTarget(WindowState targetWin, boolean interceptsGlobalDrag) { /** * Returns true if this is a drag of an application mime type. */ private boolean containsApplicationExtras(ClipDescription desc) { if (desc == null) { return false; } return desc.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY) || desc.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT) || desc.hasMimeType(MIMETYPE_APPLICATION_TASK); } private boolean isValidDropTarget(WindowState targetWin, boolean containsAppExtras, boolean interceptsGlobalDrag) { if (targetWin == null) { return false; } if (!interceptsGlobalDrag && containsAppExtras) { // App-drags can only go to windows that can intercept global drag, and not to normal // app windows return false; } if (!targetWin.isPotentialDragTarget(interceptsGlobalDrag)) { return false; } Loading Loading @@ -522,7 +544,8 @@ class DragState { if (DEBUG_DRAG) { Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin); } sendDragStartedLocked(newWin, mCurrentX, mCurrentY, mDataDescription, mData); sendDragStartedLocked(newWin, mCurrentX, mCurrentY, containsApplicationExtras(mDataDescription)); } } Loading services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +29 −0 Original line number Diff line number Diff line Loading @@ -238,6 +238,35 @@ public class DragDropControllerTests extends WindowTestsBase { }); } @Test public void testInterceptGlobalDragDropIgnoresOtherWindows() { WindowState globalInterceptWindow = createDropTargetWindow("Global drag test window", 0); globalInterceptWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; // Necessary for now since DragState.sendDragStartedLocked() will recycle drag events // immediately after dispatching, which is a problem when using mockito arguments captor // because it returns and modifies the same drag event TestIWindow iwindow = (TestIWindow) mWindow.mClient; final ArrayList<DragEvent> dragEvents = new ArrayList<>(); iwindow.setDragEventJournal(dragEvents); TestIWindow globalInterceptIWindow = (TestIWindow) globalInterceptWindow.mClient; final ArrayList<DragEvent> globalInterceptWindowDragEvents = new ArrayList<>(); globalInterceptIWindow.setDragEventJournal(globalInterceptWindowDragEvents); startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, createClipDataForActivity(null, mock(UserHandle.class)), () -> { // Verify the start-drag event is sent for the intercept window but not the // other window assertTrue(dragEvents.isEmpty()); assertTrue(globalInterceptWindowDragEvents.get(0).getAction() == ACTION_DRAG_STARTED); mTarget.reportDropWindow(globalInterceptWindow.mInputChannelToken, 0, 0); mTarget.handleMotionEvent(false, 0, 0); mToken = globalInterceptWindow.mClient.asBinder(); }); } @Test public void testValidateAppActivityArguments() { final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { Loading Loading
services/core/java/com/android/server/wm/DragState.java +28 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.server.wm; import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; Loading Loading @@ -441,8 +444,9 @@ class DragState { Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")"); } final boolean containsAppExtras = containsApplicationExtras(mDataDescription); mService.mRoot.forAllWindows(w -> { sendDragStartedLocked(w, touchX, touchY, mDataDescription, mData); sendDragStartedLocked(w, touchX, touchY, containsAppExtras); }, false /* traverseTopToBottom */); } Loading @@ -455,9 +459,9 @@ class DragState { * process, so it's safe for the caller to call recycle() on the event afterwards. */ private void sendDragStartedLocked(WindowState newWin, float touchX, float touchY, ClipDescription desc, ClipData data) { boolean containsAppExtras) { final boolean interceptsGlobalDrag = targetInterceptsGlobalDrag(newWin); if (mDragInProgress && isValidDropTarget(newWin, interceptsGlobalDrag)) { if (mDragInProgress && isValidDropTarget(newWin, containsAppExtras, interceptsGlobalDrag)) { DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED, touchX, touchY, interceptsGlobalDrag, false /* includeDragSurface */, null /* dragAndDropPermission */); Loading @@ -476,10 +480,28 @@ class DragState { } } private boolean isValidDropTarget(WindowState targetWin, boolean interceptsGlobalDrag) { /** * Returns true if this is a drag of an application mime type. */ private boolean containsApplicationExtras(ClipDescription desc) { if (desc == null) { return false; } return desc.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY) || desc.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT) || desc.hasMimeType(MIMETYPE_APPLICATION_TASK); } private boolean isValidDropTarget(WindowState targetWin, boolean containsAppExtras, boolean interceptsGlobalDrag) { if (targetWin == null) { return false; } if (!interceptsGlobalDrag && containsAppExtras) { // App-drags can only go to windows that can intercept global drag, and not to normal // app windows return false; } if (!targetWin.isPotentialDragTarget(interceptsGlobalDrag)) { return false; } Loading Loading @@ -522,7 +544,8 @@ class DragState { if (DEBUG_DRAG) { Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin); } sendDragStartedLocked(newWin, mCurrentX, mCurrentY, mDataDescription, mData); sendDragStartedLocked(newWin, mCurrentX, mCurrentY, containsApplicationExtras(mDataDescription)); } } Loading
services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +29 −0 Original line number Diff line number Diff line Loading @@ -238,6 +238,35 @@ public class DragDropControllerTests extends WindowTestsBase { }); } @Test public void testInterceptGlobalDragDropIgnoresOtherWindows() { WindowState globalInterceptWindow = createDropTargetWindow("Global drag test window", 0); globalInterceptWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; // Necessary for now since DragState.sendDragStartedLocked() will recycle drag events // immediately after dispatching, which is a problem when using mockito arguments captor // because it returns and modifies the same drag event TestIWindow iwindow = (TestIWindow) mWindow.mClient; final ArrayList<DragEvent> dragEvents = new ArrayList<>(); iwindow.setDragEventJournal(dragEvents); TestIWindow globalInterceptIWindow = (TestIWindow) globalInterceptWindow.mClient; final ArrayList<DragEvent> globalInterceptWindowDragEvents = new ArrayList<>(); globalInterceptIWindow.setDragEventJournal(globalInterceptWindowDragEvents); startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, createClipDataForActivity(null, mock(UserHandle.class)), () -> { // Verify the start-drag event is sent for the intercept window but not the // other window assertTrue(dragEvents.isEmpty()); assertTrue(globalInterceptWindowDragEvents.get(0).getAction() == ACTION_DRAG_STARTED); mTarget.reportDropWindow(globalInterceptWindow.mInputChannelToken, 0, 0); mTarget.handleMotionEvent(false, 0, 0); mToken = globalInterceptWindow.mClient.asBinder(); }); } @Test public void testValidateAppActivityArguments() { final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { Loading