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