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 
DragState(WindowManagerService service, IBinder token, SurfaceControl surface, int flags, IBinder localWin)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 
reset()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      */
register(Display display)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 
unregister()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 
getDragLayerLw()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      */
broadcastDragStartedLw(final float touchX, final float touchY)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      */
sendDragStartedLw(WindowState newWin, float touchX, float touchY, ClipDescription desc)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 
isValidDropTarget(WindowState targetWin)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 
targetWindowSupportsGlobalDrag(WindowState targetWin)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      */
sendDragStartedIfNeededLw(WindowState newWin)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 
isWindowNotified(WindowState newWin)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 
broadcastDragEndedLw()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 
endDragLw()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 
cancelDragLw()380     void cancelDragLw() {
381         if (mAnimation != null) {
382             return;
383         }
384         mAnimation = createCancelAnimationLocked();
385         mService.scheduleAnimationLocked();
386     }
387 
cleanUpDragLw()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 
notifyMoveLw(float x, float y)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 
notifyLocationLw(float x, float y)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.
notifyDropLw(float x, float y)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 
obtainDragEvent(WindowState win, int action, float x, float y, Object localState, ClipDescription description, ClipData data, IDragAndDropPermissions dragAndDropPermissions, boolean result)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 
stepAnimationLocked(long currentTimeMs)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 
createReturnAnimationLocked()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 
createCancelAnimationLocked()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 
isFromSource(int source)588     private boolean isFromSource(int source) {
589         return (mTouchSource & source) == source;
590     }
591 
overridePointerIconLw(int touchSource)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