1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 20 import static android.view.View.DRAG_FLAG_GLOBAL; 21 import static android.view.View.DRAG_FLAG_GLOBAL_SAME_APPLICATION; 22 import static android.view.View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG; 23 24 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; 25 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; 26 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 27 28 import android.annotation.NonNull; 29 import android.content.ClipData; 30 import android.content.Context; 31 import android.hardware.input.InputManagerGlobal; 32 import android.os.Binder; 33 import android.os.Handler; 34 import android.os.IBinder; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.os.RemoteException; 38 import android.os.Trace; 39 import android.util.Slog; 40 import android.view.Display; 41 import android.view.DragEvent; 42 import android.view.IWindow; 43 import android.view.InputDevice; 44 import android.view.PointerIcon; 45 import android.view.SurfaceControl; 46 import android.view.View; 47 import android.view.accessibility.AccessibilityManager; 48 import android.window.IGlobalDragListener; 49 import android.window.IUnhandledDragCallback; 50 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.server.wm.WindowManagerInternal.IDragDropCallback; 53 54 import java.util.Objects; 55 import java.util.Random; 56 import java.util.concurrent.CompletableFuture; 57 import java.util.concurrent.TimeUnit; 58 import java.util.concurrent.atomic.AtomicReference; 59 60 /** 61 * Managing drag and drop operations initiated by View#startDragAndDrop. 62 */ 63 class DragDropController { 64 private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f; 65 static final long DRAG_TIMEOUT_MS = 5000; 66 private static final int A11Y_DRAG_TIMEOUT_DEFAULT_MS = 60000; 67 68 // Messages for Handler. 69 static final int MSG_DRAG_END_TIMEOUT = 0; 70 static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 1; 71 static final int MSG_ANIMATION_END = 2; 72 static final int MSG_REMOVE_DRAG_SURFACE_TIMEOUT = 3; 73 static final int MSG_UNHANDLED_DROP_LISTENER_TIMEOUT = 4; 74 75 /** 76 * Drag state per operation. 77 * Needs a lock of {@code WindowManagerService#mWindowMap} to read this. Needs both locks of 78 * {@code mWriteLock} and {@code WindowManagerService#mWindowMap} to update this. 79 * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState 80 * itself, thus the variable can be null after calling DragState's methods. 81 */ 82 private DragState mDragState; 83 84 private WindowManagerService mService; 85 private final Handler mHandler; 86 87 // The global drag listener for handling cross-window drags 88 private IGlobalDragListener mGlobalDragListener; 89 private final IBinder.DeathRecipient mGlobalDragListenerDeathRecipient = 90 new IBinder.DeathRecipient() { 91 @Override 92 public void binderDied() { 93 synchronized (mService.mGlobalLock) { 94 if (hasPendingUnhandledDropCallback()) { 95 onUnhandledDropCallback(false /* consumedByListeners */); 96 } 97 setGlobalDragListener(null); 98 } 99 } 100 }; 101 102 /** 103 * Callback which is used to sync drag state with the vendor-specific code. 104 */ 105 @NonNull private AtomicReference<IDragDropCallback> mCallback = new AtomicReference<>( 106 new IDragDropCallback() {}); 107 DragDropController(WindowManagerService service, Looper looper)108 DragDropController(WindowManagerService service, Looper looper) { 109 mService = service; 110 mHandler = new DragHandler(service, looper); 111 } 112 113 @VisibleForTesting getHandler()114 Handler getHandler() { 115 return mHandler; 116 } 117 dragDropActiveLocked()118 boolean dragDropActiveLocked() { 119 return mDragState != null && !mDragState.isClosing(); 120 } 121 122 @VisibleForTesting dragSurfaceRelinquishedToDropTarget()123 boolean dragSurfaceRelinquishedToDropTarget() { 124 return mDragState != null && mDragState.mRelinquishDragSurfaceToDropTarget; 125 } 126 registerCallback(IDragDropCallback callback)127 void registerCallback(IDragDropCallback callback) { 128 Objects.requireNonNull(callback); 129 mCallback.set(callback); 130 } 131 132 /** 133 * Sets the listener for unhandled cross-window drags. 134 */ setGlobalDragListener(IGlobalDragListener listener)135 public void setGlobalDragListener(IGlobalDragListener listener) { 136 if (mGlobalDragListener != null && mGlobalDragListener.asBinder() != null) { 137 mGlobalDragListener.asBinder().unlinkToDeath( 138 mGlobalDragListenerDeathRecipient, 0); 139 } 140 mGlobalDragListener = listener; 141 if (listener != null && listener.asBinder() != null) { 142 try { 143 mGlobalDragListener.asBinder().linkToDeath( 144 mGlobalDragListenerDeathRecipient, 0); 145 } catch (RemoteException e) { 146 mGlobalDragListener = null; 147 } 148 } 149 } 150 sendDragStartedIfNeededLocked(WindowState window)151 void sendDragStartedIfNeededLocked(WindowState window) { 152 mDragState.sendDragStartedIfNeededLocked(window); 153 } 154 performDrag(int callerPid, int callerUid, IWindow window, int flags, SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data)155 IBinder performDrag(int callerPid, int callerUid, IWindow window, int flags, 156 SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId, 157 float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) { 158 if (DEBUG_DRAG) { 159 Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" + 160 Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + "," 161 + touchY + ") thumb center(" + thumbCenterX + "," + thumbCenterY + ")"); 162 } 163 164 final IBinder dragToken = new Binder(); 165 final boolean callbackResult = mCallback.get().prePerformDrag(window, dragToken, 166 touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data); 167 try { 168 DisplayContent displayContent = null; 169 CompletableFuture<Boolean> touchFocusTransferredFuture = null; 170 synchronized (mService.mGlobalLock) { 171 try { 172 if (!callbackResult) { 173 Slog.w(TAG_WM, "IDragDropCallback rejects the performDrag request"); 174 return null; 175 } 176 177 if (dragDropActiveLocked()) { 178 Slog.w(TAG_WM, "Drag already in progress"); 179 return null; 180 } 181 182 final WindowState callingWin = mService.windowForClientLocked( 183 null, window, false); 184 if (callingWin == null || !callingWin.canReceiveTouchInput()) { 185 Slog.w(TAG_WM, "Bad requesting window " + window); 186 return null; // !!! TODO: throw here? 187 } 188 189 // !!! TODO: if input is not still focused on the initiating window, fail 190 // the drag initiation (e.g. an alarm window popped up just as the application 191 // called performDrag() 192 193 // !!! TODO: extract the current touch (x, y) in screen coordinates. That 194 // will let us eliminate the (touchX,touchY) parameters from the API. 195 196 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as 197 // the actual drag event dispatch stuff in the dragstate 198 199 // !!! TODO(multi-display): support other displays 200 201 displayContent = callingWin.getDisplayContent(); 202 if (displayContent == null) { 203 Slog.w(TAG_WM, "display content is null"); 204 return null; 205 } 206 207 final float alpha = (flags & View.DRAG_FLAG_OPAQUE) == 0 ? 208 DRAG_SHADOW_ALPHA_TRANSPARENT : 1; 209 final IBinder winBinder = window.asBinder(); 210 IBinder token = new Binder(); 211 mDragState = new DragState(mService, this, token, surface, flags, winBinder); 212 surface = null; 213 mDragState.mPid = callerPid; 214 mDragState.mUid = callerUid; 215 mDragState.mOriginalAlpha = alpha; 216 mDragState.mAnimatedScale = callingWin.mGlobalScale; 217 mDragState.mToken = dragToken; 218 mDragState.mDisplayContent = displayContent; 219 mDragState.mData = data; 220 221 if ((flags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) == 0) { 222 final Display display = displayContent.getDisplay(); 223 touchFocusTransferredFuture = mCallback.get().registerInputChannel( 224 mDragState, display, mService.mInputManager, 225 callingWin.mInputChannel); 226 } else { 227 // Skip surface logic for a drag triggered by an AccessibilityAction 228 mDragState.broadcastDragStartedLocked(touchX, touchY); 229 230 // Timeout for the user to drop the content 231 sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, callingWin.mClient.asBinder(), 232 getAccessibilityManager().getRecommendedTimeoutMillis( 233 A11Y_DRAG_TIMEOUT_DEFAULT_MS, 234 AccessibilityManager.FLAG_CONTENT_CONTROLS)); 235 236 return dragToken; 237 } 238 } finally { 239 if (surface != null) { 240 try (final SurfaceControl.Transaction transaction = 241 mService.mTransactionFactory.get()) { 242 transaction.remove(surface); 243 transaction.apply(); 244 } 245 } 246 } 247 } 248 249 boolean touchFocusTransferred = false; 250 try { 251 touchFocusTransferred = touchFocusTransferredFuture.get(DRAG_TIMEOUT_MS, 252 TimeUnit.MILLISECONDS); 253 } catch (Exception exception) { 254 Slog.e(TAG_WM, "Exception thrown while waiting for touch focus transfer", 255 exception); 256 } 257 258 synchronized (mService.mGlobalLock) { 259 if (!touchFocusTransferred) { 260 Slog.e(TAG_WM, "Unable to transfer touch focus"); 261 mDragState.closeLocked(); 262 return null; 263 } 264 265 final SurfaceControl surfaceControl = mDragState.mSurfaceControl; 266 mDragState.broadcastDragStartedLocked(touchX, touchY); 267 if ((touchSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { 268 InputManagerGlobal.getInstance().setPointerIcon( 269 PointerIcon.getSystemIcon( 270 mService.mContext, PointerIcon.TYPE_GRABBING), 271 mDragState.mDisplayContent.getDisplayId(), touchDeviceId, 272 touchPointerId, mDragState.getInputToken()); 273 } 274 // remember the thumb offsets for later 275 mDragState.mThumbOffsetX = thumbCenterX; 276 mDragState.mThumbOffsetY = thumbCenterY; 277 278 // Make the surface visible at the proper location 279 if (SHOW_LIGHT_TRANSACTIONS) { 280 Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag"); 281 } 282 283 final SurfaceControl.Transaction transaction = mDragState.mTransaction; 284 transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha); 285 transaction.show(surfaceControl); 286 displayContent.reparentToOverlay(transaction, surfaceControl); 287 mDragState.updateDragSurfaceLocked(true, touchX, touchY); 288 if (SHOW_LIGHT_TRANSACTIONS) { 289 Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); 290 } 291 } 292 return dragToken; // success! 293 } finally { 294 mCallback.get().postPerformDrag(); 295 } 296 } 297 298 /** 299 * This is called from the drop target window that received ACTION_DROP 300 * (see DragState#reportDropWindowLock()). 301 */ reportDropResult(IWindow window, boolean consumed)302 void reportDropResult(IWindow window, boolean consumed) { 303 IBinder token = window.asBinder(); 304 if (DEBUG_DRAG) { 305 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token); 306 } 307 308 mCallback.get().preReportDropResult(window, consumed); 309 try { 310 synchronized (mService.mGlobalLock) { 311 if (mDragState == null) { 312 // Most likely the drop recipient ANRed and we ended the drag 313 // out from under it. Log the issue and move on. 314 Slog.w(TAG_WM, "Drop result given but no drag in progress"); 315 return; 316 } 317 318 if (mDragState.mToken != token) { 319 // We're in a drag, but the wrong window has responded. 320 Slog.w(TAG_WM, "Invalid drop-result claim by " + window); 321 throw new IllegalStateException("reportDropResult() by non-recipient"); 322 } 323 324 // The right window has responded, even if it's no longer around, 325 // so be sure to halt the timeout even if the later WindowState 326 // lookup fails. 327 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder()); 328 329 WindowState callingWin = mService.windowForClientLocked(null, window, false); 330 if (callingWin == null) { 331 Slog.w(TAG_WM, "Bad result-reporting window " + window); 332 return; // !!! TODO: throw here? 333 } 334 335 // If the drop was not consumed by the target window, then check if it should be 336 // consumed by the system unhandled drag listener 337 if (!consumed && notifyUnhandledDrop(mDragState.mUnhandledDropEvent, 338 "window-drop")) { 339 // If the unhandled drag listener is notified, then defer ending the drag until 340 // the listener calls back 341 return; 342 } 343 344 final boolean relinquishDragSurfaceToDropTarget = 345 consumed && mDragState.targetInterceptsGlobalDrag(callingWin); 346 final boolean isCrossWindowDrag = !mDragState.mLocalWin.equals(token); 347 mDragState.endDragLocked(consumed, relinquishDragSurfaceToDropTarget); 348 349 final Task droppedWindowTask = callingWin.getTask(); 350 if (com.android.window.flags.Flags.delegateUnhandledDrags() 351 && mGlobalDragListener != null && droppedWindowTask != null && consumed 352 && isCrossWindowDrag) { 353 try { 354 mGlobalDragListener.onCrossWindowDrop(droppedWindowTask.getTaskInfo()); 355 } catch (RemoteException e) { 356 Slog.e(TAG_WM, "Failed to call global drag listener for cross-window " 357 + "drop", e); 358 } 359 } 360 } 361 } finally { 362 mCallback.get().postReportDropResult(); 363 } 364 } 365 366 /** 367 * Notifies the unhandled drag listener if needed. 368 * @return whether the listener was notified and subsequent drag completion should be deferred 369 * until the listener calls back 370 */ notifyUnhandledDrop(DragEvent dropEvent, String reason)371 boolean notifyUnhandledDrop(DragEvent dropEvent, String reason) { 372 final boolean isLocalDrag = 373 (mDragState.mFlags & (DRAG_FLAG_GLOBAL_SAME_APPLICATION | DRAG_FLAG_GLOBAL)) == 0; 374 final boolean shouldDelegateUnhandledDrag = 375 (mDragState.mFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0; 376 if (!com.android.window.flags.Flags.delegateUnhandledDrags() 377 || mGlobalDragListener == null 378 || !shouldDelegateUnhandledDrag 379 || isLocalDrag) { 380 // Skip if the flag is disabled, there is no unhandled-drag listener, or if this is a 381 // purely local drag 382 if (DEBUG_DRAG) Slog.d(TAG_WM, "Skipping unhandled listener " 383 + "(listener=" + mGlobalDragListener + ", flags=" + mDragState.mFlags + ")"); 384 return false; 385 } 386 final int traceCookie = new Random().nextInt(); 387 Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#notifyUnhandledDrop", 388 traceCookie); 389 if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to unhandled listener (" + reason + ")"); 390 try { 391 // Schedule timeout for the unhandled drag listener to call back 392 sendTimeoutMessage(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT, null, DRAG_TIMEOUT_MS); 393 mGlobalDragListener.onUnhandledDrop(dropEvent, new IUnhandledDragCallback.Stub() { 394 @Override 395 public void notifyUnhandledDropComplete(boolean consumedByListener) { 396 if (DEBUG_DRAG) Slog.d(TAG_WM, "Unhandled listener finished handling DROP"); 397 synchronized (mService.mGlobalLock) { 398 onUnhandledDropCallback(consumedByListener); 399 Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, 400 "DragDropController#notifyUnhandledDrop", traceCookie); 401 } 402 } 403 }); 404 return true; 405 } catch (RemoteException e) { 406 Slog.e(TAG_WM, "Failed to call global drag listener for unhandled drop", e); 407 return false; 408 } 409 } 410 411 /** 412 * Called when the unhandled drag listener has completed handling the drop 413 * (if it was notififed). 414 */ 415 @VisibleForTesting onUnhandledDropCallback(boolean consumedByListener)416 void onUnhandledDropCallback(boolean consumedByListener) { 417 mHandler.removeMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT, null); 418 // If handled, then the listeners assume responsibility of cleaning up the drag surface 419 mDragState.mDragResult = consumedByListener; 420 mDragState.mRelinquishDragSurfaceToDropTarget = consumedByListener; 421 mDragState.closeLocked(); 422 } 423 424 /** 425 * Returns whether we are currently waiting for the unhandled drag listener to callback after 426 * it was notified of an unhandled drag. 427 */ hasPendingUnhandledDropCallback()428 boolean hasPendingUnhandledDropCallback() { 429 return mHandler.hasMessages(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT); 430 } 431 cancelDragAndDrop(IBinder dragToken, boolean skipAnimation)432 void cancelDragAndDrop(IBinder dragToken, boolean skipAnimation) { 433 if (DEBUG_DRAG) { 434 Slog.d(TAG_WM, "cancelDragAndDrop"); 435 } 436 437 mCallback.get().preCancelDragAndDrop(dragToken); 438 try { 439 synchronized (mService.mGlobalLock) { 440 if (mDragState == null) { 441 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()"); 442 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()"); 443 } 444 445 if (mDragState.mToken != dragToken) { 446 Slog.w(TAG_WM, 447 "cancelDragAndDrop() does not match prepareDrag()"); 448 throw new IllegalStateException( 449 "cancelDragAndDrop() does not match prepareDrag()"); 450 } 451 452 mDragState.mDragResult = false; 453 mDragState.cancelDragLocked(skipAnimation); 454 } 455 } finally { 456 mCallback.get().postCancelDragAndDrop(); 457 } 458 } 459 460 /** 461 * Handles motion events. 462 * @param keepHandling Whether if the drag operation is continuing or this is the last motion 463 * event. 464 * @param newX X coordinate value in dp in the screen coordinate 465 * @param newY Y coordinate value in dp in the screen coordinate 466 */ handleMotionEvent(boolean keepHandling, float newX, float newY)467 void handleMotionEvent(boolean keepHandling, float newX, float newY) { 468 synchronized (mService.mGlobalLock) { 469 if (!dragDropActiveLocked()) { 470 // The drag has ended but the clean-up message has not been processed by 471 // window manager. Drop events that occur after this until window manager 472 // has a chance to clean-up the input handle. 473 return; 474 } 475 476 mDragState.updateDragSurfaceLocked(keepHandling, newX, newY); 477 } 478 } 479 dragRecipientEntered(IWindow window)480 void dragRecipientEntered(IWindow window) { 481 if (DEBUG_DRAG) { 482 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder()); 483 } 484 mCallback.get().dragRecipientEntered(window); 485 } 486 dragRecipientExited(IWindow window)487 void dragRecipientExited(IWindow window) { 488 if (DEBUG_DRAG) { 489 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder()); 490 } 491 mCallback.get().dragRecipientExited(window); 492 } 493 494 /** 495 * Sends a message to the Handler managed by DragDropController. 496 */ sendHandlerMessage(int what, Object arg)497 void sendHandlerMessage(int what, Object arg) { 498 mHandler.obtainMessage(what, arg).sendToTarget(); 499 } 500 501 /** 502 * Sends a timeout message to the Handler managed by DragDropController. 503 */ sendTimeoutMessage(int what, Object arg, long timeoutMs)504 void sendTimeoutMessage(int what, Object arg, long timeoutMs) { 505 mHandler.removeMessages(what, arg); 506 final Message msg = mHandler.obtainMessage(what, arg); 507 mHandler.sendMessageDelayed(msg, timeoutMs); 508 } 509 510 /** 511 * Notifies the current drag state is closed. 512 */ onDragStateClosedLocked(DragState dragState)513 void onDragStateClosedLocked(DragState dragState) { 514 if (mDragState != dragState) { 515 Slog.wtf(TAG_WM, "Unknown drag state is closed"); 516 return; 517 } 518 mDragState = null; 519 } 520 reportDropWindow(IBinder token, float x, float y)521 void reportDropWindow(IBinder token, float x, float y) { 522 if (mDragState == null) { 523 Slog.w(TAG_WM, "Drag state is closed."); 524 return; 525 } 526 527 synchronized (mService.mGlobalLock) { 528 mDragState.reportDropWindowLock(token, x, y); 529 } 530 } 531 dropForAccessibility(IWindow window, float x, float y)532 boolean dropForAccessibility(IWindow window, float x, float y) { 533 synchronized (mService.mGlobalLock) { 534 final boolean isA11yEnabled = getAccessibilityManager().isEnabled(); 535 if (!dragDropActiveLocked()) { 536 return false; 537 } 538 if (mDragState.isAccessibilityDragDrop() && isA11yEnabled) { 539 final WindowState winState = mService.windowForClientLocked( 540 null, window, false); 541 if (!mDragState.isWindowNotified(winState)) { 542 return false; 543 } 544 IBinder token = winState.mInputChannelToken; 545 return mDragState.reportDropWindowLock(token, x, y); 546 } 547 return false; 548 } 549 } 550 getAccessibilityManager()551 AccessibilityManager getAccessibilityManager() { 552 return (AccessibilityManager) mService.mContext.getSystemService( 553 Context.ACCESSIBILITY_SERVICE); 554 } 555 556 private class DragHandler extends Handler { 557 /** 558 * Lock for window manager. 559 */ 560 private final WindowManagerService mService; 561 DragHandler(WindowManagerService service, Looper looper)562 DragHandler(WindowManagerService service, Looper looper) { 563 super(looper); 564 mService = service; 565 } 566 567 @Override handleMessage(Message msg)568 public void handleMessage(Message msg) { 569 switch (msg.what) { 570 case MSG_DRAG_END_TIMEOUT: { 571 final IBinder win = (IBinder) msg.obj; 572 if (DEBUG_DRAG) { 573 Slog.w(TAG_WM, "Timeout ending drag to win " + win); 574 } 575 576 synchronized (mService.mGlobalLock) { 577 // !!! TODO: ANR the drag-receiving app 578 if (mDragState != null) { 579 mDragState.endDragLocked(false /* consumed */, 580 false /* relinquishDragSurfaceToDropTarget */); 581 } 582 } 583 break; 584 } 585 586 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: { 587 if (DEBUG_DRAG) 588 Slog.d(TAG_WM, "Drag ending; tearing down input channel"); 589 final DragState.InputInterceptor interceptor = 590 (DragState.InputInterceptor) msg.obj; 591 if (interceptor == null) return; 592 synchronized (mService.mGlobalLock) { 593 interceptor.tearDown(); 594 } 595 break; 596 } 597 598 case MSG_ANIMATION_END: { 599 synchronized (mService.mGlobalLock) { 600 if (mDragState == null) { 601 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " + 602 "playing animation"); 603 return; 604 } 605 mDragState.closeLocked(); 606 } 607 break; 608 } 609 610 case MSG_REMOVE_DRAG_SURFACE_TIMEOUT: { 611 synchronized (mService.mGlobalLock) { 612 mService.mTransactionFactory.get().remove((SurfaceControl) msg.obj).apply(); 613 } 614 break; 615 } 616 617 case MSG_UNHANDLED_DROP_LISTENER_TIMEOUT: { 618 synchronized (mService.mGlobalLock) { 619 onUnhandledDropCallback(false /* consumedByListener */); 620 } 621 break; 622 } 623 } 624 } 625 } 626 } 627