Merge changes I3afc0622,I25723107

* changes:
  Make DragDropController#mDragState private
  Sort out various cleanup methods of DragState
This commit is contained in:
TreeHugger Robot
2017-11-07 03:17:12 +00:00
committed by Android (Google) Code Review
5 changed files with 197 additions and 197 deletions

View File

@@ -35,6 +35,7 @@ import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
import com.android.server.input.InputWindowHandle;
/**
* Managing drag and drop operations initiated by View#startDragAndDrop.
@@ -49,7 +50,12 @@ class DragDropController {
static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2;
static final int MSG_ANIMATION_END = 3;
DragState mDragState;
/**
* Drag state per operation.
* The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
* itself, thus the variable can be null after calling DragState's methods.
*/
private DragState mDragState;
private WindowManagerService mService;
private final Handler mHandler;
@@ -58,11 +64,19 @@ class DragDropController {
return mDragState != null;
}
InputWindowHandle getInputWindowHandleLocked() {
return mDragState.getInputWindowHandle();
}
DragDropController(WindowManagerService service, Looper looper) {
mService = service;
mHandler = new DragHandler(service, looper);
}
void sendDragStartedIfNeededLocked(WindowState window) {
mDragState.sendDragStartedIfNeededLocked(window);
}
IBinder prepareDrag(SurfaceSession session, int callerPid,
int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
if (DEBUG_DRAG) {
@@ -158,9 +172,7 @@ class DragDropController {
if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
mDragState.getInputChannel())) {
Slog.e(TAG_WM, "Unable to transfer touch focus");
mDragState.unregister();
mDragState.reset();
mDragState = null;
mDragState.closeLocked();
return false;
}
@@ -254,6 +266,30 @@ class DragDropController {
}
}
/**
* Handles motion events.
* @param keepHandling Whether if the drag operation is continuing or this is the last motion
* event.
* @param newX X coordinate value in dp in the screen coordinate
* @param newY Y coordinate value in dp in the screen coordinate
*/
void handleMotionEvent(boolean keepHandling, float newX, float newY) {
synchronized (mService.mWindowMap) {
if (!dragDropActiveLocked()) {
// The drag has ended but the clean-up message has not been processed by
// window manager. Drop events that occur after this until window manager
// has a chance to clean-up the input handle.
return;
}
if (keepHandling) {
mDragState.notifyMoveLocked(newX, newY);
} else {
mDragState.notifyDropLocked(newX, newY);
}
}
}
void dragRecipientEntered(IWindow window) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
@@ -282,6 +318,17 @@ class DragDropController {
mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
}
/**
* Notifies the current drag state is closed.
*/
void onDragStateClosedLocked(DragState dragState) {
if (mDragState != dragState) {
Slog.wtf(TAG_WM, "Unknown drag state is closed");
return;
}
mDragState = null;
}
private class DragHandler extends Handler {
/**
* Lock for window manager.
@@ -304,9 +351,7 @@ class DragDropController {
synchronized (mService.mWindowMap) {
// !!! TODO: ANR the app that has failed to start the drag in time
if (mDragState != null) {
mDragState.unregister();
mDragState.reset();
mDragState = null;
mDragState.closeLocked();
}
}
break;
@@ -346,7 +391,7 @@ class DragDropController {
"plyaing animation");
return;
}
mDragState.onAnimationEndLocked();
mDragState.closeLocked();
}
break;
}

View File

@@ -37,7 +37,6 @@ import android.view.MotionEvent;
* Input receiver for drag and drop
*/
class DragInputEventReceiver extends InputEventReceiver {
private final WindowManagerService mService;
private final DragDropController mDragDropController;
// Set, if stylus button was down at the start of the drag.
@@ -48,100 +47,63 @@ class DragInputEventReceiver extends InputEventReceiver {
// are still being dispatched.
private boolean mMuteInput = false;
public DragInputEventReceiver(InputChannel inputChannel, Looper looper,
DragDropController controller, WindowManagerService service) {
DragInputEventReceiver(InputChannel inputChannel, Looper looper,
DragDropController controller) {
super(inputChannel, looper);
mDragDropController = controller;
mService = service;
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
boolean handled = false;
try {
synchronized (mService.mWindowMap) {
if (!mDragDropController.dragDropActiveLocked()) {
// The drag has ended but the clean-up message has not been processed by
// window manager. Drop events that occur after this until window manager
// has a chance to clean-up the input handle.
handled = true;
return;
}
if (!(event instanceof MotionEvent)
|| (event.getSource() & SOURCE_CLASS_POINTER) == 0
|| mMuteInput) {
return;
}
final MotionEvent motionEvent = (MotionEvent) event;
boolean endDrag = false;
final float newX = motionEvent.getRawX();
final float newY = motionEvent.getRawY();
final boolean isStylusButtonDown =
(motionEvent.getButtonState() & BUTTON_STYLUS_PRIMARY) != 0;
if (mIsStartEvent) {
if (isStylusButtonDown) {
// First event and the button was down, check for the button being
// lifted in the future, if that happens we'll drop the item.
mStylusButtonDownAtStart = true;
}
mIsStartEvent = false;
}
switch (motionEvent.getAction()) {
case ACTION_DOWN: {
if (DEBUG_DRAG) Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer");
}
break;
case ACTION_MOVE: {
if (mStylusButtonDownAtStart && !isStylusButtonDown) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "Button no longer pressed; dropping at "
+ newX + "," + newY);
}
mMuteInput = true;
endDrag = mDragDropController.mDragState
.notifyDropLocked(newX, newY);
} else {
// move the surface and tell the involved window(s) where we are
mDragDropController.mDragState.notifyMoveLocked(newX, newY);
}
}
break;
case ACTION_UP: {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "Got UP on move channel; dropping at "
+ newX + "," + newY);
}
mMuteInput = true;
endDrag = mDragDropController.mDragState
.notifyDropLocked(newX, newY);
}
break;
case ACTION_CANCEL: {
if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!");
mMuteInput = true;
endDrag = true;
}
break;
}
if (endDrag) {
if (DEBUG_DRAG)
Slog.d(TAG_WM, "Drag ended; tearing down state");
// tell all the windows that the drag has ended
// endDragLocked will post back to looper to dispose the receiver
// since we still need the receiver for the last finishInputEvent.
mDragDropController.mDragState.endDragLocked();
mStylusButtonDownAtStart = false;
mIsStartEvent = true;
}
handled = true;
if (!(event instanceof MotionEvent)
|| (event.getSource() & SOURCE_CLASS_POINTER) == 0
|| mMuteInput) {
return;
}
final MotionEvent motionEvent = (MotionEvent) event;
final float newX = motionEvent.getRawX();
final float newY = motionEvent.getRawY();
final boolean isStylusButtonDown =
(motionEvent.getButtonState() & BUTTON_STYLUS_PRIMARY) != 0;
if (mIsStartEvent) {
// First event and the button was down, check for the button being
// lifted in the future, if that happens we'll drop the item.
mStylusButtonDownAtStart = isStylusButtonDown;
mIsStartEvent = false;
}
switch (motionEvent.getAction()) {
case ACTION_DOWN:
if (DEBUG_DRAG) Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer");
return;
case ACTION_MOVE:
if (mStylusButtonDownAtStart && !isStylusButtonDown) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "Button no longer pressed; dropping at " + newX + ","
+ newY);
}
mMuteInput = true;
}
break;
case ACTION_UP:
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "Got UP on move channel; dropping at " + newX + "," + newY);
}
mMuteInput = true;
break;
case ACTION_CANCEL:
if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!");
mMuteInput = true;
break;
default:
return;
}
mDragDropController.handleMotionEvent(!mMuteInput /* keepHandling */, newX, newY);
handled = true;
} catch (Exception e) {
Slog.e(TAG_WM, "Exception caught by drag handleMotion", e);
} finally {

View File

@@ -105,6 +105,11 @@ class DragState {
WindowState mTargetWindow;
ArrayList<WindowState> mNotifiedWindows;
boolean mDragInProgress;
/**
* Whether if animation is completed. Needs to be volatile to update from the animation thread
* without having a WM lock.
*/
volatile boolean mAnimationCompleted = false;
DisplayContent mDisplayContent;
@Nullable private ValueAnimator mAnimator;
@@ -122,21 +127,79 @@ class DragState {
mNotifiedWindows = new ArrayList<WindowState>();
}
void reset() {
if (mAnimator != null) {
/**
* After calling this, DragDropController#onDragStateClosedLocked is invoked, which causes
* DragDropController#mDragState becomes null.
*/
void closeLocked() {
// Unregister the input interceptor.
if (mInputInterceptor != null) {
if (DEBUG_DRAG)
Slog.d(TAG_WM, "unregistering drag input channel");
// Input channel should be disposed on the thread where the input is being handled.
mDragDropController.sendHandlerMessage(
MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor);
mInputInterceptor = null;
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
}
// Send drag end broadcast if drag start has been sent.
if (mDragInProgress) {
final int myPid = Process.myPid();
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_ENDED");
}
for (WindowState ws : mNotifiedWindows) {
float x = 0;
float y = 0;
if (!mDragResult && (ws.mSession.mPid == mPid)) {
// Report unconsumed drop location back to the app that started the drag.
x = mCurrentX;
y = mCurrentY;
}
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
x, y, null, null, null, null, mDragResult);
try {
ws.mClient.dispatchDragEvent(evt);
} catch (RemoteException e) {
Slog.w(TAG_WM, "Unable to drag-end window " + ws);
}
// if the current window is in the same process,
// the dispatch has already recycled the event
if (myPid != ws.mSession.mPid) {
evt.recycle();
}
}
mNotifiedWindows.clear();
mDragInProgress = false;
}
// Take the cursor back if it has been changed.
if (isFromSource(InputDevice.SOURCE_MOUSE)) {
mService.restorePointerIconLocked(mDisplayContent, mCurrentX, mCurrentY);
mTouchSource = 0;
}
// Clear the internal variables.
if (mSurfaceControl != null) {
mSurfaceControl.destroy();
mSurfaceControl = null;
}
if (mAnimator != null && !mAnimationCompleted) {
Slog.wtf(TAG_WM,
"Unexpectedly destroying mSurfaceControl while animation is running");
}
if (mSurfaceControl != null) {
mSurfaceControl.destroy();
}
mSurfaceControl = null;
mFlags = 0;
mLocalWin = null;
mToken = null;
mData = null;
mThumbOffsetX = mThumbOffsetY = 0;
mNotifiedWindows = null;
// Notifies the controller that the drag state is closed.
mDragDropController.onDragStateClosedLocked(this);
}
class InputInterceptor {
@@ -151,7 +214,7 @@ class DragState {
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController, mService);
mService.mH.getLooper(), mDragDropController);
mDragApplicationHandle = new InputApplicationHandle(null);
mDragApplicationHandle.name = "drag";
@@ -235,19 +298,6 @@ class DragState {
}
}
void unregister() {
if (DEBUG_DRAG) Slog.d(TAG_WM, "unregistering drag input channel");
if (mInputInterceptor == null) {
Slog.e(TAG_WM, "Unregister of nonexistent drag input channel");
} else {
// Input channel should be disposed on the thread where the input is being handled.
mDragDropController.sendHandlerMessage(
MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor);
mInputInterceptor = null;
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
}
}
int getDragLayerLocked() {
return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_DRAG)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
@@ -366,46 +416,15 @@ class DragState {
return false;
}
private void broadcastDragEndedLocked() {
final int myPid = Process.myPid();
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_ENDED");
}
for (WindowState ws : mNotifiedWindows) {
float x = 0;
float y = 0;
if (!mDragResult && (ws.mSession.mPid == mPid)) {
// Report unconsumed drop location back to the app that started the drag.
x = mCurrentX;
y = mCurrentY;
}
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
x, y, null, null, null, null, mDragResult);
try {
ws.mClient.dispatchDragEvent(evt);
} catch (RemoteException e) {
Slog.w(TAG_WM, "Unable to drag-end window " + ws);
}
// if the current window is in the same process,
// the dispatch has already recycled the event
if (myPid != ws.mSession.mPid) {
evt.recycle();
}
}
mNotifiedWindows.clear();
mDragInProgress = false;
}
void endDragLocked() {
if (mAnimator != null) {
return;
}
if (!mDragResult) {
mAnimator = createReturnAnimationLocked();
return; // Will call cleanUpDragLw when the animation is done.
return; // Will call closeLocked() when the animation is done.
}
cleanUpDragLocked();
closeLocked();
}
void cancelDragLocked() {
@@ -414,33 +433,15 @@ class DragState {
}
if (!mDragInProgress) {
// This can happen if an app invokes Session#cancelDragAndDrop before
// Session#performDrag. Reset the drag state:
// 1. without sending the end broadcast because the start broadcast has not been sent,
// and
// 2. without playing the cancel animation because H.DRAG_START_TIMEOUT may be sent to
// WindowManagerService, which will cause DragState#reset() while playing the
// cancel animation.
reset();
mDragDropController.mDragState = null;
// Session#performDrag. Reset the drag state without playing the cancel animation
// because H.DRAG_START_TIMEOUT may be sent to WindowManagerService, which will cause
// DragState#reset() while playing the cancel animation.
closeLocked();
return;
}
mAnimator = createCancelAnimationLocked();
}
private void cleanUpDragLocked() {
broadcastDragEndedLocked();
if (isFromSource(InputDevice.SOURCE_MOUSE)) {
mService.restorePointerIconLocked(mDisplayContent, mCurrentX, mCurrentY);
}
// stop intercepting input
unregister();
// free our resources and drop all the object references
reset();
mDragDropController.mDragState = null;
}
void notifyMoveLocked(float x, float y) {
if (mAnimator != null) {
return;
@@ -507,34 +508,33 @@ class DragState {
mTargetWindow = touchedWin;
}
// Find the drop target and tell it about the data. Returns 'true' if we can immediately
// dispatch the global drag-ended message, 'false' if we need to wait for a
// result from the recipient.
boolean notifyDropLocked(float x, float y) {
/**
* Finds the drop target and tells it about the data. If the drop event is not sent to the
* target, invokes {@code endDragLocked} immediately.
*/
void notifyDropLocked(float x, float y) {
if (mAnimator != null) {
return false;
return;
}
mCurrentX = x;
mCurrentY = y;
WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
final WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
if (!isWindowNotified(touchedWin)) {
// "drop" outside a valid window -- no recipient to apply a
// timeout to, and we can send the drag-ended message immediately.
mDragResult = false;
return true;
endDragLocked();
return;
}
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "sending DROP to " + touchedWin);
}
if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin);
final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
DragAndDropPermissionsHandler dragAndDropPermissions = null;
if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
(mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
final DragAndDropPermissionsHandler dragAndDropPermissions;
if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
dragAndDropPermissions = new DragAndDropPermissionsHandler(
mData,
mUid,
@@ -542,13 +542,15 @@ class DragState {
mFlags & DRAG_FLAGS_URI_PERMISSIONS,
mSourceUserId,
targetUserId);
} else {
dragAndDropPermissions = null;
}
if (mSourceUserId != targetUserId){
mData.fixUris(mSourceUserId);
}
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
final DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
null, null, mData, dragAndDropPermissions, false);
try {
touchedWin.mClient.dispatchDragEvent(evt);
@@ -557,23 +559,13 @@ class DragState {
mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, token);
} catch (RemoteException e) {
Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin);
return true;
endDragLocked();
} finally {
if (myPid != touchedWin.mSession.mPid) {
evt.recycle();
}
}
mToken = token;
return false;
}
void onAnimationEndLocked() {
if (mAnimator == null) {
Slog.wtf(TAG_WM, "Unexpected null mAnimator");
return;
}
mAnimator = null;
cleanUpDragLocked();
}
private static DragEvent obtainDragEvent(WindowState win, int action,
@@ -677,6 +669,7 @@ class DragState {
@Override
public void onAnimationEnd(Animator animator) {
mAnimationCompleted = true;
// Updating mDragState requires the WM lock so continues it on the out of
// AnimationThread.
mDragDropController.sendHandlerMessage(MSG_ANIMATION_END, null);

View File

@@ -381,7 +381,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
Log.d(TAG_WM, "Inserting drag window");
}
final InputWindowHandle dragWindowHandle =
mService.mDragDropController.mDragState.getInputWindowHandle();
mService.mDragDropController.getInputWindowHandleLocked();
if (dragWindowHandle != null) {
addInputWindowHandle(dragWindowHandle);
} else {
@@ -698,7 +698,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
// If there's a drag in progress and 'child' is a potential drop target,
// make sure it's been told about the drag
if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
mService.mDragDropController.mDragState.sendDragStartedIfNeededLocked(w);
mService.mDragDropController.sendDragStartedIfNeededLocked(w);
}
addInputWindowHandle(

View File

@@ -7087,7 +7087,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized (mWindowMap) {
if (mDragDropController.mDragState != null) {
if (mDragDropController.dragDropActiveLocked()) {
// Drag cursor overrides the app cursor.
return;
}