Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5647b92d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I3afc0622,I25723107

* changes:
  Make DragDropController#mDragState private
  Sort out various cleanup methods of DragState
parents 48b31da5 768012e4
Loading
Loading
Loading
Loading
+53 −8
Original line number Diff line number Diff line
@@ -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;
                }
+39 −77
Original line number Diff line number Diff line
@@ -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;
                    }
                mStylusButtonDownAtStart = isStylusButtonDown;
                mIsStartEvent = false;
            }

            switch (motionEvent.getAction()) {
                    case ACTION_DOWN: {
                case ACTION_DOWN:
                    if (DEBUG_DRAG) Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer");
                    }
                    break;

                    case ACTION_MOVE: {
                    return;
                case ACTION_MOVE:
                    if (mStylusButtonDownAtStart && !isStylusButtonDown) {
                        if (DEBUG_DRAG) {
                                Slog.d(TAG_WM, "Button no longer pressed; dropping at "
                                        + newX + "," + newY);
                            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: {
                case ACTION_UP:
                    if (DEBUG_DRAG) {
                            Slog.d(TAG_WM, "Got UP on move channel; dropping at "
                                    + newX + "," + newY);
                        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: {
                case ACTION_CANCEL:
                    if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!");
                    mMuteInput = true;
                        endDrag = true;
                    }
                    break;
                default:
                    return;
            }

                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;
                }

            mDragDropController.handleMotionEvent(!mMuteInput /* keepHandling */, newX, newY);
            handled = true;
            }
        } catch (Exception e) {
            Slog.e(TAG_WM, "Exception caught by drag handleMotion", e);
        } finally {
+92 −99
Original line number Diff line number Diff line
@@ -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) {
            Slog.wtf(TAG_WM,
                    "Unexpectedly destroying mSurfaceControl while animation is running");
    /**
     * 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");
        }
        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);
+2 −2
Original line number Diff line number Diff line
@@ -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(
+1 −1
Original line number Diff line number Diff line
@@ -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;
            }