1 /* 2 * Copyright (C) 2011 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 com.android.server.wm.DragDropController.MSG_ANIMATION_END; 20 import static com.android.server.wm.DragDropController.MSG_DRAG_END_TIMEOUT; 21 import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT; 22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; 23 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; 24 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; 25 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 26 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 27 28 import android.animation.Animator; 29 import android.animation.PropertyValuesHolder; 30 import android.animation.ValueAnimator; 31 import android.annotation.Nullable; 32 import android.content.ClipData; 33 import android.content.ClipDescription; 34 import android.graphics.Point; 35 import android.graphics.Rect; 36 import android.hardware.input.InputManager; 37 import android.os.Binder; 38 import android.os.Build; 39 import android.os.IBinder; 40 import android.os.Process; 41 import android.os.RemoteException; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.os.UserManagerInternal; 45 import android.util.Slog; 46 import android.view.Display; 47 import android.view.DragEvent; 48 import android.view.InputApplicationHandle; 49 import android.view.InputChannel; 50 import android.view.InputDevice; 51 import android.view.InputWindowHandle; 52 import android.view.PointerIcon; 53 import android.view.SurfaceControl; 54 import android.view.View; 55 import android.view.WindowManager; 56 import android.view.animation.DecelerateInterpolator; 57 import android.view.animation.Interpolator; 58 59 import com.android.internal.view.IDragAndDropPermissions; 60 import com.android.server.LocalServices; 61 62 import java.util.ArrayList; 63 64 /** 65 * Drag/drop state 66 */ 67 class DragState { 68 private static final long MIN_ANIMATION_DURATION_MS = 195; 69 private static final long MAX_ANIMATION_DURATION_MS = 375; 70 71 private static final int DRAG_FLAGS_URI_ACCESS = View.DRAG_FLAG_GLOBAL_URI_READ | 72 View.DRAG_FLAG_GLOBAL_URI_WRITE; 73 74 private static final int DRAG_FLAGS_URI_PERMISSIONS = DRAG_FLAGS_URI_ACCESS | 75 View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION | 76 View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION; 77 78 // Property names for animations 79 private static final String ANIMATED_PROPERTY_X = "x"; 80 private static final String ANIMATED_PROPERTY_Y = "y"; 81 private static final String ANIMATED_PROPERTY_ALPHA = "alpha"; 82 private static final String ANIMATED_PROPERTY_SCALE = "scale"; 83 84 final WindowManagerService mService; 85 final DragDropController mDragDropController; 86 IBinder mToken; 87 /** 88 * Do not use the variable from the out of animation thread while mAnimator is not null. 89 */ 90 SurfaceControl mSurfaceControl; 91 int mFlags; 92 IBinder mLocalWin; 93 int mPid; 94 int mUid; 95 int mSourceUserId; 96 boolean mCrossProfileCopyAllowed; 97 ClipData mData; 98 ClipDescription mDataDescription; 99 int mTouchSource; 100 boolean mDragResult; 101 float mOriginalAlpha; 102 float mOriginalX, mOriginalY; 103 float mCurrentX, mCurrentY; 104 float mThumbOffsetX, mThumbOffsetY; 105 InputInterceptor mInputInterceptor; 106 WindowState mTargetWindow; 107 ArrayList<WindowState> mNotifiedWindows; 108 boolean mDragInProgress; 109 /** 110 * Whether if animation is completed. Needs to be volatile to update from the animation thread 111 * without having a WM lock. 112 */ 113 volatile boolean mAnimationCompleted = false; 114 DisplayContent mDisplayContent; 115 116 @Nullable private ValueAnimator mAnimator; 117 private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); 118 private Point mDisplaySize = new Point(); 119 120 // A surface used to catch input events for the drag-and-drop operation. 121 SurfaceControl mInputSurface; 122 123 private final SurfaceControl.Transaction mTransaction; 124 125 private final Rect mTmpClipRect = new Rect(); 126 127 /** 128 * Whether we are finishing this drag and drop. This starts with {@code false}, and is set to 129 * {@code true} when {@link #closeLocked()} is called. 130 */ 131 private boolean mIsClosing; 132 IBinder mTransferTouchFromToken; 133 DragState(WindowManagerService service, DragDropController controller, IBinder token, SurfaceControl surface, int flags, IBinder localWin)134 DragState(WindowManagerService service, DragDropController controller, IBinder token, 135 SurfaceControl surface, int flags, IBinder localWin) { 136 mService = service; 137 mDragDropController = controller; 138 mToken = token; 139 mSurfaceControl = surface; 140 mFlags = flags; 141 mLocalWin = localWin; 142 mNotifiedWindows = new ArrayList<WindowState>(); 143 mTransaction = service.mTransactionFactory.make(); 144 } 145 isClosing()146 boolean isClosing() { 147 return mIsClosing; 148 } 149 hideInputSurface()150 private void hideInputSurface() { 151 if (mInputSurface != null) { 152 mTransaction.hide(mInputSurface).apply(); 153 } 154 } 155 showInputSurface()156 private void showInputSurface() { 157 if (mInputSurface == null) { 158 mInputSurface = mService.makeSurfaceBuilder( 159 mService.mRoot.getDisplayContent(mDisplayContent.getDisplayId()).getSession()) 160 .setContainerLayer() 161 .setName("Drag and Drop Input Consumer").build(); 162 } 163 final InputWindowHandle h = getInputWindowHandle(); 164 if (h == null) { 165 Slog.w(TAG_WM, "Drag is in progress but there is no " 166 + "drag window handle."); 167 return; 168 } 169 170 mTransaction.show(mInputSurface); 171 mTransaction.setInputWindowInfo(mInputSurface, h); 172 mTransaction.setLayer(mInputSurface, Integer.MAX_VALUE); 173 174 mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); 175 mTransaction.setWindowCrop(mInputSurface, mTmpClipRect); 176 mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token); 177 mTransferTouchFromToken = null; 178 179 // syncInputWindows here to ensure the input channel isn't removed before the transfer. 180 mTransaction.syncInputWindows(); 181 mTransaction.apply(); 182 } 183 184 /** 185 * After calling this, DragDropController#onDragStateClosedLocked is invoked, which causes 186 * DragDropController#mDragState becomes null. 187 */ closeLocked()188 void closeLocked() { 189 mIsClosing = true; 190 // Unregister the input interceptor. 191 if (mInputInterceptor != null) { 192 if (DEBUG_DRAG) 193 Slog.d(TAG_WM, "unregistering drag input channel"); 194 195 // Input channel should be disposed on the thread where the input is being handled. 196 mDragDropController.sendHandlerMessage( 197 MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor); 198 mInputInterceptor = null; 199 } 200 201 hideInputSurface(); 202 203 // Send drag end broadcast if drag start has been sent. 204 if (mDragInProgress) { 205 final int myPid = Process.myPid(); 206 207 if (DEBUG_DRAG) { 208 Slog.d(TAG_WM, "broadcasting DRAG_ENDED"); 209 } 210 for (WindowState ws : mNotifiedWindows) { 211 float x = 0; 212 float y = 0; 213 if (!mDragResult && (ws.mSession.mPid == mPid)) { 214 // Report unconsumed drop location back to the app that started the drag. 215 x = mCurrentX; 216 y = mCurrentY; 217 } 218 DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, 219 x, y, null, null, null, null, mDragResult); 220 try { 221 ws.mClient.dispatchDragEvent(evt); 222 } catch (RemoteException e) { 223 Slog.w(TAG_WM, "Unable to drag-end window " + ws); 224 } 225 // if the current window is in the same process, 226 // the dispatch has already recycled the event 227 if (myPid != ws.mSession.mPid) { 228 evt.recycle(); 229 } 230 } 231 mNotifiedWindows.clear(); 232 mDragInProgress = false; 233 } 234 235 // Take the cursor back if it has been changed. 236 if (isFromSource(InputDevice.SOURCE_MOUSE)) { 237 mService.restorePointerIconLocked(mDisplayContent, mCurrentX, mCurrentY); 238 mTouchSource = 0; 239 } 240 241 // Clear the internal variables. 242 if (mSurfaceControl != null) { 243 mTransaction.reparent(mSurfaceControl, null).apply(); 244 mSurfaceControl = null; 245 } 246 if (mAnimator != null && !mAnimationCompleted) { 247 Slog.wtf(TAG_WM, 248 "Unexpectedly destroying mSurfaceControl while animation is running"); 249 } 250 mFlags = 0; 251 mLocalWin = null; 252 mToken = null; 253 mData = null; 254 mThumbOffsetX = mThumbOffsetY = 0; 255 mNotifiedWindows = null; 256 257 // Notifies the controller that the drag state is closed. 258 mDragDropController.onDragStateClosedLocked(this); 259 } 260 261 class InputInterceptor { 262 InputChannel mServerChannel, mClientChannel; 263 DragInputEventReceiver mInputEventReceiver; 264 InputApplicationHandle mDragApplicationHandle; 265 InputWindowHandle mDragWindowHandle; 266 InputInterceptor(Display display)267 InputInterceptor(Display display) { 268 InputChannel[] channels = InputChannel.openInputChannelPair("drag"); 269 mServerChannel = channels[0]; 270 mClientChannel = channels[1]; 271 mService.mInputManager.registerInputChannel(mServerChannel, null); 272 mInputEventReceiver = new DragInputEventReceiver(mClientChannel, 273 mService.mH.getLooper(), mDragDropController); 274 275 mDragApplicationHandle = new InputApplicationHandle(new Binder()); 276 mDragApplicationHandle.name = "drag"; 277 mDragApplicationHandle.dispatchingTimeoutNanos = 278 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; 279 280 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, 281 display.getDisplayId()); 282 mDragWindowHandle.name = "drag"; 283 mDragWindowHandle.token = mServerChannel.getToken(); 284 mDragWindowHandle.layer = getDragLayerLocked(); 285 mDragWindowHandle.layoutParamsFlags = 0; 286 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; 287 mDragWindowHandle.dispatchingTimeoutNanos = 288 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; 289 mDragWindowHandle.visible = true; 290 mDragWindowHandle.canReceiveKeys = false; 291 mDragWindowHandle.hasFocus = true; 292 mDragWindowHandle.hasWallpaper = false; 293 mDragWindowHandle.paused = false; 294 mDragWindowHandle.ownerPid = Process.myPid(); 295 mDragWindowHandle.ownerUid = Process.myUid(); 296 mDragWindowHandle.inputFeatures = 0; 297 mDragWindowHandle.scaleFactor = 1.0f; 298 299 // The drag window cannot receive new touches. 300 mDragWindowHandle.touchableRegion.setEmpty(); 301 302 // The drag window covers the entire display 303 mDragWindowHandle.frameLeft = 0; 304 mDragWindowHandle.frameTop = 0; 305 mDragWindowHandle.frameRight = mDisplaySize.x; 306 mDragWindowHandle.frameBottom = mDisplaySize.y; 307 308 // Pause rotations before a drag. 309 if (DEBUG_ORIENTATION) { 310 Slog.d(TAG_WM, "Pausing rotation during drag"); 311 } 312 mDisplayContent.pauseRotationLocked(); 313 } 314 tearDown()315 void tearDown() { 316 mService.mInputManager.unregisterInputChannel(mServerChannel); 317 mInputEventReceiver.dispose(); 318 mInputEventReceiver = null; 319 mClientChannel.dispose(); 320 mServerChannel.dispose(); 321 mClientChannel = null; 322 mServerChannel = null; 323 324 mDragWindowHandle = null; 325 mDragApplicationHandle = null; 326 327 // Resume rotations after a drag. 328 if (DEBUG_ORIENTATION) { 329 Slog.d(TAG_WM, "Resuming rotation after drag"); 330 } 331 mDisplayContent.resumeRotationLocked(); 332 } 333 } 334 getInputChannel()335 InputChannel getInputChannel() { 336 return mInputInterceptor == null ? null : mInputInterceptor.mServerChannel; 337 } 338 getInputWindowHandle()339 InputWindowHandle getInputWindowHandle() { 340 return mInputInterceptor == null ? null : mInputInterceptor.mDragWindowHandle; 341 } 342 343 /** 344 * @param display The Display that the window being dragged is on. 345 */ register(Display display)346 void register(Display display) { 347 display.getRealSize(mDisplaySize); 348 if (DEBUG_DRAG) Slog.d(TAG_WM, "registering drag input channel"); 349 if (mInputInterceptor != null) { 350 Slog.e(TAG_WM, "Duplicate register of drag input channel"); 351 } else { 352 mInputInterceptor = new InputInterceptor(display); 353 showInputSurface(); 354 } 355 } 356 getDragLayerLocked()357 int getDragLayerLocked() { 358 return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_DRAG) 359 * WindowManagerService.TYPE_LAYER_MULTIPLIER 360 + WindowManagerService.TYPE_LAYER_OFFSET; 361 } 362 363 /* call out to each visible window/session informing it about the drag 364 */ broadcastDragStartedLocked(final float touchX, final float touchY)365 void broadcastDragStartedLocked(final float touchX, final float touchY) { 366 mOriginalX = mCurrentX = touchX; 367 mOriginalY = mCurrentY = touchY; 368 369 // Cache a base-class instance of the clip metadata so that parceling 370 // works correctly in calling out to the apps. 371 mDataDescription = (mData != null) ? mData.getDescription() : null; 372 mNotifiedWindows.clear(); 373 mDragInProgress = true; 374 375 mSourceUserId = UserHandle.getUserId(mUid); 376 377 final UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class); 378 mCrossProfileCopyAllowed = !userManager.getUserRestriction( 379 mSourceUserId, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE); 380 381 if (DEBUG_DRAG) { 382 Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")"); 383 } 384 385 mDisplayContent.forAllWindows(w -> { 386 sendDragStartedLocked(w, touchX, touchY, mDataDescription); 387 }, false /* traverseTopToBottom */ ); 388 } 389 390 /* helper - send a ACTION_DRAG_STARTED event, if the 391 * designated window is potentially a drop recipient. There are race situations 392 * around DRAG_ENDED broadcast, so we make sure that once we've declared that 393 * the drag has ended, we never send out another DRAG_STARTED for this drag action. 394 * 395 * This method clones the 'event' parameter if it's being delivered to the same 396 * process, so it's safe for the caller to call recycle() on the event afterwards. 397 */ sendDragStartedLocked(WindowState newWin, float touchX, float touchY, ClipDescription desc)398 private void sendDragStartedLocked(WindowState newWin, float touchX, float touchY, 399 ClipDescription desc) { 400 if (mDragInProgress && isValidDropTarget(newWin)) { 401 DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED, 402 touchX, touchY, null, desc, null, null, false); 403 try { 404 newWin.mClient.dispatchDragEvent(event); 405 // track each window that we've notified that the drag is starting 406 mNotifiedWindows.add(newWin); 407 } catch (RemoteException e) { 408 Slog.w(TAG_WM, "Unable to drag-start window " + newWin); 409 } finally { 410 // if the callee was local, the dispatch has already recycled the event 411 if (Process.myPid() != newWin.mSession.mPid) { 412 event.recycle(); 413 } 414 } 415 } 416 } 417 isValidDropTarget(WindowState targetWin)418 private boolean isValidDropTarget(WindowState targetWin) { 419 if (targetWin == null) { 420 return false; 421 } 422 if (!targetWin.isPotentialDragTarget()) { 423 return false; 424 } 425 if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0 || !targetWindowSupportsGlobalDrag(targetWin)) { 426 // Drag is limited to the current window. 427 if (mLocalWin != targetWin.mClient.asBinder()) { 428 return false; 429 } 430 } 431 432 return mCrossProfileCopyAllowed || 433 mSourceUserId == UserHandle.getUserId(targetWin.getOwningUid()); 434 } 435 targetWindowSupportsGlobalDrag(WindowState targetWin)436 private boolean targetWindowSupportsGlobalDrag(WindowState targetWin) { 437 // Global drags are limited to system windows, and windows for apps that are targeting N and 438 // above. 439 return targetWin.mAppToken == null 440 || targetWin.mAppToken.mTargetSdk >= Build.VERSION_CODES.N; 441 } 442 443 /* helper - send a ACTION_DRAG_STARTED event only if the window has not 444 * previously been notified, i.e. it became visible after the drag operation 445 * was begun. This is a rare case. 446 */ sendDragStartedIfNeededLocked(WindowState newWin)447 void sendDragStartedIfNeededLocked(WindowState newWin) { 448 if (mDragInProgress) { 449 // If we have sent the drag-started, we needn't do so again 450 if (isWindowNotified(newWin)) { 451 return; 452 } 453 if (DEBUG_DRAG) { 454 Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin); 455 } 456 sendDragStartedLocked(newWin, mCurrentX, mCurrentY, mDataDescription); 457 } 458 } 459 isWindowNotified(WindowState newWin)460 private boolean isWindowNotified(WindowState newWin) { 461 for (WindowState ws : mNotifiedWindows) { 462 if (ws == newWin) { 463 return true; 464 } 465 } 466 return false; 467 } 468 endDragLocked()469 void endDragLocked() { 470 if (mAnimator != null) { 471 return; 472 } 473 if (!mDragResult) { 474 mAnimator = createReturnAnimationLocked(); 475 return; // Will call closeLocked() when the animation is done. 476 } 477 closeLocked(); 478 } 479 cancelDragLocked(boolean skipAnimation)480 void cancelDragLocked(boolean skipAnimation) { 481 if (mAnimator != null) { 482 return; 483 } 484 if (!mDragInProgress || skipAnimation) { 485 // mDragInProgress is false if an app invokes Session#cancelDragAndDrop before 486 // Session#performDrag. Reset the drag state without playing the cancel animation 487 // because H.DRAG_START_TIMEOUT may be sent to WindowManagerService, which will cause 488 // DragState#reset() while playing the cancel animation. 489 // skipAnimation is true when a caller requests to skip the drag cancel animation. 490 closeLocked(); 491 return; 492 } 493 mAnimator = createCancelAnimationLocked(); 494 } 495 notifyMoveLocked(float x, float y)496 void notifyMoveLocked(float x, float y) { 497 if (mAnimator != null) { 498 return; 499 } 500 mCurrentX = x; 501 mCurrentY = y; 502 503 // Move the surface to the given touch 504 if (SHOW_LIGHT_TRANSACTIONS) { 505 Slog.i(TAG_WM, ">>> OPEN TRANSACTION notifyMoveLocked"); 506 } 507 mTransaction.setPosition(mSurfaceControl, x - mThumbOffsetX, y - mThumbOffsetY).apply(); 508 if (SHOW_TRANSACTIONS) { 509 Slog.i(TAG_WM, " DRAG " + mSurfaceControl + ": pos=(" + (int) (x - mThumbOffsetX) + "," 510 + (int) (y - mThumbOffsetY) + ")"); 511 } 512 notifyLocationLocked(x, y); 513 } 514 notifyLocationLocked(float x, float y)515 void notifyLocationLocked(float x, float y) { 516 // Tell the affected window 517 WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y); 518 if (touchedWin != null && !isWindowNotified(touchedWin)) { 519 // The drag point is over a window which was not notified about a drag start. 520 // Pretend it's over empty space. 521 touchedWin = null; 522 } 523 524 try { 525 final int myPid = Process.myPid(); 526 527 // have we dragged over a new window? 528 if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) { 529 if (DEBUG_DRAG) { 530 Slog.d(TAG_WM, "sending DRAG_EXITED to " + mTargetWindow); 531 } 532 // force DRAG_EXITED_EVENT if appropriate 533 DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED, 534 0, 0, null, null, null, null, false); 535 mTargetWindow.mClient.dispatchDragEvent(evt); 536 if (myPid != mTargetWindow.mSession.mPid) { 537 evt.recycle(); 538 } 539 } 540 if (touchedWin != null) { 541 if (false && DEBUG_DRAG) { 542 Slog.d(TAG_WM, "sending DRAG_LOCATION to " + touchedWin); 543 } 544 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION, 545 x, y, null, null, null, null, false); 546 touchedWin.mClient.dispatchDragEvent(evt); 547 if (myPid != touchedWin.mSession.mPid) { 548 evt.recycle(); 549 } 550 } 551 } catch (RemoteException e) { 552 Slog.w(TAG_WM, "can't send drag notification to windows"); 553 } 554 mTargetWindow = touchedWin; 555 } 556 557 /** 558 * Finds the drop target and tells it about the data. If the drop event is not sent to the 559 * target, invokes {@code endDragLocked} immediately. 560 */ notifyDropLocked(float x, float y)561 void notifyDropLocked(float x, float y) { 562 if (mAnimator != null) { 563 return; 564 } 565 mCurrentX = x; 566 mCurrentY = y; 567 568 final WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y); 569 570 if (!isWindowNotified(touchedWin)) { 571 // "drop" outside a valid window -- no recipient to apply a 572 // timeout to, and we can send the drag-ended message immediately. 573 mDragResult = false; 574 endDragLocked(); 575 return; 576 } 577 578 if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin); 579 580 final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid()); 581 582 final DragAndDropPermissionsHandler dragAndDropPermissions; 583 if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0 584 && mData != null) { 585 dragAndDropPermissions = new DragAndDropPermissionsHandler( 586 mData, 587 mUid, 588 touchedWin.getOwningPackage(), 589 mFlags & DRAG_FLAGS_URI_PERMISSIONS, 590 mSourceUserId, 591 targetUserId); 592 } else { 593 dragAndDropPermissions = null; 594 } 595 if (mSourceUserId != targetUserId){ 596 if (mData != null) { 597 mData.fixUris(mSourceUserId); 598 } 599 } 600 final int myPid = Process.myPid(); 601 final IBinder token = touchedWin.mClient.asBinder(); 602 final DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y, 603 null, null, mData, dragAndDropPermissions, false); 604 try { 605 touchedWin.mClient.dispatchDragEvent(evt); 606 607 // 5 second timeout for this window to respond to the drop 608 mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, token); 609 } catch (RemoteException e) { 610 Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin); 611 endDragLocked(); 612 } finally { 613 if (myPid != touchedWin.mSession.mPid) { 614 evt.recycle(); 615 } 616 } 617 mToken = token; 618 } 619 620 /** 621 * Returns true if it has sent DRAG_STARTED broadcast out but has not been sent DRAG_END 622 * broadcast. 623 */ isInProgress()624 boolean isInProgress() { 625 return mDragInProgress; 626 } 627 obtainDragEvent(WindowState win, int action, float x, float y, Object localState, ClipDescription description, ClipData data, IDragAndDropPermissions dragAndDropPermissions, boolean result)628 private static DragEvent obtainDragEvent(WindowState win, int action, 629 float x, float y, Object localState, 630 ClipDescription description, ClipData data, 631 IDragAndDropPermissions dragAndDropPermissions, 632 boolean result) { 633 final float winX = win.translateToWindowX(x); 634 final float winY = win.translateToWindowY(y); 635 return DragEvent.obtain(action, winX, winY, localState, description, data, 636 dragAndDropPermissions, result); 637 } 638 createReturnAnimationLocked()639 private ValueAnimator createReturnAnimationLocked() { 640 final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder( 641 PropertyValuesHolder.ofFloat( 642 ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, 643 mOriginalX - mThumbOffsetX), 644 PropertyValuesHolder.ofFloat( 645 ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, 646 mOriginalY - mThumbOffsetY), 647 PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 1), 648 PropertyValuesHolder.ofFloat( 649 ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, mOriginalAlpha / 2)); 650 651 final float translateX = mOriginalX - mCurrentX; 652 final float translateY = mOriginalY - mCurrentY; 653 // Adjust the duration to the travel distance. 654 final double travelDistance = Math.sqrt(translateX * translateX + translateY * translateY); 655 final double displayDiagonal = 656 Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y); 657 final long duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal 658 * (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS)); 659 final AnimationListener listener = new AnimationListener(); 660 animator.setDuration(duration); 661 animator.setInterpolator(mCubicEaseOutInterpolator); 662 animator.addListener(listener); 663 animator.addUpdateListener(listener); 664 665 mService.mAnimationHandler.post(() -> animator.start()); 666 return animator; 667 } 668 createCancelAnimationLocked()669 private ValueAnimator createCancelAnimationLocked() { 670 final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder( 671 PropertyValuesHolder.ofFloat( 672 ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, mCurrentX), 673 PropertyValuesHolder.ofFloat( 674 ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, mCurrentY), 675 PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 0), 676 PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0)); 677 final AnimationListener listener = new AnimationListener(); 678 animator.setDuration(MIN_ANIMATION_DURATION_MS); 679 animator.setInterpolator(mCubicEaseOutInterpolator); 680 animator.addListener(listener); 681 animator.addUpdateListener(listener); 682 683 mService.mAnimationHandler.post(() -> animator.start()); 684 return animator; 685 } 686 isFromSource(int source)687 private boolean isFromSource(int source) { 688 return (mTouchSource & source) == source; 689 } 690 overridePointerIconLocked(int touchSource)691 void overridePointerIconLocked(int touchSource) { 692 mTouchSource = touchSource; 693 if (isFromSource(InputDevice.SOURCE_MOUSE)) { 694 InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_GRABBING); 695 } 696 } 697 698 private class AnimationListener 699 implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { 700 @Override onAnimationUpdate(ValueAnimator animation)701 public void onAnimationUpdate(ValueAnimator animation) { 702 try (final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) { 703 transaction.setPosition( 704 mSurfaceControl, 705 (float) animation.getAnimatedValue(ANIMATED_PROPERTY_X), 706 (float) animation.getAnimatedValue(ANIMATED_PROPERTY_Y)); 707 transaction.setAlpha( 708 mSurfaceControl, 709 (float) animation.getAnimatedValue(ANIMATED_PROPERTY_ALPHA)); 710 transaction.setMatrix( 711 mSurfaceControl, 712 (float) animation.getAnimatedValue(ANIMATED_PROPERTY_SCALE), 0, 713 0, (float) animation.getAnimatedValue(ANIMATED_PROPERTY_SCALE)); 714 transaction.apply(); 715 } 716 } 717 718 @Override onAnimationStart(Animator animator)719 public void onAnimationStart(Animator animator) {} 720 721 @Override onAnimationCancel(Animator animator)722 public void onAnimationCancel(Animator animator) {} 723 724 @Override onAnimationRepeat(Animator animator)725 public void onAnimationRepeat(Animator animator) {} 726 727 @Override onAnimationEnd(Animator animator)728 public void onAnimationEnd(Animator animator) { 729 mAnimationCompleted = true; 730 // Updating mDragState requires the WM lock so continues it on the out of 731 // AnimationThread. 732 mDragDropController.sendHandlerMessage(MSG_ANIMATION_END, null); 733 } 734 } 735 } 736