diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index cbe6e69cbef3b..092502709f34d 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -2605,6 +2605,11 @@ public class InputManagerService extends IInputManager.Stub mWindowManagerCallbacks.notifyFocusChanged(oldToken, newToken); } + // Native callback + private void notifyDropWindow(IBinder token, float x, float y) { + mWindowManagerCallbacks.notifyDropWindow(token, x, y); + } + // Native callback private void notifyUntrustedTouch(String packageName) { // TODO(b/169067926): Remove toast after gathering feedback on dogfood. @@ -3035,6 +3040,11 @@ public class InputManagerService extends IInputManager.Stub * Called when the focused window has changed. */ void notifyFocusChanged(IBinder oldToken, IBinder newToken); + + /** + * Called when the drag over window has changed. + */ + void notifyDropWindow(IBinder token, float x, float y); } /** diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 1120a074aa8c7..d12d07ab74481 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -283,11 +283,7 @@ class DragDropController { return; } - if (keepHandling) { - mDragState.notifyMoveLocked(newX, newY); - } else { - mDragState.notifyDropLocked(newX, newY); - } + mDragState.updateDragSurfaceLocked(keepHandling, newX, newY); } } @@ -330,6 +326,12 @@ class DragDropController { mDragState = null; } + void reportDropWindow(IBinder token, float x, float y) { + synchronized (mService.mGlobalLock) { + mDragState.reportDropWindowLock(token, x, y); + } + } + private class DragHandler extends Handler { /** * Lock for window manager. diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 08d5e800a8085..fd4bbd7b36e49 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -109,7 +109,6 @@ class DragState { float mCurrentX, mCurrentY; float mThumbOffsetX, mThumbOffsetY; InputInterceptor mInputInterceptor; - WindowState mTargetWindow; ArrayList mNotifiedWindows; boolean mDragInProgress; /** @@ -217,18 +216,18 @@ class DragState { x = mCurrentX; y = mCurrentY; } - DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, + DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, null, null, mDragResult); try { - ws.mClient.dispatchDragEvent(evt); + ws.mClient.dispatchDragEvent(event); } catch (RemoteException e) { Slog.w(TAG_WM, "Unable to drag-end window " + ws); } // if the current window is in the same process, // the dispatch has already recycled the event if (myPid != ws.mSession.mPid) { - evt.recycle(); + event.recycle(); } } mNotifiedWindows.clear(); @@ -270,6 +269,68 @@ class DragState { mDragDropController.onDragStateClosedLocked(this); } + /** + * Notify the drop target and tells it about the data. If the drop event is not sent to the + * target, invokes {@code endDragLocked} immediately. + */ + void reportDropWindowLock(IBinder token, float x, float y) { + if (mAnimator != null) { + return; + } + + final WindowState touchedWin = mService.mInputToWindowMap.get(token); + if (!isWindowNotified(touchedWin)) { + // "drop" outside a valid window -- no recipient to apply a + // timeout to, and we can send the drag-ended message immediately. + mDragResult = false; + endDragLocked(); + if (DEBUG_DRAG) Slog.d(TAG_WM, "Drop outside a valid window " + touchedWin); + return; + } + + if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin); + + final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid()); + + final DragAndDropPermissionsHandler dragAndDropPermissions; + if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0 + && mData != null) { + dragAndDropPermissions = new DragAndDropPermissionsHandler(mService.mGlobalLock, + mData, + mUid, + touchedWin.getOwningPackage(), + mFlags & DRAG_FLAGS_URI_PERMISSIONS, + mSourceUserId, + targetUserId); + } else { + dragAndDropPermissions = null; + } + if (mSourceUserId != targetUserId) { + if (mData != null) { + mData.fixUris(mSourceUserId); + } + } + final int myPid = Process.myPid(); + final IBinder clientToken = touchedWin.mClient.asBinder(); + final DragEvent event = obtainDragEvent(DragEvent.ACTION_DROP, x, y, + true /* includeData */, targetInterceptsGlobalDrag(touchedWin), + dragAndDropPermissions); + try { + touchedWin.mClient.dispatchDragEvent(event); + + // 5 second timeout for this window to respond to the drop + mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, clientToken); + } catch (RemoteException e) { + Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin); + endDragLocked(); + } finally { + if (myPid != touchedWin.mSession.mPid) { + event.recycle(); + } + } + mToken = clientToken; + } + class InputInterceptor { InputChannel mClientChannel; DragInputEventReceiver mInputEventReceiver; @@ -397,9 +458,9 @@ class DragState { ClipDescription desc, ClipData data) { final boolean interceptsGlobalDrag = targetInterceptsGlobalDrag(newWin); if (mDragInProgress && isValidDropTarget(newWin, interceptsGlobalDrag)) { - DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED, - touchX, touchY, mThumbOffsetX, mThumbOffsetY, null, desc, - interceptsGlobalDrag ? data : null, null, null, false); + DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED, touchX, touchY, + interceptsGlobalDrag, false /* includeDragSurface */, + null /* dragAndDropPermission */); try { newWin.mClient.dispatchDragEvent(event); // track each window that we've notified that the drag is starting @@ -501,13 +562,17 @@ class DragState { mAnimator = createCancelAnimationLocked(); } - void notifyMoveLocked(float x, float y) { + void updateDragSurfaceLocked(boolean keepHandling, float x, float y) { if (mAnimator != null) { return; } mCurrentX = x; mCurrentY = y; + if (!keepHandling) { + return; + } + // Move the surface to the given touch if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG_WM, ">>> OPEN TRANSACTION notifyMoveLocked"); @@ -517,71 +582,6 @@ class DragState { (int) (x - mThumbOffsetX), (int) (y - mThumbOffsetY)); } - /** - * Finds the drop target and tells it about the data. If the drop event is not sent to the - * target, invokes {@code endDragLocked} immediately. - */ - void notifyDropLocked(float x, float y) { - if (mAnimator != null) { - return; - } - mCurrentX = x; - mCurrentY = y; - - final WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y); - - if (!isWindowNotified(touchedWin)) { - // "drop" outside a valid window -- no recipient to apply a - // timeout to, and we can send the drag-ended message immediately. - mDragResult = false; - endDragLocked(); - return; - } - - if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin); - - final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid()); - - final DragAndDropPermissionsHandler dragAndDropPermissions; - if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0 - && mData != null) { - dragAndDropPermissions = new DragAndDropPermissionsHandler(mService.mGlobalLock, - mData, - mUid, - touchedWin.getOwningPackage(), - mFlags & DRAG_FLAGS_URI_PERMISSIONS, - mSourceUserId, - targetUserId); - } else { - dragAndDropPermissions = null; - } - if (mSourceUserId != targetUserId){ - if (mData != null) { - mData.fixUris(mSourceUserId); - } - } - final int myPid = Process.myPid(); - final IBinder token = touchedWin.mClient.asBinder(); - final DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y, - mThumbOffsetX, mThumbOffsetY, null, null, mData, - targetInterceptsGlobalDrag(touchedWin) ? mSurfaceControl : null, - dragAndDropPermissions, false); - try { - touchedWin.mClient.dispatchDragEvent(evt); - - // 5 second timeout for this window to respond to the drop - mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, token); - } catch (RemoteException e) { - Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin); - endDragLocked(); - } finally { - if (myPid != touchedWin.mSession.mPid) { - evt.recycle(); - } - } - mToken = token; - } - /** * Returns true if it has sent DRAG_STARTED broadcast out but has not been sent DRAG_END * broadcast. @@ -590,14 +590,12 @@ class DragState { return mDragInProgress; } - private static DragEvent obtainDragEvent(WindowState win, int action, float x, float y, - float offsetX, float offsetY, Object localState, ClipDescription description, - ClipData data, SurfaceControl dragSurface, - IDragAndDropPermissions dragAndDropPermissions, boolean result) { - final float winX = win.translateToWindowX(x); - final float winY = win.translateToWindowY(y); - return DragEvent.obtain(action, winX, winY, offsetX, offsetY, localState, description, data, - dragSurface, dragAndDropPermissions, result); + private DragEvent obtainDragEvent(int action, float x, float y, boolean includeData, + boolean includeDragSurface, IDragAndDropPermissions dragAndDropPermissions) { + return DragEvent.obtain(action, x, y, mThumbOffsetX, mThumbOffsetY, + null /* localState */, mDataDescription, + includeData ? mData : null, includeDragSurface ? mSurfaceControl : null, + dragAndDropPermissions, false /* result */); } private ValueAnimator createReturnAnimationLocked() { diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 6e89581bc34a5..84616c0bdc8e2 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -229,6 +229,12 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal mService::reportFocusChanged, oldToken, newToken)); } + @Override + public void notifyDropWindow(IBinder token, float x, float y) { + mService.mH.sendMessage(PooledLambda.obtainMessage( + mService.mDragDropController::reportDropWindow, token, x, y)); + } + /** Waits until the built-in input devices have been configured. */ public boolean waitForInputDevicesReady(long timeoutMillis) { synchronized (mInputDevicesReadyMonitor) { diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java index 74337c2b38ed5..08404411c02b5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java +++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java @@ -44,7 +44,7 @@ public class WindowManagerDebugConfig { static final boolean DEBUG_STARTING_WINDOW_VERBOSE = false; static final boolean DEBUG_WALLPAPER = false; static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER; - static final boolean DEBUG_DRAG = false; + static final boolean DEBUG_DRAG = true; static final boolean DEBUG_SCREENSHOT = false; static final boolean DEBUG_LAYOUT_REPEATS = false; static final boolean DEBUG_WINDOW_TRACE = false; diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index be06d0395499e..3a674c4d7ee56 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -131,6 +131,7 @@ static struct { jmethodID getDeviceAlias; jmethodID getTouchCalibrationForInputDevice; jmethodID getContextForDisplay; + jmethodID notifyDropWindow; } gServiceClassInfo; static struct { @@ -335,6 +336,7 @@ public: bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override; void onPointerDownOutsideFocus(const sp& touchedToken) override; void setPointerCapture(bool enabled) override; + void notifyDropWindow(const sp& token, float x, float y) override; /* --- PointerControllerPolicyInterface implementation --- */ @@ -905,6 +907,20 @@ void NativeInputManager::notifyFocusChanged(const sp& oldToken, checkAndClearExceptionFromCallback(env, "notifyFocusChanged"); } +void NativeInputManager::notifyDropWindow(const sp& token, float x, float y) { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("notifyDropWindow"); +#endif + ATRACE_CALL(); + + JNIEnv* env = jniEnv(); + ScopedLocalFrame localFrame(env); + + jobject tokenObj = javaObjectForIBinder(env, token); + env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyDropWindow, tokenObj, x, y); + checkAndClearExceptionFromCallback(env, "notifyDropWindow"); +} + void NativeInputManager::notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, const std::vector& values) { @@ -2350,6 +2366,8 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz, "notifyFocusChanged", "(Landroid/os/IBinder;Landroid/os/IBinder;)V"); + GET_METHOD_ID(gServiceClassInfo.notifyDropWindow, clazz, "notifyDropWindow", + "(Landroid/os/IBinder;FF)V"); GET_METHOD_ID(gServiceClassInfo.notifySensorEvent, clazz, "notifySensorEvent", "(IIIJ[F)V"); diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 7f9e7da995793..4e2697ab64f8a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -134,6 +134,7 @@ public class DragDropControllerTests extends WindowTestsBase { null, TYPE_BASE_APPLICATION, activity, name, ownerId, false, new TestIWindow()); window.mInputChannel = new InputChannel(); window.mHasSurface = true; + mWm.mInputToWindowMap.put(window.mInputChannelToken, window); return window; } @@ -226,7 +227,7 @@ public class DragDropControllerTests extends WindowTestsBase { // Verify after consuming that the drag surface is relinquished try { mTarget.mDeferDragStateClosed = true; - + mTarget.reportDropWindow(mWindow.mInputChannelToken, 0, 0); // Verify the drop event includes the drag surface mTarget.handleMotionEvent(false, 0, 0); final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1); @@ -355,6 +356,7 @@ public class DragDropControllerTests extends WindowTestsBase { private void doDragAndDrop(int flags, ClipData data, float dropX, float dropY) { startDrag(flags, data, () -> { + mTarget.reportDropWindow(mWindow.mInputChannelToken, dropX, dropY); mTarget.handleMotionEvent(false, dropX, dropY); mToken = mWindow.mClient.asBinder(); });