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