Loading services/core/java/com/android/server/wm/DragDropController.java +214 −165 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; Loading @@ -85,8 +119,7 @@ class DragDropController { + " asbinder=" + window.asBinder()); } IBinder token = null; synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (dragDropActiveLocked()) { Slog.w(TAG_WM, "Drag already in progress"); Loading @@ -110,10 +143,11 @@ class DragDropController { } surface.setAlpha(alpha); if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE"); if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE"); outSurface.copyFrom(surface); final IBinder winBinder = window.asBinder(); token = new Binder(); IBinder token = new Binder(); mDragState = new DragState(mService, token, surface, flags, winBinder); mDragState.mPid = callerPid; mDragState.mUid = callerUid; Loading @@ -122,10 +156,10 @@ class DragDropController { // 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, int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, Loading @@ -134,6 +168,7 @@ class DragDropController { Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data); } synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (mDragState == null) { Slog.w(TAG_WM, "No drag prepared"); Loading Loading @@ -187,8 +222,7 @@ class DragDropController { // Make the surface visible at the proper location final SurfaceControl surfaceControl = mDragState.mSurfaceControl; if (SHOW_LIGHT_TRANSACTIONS) Slog.i( TAG_WM, ">>> OPEN TRANSACTION performDrag"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag"); mService.openSurfaceTransaction(); try { surfaceControl.setPosition(touchX - thumbCenterX, Loading @@ -198,12 +232,14 @@ class DragDropController { surfaceControl.show(); } finally { mService.closeSurfaceTransaction("performDrag"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i( TAG_WM, "<<< CLOSE TRANSACTION performDrag"); if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); } } mDragState.notifyLocationLocked(touchX, touchY); } } return true; // success! } Loading @@ -214,6 +250,7 @@ class DragDropController { Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token); } synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (mDragState == null) { // Most likely the drop recipient ANRed and we ended the drag Loading @@ -238,16 +275,19 @@ class DragDropController { return; // !!! TODO: throw here? } mDragState.mDragResult = consumed; mDragState.endDragLocked(); } } } void cancelDragAndDrop(IBinder dragToken) { if (DEBUG_DRAG) { Slog.d(TAG_WM, "cancelDragAndDrop"); } synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (mDragState == null) { Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()"); Loading @@ -265,6 +305,7 @@ class DragDropController { mDragState.cancelDragLocked(); } } } /** * Handles motion events. Loading @@ -274,6 +315,7 @@ class DragDropController { * @param newY Y coordinate value in dp in the screen coordinate */ void handleMotionEvent(boolean keepHandling, float newX, float newY) { synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (!dragDropActiveLocked()) { // The drag has ended but the clean-up message has not been processed by Loading @@ -289,6 +331,7 @@ class DragDropController { } } } } void dragRecipientEntered(IWindow window) { if (DEBUG_DRAG) { Loading Loading @@ -348,20 +391,23 @@ class DragDropController { if (DEBUG_DRAG) { Slog.w(TAG_WM, "Timeout starting drag by win " + win); } 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 (mWriteLock) { synchronized (mService.mWindowMap) { // !!! TODO: ANR the drag-receiving app if (mDragState != null) { Loading @@ -369,22 +415,24 @@ class DragDropController { mDragState.endDragLocked(); } } } break; } 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) { final DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj; if (interceptor == null) return; synchronized (mService.mWindowMap) { interceptor.tearDown(); } } break; } case MSG_ANIMATION_END: { synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (mDragState == null) { Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " + Loading @@ -393,6 +441,7 @@ class DragDropController { } mDragState.closeLocked(); } } break; } } Loading Loading
services/core/java/com/android/server/wm/DragDropController.java +214 −165 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; Loading @@ -85,8 +119,7 @@ class DragDropController { + " asbinder=" + window.asBinder()); } IBinder token = null; synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (dragDropActiveLocked()) { Slog.w(TAG_WM, "Drag already in progress"); Loading @@ -110,10 +143,11 @@ class DragDropController { } surface.setAlpha(alpha); if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE"); if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE"); outSurface.copyFrom(surface); final IBinder winBinder = window.asBinder(); token = new Binder(); IBinder token = new Binder(); mDragState = new DragState(mService, token, surface, flags, winBinder); mDragState.mPid = callerPid; mDragState.mUid = callerUid; Loading @@ -122,10 +156,10 @@ class DragDropController { // 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, int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, Loading @@ -134,6 +168,7 @@ class DragDropController { Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data); } synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (mDragState == null) { Slog.w(TAG_WM, "No drag prepared"); Loading Loading @@ -187,8 +222,7 @@ class DragDropController { // Make the surface visible at the proper location final SurfaceControl surfaceControl = mDragState.mSurfaceControl; if (SHOW_LIGHT_TRANSACTIONS) Slog.i( TAG_WM, ">>> OPEN TRANSACTION performDrag"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag"); mService.openSurfaceTransaction(); try { surfaceControl.setPosition(touchX - thumbCenterX, Loading @@ -198,12 +232,14 @@ class DragDropController { surfaceControl.show(); } finally { mService.closeSurfaceTransaction("performDrag"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i( TAG_WM, "<<< CLOSE TRANSACTION performDrag"); if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); } } mDragState.notifyLocationLocked(touchX, touchY); } } return true; // success! } Loading @@ -214,6 +250,7 @@ class DragDropController { Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token); } synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (mDragState == null) { // Most likely the drop recipient ANRed and we ended the drag Loading @@ -238,16 +275,19 @@ class DragDropController { return; // !!! TODO: throw here? } mDragState.mDragResult = consumed; mDragState.endDragLocked(); } } } void cancelDragAndDrop(IBinder dragToken) { if (DEBUG_DRAG) { Slog.d(TAG_WM, "cancelDragAndDrop"); } synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (mDragState == null) { Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()"); Loading @@ -265,6 +305,7 @@ class DragDropController { mDragState.cancelDragLocked(); } } } /** * Handles motion events. Loading @@ -274,6 +315,7 @@ class DragDropController { * @param newY Y coordinate value in dp in the screen coordinate */ void handleMotionEvent(boolean keepHandling, float newX, float newY) { synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (!dragDropActiveLocked()) { // The drag has ended but the clean-up message has not been processed by Loading @@ -289,6 +331,7 @@ class DragDropController { } } } } void dragRecipientEntered(IWindow window) { if (DEBUG_DRAG) { Loading Loading @@ -348,20 +391,23 @@ class DragDropController { if (DEBUG_DRAG) { Slog.w(TAG_WM, "Timeout starting drag by win " + win); } 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 (mWriteLock) { synchronized (mService.mWindowMap) { // !!! TODO: ANR the drag-receiving app if (mDragState != null) { Loading @@ -369,22 +415,24 @@ class DragDropController { mDragState.endDragLocked(); } } } break; } 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) { final DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj; if (interceptor == null) return; synchronized (mService.mWindowMap) { interceptor.tearDown(); } } break; } case MSG_ANIMATION_END: { synchronized (mWriteLock) { synchronized (mService.mWindowMap) { if (mDragState == null) { Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " + Loading @@ -393,6 +441,7 @@ class DragDropController { } mDragState.closeLocked(); } } break; } } Loading