Move drag and drop related lines to DragDropController

The CL creates new DragDropController class where we can get together
everything related with drag and drop, and moves the following methods
to DragDropController.

  * WindowManagerService#preapreDragSurface
  * WindowManagerService.H#handleMessage for DRAG_START_TIMEOUT,
    DRAG_END_TIMEOUT, and TEAR_DOWN_DRAG_AND_DROP_INPUT
  * Session#preapreDrag
  * Session#performDrag
  * Session#reportDropResult
  * Session#cancelDragAndDrop
  * Session#dragRecipientEntered
  * Session#dragRecipientExited

Note that the CL does not remove the binder interface methods in
Session e.g. Session#prepareDrag. These methods start delegating drag
and drop work to DragDropController.

Bug: 65564090
Test: Manually drag and drop files between apps, CTS CrossAppDragAndDropTests
Change-Id: I50f6f022f2ecd0ab160b5187ae39f0f533f05969
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
new file mode 100644
index 0000000..36871b1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -0,0 +1,306 @@
+/*
+ * 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;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 2bb0235..717c577 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -81,6 +81,7 @@
     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 @@
         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,94 +306,56 @@
 
     /* 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);
+        return mDragDropController.performDrag(mService, window, dragToken, touchSource,
+                touchX, touchY, thumbCenterX, thumbCenterY, data);
+    }
+
+    @Override
+    public void reportDropResult(IWindow window, boolean consumed) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mDragDropController.reportDropResult(mService, window, consumed);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
+    }
 
-        synchronized (mService.mWindowMap) {
-            if (mService.mDragState == null) {
-                Slog.w(TAG_WM, "No drag prepared");
-                throw new IllegalStateException("performDrag() without prepareDrag()");
-            }
-
-            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();
-            try {
-                surfaceControl.setPosition(touchX - thumbCenterX,
-                        touchY - thumbCenterY);
-                surfaceControl.setLayer(mService.mDragState.getDragLayerLw());
-                surfaceControl.setLayerStack(display.getLayerStack());
-                surfaceControl.show();
-            } finally {
-                mService.closeSurfaceTransaction();
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
-                        TAG_WM, "<<< CLOSE TRANSACTION performDrag");
-            }
-
-            mService.mDragState.notifyLocationLw(touchX, touchY);
+    @Override
+    public void cancelDragAndDrop(IBinder dragToken) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mDragDropController.cancelDragAndDrop(mService, dragToken);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
+    }
 
-        return true;    // success!
+    @Override
+    public void dragRecipientEntered(IWindow window) {
+        mDragDropController.dragRecipientEntered(window);
+    }
+
+    @Override
+    public void dragRecipientExited(IWindow window) {
+        mDragDropController.dragRecipientExited(window);
     }
 
     @Override
@@ -408,90 +372,6 @@
     }
 
     @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?
-                }
-
-                mService.mDragState.mDragResult = consumed;
-                mService.mDragState.endDragLw();
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void cancelDragAndDrop(IBinder dragToken) {
-        if (DEBUG_DRAG) {
-            Slog.d(TAG_WM, "cancelDragAndDrop");
-        }
-
-        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();
-            } 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) {
         synchronized(mService.mWindowMap) {
             long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f0da474..3d60475 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -357,8 +357,6 @@
     // trying to apply a new one.
     private static final boolean ALWAYS_KEEP_CURRENT = true;
 
-    private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
-
     // Enums for animation scale update types.
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
@@ -752,6 +750,7 @@
     boolean mAllowTheaterModeWakeFromLayout;
 
     TaskPositioner mTaskPositioner;
+    final DragDropController mDragDropController = new DragDropController();
     DragState mDragState = null;
 
     // For frozen screen animations.
@@ -4637,73 +4636,6 @@
     }
 
     // -------------------------------------------------------------
-    // Drag and drop
-    // -------------------------------------------------------------
-
-    IBinder prepareDragSurface(IWindow window, SurfaceSession session,
-            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());
-        }
-
-        final int callerPid = Binder.getCallingPid();
-        final int callerUid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        IBinder token = null;
-
-        try {
-            synchronized (mWindowMap) {
-                try {
-                    if (mDragState == null) {
-                        // TODO(multi-display): support other displays
-                        final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                        final Display display = displayContent.getDisplay();
-
-                        SurfaceControl surface = new SurfaceControl(session, "drag surface",
-                                width, height, PixelFormat.TRANSLUCENT, SurfaceControl.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();
-                        mDragState = new DragState(this, token, surface, flags, winBinder);
-                        mDragState.mPid = callerPid;
-                        mDragState.mUid = callerUid;
-                        mDragState.mOriginalAlpha = alpha;
-                        token = mDragState.mToken = new Binder();
-
-                        // 5 second timeout for this window to actually begin the drag
-                        mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
-                        Message msg = mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
-                        mH.sendMessageDelayed(msg, 5000);
-                    } 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 (mDragState != null) {
-                        mDragState.reset();
-                        mDragState = null;
-                    }
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-
-        return token;
-    }
-
-    // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
 
@@ -4866,6 +4798,7 @@
         public static final int REPORT_WINDOWS_CHANGE = 19;
         public static final int DRAG_START_TIMEOUT = 20;
         public static final int DRAG_END_TIMEOUT = 21;
+
         public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
         public static final int BOOT_TIMEOUT = 23;
         public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
@@ -5120,47 +5053,12 @@
                     break;
                 }
 
-                case DRAG_START_TIMEOUT: {
-                    IBinder win = (IBinder)msg.obj;
-                    if (DEBUG_DRAG) {
-                        Slog.w(TAG_WM, "Timeout starting drag by win " + win);
-                    }
-                    synchronized (mWindowMap) {
-                        // !!! TODO: ANR the app that has failed to start the drag in time
-                        if (mDragState != null) {
-                            mDragState.unregister();
-                            mDragState.reset();
-                            mDragState = null;
-                        }
-                    }
-                    break;
-                }
-
-                case DRAG_END_TIMEOUT: {
-                    IBinder win = (IBinder)msg.obj;
-                    if (DEBUG_DRAG) {
-                        Slog.w(TAG_WM, "Timeout ending drag to win " + win);
-                    }
-                    synchronized (mWindowMap) {
-                        // !!! TODO: ANR the drag-receiving app
-                        if (mDragState != null) {
-                            mDragState.mDragResult = false;
-                            mDragState.endDragLw();
-                        }
-                    }
-                    break;
-                }
-
+                case DRAG_START_TIMEOUT:
+                case DRAG_END_TIMEOUT:
                 case 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 (mWindowMap) {
-                            interceptor.tearDown();
-                        }
-                    }
+                    mDragDropController.handleMessage(WindowManagerService.this, msg);
+                    break;
                 }
-                break;
 
                 case REPORT_HARD_KEYBOARD_STATUS_CHANGE: {
                     notifyHardKeyboardStatusChange();