Home | History | Annotate | Download | only in wm
      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 
    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      */
    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 
    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 
    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 
    281     InputChannel getInputChannel() {
    282         return mInputInterceptor == null ? null : mInputInterceptor.mServerChannel;
    283     }
    284 
    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      */
    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 
    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      */
    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      */
    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 
    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 
    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      */
    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 
    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 
    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 
    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 
    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 
    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      */
    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      */
    574     boolean isInProgress() {
    575         return mDragInProgress;
    576     }
    577 
    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 
    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 
    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 
    637     private boolean isFromSource(int source) {
    638         return (mTouchSource & source) == source;
    639     }
    640 
    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
    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
    669         public void onAnimationStart(Animator animator) {}
    670 
    671         @Override
    672         public void onAnimationCancel(Animator animator) {}
    673 
    674         @Override
    675         public void onAnimationRepeat(Animator animator) {}
    676 
    677         @Override
    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