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