• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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