diff options
-rw-r--r-- | services/core/java/com/android/server/wm/DragDropController.java | 379 |
1 files changed, 214 insertions, 165 deletions
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index cc4e0f8a5004..76e25bae5db2 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -52,6 +52,8 @@ class DragDropController { /** * Drag state per operation. + * Needs a lock of {@code WindowManagerService#mWindowMap} to read this. Needs both locks of + * {@code mWriteLock} and {@code WindowManagerService#mWindowMap} to update this. * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState * itself, thus the variable can be null after calling DragState's methods. */ @@ -59,6 +61,38 @@ class DragDropController { private WindowManagerService mService; private final Handler mHandler; + /** + * Lock to preserve the order of state updates. + * The lock is used to process drag and drop state updates in order without having the window + * manager lock. + * + * Suppose DragDropController invokes a callback method A, then processes the following update + * A'. Same for a callback method B and the following update B'. The callback wants + * DragDropController to processes the updates in the order of A' then B'. + * + * Without mWriteLock: the following race can happen. + * + * 1. Thread a calls A. + * 2. Thread b calls B. + * 3. Thread b acquires the window manager lock + * 4. thread b processes the update B' + * 5. Thread a acquires the window manager lock + * 6. thread a processes the update A' + * + * With mWriteLock we can ensure the order of A' and B' + * + * 1. Thread a acquire mWriteLock + * 2. Thread a calls A + * 3. Thread a acquire the window manager lock + * 4. Thread a processes A' + * 5. Thread b acquire mWriteLock + * 6. Thread b calls B + * 7. Thread b acquire the window manager lock + * 8. Thread b processes B' + * + * Don't acquire the lock while holding the window manager lock, otherwise it causes a deadlock. + */ + private final Object mWriteLock = new Object(); boolean dragDropActiveLocked() { return mDragState != null; @@ -85,46 +119,46 @@ class DragDropController { + " asbinder=" + window.asBinder()); } - IBinder token = null; - - synchronized (mService.mWindowMap) { - if (dragDropActiveLocked()) { - Slog.w(TAG_WM, "Drag already in progress"); - return null; - } + synchronized (mWriteLock) { + synchronized (mService.mWindowMap) { + if (dragDropActiveLocked()) { + Slog.w(TAG_WM, "Drag already in progress"); + return null; + } - // TODO(multi-display): support other displays - final DisplayContent displayContent = - mService.getDefaultDisplayContentLocked(); - final Display display = displayContent.getDisplay(); - - final SurfaceControl surface = new SurfaceControl.Builder(session) - .setName("drag surface") - .setSize(width, height) - .setFormat(PixelFormat.TRANSLUCENT) - .build(); - surface.setLayerStack(display.getLayerStack()); - float alpha = 1; - if ((flags & View.DRAG_FLAG_OPAQUE) == 0) { - alpha = DRAG_SHADOW_ALPHA_TRANSPARENT; + // TODO(multi-display): support other displays + final DisplayContent displayContent = + mService.getDefaultDisplayContentLocked(); + final Display display = displayContent.getDisplay(); + + final SurfaceControl surface = new SurfaceControl.Builder(session) + .setName("drag surface") + .setSize(width, height) + .setFormat(PixelFormat.TRANSLUCENT) + .build(); + surface.setLayerStack(display.getLayerStack()); + float alpha = 1; + if ((flags & View.DRAG_FLAG_OPAQUE) == 0) { + alpha = DRAG_SHADOW_ALPHA_TRANSPARENT; + } + surface.setAlpha(alpha); + + if (SHOW_TRANSACTIONS) + Slog.i(TAG_WM, " DRAG " + surface + ": CREATE"); + outSurface.copyFrom(surface); + final IBinder winBinder = window.asBinder(); + IBinder token = new Binder(); + mDragState = new DragState(mService, token, surface, flags, winBinder); + mDragState.mPid = callerPid; + mDragState.mUid = callerUid; + mDragState.mOriginalAlpha = alpha; + token = mDragState.mToken = new Binder(); + + // 5 second timeout for this window to actually begin the drag + sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder); + return token; } - surface.setAlpha(alpha); - - if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE"); - outSurface.copyFrom(surface); - final IBinder winBinder = window.asBinder(); - token = new Binder(); - mDragState = new DragState(mService, token, surface, flags, winBinder); - mDragState.mPid = callerPid; - mDragState.mUid = callerUid; - mDragState.mOriginalAlpha = alpha; - token = mDragState.mToken = new Binder(); - - // 5 second timeout for this window to actually begin the drag - sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder); } - - return token; } boolean performDrag(IWindow window, IBinder dragToken, @@ -134,75 +168,77 @@ class DragDropController { Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data); } - synchronized (mService.mWindowMap) { - if (mDragState == null) { - Slog.w(TAG_WM, "No drag prepared"); - throw new IllegalStateException("performDrag() without prepareDrag()"); - } + synchronized (mWriteLock) { + synchronized (mService.mWindowMap) { + if (mDragState == null) { + Slog.w(TAG_WM, "No drag prepared"); + throw new IllegalStateException("performDrag() without prepareDrag()"); + } - if (dragToken != mDragState.mToken) { - Slog.w(TAG_WM, "Performing mismatched drag"); - throw new IllegalStateException("performDrag() does not match prepareDrag()"); - } + if (dragToken != mDragState.mToken) { + Slog.w(TAG_WM, "Performing mismatched drag"); + throw new IllegalStateException("performDrag() does not match prepareDrag()"); + } - final WindowState callingWin = mService.windowForClientLocked(null, window, false); - if (callingWin == null) { - Slog.w(TAG_WM, "Bad requesting window " + window); - return false; // !!! TODO: throw here? - } + final WindowState callingWin = mService.windowForClientLocked(null, window, false); + if (callingWin == null) { + Slog.w(TAG_WM, "Bad requesting window " + window); + return false; // !!! TODO: throw here? + } - // !!! TODO: if input is not still focused on the initiating window, fail - // the drag initiation (e.g. an alarm window popped up just as the application - // called performDrag() + // !!! TODO: if input is not still focused on the initiating window, fail + // the drag initiation (e.g. an alarm window popped up just as the application + // called performDrag() - mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder()); + mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder()); - // !!! TODO: extract the current touch (x, y) in screen coordinates. That - // will let us eliminate the (touchX,touchY) parameters from the API. + // !!! TODO: extract the current touch (x, y) in screen coordinates. That + // will let us eliminate the (touchX,touchY) parameters from the API. - // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as - // the actual drag event dispatch stuff in the dragstate + // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as + // the actual drag event dispatch stuff in the dragstate - final DisplayContent displayContent = callingWin.getDisplayContent(); - if (displayContent == null) { - return false; - } - Display display = displayContent.getDisplay(); - mDragState.register(display); - if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, - mDragState.getInputChannel())) { - Slog.e(TAG_WM, "Unable to transfer touch focus"); - mDragState.closeLocked(); - return false; - } + final DisplayContent displayContent = callingWin.getDisplayContent(); + if (displayContent == null) { + return false; + } + Display display = displayContent.getDisplay(); + mDragState.register(display); + if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, + mDragState.getInputChannel())) { + Slog.e(TAG_WM, "Unable to transfer touch focus"); + mDragState.closeLocked(); + return false; + } - mDragState.mDisplayContent = displayContent; - mDragState.mData = data; - mDragState.broadcastDragStartedLocked(touchX, touchY); - mDragState.overridePointerIconLocked(touchSource); - - // remember the thumb offsets for later - mDragState.mThumbOffsetX = thumbCenterX; - mDragState.mThumbOffsetY = thumbCenterY; - - // Make the surface visible at the proper location - final SurfaceControl surfaceControl = mDragState.mSurfaceControl; - if (SHOW_LIGHT_TRANSACTIONS) Slog.i( - TAG_WM, ">>> OPEN TRANSACTION performDrag"); - mService.openSurfaceTransaction(); - try { - surfaceControl.setPosition(touchX - thumbCenterX, - touchY - thumbCenterY); - surfaceControl.setLayer(mDragState.getDragLayerLocked()); - surfaceControl.setLayerStack(display.getLayerStack()); - surfaceControl.show(); - } finally { - mService.closeSurfaceTransaction("performDrag"); - if (SHOW_LIGHT_TRANSACTIONS) Slog.i( - TAG_WM, "<<< CLOSE TRANSACTION performDrag"); - } + mDragState.mDisplayContent = displayContent; + mDragState.mData = data; + mDragState.broadcastDragStartedLocked(touchX, touchY); + mDragState.overridePointerIconLocked(touchSource); + + // remember the thumb offsets for later + mDragState.mThumbOffsetX = thumbCenterX; + mDragState.mThumbOffsetY = thumbCenterY; + + // Make the surface visible at the proper location + final SurfaceControl surfaceControl = mDragState.mSurfaceControl; + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag"); + mService.openSurfaceTransaction(); + try { + surfaceControl.setPosition(touchX - thumbCenterX, + touchY - thumbCenterY); + surfaceControl.setLayer(mDragState.getDragLayerLocked()); + surfaceControl.setLayerStack(display.getLayerStack()); + surfaceControl.show(); + } finally { + mService.closeSurfaceTransaction("performDrag"); + if (SHOW_LIGHT_TRANSACTIONS) { + Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); + } + } - mDragState.notifyLocationLocked(touchX, touchY); + mDragState.notifyLocationLocked(touchX, touchY); + } } return true; // success! @@ -214,32 +250,35 @@ class DragDropController { Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token); } - synchronized (mService.mWindowMap) { - if (mDragState == null) { - // Most likely the drop recipient ANRed and we ended the drag - // out from under it. Log the issue and move on. - Slog.w(TAG_WM, "Drop result given but no drag in progress"); - return; - } + synchronized (mWriteLock) { + synchronized (mService.mWindowMap) { + if (mDragState == null) { + // Most likely the drop recipient ANRed and we ended the drag + // out from under it. Log the issue and move on. + Slog.w(TAG_WM, "Drop result given but no drag in progress"); + return; + } - if (mDragState.mToken != token) { - // We're in a drag, but the wrong window has responded. - Slog.w(TAG_WM, "Invalid drop-result claim by " + window); - throw new IllegalStateException("reportDropResult() by non-recipient"); - } + if (mDragState.mToken != token) { + // We're in a drag, but the wrong window has responded. + Slog.w(TAG_WM, "Invalid drop-result claim by " + window); + throw new IllegalStateException("reportDropResult() by non-recipient"); + } + + // The right window has responded, even if it's no longer around, + // so be sure to halt the timeout even if the later WindowState + // lookup fails. + mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder()); + WindowState callingWin = mService.windowForClientLocked(null, window, false); + if (callingWin == null) { + Slog.w(TAG_WM, "Bad result-reporting window " + window); + return; // !!! TODO: throw here? + } - // The right window has responded, even if it's no longer around, - // so be sure to halt the timeout even if the later WindowState - // lookup fails. - mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder()); - WindowState callingWin = mService.windowForClientLocked(null, window, false); - if (callingWin == null) { - Slog.w(TAG_WM, "Bad result-reporting window " + window); - return; // !!! TODO: throw here? - } - mDragState.mDragResult = consumed; - mDragState.endDragLocked(); + mDragState.mDragResult = consumed; + mDragState.endDragLocked(); + } } } @@ -248,21 +287,23 @@ class DragDropController { Slog.d(TAG_WM, "cancelDragAndDrop"); } - synchronized (mService.mWindowMap) { - if (mDragState == null) { - Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()"); - throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()"); - } + synchronized (mWriteLock) { + synchronized (mService.mWindowMap) { + if (mDragState == null) { + Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()"); + throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()"); + } - if (mDragState.mToken != dragToken) { - Slog.w(TAG_WM, - "cancelDragAndDrop() does not match prepareDrag()"); - throw new IllegalStateException( - "cancelDragAndDrop() does not match prepareDrag()"); - } + if (mDragState.mToken != dragToken) { + Slog.w(TAG_WM, + "cancelDragAndDrop() does not match prepareDrag()"); + throw new IllegalStateException( + "cancelDragAndDrop() does not match prepareDrag()"); + } - mDragState.mDragResult = false; - mDragState.cancelDragLocked(); + mDragState.mDragResult = false; + mDragState.cancelDragLocked(); + } } } @@ -274,18 +315,20 @@ class DragDropController { * @param newY Y coordinate value in dp in the screen coordinate */ void handleMotionEvent(boolean keepHandling, float newX, float newY) { - synchronized (mService.mWindowMap) { - if (!dragDropActiveLocked()) { - // The drag has ended but the clean-up message has not been processed by - // window manager. Drop events that occur after this until window manager - // has a chance to clean-up the input handle. - return; - } + synchronized (mWriteLock) { + synchronized (mService.mWindowMap) { + if (!dragDropActiveLocked()) { + // The drag has ended but the clean-up message has not been processed by + // window manager. Drop events that occur after this until window manager + // has a chance to clean-up the input handle. + return; + } - if (keepHandling) { - mDragState.notifyMoveLocked(newX, newY); - } else { - mDragState.notifyDropLocked(newX, newY); + if (keepHandling) { + mDragState.notifyMoveLocked(newX, newY); + } else { + mDragState.notifyDropLocked(newX, newY); + } } } } @@ -348,25 +391,29 @@ class DragDropController { if (DEBUG_DRAG) { Slog.w(TAG_WM, "Timeout starting drag by win " + win); } - synchronized (mService.mWindowMap) { - // !!! TODO: ANR the app that has failed to start the drag in time - if (mDragState != null) { - mDragState.closeLocked(); + synchronized (mWriteLock) { + synchronized (mService.mWindowMap) { + // !!! TODO: ANR the app that has failed to start the drag in time + if (mDragState != null) { + mDragState.closeLocked(); + } } } break; } case MSG_DRAG_END_TIMEOUT: { - IBinder win = (IBinder) msg.obj; + final IBinder win = (IBinder) msg.obj; if (DEBUG_DRAG) { Slog.w(TAG_WM, "Timeout ending drag to win " + win); } - synchronized (mService.mWindowMap) { - // !!! TODO: ANR the drag-receiving app - if (mDragState != null) { - mDragState.mDragResult = false; - mDragState.endDragLocked(); + synchronized (mWriteLock) { + synchronized (mService.mWindowMap) { + // !!! TODO: ANR the drag-receiving app + if (mDragState != null) { + mDragState.mDragResult = false; + mDragState.endDragLocked(); + } } } break; @@ -375,23 +422,25 @@ class DragDropController { case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: { if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag ending; tearing down input channel"); - DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj; - if (interceptor != null) { - synchronized (mService.mWindowMap) { - interceptor.tearDown(); - } + final DragState.InputInterceptor interceptor = + (DragState.InputInterceptor) msg.obj; + if (interceptor == null) return; + synchronized (mService.mWindowMap) { + interceptor.tearDown(); } break; } case MSG_ANIMATION_END: { - synchronized (mService.mWindowMap) { - if (mDragState == null) { - Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " + - "plyaing animation"); - return; + synchronized (mWriteLock) { + synchronized (mService.mWindowMap) { + if (mDragState == null) { + Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " + + "plyaing animation"); + return; + } + mDragState.closeLocked(); } - mDragState.closeLocked(); } break; } |