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