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