blob: 4567e101157d059bf68927fee46d7909ecd5d670 [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;
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090039import android.view.WindowManagerInternal.IDragDropCallback;
40import com.android.internal.util.Preconditions;
Daichi Hirono768012e2017-10-30 10:05:37 +090041import com.android.server.input.InputWindowHandle;
Daichi Hironodf5cf622017-09-11 14:59:26 +090042
43/**
44 * Managing drag and drop operations initiated by View#startDragAndDrop.
45 */
46class DragDropController {
47 private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
48 private static final long DRAG_TIMEOUT_MS = 5000;
Daichi Hirono58e25e12017-10-25 15:48:08 +090049
50 // Messages for Handler.
51 private static final int MSG_DRAG_START_TIMEOUT = 0;
52 static final int MSG_DRAG_END_TIMEOUT = 1;
53 static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2;
54 static final int MSG_ANIMATION_END = 3;
55
Daichi Hirono3bae0b02017-10-27 13:05:13 +090056 /**
57 * Drag state per operation.
Daichi Hirono7a5d0072017-10-30 10:07:24 +090058 * Needs a lock of {@code WindowManagerService#mWindowMap} to read this. Needs both locks of
59 * {@code mWriteLock} and {@code WindowManagerService#mWindowMap} to update this.
Daichi Hirono3bae0b02017-10-27 13:05:13 +090060 * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
61 * itself, thus the variable can be null after calling DragState's methods.
62 */
Daichi Hirono768012e2017-10-30 10:05:37 +090063 private DragState mDragState;
Daichi Hirono76a26aa2017-09-11 15:13:38 +090064
Daichi Hirono58e25e12017-10-25 15:48:08 +090065 private WindowManagerService mService;
66 private final Handler mHandler;
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090067
Daichi Hirono7a5d0072017-10-30 10:07:24 +090068 /**
69 * Lock to preserve the order of state updates.
70 * The lock is used to process drag and drop state updates in order without having the window
71 * manager lock.
72 *
73 * Suppose DragDropController invokes a callback method A, then processes the following update
74 * A'. Same for a callback method B and the following update B'. The callback wants
75 * DragDropController to processes the updates in the order of A' then B'.
76 *
77 * Without mWriteLock: the following race can happen.
78 *
79 * 1. Thread a calls A.
80 * 2. Thread b calls B.
81 * 3. Thread b acquires the window manager lock
82 * 4. thread b processes the update B'
83 * 5. Thread a acquires the window manager lock
84 * 6. thread a processes the update A'
85 *
86 * With mWriteLock we can ensure the order of A' and B'
87 *
88 * 1. Thread a acquire mWriteLock
89 * 2. Thread a calls A
90 * 3. Thread a acquire the window manager lock
91 * 4. Thread a processes A'
92 * 5. Thread b acquire mWriteLock
93 * 6. Thread b calls B
94 * 7. Thread b acquire the window manager lock
95 * 8. Thread b processes B'
96 *
97 * Don't acquire the lock while holding the window manager lock, otherwise it causes a deadlock.
98 */
99 private final Object mWriteLock = new Object();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900100
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900101 /**
102 * Callback which is used to sync drag state with the vendor-specific code.
103 */
104 @NonNull private IDragDropCallback mCallback = new IDragDropCallback() {};
105
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900106 boolean dragDropActiveLocked() {
107 return mDragState != null;
108 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900109
Daichi Hirono768012e2017-10-30 10:05:37 +0900110 InputWindowHandle getInputWindowHandleLocked() {
111 return mDragState.getInputWindowHandle();
112 }
113
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900114 void registerCallback(IDragDropCallback callback) {
115 Preconditions.checkNotNull(callback);
116 synchronized (mWriteLock) {
117 mCallback = callback;
118 }
119 }
120
Daichi Hirono58e25e12017-10-25 15:48:08 +0900121 DragDropController(WindowManagerService service, Looper looper) {
122 mService = service;
123 mHandler = new DragHandler(service, looper);
124 }
125
Daichi Hirono768012e2017-10-30 10:05:37 +0900126 void sendDragStartedIfNeededLocked(WindowState window) {
127 mDragState.sendDragStartedIfNeededLocked(window);
128 }
129
Daichi Hirono58e25e12017-10-25 15:48:08 +0900130 IBinder prepareDrag(SurfaceSession session, int callerPid,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900131 int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
132 if (DEBUG_DRAG) {
133 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
134 + " flags=" + Integer.toHexString(flags) + " win=" + window
135 + " asbinder=" + window.asBinder());
136 }
137
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900138 synchronized (mWriteLock) {
139 synchronized (mService.mWindowMap) {
140 if (dragDropActiveLocked()) {
141 Slog.w(TAG_WM, "Drag already in progress");
142 return null;
143 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900144
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900145 // TODO(multi-display): support other displays
146 final DisplayContent displayContent =
147 mService.getDefaultDisplayContentLocked();
148 final Display display = displayContent.getDisplay();
149
150 final SurfaceControl surface = new SurfaceControl.Builder(session)
151 .setName("drag surface")
152 .setSize(width, height)
153 .setFormat(PixelFormat.TRANSLUCENT)
154 .build();
155 surface.setLayerStack(display.getLayerStack());
156 float alpha = 1;
157 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
158 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
159 }
160 surface.setAlpha(alpha);
161
162 if (SHOW_TRANSACTIONS)
163 Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
164 outSurface.copyFrom(surface);
165 final IBinder winBinder = window.asBinder();
166 IBinder token = new Binder();
167 mDragState = new DragState(mService, token, surface, flags, winBinder);
168 mDragState.mPid = callerPid;
169 mDragState.mUid = callerUid;
170 mDragState.mOriginalAlpha = alpha;
171 token = mDragState.mToken = new Binder();
172
173 // 5 second timeout for this window to actually begin the drag
174 sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
175 return token;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900176 }
177 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900178 }
179
Daichi Hirono58e25e12017-10-25 15:48:08 +0900180 boolean performDrag(IWindow window, IBinder dragToken,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900181 int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
182 ClipData data) {
183 if (DEBUG_DRAG) {
184 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
185 }
186
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900187 synchronized (mWriteLock) {
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900188 if (!mCallback.performDrag(window, dragToken, touchSource, touchX, touchY, thumbCenterX,
189 thumbCenterY, data)) {
190 return false;
191 }
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900192 synchronized (mService.mWindowMap) {
193 if (mDragState == null) {
194 Slog.w(TAG_WM, "No drag prepared");
195 throw new IllegalStateException("performDrag() without prepareDrag()");
196 }
197
198 if (dragToken != mDragState.mToken) {
199 Slog.w(TAG_WM, "Performing mismatched drag");
200 throw new IllegalStateException("performDrag() does not match prepareDrag()");
201 }
202
203 final WindowState callingWin = mService.windowForClientLocked(null, window, false);
204 if (callingWin == null) {
205 Slog.w(TAG_WM, "Bad requesting window " + window);
206 return false; // !!! TODO: throw here?
207 }
208
209 // !!! TODO: if input is not still focused on the initiating window, fail
210 // the drag initiation (e.g. an alarm window popped up just as the application
211 // called performDrag()
212
213 mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
214
215 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
216 // will let us eliminate the (touchX,touchY) parameters from the API.
217
218 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
219 // the actual drag event dispatch stuff in the dragstate
220
221 final DisplayContent displayContent = callingWin.getDisplayContent();
222 if (displayContent == null) {
223 return false;
224 }
225 Display display = displayContent.getDisplay();
226 mDragState.register(display);
227 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
228 mDragState.getInputChannel())) {
229 Slog.e(TAG_WM, "Unable to transfer touch focus");
230 mDragState.closeLocked();
231 return false;
232 }
233
234 mDragState.mDisplayContent = displayContent;
235 mDragState.mData = data;
236 mDragState.broadcastDragStartedLocked(touchX, touchY);
237 mDragState.overridePointerIconLocked(touchSource);
238
239 // remember the thumb offsets for later
240 mDragState.mThumbOffsetX = thumbCenterX;
241 mDragState.mThumbOffsetY = thumbCenterY;
242
243 // Make the surface visible at the proper location
244 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
245 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
246 mService.openSurfaceTransaction();
247 try {
248 surfaceControl.setPosition(touchX - thumbCenterX,
249 touchY - thumbCenterY);
250 surfaceControl.setLayer(mDragState.getDragLayerLocked());
251 surfaceControl.setLayerStack(display.getLayerStack());
252 surfaceControl.show();
253 } finally {
254 mService.closeSurfaceTransaction("performDrag");
255 if (SHOW_LIGHT_TRANSACTIONS) {
256 Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
257 }
258 }
259
260 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900261 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900262 }
263
264 return true; // success!
265 }
266
Daichi Hirono58e25e12017-10-25 15:48:08 +0900267 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900268 IBinder token = window.asBinder();
269 if (DEBUG_DRAG) {
270 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
271 }
272
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900273 synchronized (mWriteLock) {
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900274 mCallback.reportDropResult(window, consumed);
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900275 synchronized (mService.mWindowMap) {
276 if (mDragState == null) {
277 // Most likely the drop recipient ANRed and we ended the drag
278 // out from under it. Log the issue and move on.
279 Slog.w(TAG_WM, "Drop result given but no drag in progress");
280 return;
281 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900282
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900283 if (mDragState.mToken != token) {
284 // We're in a drag, but the wrong window has responded.
285 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
286 throw new IllegalStateException("reportDropResult() by non-recipient");
287 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900288
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900289 // The right window has responded, even if it's no longer around,
290 // so be sure to halt the timeout even if the later WindowState
291 // lookup fails.
292 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
293 WindowState callingWin = mService.windowForClientLocked(null, window, false);
294 if (callingWin == null) {
295 Slog.w(TAG_WM, "Bad result-reporting window " + window);
296 return; // !!! TODO: throw here?
297 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900298
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900299
300 mDragState.mDragResult = consumed;
301 mDragState.endDragLocked();
302 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900303 }
304 }
305
Daichi Hirono58e25e12017-10-25 15:48:08 +0900306 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900307 if (DEBUG_DRAG) {
308 Slog.d(TAG_WM, "cancelDragAndDrop");
309 }
310
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900311 synchronized (mWriteLock) {
Daichi Hirono3c6c95e2017-09-13 12:23:57 +0900312 mCallback.cancelDragAndDrop(dragToken);
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900313 synchronized (mService.mWindowMap) {
314 if (mDragState == null) {
315 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
316 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
317 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900318
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900319 if (mDragState.mToken != dragToken) {
320 Slog.w(TAG_WM,
321 "cancelDragAndDrop() does not match prepareDrag()");
322 throw new IllegalStateException(
323 "cancelDragAndDrop() does not match prepareDrag()");
324 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900325
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900326 mDragState.mDragResult = false;
327 mDragState.cancelDragLocked();
328 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900329 }
330 }
331
Daichi Hirono768012e2017-10-30 10:05:37 +0900332 /**
333 * Handles motion events.
334 * @param keepHandling Whether if the drag operation is continuing or this is the last motion
335 * event.
336 * @param newX X coordinate value in dp in the screen coordinate
337 * @param newY Y coordinate value in dp in the screen coordinate
338 */
339 void handleMotionEvent(boolean keepHandling, float newX, float newY) {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900340 synchronized (mWriteLock) {
341 synchronized (mService.mWindowMap) {
342 if (!dragDropActiveLocked()) {
343 // The drag has ended but the clean-up message has not been processed by
344 // window manager. Drop events that occur after this until window manager
345 // has a chance to clean-up the input handle.
346 return;
347 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900348
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900349 if (keepHandling) {
350 mDragState.notifyMoveLocked(newX, newY);
351 } else {
352 mDragState.notifyDropLocked(newX, newY);
353 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900354 }
355 }
356 }
357
Daichi Hironodf5cf622017-09-11 14:59:26 +0900358 void dragRecipientEntered(IWindow window) {
359 if (DEBUG_DRAG) {
360 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
361 }
362 }
363
364 void dragRecipientExited(IWindow window) {
365 if (DEBUG_DRAG) {
366 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
367 }
368 }
369
Daichi Hirono58e25e12017-10-25 15:48:08 +0900370 /**
371 * Sends a message to the Handler managed by DragDropController.
372 */
373 void sendHandlerMessage(int what, Object arg) {
374 mHandler.obtainMessage(what, arg).sendToTarget();
375 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900376
Daichi Hirono58e25e12017-10-25 15:48:08 +0900377 /**
378 * Sends a timeout message to the Handler managed by DragDropController.
379 */
380 void sendTimeoutMessage(int what, Object arg) {
381 mHandler.removeMessages(what, arg);
382 final Message msg = mHandler.obtainMessage(what, arg);
383 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
384 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900385
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900386 /**
387 * Notifies the current drag state is closed.
388 */
389 void onDragStateClosedLocked(DragState dragState) {
390 if (mDragState != dragState) {
391 Slog.wtf(TAG_WM, "Unknown drag state is closed");
392 return;
393 }
394 mDragState = null;
395 }
396
Daichi Hirono58e25e12017-10-25 15:48:08 +0900397 private class DragHandler extends Handler {
398 /**
399 * Lock for window manager.
400 */
401 private final WindowManagerService mService;
402
403 DragHandler(WindowManagerService service, Looper looper) {
404 super(looper);
405 mService = service;
406 }
407
408 @Override
409 public void handleMessage(Message msg) {
410 switch (msg.what) {
411 case MSG_DRAG_START_TIMEOUT: {
412 IBinder win = (IBinder) msg.obj;
413 if (DEBUG_DRAG) {
414 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900415 }
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900416 synchronized (mWriteLock) {
417 synchronized (mService.mWindowMap) {
418 // !!! TODO: ANR the app that has failed to start the drag in time
419 if (mDragState != null) {
420 mDragState.closeLocked();
421 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900422 }
423 }
424 break;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900425 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900426
427 case MSG_DRAG_END_TIMEOUT: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900428 final IBinder win = (IBinder) msg.obj;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900429 if (DEBUG_DRAG) {
430 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
431 }
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900432 synchronized (mWriteLock) {
433 synchronized (mService.mWindowMap) {
434 // !!! TODO: ANR the drag-receiving app
435 if (mDragState != null) {
436 mDragState.mDragResult = false;
437 mDragState.endDragLocked();
438 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900439 }
440 }
441 break;
442 }
443
444 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
445 if (DEBUG_DRAG)
446 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900447 final DragState.InputInterceptor interceptor =
448 (DragState.InputInterceptor) msg.obj;
449 if (interceptor == null) return;
450 synchronized (mService.mWindowMap) {
451 interceptor.tearDown();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900452 }
453 break;
454 }
455
456 case MSG_ANIMATION_END: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900457 synchronized (mWriteLock) {
458 synchronized (mService.mWindowMap) {
459 if (mDragState == null) {
460 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
461 "plyaing animation");
462 return;
463 }
464 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900465 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900466 }
467 break;
468 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900469 }
470 }
471 }
472}