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