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