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