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.WindowManagerDebugConfig.DEBUG_DRAG;
     20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
     21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
     22 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
     23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     24 
     25 import android.content.ClipData;
     26 import android.content.ClipDescription;
     27 import android.content.Context;
     28 import android.graphics.Matrix;
     29 import android.graphics.Point;
     30 import android.hardware.input.InputManager;
     31 import android.os.Build;
     32 import android.os.IBinder;
     33 import android.os.Message;
     34 import android.os.Process;
     35 import android.os.RemoteException;
     36 import android.os.ServiceManager;
     37 import android.os.UserHandle;
     38 import android.os.UserManager;
     39 import android.os.IUserManager;
     40 import android.util.Slog;
     41 import android.view.Display;
     42 import android.view.DragEvent;
     43 import android.view.InputChannel;
     44 import android.view.InputDevice;
     45 import android.view.PointerIcon;
     46 import android.view.SurfaceControl;
     47 import android.view.View;
     48 import android.view.WindowManager;
     49 import android.view.animation.AlphaAnimation;
     50 import android.view.animation.Animation;
     51 import android.view.animation.AnimationSet;
     52 import android.view.animation.DecelerateInterpolator;
     53 import android.view.animation.Interpolator;
     54 import android.view.animation.ScaleAnimation;
     55 import android.view.animation.Transformation;
     56 import android.view.animation.TranslateAnimation;
     57 
     58 import com.android.server.input.InputApplicationHandle;
     59 import com.android.server.input.InputWindowHandle;
     60 import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
     61 import com.android.server.wm.WindowManagerService.H;
     62 
     63 import com.android.internal.view.IDragAndDropPermissions;
     64 
     65 import java.util.ArrayList;
     66 
     67 /**
     68  * Drag/drop state
     69  */
     70 class DragState {
     71     private static final long ANIMATION_DURATION_MS = 500;
     72 
     73     private static final int DRAG_FLAGS_URI_ACCESS = View.DRAG_FLAG_GLOBAL_URI_READ |
     74             View.DRAG_FLAG_GLOBAL_URI_WRITE;
     75 
     76     private static final int DRAG_FLAGS_URI_PERMISSIONS = DRAG_FLAGS_URI_ACCESS |
     77             View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION |
     78             View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION;
     79 
     80     final WindowManagerService mService;
     81     IBinder mToken;
     82     SurfaceControl mSurfaceControl;
     83     int mFlags;
     84     IBinder mLocalWin;
     85     int mPid;
     86     int mUid;
     87     int mSourceUserId;
     88     boolean mCrossProfileCopyAllowed;
     89     ClipData mData;
     90     ClipDescription mDataDescription;
     91     int mTouchSource;
     92     boolean mDragResult;
     93     float mOriginalAlpha;
     94     float mOriginalX, mOriginalY;
     95     float mCurrentX, mCurrentY;
     96     float mThumbOffsetX, mThumbOffsetY;
     97     InputChannel mServerChannel, mClientChannel;
     98     DragInputEventReceiver mInputEventReceiver;
     99     InputApplicationHandle mDragApplicationHandle;
    100     InputWindowHandle mDragWindowHandle;
    101     WindowState mTargetWindow;
    102     ArrayList<WindowState> mNotifiedWindows;
    103     boolean mDragInProgress;
    104     DisplayContent mDisplayContent;
    105 
    106     private Animation mAnimation;
    107     final Transformation mTransformation = new Transformation();
    108     private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
    109 
    110     DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
    111             int flags, IBinder localWin) {
    112         mService = service;
    113         mToken = token;
    114         mSurfaceControl = surface;
    115         mFlags = flags;
    116         mLocalWin = localWin;
    117         mNotifiedWindows = new ArrayList<WindowState>();
    118     }
    119 
    120     void reset() {
    121         if (mSurfaceControl != null) {
    122             mSurfaceControl.destroy();
    123         }
    124         mSurfaceControl = null;
    125         mFlags = 0;
    126         mLocalWin = null;
    127         mToken = null;
    128         mData = null;
    129         mThumbOffsetX = mThumbOffsetY = 0;
    130         mNotifiedWindows = null;
    131     }
    132 
    133     /**
    134      * @param display The Display that the window being dragged is on.
    135      */
    136     void register(Display display) {
    137         if (DEBUG_DRAG) Slog.d(TAG_WM, "registering drag input channel");
    138         if (mClientChannel != null) {
    139             Slog.e(TAG_WM, "Duplicate register of drag input channel");
    140         } else {
    141             mDisplayContent = mService.getDisplayContentLocked(display.getDisplayId());
    142 
    143             InputChannel[] channels = InputChannel.openInputChannelPair("drag");
    144             mServerChannel = channels[0];
    145             mClientChannel = channels[1];
    146             mService.mInputManager.registerInputChannel(mServerChannel, null);
    147             mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
    148                     mService.mH.getLooper());
    149 
    150             mDragApplicationHandle = new InputApplicationHandle(null);
    151             mDragApplicationHandle.name = "drag";
    152             mDragApplicationHandle.dispatchingTimeoutNanos =
    153                     WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
    154 
    155             mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
    156                     display.getDisplayId());
    157             mDragWindowHandle.name = "drag";
    158             mDragWindowHandle.inputChannel = mServerChannel;
    159             mDragWindowHandle.layer = getDragLayerLw();
    160             mDragWindowHandle.layoutParamsFlags = 0;
    161             mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
    162             mDragWindowHandle.dispatchingTimeoutNanos =
    163                     WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
    164             mDragWindowHandle.visible = true;
    165             mDragWindowHandle.canReceiveKeys = false;
    166             mDragWindowHandle.hasFocus = true;
    167             mDragWindowHandle.hasWallpaper = false;
    168             mDragWindowHandle.paused = false;
    169             mDragWindowHandle.ownerPid = Process.myPid();
    170             mDragWindowHandle.ownerUid = Process.myUid();
    171             mDragWindowHandle.inputFeatures = 0;
    172             mDragWindowHandle.scaleFactor = 1.0f;
    173 
    174             // The drag window cannot receive new touches.
    175             mDragWindowHandle.touchableRegion.setEmpty();
    176 
    177             // The drag window covers the entire display
    178             mDragWindowHandle.frameLeft = 0;
    179             mDragWindowHandle.frameTop = 0;
    180             Point p = new Point();
    181             display.getRealSize(p);
    182             mDragWindowHandle.frameRight = p.x;
    183             mDragWindowHandle.frameBottom = p.y;
    184 
    185             // Pause rotations before a drag.
    186             if (DEBUG_ORIENTATION) {
    187                 Slog.d(TAG_WM, "Pausing rotation during drag");
    188             }
    189             mService.pauseRotationLocked();
    190         }
    191     }
    192 
    193     void unregister() {
    194         if (DEBUG_DRAG) Slog.d(TAG_WM, "unregistering drag input channel");
    195         if (mClientChannel == null) {
    196             Slog.e(TAG_WM, "Unregister of nonexistent drag input channel");
    197         } else {
    198             mService.mInputManager.unregisterInputChannel(mServerChannel);
    199             mInputEventReceiver.dispose();
    200             mInputEventReceiver = null;
    201             mClientChannel.dispose();
    202             mServerChannel.dispose();
    203             mClientChannel = null;
    204             mServerChannel = null;
    205 
    206             mDragWindowHandle = null;
    207             mDragApplicationHandle = null;
    208 
    209             // Resume rotations after a drag.
    210             if (DEBUG_ORIENTATION) {
    211                 Slog.d(TAG_WM, "Resuming rotation after drag");
    212             }
    213             mService.resumeRotationLocked();
    214         }
    215     }
    216 
    217     int getDragLayerLw() {
    218         return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
    219                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
    220                 + WindowManagerService.TYPE_LAYER_OFFSET;
    221     }
    222 
    223     /* call out to each visible window/session informing it about the drag
    224      */
    225     void broadcastDragStartedLw(final float touchX, final float touchY) {
    226         mOriginalX = mCurrentX = touchX;
    227         mOriginalY = mCurrentY = touchY;
    228 
    229         // Cache a base-class instance of the clip metadata so that parceling
    230         // works correctly in calling out to the apps.
    231         mDataDescription = (mData != null) ? mData.getDescription() : null;
    232         mNotifiedWindows.clear();
    233         mDragInProgress = true;
    234 
    235         mSourceUserId = UserHandle.getUserId(mUid);
    236 
    237         final IUserManager userManager =
    238                 (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
    239         try {
    240             mCrossProfileCopyAllowed = !userManager.getUserRestrictions(mSourceUserId).getBoolean(
    241                     UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
    242         } catch (RemoteException e) {
    243             Slog.e(TAG_WM, "Remote Exception calling UserManager: " + e);
    244             mCrossProfileCopyAllowed = false;
    245         }
    246 
    247         if (DEBUG_DRAG) {
    248             Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
    249         }
    250 
    251         final WindowList windows = mDisplayContent.getWindowList();
    252         final int N = windows.size();
    253         for (int i = 0; i < N; i++) {
    254             sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
    255         }
    256     }
    257 
    258     /* helper - send a ACTION_DRAG_STARTED event, if the
    259      * designated window is potentially a drop recipient.  There are race situations
    260      * around DRAG_ENDED broadcast, so we make sure that once we've declared that
    261      * the drag has ended, we never send out another DRAG_STARTED for this drag action.
    262      *
    263      * This method clones the 'event' parameter if it's being delivered to the same
    264      * process, so it's safe for the caller to call recycle() on the event afterwards.
    265      */
    266     private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
    267             ClipDescription desc) {
    268         if (mDragInProgress && isValidDropTarget(newWin)) {
    269             DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
    270                     touchX, touchY, null, desc, null, null, false);
    271             try {
    272                 newWin.mClient.dispatchDragEvent(event);
    273                 // track each window that we've notified that the drag is starting
    274                 mNotifiedWindows.add(newWin);
    275             } catch (RemoteException e) {
    276                 Slog.w(TAG_WM, "Unable to drag-start window " + newWin);
    277             } finally {
    278                 // if the callee was local, the dispatch has already recycled the event
    279                 if (Process.myPid() != newWin.mSession.mPid) {
    280                     event.recycle();
    281                 }
    282             }
    283         }
    284     }
    285 
    286     private boolean isValidDropTarget(WindowState targetWin) {
    287         if (targetWin == null) {
    288             return false;
    289         }
    290         if (!targetWin.isPotentialDragTarget()) {
    291             return false;
    292         }
    293         if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0 || !targetWindowSupportsGlobalDrag(targetWin)) {
    294             // Drag is limited to the current window.
    295             if (mLocalWin != targetWin.mClient.asBinder()) {
    296                 return false;
    297             }
    298         }
    299 
    300         return mCrossProfileCopyAllowed ||
    301                 mSourceUserId == UserHandle.getUserId(targetWin.getOwningUid());
    302     }
    303 
    304     private boolean targetWindowSupportsGlobalDrag(WindowState targetWin) {
    305         // Global drags are limited to system windows, and windows for apps that are targeting N and
    306         // above.
    307         return targetWin.mAppToken == null
    308                 || targetWin.mAppToken.targetSdk >= Build.VERSION_CODES.N;
    309     }
    310 
    311     /* helper - send a ACTION_DRAG_STARTED event only if the window has not
    312      * previously been notified, i.e. it became visible after the drag operation
    313      * was begun.  This is a rare case.
    314      */
    315     void sendDragStartedIfNeededLw(WindowState newWin) {
    316         if (mDragInProgress) {
    317             // If we have sent the drag-started, we needn't do so again
    318             if (isWindowNotified(newWin)) {
    319                 return;
    320             }
    321             if (DEBUG_DRAG) {
    322                 Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin);
    323             }
    324             sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
    325         }
    326     }
    327 
    328     private boolean isWindowNotified(WindowState newWin) {
    329         for (WindowState ws : mNotifiedWindows) {
    330             if (ws == newWin) {
    331                 return true;
    332             }
    333         }
    334         return false;
    335     }
    336 
    337     private void broadcastDragEndedLw() {
    338         final int myPid = Process.myPid();
    339 
    340         if (DEBUG_DRAG) {
    341             Slog.d(TAG_WM, "broadcasting DRAG_ENDED");
    342         }
    343         for (WindowState ws : mNotifiedWindows) {
    344             float x = 0;
    345             float y = 0;
    346             if (!mDragResult && (ws.mSession.mPid == mPid)) {
    347                 // Report unconsumed drop location back to the app that started the drag.
    348                 x = mCurrentX;
    349                 y = mCurrentY;
    350             }
    351             DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
    352                     x, y, null, null, null, null, mDragResult);
    353             try {
    354                 ws.mClient.dispatchDragEvent(evt);
    355             } catch (RemoteException e) {
    356                 Slog.w(TAG_WM, "Unable to drag-end window " + ws);
    357             }
    358             // if the current window is in the same process,
    359             // the dispatch has already recycled the event
    360             if (myPid != ws.mSession.mPid) {
    361                 evt.recycle();
    362             }
    363         }
    364         mNotifiedWindows.clear();
    365         mDragInProgress = false;
    366     }
    367 
    368     void endDragLw() {
    369         if (mAnimation != null) {
    370             return;
    371         }
    372         if (!mDragResult) {
    373             mAnimation = createReturnAnimationLocked();
    374             mService.scheduleAnimationLocked();
    375             return;  // Will call cleanUpDragLw when the animation is done.
    376         }
    377         cleanUpDragLw();
    378     }
    379 
    380     void cancelDragLw() {
    381         if (mAnimation != null) {
    382             return;
    383         }
    384         mAnimation = createCancelAnimationLocked();
    385         mService.scheduleAnimationLocked();
    386     }
    387 
    388     private void cleanUpDragLw() {
    389         broadcastDragEndedLw();
    390         if (isFromSource(InputDevice.SOURCE_MOUSE)) {
    391             mService.restorePointerIconLocked(mDisplayContent, mCurrentX, mCurrentY);
    392         }
    393 
    394         // stop intercepting input
    395         unregister();
    396 
    397         // free our resources and drop all the object references
    398         reset();
    399         mService.mDragState = null;
    400 
    401         mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
    402     }
    403 
    404     void notifyMoveLw(float x, float y) {
    405         if (mAnimation != null) {
    406             return;
    407         }
    408         mCurrentX = x;
    409         mCurrentY = y;
    410 
    411         // Move the surface to the given touch
    412         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
    413                 TAG_WM, ">>> OPEN TRANSACTION notifyMoveLw");
    414         SurfaceControl.openTransaction();
    415         try {
    416             mSurfaceControl.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
    417             if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  DRAG "
    418                     + mSurfaceControl + ": pos=(" +
    419                     (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
    420         } finally {
    421             SurfaceControl.closeTransaction();
    422             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
    423                     TAG_WM, "<<< CLOSE TRANSACTION notifyMoveLw");
    424         }
    425         notifyLocationLw(x, y);
    426     }
    427 
    428     void notifyLocationLw(float x, float y) {
    429         // Tell the affected window
    430         WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
    431         if (touchedWin != null && !isWindowNotified(touchedWin)) {
    432             // The drag point is over a window which was not notified about a drag start.
    433             // Pretend it's over empty space.
    434             touchedWin = null;
    435         }
    436 
    437         try {
    438             final int myPid = Process.myPid();
    439 
    440             // have we dragged over a new window?
    441             if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
    442                 if (DEBUG_DRAG) {
    443                     Slog.d(TAG_WM, "sending DRAG_EXITED to " + mTargetWindow);
    444                 }
    445                 // force DRAG_EXITED_EVENT if appropriate
    446                 DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
    447                         0, 0, null, null, null, null, false);
    448                 mTargetWindow.mClient.dispatchDragEvent(evt);
    449                 if (myPid != mTargetWindow.mSession.mPid) {
    450                     evt.recycle();
    451                 }
    452             }
    453             if (touchedWin != null) {
    454                 if (false && DEBUG_DRAG) {
    455                     Slog.d(TAG_WM, "sending DRAG_LOCATION to " + touchedWin);
    456                 }
    457                 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
    458                         x, y, null, null, null, null, false);
    459                 touchedWin.mClient.dispatchDragEvent(evt);
    460                 if (myPid != touchedWin.mSession.mPid) {
    461                     evt.recycle();
    462                 }
    463             }
    464         } catch (RemoteException e) {
    465             Slog.w(TAG_WM, "can't send drag notification to windows");
    466         }
    467         mTargetWindow = touchedWin;
    468     }
    469 
    470     // Find the drop target and tell it about the data.  Returns 'true' if we can immediately
    471     // dispatch the global drag-ended message, 'false' if we need to wait for a
    472     // result from the recipient.
    473     boolean notifyDropLw(float x, float y) {
    474         if (mAnimation != null) {
    475             return false;
    476         }
    477         mCurrentX = x;
    478         mCurrentY = y;
    479 
    480         WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
    481 
    482         if (!isWindowNotified(touchedWin)) {
    483             // "drop" outside a valid window -- no recipient to apply a
    484             // timeout to, and we can send the drag-ended message immediately.
    485             mDragResult = false;
    486             return true;
    487         }
    488 
    489         if (DEBUG_DRAG) {
    490             Slog.d(TAG_WM, "sending DROP to " + touchedWin);
    491         }
    492 
    493         final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
    494 
    495         DragAndDropPermissionsHandler dragAndDropPermissions = null;
    496         if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
    497                 (mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
    498             dragAndDropPermissions = new DragAndDropPermissionsHandler(
    499                     mData,
    500                     mUid,
    501                     touchedWin.getOwningPackage(),
    502                     mFlags & DRAG_FLAGS_URI_PERMISSIONS,
    503                     mSourceUserId,
    504                     targetUserId);
    505         }
    506         if (mSourceUserId != targetUserId){
    507             mData.fixUris(mSourceUserId);
    508         }
    509         final int myPid = Process.myPid();
    510         final IBinder token = touchedWin.mClient.asBinder();
    511         DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
    512                 null, null, mData, dragAndDropPermissions, false);
    513         try {
    514             touchedWin.mClient.dispatchDragEvent(evt);
    515 
    516             // 5 second timeout for this window to respond to the drop
    517             mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token);
    518             Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
    519             mService.mH.sendMessageDelayed(msg, 5000);
    520         } catch (RemoteException e) {
    521             Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin);
    522             return true;
    523         } finally {
    524             if (myPid != touchedWin.mSession.mPid) {
    525                 evt.recycle();
    526             }
    527         }
    528         mToken = token;
    529         return false;
    530     }
    531 
    532     private static DragEvent obtainDragEvent(WindowState win, int action,
    533             float x, float y, Object localState,
    534             ClipDescription description, ClipData data,
    535             IDragAndDropPermissions dragAndDropPermissions,
    536             boolean result) {
    537         final float winX = win.translateToWindowX(x);
    538         final float winY = win.translateToWindowY(y);
    539         return DragEvent.obtain(action, winX, winY, localState, description, data,
    540                 dragAndDropPermissions, result);
    541     }
    542 
    543     boolean stepAnimationLocked(long currentTimeMs) {
    544         if (mAnimation == null) {
    545             return false;
    546         }
    547 
    548         mTransformation.clear();
    549         if (!mAnimation.getTransformation(currentTimeMs, mTransformation)) {
    550             cleanUpDragLw();
    551             return false;
    552         }
    553 
    554         mTransformation.getMatrix().postTranslate(
    555                 mCurrentX - mThumbOffsetX, mCurrentY - mThumbOffsetY);
    556         final float tmpFloats[] = mService.mTmpFloats;
    557         mTransformation.getMatrix().getValues(tmpFloats);
    558         mSurfaceControl.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
    559         mSurfaceControl.setAlpha(mTransformation.getAlpha());
    560         mSurfaceControl.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
    561                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
    562         return true;
    563     }
    564 
    565     private Animation createReturnAnimationLocked() {
    566         final AnimationSet set = new AnimationSet(false);
    567         set.addAnimation(new TranslateAnimation(
    568                 0, mOriginalX - mCurrentX, 0, mOriginalY - mCurrentY));
    569         set.addAnimation(new AlphaAnimation(mOriginalAlpha, mOriginalAlpha / 2));
    570         set.setDuration(ANIMATION_DURATION_MS);
    571         set.setInterpolator(mCubicEaseOutInterpolator);
    572         set.initialize(0, 0, 0, 0);
    573         set.start();  // Will start on the first call to getTransformation.
    574         return set;
    575     }
    576 
    577     private Animation createCancelAnimationLocked() {
    578         final AnimationSet set = new AnimationSet(false);
    579         set.addAnimation(new ScaleAnimation(1, 0, 1, 0, mThumbOffsetX, mThumbOffsetY));
    580         set.addAnimation(new AlphaAnimation(mOriginalAlpha, 0));
    581         set.setDuration(ANIMATION_DURATION_MS);
    582         set.setInterpolator(mCubicEaseOutInterpolator);
    583         set.initialize(0, 0, 0, 0);
    584         set.start();  // Will start on the first call to getTransformation.
    585         return set;
    586     }
    587 
    588     private boolean isFromSource(int source) {
    589         return (mTouchSource & source) == source;
    590     }
    591 
    592     void overridePointerIconLw(int touchSource) {
    593         mTouchSource = touchSource;
    594         if (isFromSource(InputDevice.SOURCE_MOUSE)) {
    595             InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_GRABBING);
    596         }
    597     }
    598 }
    599