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