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

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

Merge "Move drag and drop related lines to DragDropController"

parents 7ff3a5ff df5cf625
Loading
Loading
Loading
Loading
+306 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import static android.graphics.PixelFormat.TRANSLUCENT;
import static android.view.SurfaceControl.HIDDEN;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.content.ClipData;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Slog;
import android.view.Display;
import android.view.IWindow;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
import com.android.server.wm.WindowManagerService.H;

/**
 * Managing drag and drop operations initiated by View#startDragAndDrop.
 */
class DragDropController {
    private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
    private static final long DRAG_TIMEOUT_MS = 5000;

    IBinder prepareDrag(WindowManagerService service, SurfaceSession session, int callerPid,
            int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
                    + " flags=" + Integer.toHexString(flags) + " win=" + window
                    + " asbinder=" + window.asBinder());
        }

        IBinder token = null;

        synchronized (service.mWindowMap) {
            try {
                if (service.mDragState == null) {
                    // TODO(multi-display): support other displays
                    final DisplayContent displayContent =
                            service.getDefaultDisplayContentLocked();
                    final Display display = displayContent.getDisplay();

                    SurfaceControl surface = new SurfaceControl(session, "drag surface",
                            width, height, TRANSLUCENT, HIDDEN);
                    surface.setLayerStack(display.getLayerStack());
                    float alpha = 1;
                    if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
                        alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
                    }
                    surface.setAlpha(alpha);

                    if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  DRAG "
                            + surface + ": CREATE");
                    outSurface.copyFrom(surface);
                    final IBinder winBinder = window.asBinder();
                    token = new Binder();
                    service.mDragState =
                            new DragState(service, token, surface, flags, winBinder);
                    service.mDragState.mPid = callerPid;
                    service.mDragState.mUid = callerUid;
                    service.mDragState.mOriginalAlpha = alpha;
                    token = service.mDragState.mToken = new Binder();

                    // 5 second timeout for this window to actually begin the drag
                    service.mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
                    Message msg = service.mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
                    service.mH.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
                } else {
                    Slog.w(TAG_WM, "Drag already in progress");
                }
            } catch (OutOfResourcesException e) {
                Slog.e(TAG_WM, "Can't allocate drag surface w=" + width + " h=" + height,
                        e);
                if (service.mDragState != null) {
                    service.mDragState.reset();
                    service.mDragState = null;
                }
            }
        }

        return token;
    }

    boolean performDrag(WindowManagerService service, IWindow window, IBinder dragToken,
            int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
            ClipData data) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
        }

        synchronized (service.mWindowMap) {
            if (service.mDragState == null) {
                Slog.w(TAG_WM, "No drag prepared");
                throw new IllegalStateException("performDrag() without prepareDrag()");
            }

            if (dragToken != service.mDragState.mToken) {
                Slog.w(TAG_WM, "Performing mismatched drag");
                throw new IllegalStateException("performDrag() does not match prepareDrag()");
            }

            final WindowState callingWin = service.windowForClientLocked(null, window, false);
            if (callingWin == null) {
                Slog.w(TAG_WM, "Bad requesting window " + window);
                return false;  // !!! TODO: throw here?
            }

            // !!! TODO: if input is not still focused on the initiating window, fail
            // the drag initiation (e.g. an alarm window popped up just as the application
            // called performDrag()

            service.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());

            // !!! TODO: extract the current touch (x, y) in screen coordinates.  That
            // will let us eliminate the (touchX,touchY) parameters from the API.

            // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
            // the actual drag event dispatch stuff in the dragstate

            final DisplayContent displayContent = callingWin.getDisplayContent();
            if (displayContent == null) {
                return false;
            }
            Display display = displayContent.getDisplay();
            service.mDragState.register(display);
            if (!service.mInputManager.transferTouchFocus(callingWin.mInputChannel,
                    service.mDragState.getInputChannel())) {
                Slog.e(TAG_WM, "Unable to transfer touch focus");
                service.mDragState.unregister();
                service.mDragState.reset();
                service.mDragState = null;
                return false;
            }

            service.mDragState.mDisplayContent = displayContent;
            service.mDragState.mData = data;
            service.mDragState.broadcastDragStartedLw(touchX, touchY);
            service.mDragState.overridePointerIconLw(touchSource);

            // remember the thumb offsets for later
            service.mDragState.mThumbOffsetX = thumbCenterX;
            service.mDragState.mThumbOffsetY = thumbCenterY;

            // Make the surface visible at the proper location
            final SurfaceControl surfaceControl = service.mDragState.mSurfaceControl;
            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
                    TAG_WM, ">>> OPEN TRANSACTION performDrag");
            service.openSurfaceTransaction();
            try {
                surfaceControl.setPosition(touchX - thumbCenterX,
                        touchY - thumbCenterY);
                surfaceControl.setLayer(service.mDragState.getDragLayerLw());
                surfaceControl.setLayerStack(display.getLayerStack());
                surfaceControl.show();
            } finally {
                service.closeSurfaceTransaction();
                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
                        TAG_WM, "<<< CLOSE TRANSACTION performDrag");
            }

            service.mDragState.notifyLocationLw(touchX, touchY);
        }

        return true;    // success!
    }

    void reportDropResult(WindowManagerService service, IWindow window, boolean consumed) {
        IBinder token = window.asBinder();
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
        }

        synchronized (service.mWindowMap) {
            if (service.mDragState == null) {
                // Most likely the drop recipient ANRed and we ended the drag
                // out from under it.  Log the issue and move on.
                Slog.w(TAG_WM, "Drop result given but no drag in progress");
                return;
            }

            if (service.mDragState.mToken != token) {
                // We're in a drag, but the wrong window has responded.
                Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
                throw new IllegalStateException("reportDropResult() by non-recipient");
            }

            // The right window has responded, even if it's no longer around,
            // so be sure to halt the timeout even if the later WindowState
            // lookup fails.
            service.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
            WindowState callingWin = service.windowForClientLocked(null, window, false);
            if (callingWin == null) {
                Slog.w(TAG_WM, "Bad result-reporting window " + window);
                return;  // !!! TODO: throw here?
            }

            service.mDragState.mDragResult = consumed;
            service.mDragState.endDragLw();
        }
    }

    void cancelDragAndDrop(WindowManagerService service, IBinder dragToken) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "cancelDragAndDrop");
        }

        synchronized (service.mWindowMap) {
            if (service.mDragState == null) {
                Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
                throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
            }

            if (service.mDragState.mToken != dragToken) {
                Slog.w(TAG_WM,
                        "cancelDragAndDrop() does not match prepareDrag()");
                throw new IllegalStateException(
                        "cancelDragAndDrop() does not match prepareDrag()");
            }

            service.mDragState.mDragResult = false;
            service.mDragState.cancelDragLw();
        }
    }

    void dragRecipientEntered(IWindow window) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
        }
    }

    void dragRecipientExited(IWindow window) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
        }
    }

    void handleMessage(WindowManagerService service, Message msg) {
        switch (msg.what) {
            case H.DRAG_START_TIMEOUT: {
                IBinder win = (IBinder) msg.obj;
                if (DEBUG_DRAG) {
                    Slog.w(TAG_WM, "Timeout starting drag by win " + win);
                }
                synchronized (service.mWindowMap) {
                    // !!! TODO: ANR the app that has failed to start the drag in time
                    if (service.mDragState != null) {
                        service.mDragState.unregister();
                        service.mDragState.reset();
                        service.mDragState = null;
                    }
                }
                break;
            }

            case H.DRAG_END_TIMEOUT: {
                IBinder win = (IBinder) msg.obj;
                if (DEBUG_DRAG) {
                    Slog.w(TAG_WM, "Timeout ending drag to win " + win);
                }
                synchronized (service.mWindowMap) {
                    // !!! TODO: ANR the drag-receiving app
                    if (service.mDragState != null) {
                        service.mDragState.mDragResult = false;
                        service.mDragState.endDragLw();
                    }
                }
                break;
            }

            case H.TEAR_DOWN_DRAG_AND_DROP_INPUT: {
                if (DEBUG_DRAG)
                    Slog.d(TAG_WM, "Drag ending; tearing down input channel");
                DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
                if (interceptor != null) {
                    synchronized (service.mWindowMap) {
                        interceptor.tearDown();
                    }
                }
                break;
            }
        }
    }
}
+38 −158
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    private final Set<WindowSurfaceController> mAppOverlaySurfaces = new HashSet<>();
    // Set of visible alert window surfaces connected to this session.
    private final Set<WindowSurfaceController> mAlertWindowSurfaces = new HashSet<>();
    private final DragDropController mDragDropController;
    final boolean mCanAddInternalSystemWindow;
    final boolean mCanHideNonSystemOverlayWindows;
    final boolean mCanAcquireSleepToken;
@@ -106,6 +107,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
        mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
                == PERMISSION_GRANTED;
        mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
        mDragDropController = mService.mDragDropController;
        StringBuilder sb = new StringBuilder();
        sb.append("Session{");
        sb.append(Integer.toHexString(System.identityHashCode(this)));
@@ -304,192 +306,70 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {

    /* Drag/drop */
    @Override
    public IBinder prepareDrag(IWindow window, int flags,
            int width, int height, Surface outSurface) {
        return mService.prepareDragSurface(window, mSurfaceSession, flags,
                width, height, outSurface);
    public IBinder prepareDrag(IWindow window, int flags, int width, int height,
            Surface outSurface) {
        final int callerPid = Binder.getCallingPid();
        final int callerUid = Binder.getCallingUid();
        final long ident = Binder.clearCallingIdentity();
        try {
            return mDragDropController.prepareDrag(
                    mService, mSurfaceSession, callerPid, callerUid, window, flags, width, height,
                    outSurface);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @Override
    public boolean performDrag(IWindow window, IBinder dragToken,
            int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
            ClipData data) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
        }

        synchronized (mService.mWindowMap) {
            if (mService.mDragState == null) {
                Slog.w(TAG_WM, "No drag prepared");
                throw new IllegalStateException("performDrag() without prepareDrag()");
        return mDragDropController.performDrag(mService, window, dragToken, touchSource,
                touchX, touchY, thumbCenterX, thumbCenterY, data);
    }

            if (dragToken != mService.mDragState.mToken) {
                Slog.w(TAG_WM, "Performing mismatched drag");
                throw new IllegalStateException("performDrag() does not match prepareDrag()");
            }

            WindowState callingWin = mService.windowForClientLocked(null, window, false);
            if (callingWin == null) {
                Slog.w(TAG_WM, "Bad requesting window " + window);
                return false;  // !!! TODO: throw here?
            }

            // !!! TODO: if input is not still focused on the initiating window, fail
            // the drag initiation (e.g. an alarm window popped up just as the application
            // called performDrag()

            mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());

            // !!! TODO: extract the current touch (x, y) in screen coordinates.  That
            // will let us eliminate the (touchX,touchY) parameters from the API.

            // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
            // the actual drag event dispatch stuff in the dragstate

            final DisplayContent displayContent = callingWin.getDisplayContent();
            if (displayContent == null) {
               return false;
            }
            Display display = displayContent.getDisplay();
            mService.mDragState.register(display);
            if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
                    mService.mDragState.getInputChannel())) {
                Slog.e(TAG_WM, "Unable to transfer touch focus");
                mService.mDragState.unregister();
                mService.mDragState.reset();
                mService.mDragState = null;
                return false;
            }

            mService.mDragState.mDisplayContent = displayContent;
            mService.mDragState.mData = data;
            mService.mDragState.broadcastDragStartedLw(touchX, touchY);
            mService.mDragState.overridePointerIconLw(touchSource);

            // remember the thumb offsets for later
            mService.mDragState.mThumbOffsetX = thumbCenterX;
            mService.mDragState.mThumbOffsetY = thumbCenterY;

            // Make the surface visible at the proper location
            final SurfaceControl surfaceControl = mService.mDragState.mSurfaceControl;
            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
                    TAG_WM, ">>> OPEN TRANSACTION performDrag");
            mService.openSurfaceTransaction();
    @Override
    public void reportDropResult(IWindow window, boolean consumed) {
        final long ident = Binder.clearCallingIdentity();
        try {
                surfaceControl.setPosition(touchX - thumbCenterX,
                        touchY - thumbCenterY);
                surfaceControl.setLayer(mService.mDragState.getDragLayerLw());
                surfaceControl.setLayerStack(display.getLayerStack());
                surfaceControl.show();
            mDragDropController.reportDropResult(mService, window, consumed);
        } finally {
                mService.closeSurfaceTransaction();
                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
                        TAG_WM, "<<< CLOSE TRANSACTION performDrag");
            }

            mService.mDragState.notifyLocationLw(touchX, touchY);
            Binder.restoreCallingIdentity(ident);
        }

        return true;    // success!
    }

    @Override
    public boolean startMovingTask(IWindow window, float startX, float startY) {
        if (DEBUG_TASK_POSITIONING) Slog.d(
                TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");

        long ident = Binder.clearCallingIdentity();
    public void cancelDragAndDrop(IBinder dragToken) {
        final long ident = Binder.clearCallingIdentity();
        try {
            return mService.startMovingTask(window, startX, startY);
            mDragDropController.cancelDragAndDrop(mService, dragToken);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @Override
    public void reportDropResult(IWindow window, boolean consumed) {
        IBinder token = window.asBinder();
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
        }

        synchronized (mService.mWindowMap) {
            long ident = Binder.clearCallingIdentity();
            try {
                if (mService.mDragState == null) {
                    // Most likely the drop recipient ANRed and we ended the drag
                    // out from under it.  Log the issue and move on.
                    Slog.w(TAG_WM, "Drop result given but no drag in progress");
                    return;
                }

                if (mService.mDragState.mToken != token) {
                    // We're in a drag, but the wrong window has responded.
                    Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
                    throw new IllegalStateException("reportDropResult() by non-recipient");
                }

                // The right window has responded, even if it's no longer around,
                // so be sure to halt the timeout even if the later WindowState
                // lookup fails.
                mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
                WindowState callingWin = mService.windowForClientLocked(null, window, false);
                if (callingWin == null) {
                    Slog.w(TAG_WM, "Bad result-reporting window " + window);
                    return;  // !!! TODO: throw here?
    public void dragRecipientEntered(IWindow window) {
        mDragDropController.dragRecipientEntered(window);
    }

                mService.mDragState.mDragResult = consumed;
                mService.mDragState.endDragLw();
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    @Override
    public void dragRecipientExited(IWindow window) {
        mDragDropController.dragRecipientExited(window);
    }

    @Override
    public void cancelDragAndDrop(IBinder dragToken) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "cancelDragAndDrop");
        }
    public boolean startMovingTask(IWindow window, float startX, float startY) {
        if (DEBUG_TASK_POSITIONING) Slog.d(
                TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");

        synchronized (mService.mWindowMap) {
        long ident = Binder.clearCallingIdentity();
        try {
                if (mService.mDragState == null) {
                    Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
                    throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
                }

                if (mService.mDragState.mToken != dragToken) {
                    Slog.w(TAG_WM,
                            "cancelDragAndDrop() does not match prepareDrag()");
                    throw new IllegalStateException(
                            "cancelDragAndDrop() does not match prepareDrag()");
                }

                mService.mDragState.mDragResult = false;
                mService.mDragState.cancelDragLw();
            return mService.startMovingTask(window, startX, startY);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
    }

    @Override
    public void dragRecipientEntered(IWindow window) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
        }
    }

    @Override
    public void dragRecipientExited(IWindow window) {
        if (DEBUG_DRAG) {
            Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
        }
    }

    @Override
    public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
+6 −108

File changed.

Preview size limit exceeded, changes collapsed.