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