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