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