blob: 860ff387d66814e0ac30ea35500f58ca4cbeae75 [file] [log] [blame]
Daichi Hironodf5cf622017-09-11 14:59:26 +09001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
Daichi Hironodf5cf622017-09-11 14:59:26 +090019import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
20import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
21import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
22import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
23
24import android.content.ClipData;
Robert Carre625fcf2017-09-01 12:36:28 -070025import android.graphics.PixelFormat;
Daichi Hironodf5cf622017-09-11 14:59:26 +090026import android.os.Binder;
Daichi Hirono58e25e12017-10-25 15:48:08 +090027import android.os.Handler;
Daichi Hironodf5cf622017-09-11 14:59:26 +090028import android.os.IBinder;
Daichi Hirono58e25e12017-10-25 15:48:08 +090029import android.os.Looper;
Daichi Hironodf5cf622017-09-11 14:59:26 +090030import android.os.Message;
31import android.util.Slog;
32import android.view.Display;
33import android.view.IWindow;
34import android.view.Surface;
Daichi Hironodf5cf622017-09-11 14:59:26 +090035import android.view.SurfaceControl;
36import android.view.SurfaceSession;
37import android.view.View;
Daichi Hironodf5cf622017-09-11 14:59:26 +090038
39/**
40 * Managing drag and drop operations initiated by View#startDragAndDrop.
41 */
42class DragDropController {
43 private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
44 private static final long DRAG_TIMEOUT_MS = 5000;
Daichi Hirono58e25e12017-10-25 15:48:08 +090045
46 // Messages for Handler.
47 private static final int MSG_DRAG_START_TIMEOUT = 0;
48 static final int MSG_DRAG_END_TIMEOUT = 1;
49 static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2;
50 static final int MSG_ANIMATION_END = 3;
51
Daichi Hirono76a26aa2017-09-11 15:13:38 +090052 DragState mDragState;
53
Daichi Hirono58e25e12017-10-25 15:48:08 +090054 private WindowManagerService mService;
55 private final Handler mHandler;
56
Daichi Hirono76a26aa2017-09-11 15:13:38 +090057 boolean dragDropActiveLocked() {
58 return mDragState != null;
59 }
Daichi Hironodf5cf622017-09-11 14:59:26 +090060
Daichi Hirono58e25e12017-10-25 15:48:08 +090061 DragDropController(WindowManagerService service, Looper looper) {
62 mService = service;
63 mHandler = new DragHandler(service, looper);
64 }
65
66 IBinder prepareDrag(SurfaceSession session, int callerPid,
Daichi Hironodf5cf622017-09-11 14:59:26 +090067 int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
68 if (DEBUG_DRAG) {
69 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
70 + " flags=" + Integer.toHexString(flags) + " win=" + window
71 + " asbinder=" + window.asBinder());
72 }
73
74 IBinder token = null;
75
Daichi Hirono58e25e12017-10-25 15:48:08 +090076 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +090077 if (dragDropActiveLocked()) {
78 Slog.w(TAG_WM, "Drag already in progress");
79 return null;
Daichi Hironodf5cf622017-09-11 14:59:26 +090080 }
Daichi Hirono76a26aa2017-09-11 15:13:38 +090081
82 // TODO(multi-display): support other displays
83 final DisplayContent displayContent =
Daichi Hirono58e25e12017-10-25 15:48:08 +090084 mService.getDefaultDisplayContentLocked();
Daichi Hirono76a26aa2017-09-11 15:13:38 +090085 final Display display = displayContent.getDisplay();
86
87 final SurfaceControl surface = new SurfaceControl.Builder(session)
88 .setName("drag surface")
89 .setSize(width, height)
90 .setFormat(PixelFormat.TRANSLUCENT)
91 .build();
92 surface.setLayerStack(display.getLayerStack());
93 float alpha = 1;
94 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
95 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
96 }
97 surface.setAlpha(alpha);
98
99 if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
100 outSurface.copyFrom(surface);
101 final IBinder winBinder = window.asBinder();
102 token = new Binder();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900103 mDragState = new DragState(mService, token, surface, flags, winBinder);
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900104 mDragState.mPid = callerPid;
105 mDragState.mUid = callerUid;
106 mDragState.mOriginalAlpha = alpha;
107 token = mDragState.mToken = new Binder();
108
109 // 5 second timeout for this window to actually begin the drag
Daichi Hirono58e25e12017-10-25 15:48:08 +0900110 sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900111 }
112
113 return token;
114 }
115
Daichi Hirono58e25e12017-10-25 15:48:08 +0900116 boolean performDrag(IWindow window, IBinder dragToken,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900117 int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
118 ClipData data) {
119 if (DEBUG_DRAG) {
120 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
121 }
122
Daichi Hirono58e25e12017-10-25 15:48:08 +0900123 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900124 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900125 Slog.w(TAG_WM, "No drag prepared");
126 throw new IllegalStateException("performDrag() without prepareDrag()");
127 }
128
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900129 if (dragToken != mDragState.mToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900130 Slog.w(TAG_WM, "Performing mismatched drag");
131 throw new IllegalStateException("performDrag() does not match prepareDrag()");
132 }
133
Daichi Hirono58e25e12017-10-25 15:48:08 +0900134 final WindowState callingWin = mService.windowForClientLocked(null, window, false);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900135 if (callingWin == null) {
136 Slog.w(TAG_WM, "Bad requesting window " + window);
137 return false; // !!! TODO: throw here?
138 }
139
140 // !!! TODO: if input is not still focused on the initiating window, fail
141 // the drag initiation (e.g. an alarm window popped up just as the application
142 // called performDrag()
143
Daichi Hirono58e25e12017-10-25 15:48:08 +0900144 mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
Daichi Hironodf5cf622017-09-11 14:59:26 +0900145
146 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
147 // will let us eliminate the (touchX,touchY) parameters from the API.
148
Daichi Hirono58e25e12017-10-25 15:48:08 +0900149 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
Daichi Hironodf5cf622017-09-11 14:59:26 +0900150 // the actual drag event dispatch stuff in the dragstate
151
152 final DisplayContent displayContent = callingWin.getDisplayContent();
153 if (displayContent == null) {
154 return false;
155 }
156 Display display = displayContent.getDisplay();
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900157 mDragState.register(display);
Daichi Hirono58e25e12017-10-25 15:48:08 +0900158 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900159 mDragState.getInputChannel())) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900160 Slog.e(TAG_WM, "Unable to transfer touch focus");
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900161 mDragState.unregister();
162 mDragState.reset();
163 mDragState = null;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900164 return false;
165 }
166
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900167 mDragState.mDisplayContent = displayContent;
168 mDragState.mData = data;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900169 mDragState.broadcastDragStartedLocked(touchX, touchY);
170 mDragState.overridePointerIconLocked(touchSource);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900171
172 // remember the thumb offsets for later
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900173 mDragState.mThumbOffsetX = thumbCenterX;
174 mDragState.mThumbOffsetY = thumbCenterY;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900175
176 // Make the surface visible at the proper location
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900177 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900178 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
179 TAG_WM, ">>> OPEN TRANSACTION performDrag");
Daichi Hirono58e25e12017-10-25 15:48:08 +0900180 mService.openSurfaceTransaction();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900181 try {
182 surfaceControl.setPosition(touchX - thumbCenterX,
183 touchY - thumbCenterY);
Daichi Hirono58e25e12017-10-25 15:48:08 +0900184 surfaceControl.setLayer(mDragState.getDragLayerLocked());
Daichi Hironodf5cf622017-09-11 14:59:26 +0900185 surfaceControl.setLayerStack(display.getLayerStack());
186 surfaceControl.show();
187 } finally {
Daichi Hirono58e25e12017-10-25 15:48:08 +0900188 mService.closeSurfaceTransaction();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900189 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
190 TAG_WM, "<<< CLOSE TRANSACTION performDrag");
191 }
192
Daichi Hirono58e25e12017-10-25 15:48:08 +0900193 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900194 }
195
196 return true; // success!
197 }
198
Daichi Hirono58e25e12017-10-25 15:48:08 +0900199 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900200 IBinder token = window.asBinder();
201 if (DEBUG_DRAG) {
202 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
203 }
204
Daichi Hirono58e25e12017-10-25 15:48:08 +0900205 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900206 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900207 // Most likely the drop recipient ANRed and we ended the drag
208 // out from under it. Log the issue and move on.
209 Slog.w(TAG_WM, "Drop result given but no drag in progress");
210 return;
211 }
212
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900213 if (mDragState.mToken != token) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900214 // We're in a drag, but the wrong window has responded.
215 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
216 throw new IllegalStateException("reportDropResult() by non-recipient");
217 }
218
219 // The right window has responded, even if it's no longer around,
220 // so be sure to halt the timeout even if the later WindowState
221 // lookup fails.
Daichi Hirono58e25e12017-10-25 15:48:08 +0900222 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
223 WindowState callingWin = mService.windowForClientLocked(null, window, false);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900224 if (callingWin == null) {
225 Slog.w(TAG_WM, "Bad result-reporting window " + window);
226 return; // !!! TODO: throw here?
227 }
228
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900229 mDragState.mDragResult = consumed;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900230 mDragState.endDragLocked();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900231 }
232 }
233
Daichi Hirono58e25e12017-10-25 15:48:08 +0900234 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900235 if (DEBUG_DRAG) {
236 Slog.d(TAG_WM, "cancelDragAndDrop");
237 }
238
Daichi Hirono58e25e12017-10-25 15:48:08 +0900239 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900240 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900241 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
242 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
243 }
244
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900245 if (mDragState.mToken != dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900246 Slog.w(TAG_WM,
247 "cancelDragAndDrop() does not match prepareDrag()");
248 throw new IllegalStateException(
249 "cancelDragAndDrop() does not match prepareDrag()");
250 }
251
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900252 mDragState.mDragResult = false;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900253 mDragState.cancelDragLocked();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900254 }
255 }
256
257 void dragRecipientEntered(IWindow window) {
258 if (DEBUG_DRAG) {
259 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
260 }
261 }
262
263 void dragRecipientExited(IWindow window) {
264 if (DEBUG_DRAG) {
265 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
266 }
267 }
268
Daichi Hirono58e25e12017-10-25 15:48:08 +0900269 /**
270 * Sends a message to the Handler managed by DragDropController.
271 */
272 void sendHandlerMessage(int what, Object arg) {
273 mHandler.obtainMessage(what, arg).sendToTarget();
274 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900275
Daichi Hirono58e25e12017-10-25 15:48:08 +0900276 /**
277 * Sends a timeout message to the Handler managed by DragDropController.
278 */
279 void sendTimeoutMessage(int what, Object arg) {
280 mHandler.removeMessages(what, arg);
281 final Message msg = mHandler.obtainMessage(what, arg);
282 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
283 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900284
Daichi Hirono58e25e12017-10-25 15:48:08 +0900285 private class DragHandler extends Handler {
286 /**
287 * Lock for window manager.
288 */
289 private final WindowManagerService mService;
290
291 DragHandler(WindowManagerService service, Looper looper) {
292 super(looper);
293 mService = service;
294 }
295
296 @Override
297 public void handleMessage(Message msg) {
298 switch (msg.what) {
299 case MSG_DRAG_START_TIMEOUT: {
300 IBinder win = (IBinder) msg.obj;
301 if (DEBUG_DRAG) {
302 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900303 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900304 synchronized (mService.mWindowMap) {
305 // !!! TODO: ANR the app that has failed to start the drag in time
306 if (mDragState != null) {
307 mDragState.unregister();
308 mDragState.reset();
309 mDragState = null;
310 }
311 }
312 break;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900313 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900314
315 case MSG_DRAG_END_TIMEOUT: {
316 IBinder win = (IBinder) msg.obj;
317 if (DEBUG_DRAG) {
318 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
319 }
320 synchronized (mService.mWindowMap) {
321 // !!! TODO: ANR the drag-receiving app
322 if (mDragState != null) {
323 mDragState.mDragResult = false;
324 mDragState.endDragLocked();
325 }
326 }
327 break;
328 }
329
330 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
331 if (DEBUG_DRAG)
332 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
333 DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
334 if (interceptor != null) {
335 synchronized (mService.mWindowMap) {
336 interceptor.tearDown();
337 }
338 }
339 break;
340 }
341
342 case MSG_ANIMATION_END: {
343 synchronized (mService.mWindowMap) {
344 if (mDragState == null) {
345 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
346 "plyaing animation");
347 return;
348 }
349 mDragState.onAnimationEndLocked();
350 }
351 break;
352 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900353 }
354 }
355 }
356}