blob: 65951dc634f81613211fed3f3f2da87ef21b3ba9 [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
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090024import android.annotation.NonNull;
Daichi Hironodf5cf622017-09-11 14:59:26 +090025import android.content.ClipData;
Robert Carre625fcf2017-09-01 12:36:28 -070026import android.graphics.PixelFormat;
Daichi Hironodf5cf622017-09-11 14:59:26 +090027import android.os.Binder;
Daichi Hirono58e25e12017-10-25 15:48:08 +090028import android.os.Handler;
Daichi Hironodf5cf622017-09-11 14:59:26 +090029import android.os.IBinder;
Daichi Hirono58e25e12017-10-25 15:48:08 +090030import android.os.Looper;
Daichi Hironodf5cf622017-09-11 14:59:26 +090031import android.os.Message;
32import android.util.Slog;
33import android.view.Display;
34import android.view.IWindow;
35import android.view.Surface;
Daichi Hironodf5cf622017-09-11 14:59:26 +090036import android.view.SurfaceControl;
37import android.view.SurfaceSession;
38import android.view.View;
Adrian Roose99bc052017-11-20 17:55:31 +010039
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090040import com.android.internal.util.Preconditions;
Daichi Hirono768012e2017-10-30 10:05:37 +090041import com.android.server.input.InputWindowHandle;
Adrian Roose99bc052017-11-20 17:55:31 +010042import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
Daichi Hironodf5cf622017-09-11 14:59:26 +090043
44/**
45 * Managing drag and drop operations initiated by View#startDragAndDrop.
46 */
47class DragDropController {
48 private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
49 private static final long DRAG_TIMEOUT_MS = 5000;
Daichi Hirono58e25e12017-10-25 15:48:08 +090050
51 // Messages for Handler.
52 private static final int MSG_DRAG_START_TIMEOUT = 0;
53 static final int MSG_DRAG_END_TIMEOUT = 1;
54 static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2;
55 static final int MSG_ANIMATION_END = 3;
56
Daichi Hirono3bae0b02017-10-27 13:05:13 +090057 /**
58 * Drag state per operation.
Daichi Hirono7a5d0072017-10-30 10:07:24 +090059 * Needs a lock of {@code WindowManagerService#mWindowMap} to read this. Needs both locks of
60 * {@code mWriteLock} and {@code WindowManagerService#mWindowMap} to update this.
Daichi Hirono3bae0b02017-10-27 13:05:13 +090061 * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
62 * itself, thus the variable can be null after calling DragState's methods.
63 */
Daichi Hirono768012e2017-10-30 10:05:37 +090064 private DragState mDragState;
Daichi Hirono76a26aa2017-09-11 15:13:38 +090065
Daichi Hirono58e25e12017-10-25 15:48:08 +090066 private WindowManagerService mService;
67 private final Handler mHandler;
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090068
Daichi Hirono7a5d0072017-10-30 10:07:24 +090069 /**
70 * Lock to preserve the order of state updates.
71 * The lock is used to process drag and drop state updates in order without having the window
72 * manager lock.
73 *
74 * Suppose DragDropController invokes a callback method A, then processes the following update
75 * A'. Same for a callback method B and the following update B'. The callback wants
76 * DragDropController to processes the updates in the order of A' then B'.
77 *
78 * Without mWriteLock: the following race can happen.
79 *
80 * 1. Thread a calls A.
81 * 2. Thread b calls B.
82 * 3. Thread b acquires the window manager lock
83 * 4. thread b processes the update B'
84 * 5. Thread a acquires the window manager lock
85 * 6. thread a processes the update A'
86 *
87 * With mWriteLock we can ensure the order of A' and B'
88 *
89 * 1. Thread a acquire mWriteLock
90 * 2. Thread a calls A
91 * 3. Thread a acquire the window manager lock
92 * 4. Thread a processes A'
93 * 5. Thread b acquire mWriteLock
94 * 6. Thread b calls B
95 * 7. Thread b acquire the window manager lock
96 * 8. Thread b processes B'
97 *
98 * Don't acquire the lock while holding the window manager lock, otherwise it causes a deadlock.
99 */
100 private final Object mWriteLock = new Object();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900101
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900102 /**
103 * Callback which is used to sync drag state with the vendor-specific code.
104 */
105 @NonNull private IDragDropCallback mCallback = new IDragDropCallback() {};
106
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900107 boolean dragDropActiveLocked() {
108 return mDragState != null;
109 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900110
Daichi Hirono768012e2017-10-30 10:05:37 +0900111 InputWindowHandle getInputWindowHandleLocked() {
112 return mDragState.getInputWindowHandle();
113 }
114
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900115 void registerCallback(IDragDropCallback callback) {
116 Preconditions.checkNotNull(callback);
117 synchronized (mWriteLock) {
118 mCallback = callback;
119 }
120 }
121
Daichi Hirono58e25e12017-10-25 15:48:08 +0900122 DragDropController(WindowManagerService service, Looper looper) {
123 mService = service;
124 mHandler = new DragHandler(service, looper);
125 }
126
Daichi Hirono768012e2017-10-30 10:05:37 +0900127 void sendDragStartedIfNeededLocked(WindowState window) {
128 mDragState.sendDragStartedIfNeededLocked(window);
129 }
130
Daichi Hirono58e25e12017-10-25 15:48:08 +0900131 IBinder prepareDrag(SurfaceSession session, int callerPid,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900132 int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
133 if (DEBUG_DRAG) {
134 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
135 + " flags=" + Integer.toHexString(flags) + " win=" + window
136 + " asbinder=" + window.asBinder());
137 }
138
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900139 synchronized (mWriteLock) {
140 synchronized (mService.mWindowMap) {
141 if (dragDropActiveLocked()) {
142 Slog.w(TAG_WM, "Drag already in progress");
143 return null;
144 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900145
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900146 // TODO(multi-display): support other displays
147 final DisplayContent displayContent =
148 mService.getDefaultDisplayContentLocked();
149 final Display display = displayContent.getDisplay();
150
151 final SurfaceControl surface = new SurfaceControl.Builder(session)
152 .setName("drag surface")
153 .setSize(width, height)
154 .setFormat(PixelFormat.TRANSLUCENT)
155 .build();
156 surface.setLayerStack(display.getLayerStack());
157 float alpha = 1;
158 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
159 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
160 }
161 surface.setAlpha(alpha);
162
163 if (SHOW_TRANSACTIONS)
164 Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
165 outSurface.copyFrom(surface);
166 final IBinder winBinder = window.asBinder();
167 IBinder token = new Binder();
168 mDragState = new DragState(mService, token, surface, flags, winBinder);
169 mDragState.mPid = callerPid;
170 mDragState.mUid = callerUid;
171 mDragState.mOriginalAlpha = alpha;
172 token = mDragState.mToken = new Binder();
173
174 // 5 second timeout for this window to actually begin the drag
175 sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
176 return token;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900177 }
178 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900179 }
180
Daichi Hirono58e25e12017-10-25 15:48:08 +0900181 boolean performDrag(IWindow window, IBinder dragToken,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900182 int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
183 ClipData data) {
184 if (DEBUG_DRAG) {
185 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
186 }
187
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900188 synchronized (mWriteLock) {
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900189 if (!mCallback.performDrag(window, dragToken, touchSource, touchX, touchY, thumbCenterX,
190 thumbCenterY, data)) {
191 return false;
192 }
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900193 synchronized (mService.mWindowMap) {
194 if (mDragState == null) {
195 Slog.w(TAG_WM, "No drag prepared");
196 throw new IllegalStateException("performDrag() without prepareDrag()");
197 }
198
199 if (dragToken != mDragState.mToken) {
200 Slog.w(TAG_WM, "Performing mismatched drag");
201 throw new IllegalStateException("performDrag() does not match prepareDrag()");
202 }
203
204 final WindowState callingWin = mService.windowForClientLocked(null, window, false);
205 if (callingWin == null) {
206 Slog.w(TAG_WM, "Bad requesting window " + window);
207 return false; // !!! TODO: throw here?
208 }
209
210 // !!! TODO: if input is not still focused on the initiating window, fail
211 // the drag initiation (e.g. an alarm window popped up just as the application
212 // called performDrag()
213
214 mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
215
216 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
217 // will let us eliminate the (touchX,touchY) parameters from the API.
218
219 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
220 // the actual drag event dispatch stuff in the dragstate
221
222 final DisplayContent displayContent = callingWin.getDisplayContent();
223 if (displayContent == null) {
224 return false;
225 }
226 Display display = displayContent.getDisplay();
227 mDragState.register(display);
228 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
229 mDragState.getInputChannel())) {
230 Slog.e(TAG_WM, "Unable to transfer touch focus");
231 mDragState.closeLocked();
232 return false;
233 }
234
235 mDragState.mDisplayContent = displayContent;
236 mDragState.mData = data;
237 mDragState.broadcastDragStartedLocked(touchX, touchY);
238 mDragState.overridePointerIconLocked(touchSource);
239
240 // remember the thumb offsets for later
241 mDragState.mThumbOffsetX = thumbCenterX;
242 mDragState.mThumbOffsetY = thumbCenterY;
243
244 // Make the surface visible at the proper location
245 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
246 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
247 mService.openSurfaceTransaction();
248 try {
249 surfaceControl.setPosition(touchX - thumbCenterX,
250 touchY - thumbCenterY);
251 surfaceControl.setLayer(mDragState.getDragLayerLocked());
252 surfaceControl.setLayerStack(display.getLayerStack());
253 surfaceControl.show();
254 } finally {
255 mService.closeSurfaceTransaction("performDrag");
256 if (SHOW_LIGHT_TRANSACTIONS) {
257 Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
258 }
259 }
260
261 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900262 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900263 }
264
265 return true; // success!
266 }
267
Daichi Hirono58e25e12017-10-25 15:48:08 +0900268 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900269 IBinder token = window.asBinder();
270 if (DEBUG_DRAG) {
271 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
272 }
273
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900274 synchronized (mWriteLock) {
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900275 mCallback.reportDropResult(window, consumed);
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900276 synchronized (mService.mWindowMap) {
277 if (mDragState == null) {
278 // Most likely the drop recipient ANRed and we ended the drag
279 // out from under it. Log the issue and move on.
280 Slog.w(TAG_WM, "Drop result given but no drag in progress");
281 return;
282 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900283
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900284 if (mDragState.mToken != token) {
285 // We're in a drag, but the wrong window has responded.
286 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
287 throw new IllegalStateException("reportDropResult() by non-recipient");
288 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900289
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900290 // The right window has responded, even if it's no longer around,
291 // so be sure to halt the timeout even if the later WindowState
292 // lookup fails.
293 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
294 WindowState callingWin = mService.windowForClientLocked(null, window, false);
295 if (callingWin == null) {
296 Slog.w(TAG_WM, "Bad result-reporting window " + window);
297 return; // !!! TODO: throw here?
298 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900299
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900300
301 mDragState.mDragResult = consumed;
302 mDragState.endDragLocked();
303 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900304 }
305 }
306
Daichi Hirono58e25e12017-10-25 15:48:08 +0900307 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900308 if (DEBUG_DRAG) {
309 Slog.d(TAG_WM, "cancelDragAndDrop");
310 }
311
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900312 synchronized (mWriteLock) {
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900313 mCallback.cancelDragAndDrop(dragToken);
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900314 synchronized (mService.mWindowMap) {
315 if (mDragState == null) {
316 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
317 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
318 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900319
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900320 if (mDragState.mToken != dragToken) {
321 Slog.w(TAG_WM,
322 "cancelDragAndDrop() does not match prepareDrag()");
323 throw new IllegalStateException(
324 "cancelDragAndDrop() does not match prepareDrag()");
325 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900326
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900327 mDragState.mDragResult = false;
328 mDragState.cancelDragLocked();
329 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900330 }
331 }
332
Daichi Hirono768012e2017-10-30 10:05:37 +0900333 /**
334 * Handles motion events.
335 * @param keepHandling Whether if the drag operation is continuing or this is the last motion
336 * event.
337 * @param newX X coordinate value in dp in the screen coordinate
338 * @param newY Y coordinate value in dp in the screen coordinate
339 */
340 void handleMotionEvent(boolean keepHandling, float newX, float newY) {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900341 synchronized (mWriteLock) {
342 synchronized (mService.mWindowMap) {
343 if (!dragDropActiveLocked()) {
344 // The drag has ended but the clean-up message has not been processed by
345 // window manager. Drop events that occur after this until window manager
346 // has a chance to clean-up the input handle.
347 return;
348 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900349
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900350 if (keepHandling) {
351 mDragState.notifyMoveLocked(newX, newY);
352 } else {
353 mDragState.notifyDropLocked(newX, newY);
354 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900355 }
356 }
357 }
358
Daichi Hironodf5cf622017-09-11 14:59:26 +0900359 void dragRecipientEntered(IWindow window) {
360 if (DEBUG_DRAG) {
361 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
362 }
363 }
364
365 void dragRecipientExited(IWindow window) {
366 if (DEBUG_DRAG) {
367 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
368 }
369 }
370
Daichi Hirono58e25e12017-10-25 15:48:08 +0900371 /**
372 * Sends a message to the Handler managed by DragDropController.
373 */
374 void sendHandlerMessage(int what, Object arg) {
375 mHandler.obtainMessage(what, arg).sendToTarget();
376 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900377
Daichi Hirono58e25e12017-10-25 15:48:08 +0900378 /**
379 * Sends a timeout message to the Handler managed by DragDropController.
380 */
381 void sendTimeoutMessage(int what, Object arg) {
382 mHandler.removeMessages(what, arg);
383 final Message msg = mHandler.obtainMessage(what, arg);
384 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
385 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900386
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900387 /**
388 * Notifies the current drag state is closed.
389 */
390 void onDragStateClosedLocked(DragState dragState) {
391 if (mDragState != dragState) {
392 Slog.wtf(TAG_WM, "Unknown drag state is closed");
393 return;
394 }
395 mDragState = null;
396 }
397
Daichi Hirono58e25e12017-10-25 15:48:08 +0900398 private class DragHandler extends Handler {
399 /**
400 * Lock for window manager.
401 */
402 private final WindowManagerService mService;
403
404 DragHandler(WindowManagerService service, Looper looper) {
405 super(looper);
406 mService = service;
407 }
408
409 @Override
410 public void handleMessage(Message msg) {
411 switch (msg.what) {
412 case MSG_DRAG_START_TIMEOUT: {
413 IBinder win = (IBinder) msg.obj;
414 if (DEBUG_DRAG) {
415 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900416 }
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900417 synchronized (mWriteLock) {
418 synchronized (mService.mWindowMap) {
419 // !!! TODO: ANR the app that has failed to start the drag in time
420 if (mDragState != null) {
421 mDragState.closeLocked();
422 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900423 }
424 }
425 break;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900426 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900427
428 case MSG_DRAG_END_TIMEOUT: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900429 final IBinder win = (IBinder) msg.obj;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900430 if (DEBUG_DRAG) {
431 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
432 }
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900433 synchronized (mWriteLock) {
434 synchronized (mService.mWindowMap) {
435 // !!! TODO: ANR the drag-receiving app
436 if (mDragState != null) {
437 mDragState.mDragResult = false;
438 mDragState.endDragLocked();
439 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900440 }
441 }
442 break;
443 }
444
445 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
446 if (DEBUG_DRAG)
447 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900448 final DragState.InputInterceptor interceptor =
449 (DragState.InputInterceptor) msg.obj;
450 if (interceptor == null) return;
451 synchronized (mService.mWindowMap) {
452 interceptor.tearDown();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900453 }
454 break;
455 }
456
457 case MSG_ANIMATION_END: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900458 synchronized (mWriteLock) {
459 synchronized (mService.mWindowMap) {
460 if (mDragState == null) {
461 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
462 "plyaing animation");
463 return;
464 }
465 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900466 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900467 }
468 break;
469 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900470 }
471 }
472 }
473}