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