blob: 76e25bae5db26345f311b70097618f0f24691a99 [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 Hirono768012e2017-10-30 10:05:37 +090038import com.android.server.input.InputWindowHandle;
Daichi Hironodf5cf622017-09-11 14:59:26 +090039
40/**
41 * Managing drag and drop operations initiated by View#startDragAndDrop.
42 */
43class DragDropController {
44 private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
45 private static final long DRAG_TIMEOUT_MS = 5000;
Daichi Hirono58e25e12017-10-25 15:48:08 +090046
47 // Messages for Handler.
48 private static final int MSG_DRAG_START_TIMEOUT = 0;
49 static final int MSG_DRAG_END_TIMEOUT = 1;
50 static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2;
51 static final int MSG_ANIMATION_END = 3;
52
Daichi Hirono3bae0b02017-10-27 13:05:13 +090053 /**
54 * Drag state per operation.
Daichi Hirono7a5d0072017-10-30 10:07:24 +090055 * Needs a lock of {@code WindowManagerService#mWindowMap} to read this. Needs both locks of
56 * {@code mWriteLock} and {@code WindowManagerService#mWindowMap} to update this.
Daichi Hirono3bae0b02017-10-27 13:05:13 +090057 * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
58 * itself, thus the variable can be null after calling DragState's methods.
59 */
Daichi Hirono768012e2017-10-30 10:05:37 +090060 private DragState mDragState;
Daichi Hirono76a26aa2017-09-11 15:13:38 +090061
Daichi Hirono58e25e12017-10-25 15:48:08 +090062 private WindowManagerService mService;
63 private final Handler mHandler;
Daichi Hirono7a5d0072017-10-30 10:07:24 +090064 /**
65 * Lock to preserve the order of state updates.
66 * The lock is used to process drag and drop state updates in order without having the window
67 * manager lock.
68 *
69 * Suppose DragDropController invokes a callback method A, then processes the following update
70 * A'. Same for a callback method B and the following update B'. The callback wants
71 * DragDropController to processes the updates in the order of A' then B'.
72 *
73 * Without mWriteLock: the following race can happen.
74 *
75 * 1. Thread a calls A.
76 * 2. Thread b calls B.
77 * 3. Thread b acquires the window manager lock
78 * 4. thread b processes the update B'
79 * 5. Thread a acquires the window manager lock
80 * 6. thread a processes the update A'
81 *
82 * With mWriteLock we can ensure the order of A' and B'
83 *
84 * 1. Thread a acquire mWriteLock
85 * 2. Thread a calls A
86 * 3. Thread a acquire the window manager lock
87 * 4. Thread a processes A'
88 * 5. Thread b acquire mWriteLock
89 * 6. Thread b calls B
90 * 7. Thread b acquire the window manager lock
91 * 8. Thread b processes B'
92 *
93 * Don't acquire the lock while holding the window manager lock, otherwise it causes a deadlock.
94 */
95 private final Object mWriteLock = new Object();
Daichi Hirono58e25e12017-10-25 15:48:08 +090096
Daichi Hirono76a26aa2017-09-11 15:13:38 +090097 boolean dragDropActiveLocked() {
98 return mDragState != null;
99 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900100
Daichi Hirono768012e2017-10-30 10:05:37 +0900101 InputWindowHandle getInputWindowHandleLocked() {
102 return mDragState.getInputWindowHandle();
103 }
104
Daichi Hirono58e25e12017-10-25 15:48:08 +0900105 DragDropController(WindowManagerService service, Looper looper) {
106 mService = service;
107 mHandler = new DragHandler(service, looper);
108 }
109
Daichi Hirono768012e2017-10-30 10:05:37 +0900110 void sendDragStartedIfNeededLocked(WindowState window) {
111 mDragState.sendDragStartedIfNeededLocked(window);
112 }
113
Daichi Hirono58e25e12017-10-25 15:48:08 +0900114 IBinder prepareDrag(SurfaceSession session, int callerPid,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900115 int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
116 if (DEBUG_DRAG) {
117 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
118 + " flags=" + Integer.toHexString(flags) + " win=" + window
119 + " asbinder=" + window.asBinder());
120 }
121
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900122 synchronized (mWriteLock) {
123 synchronized (mService.mWindowMap) {
124 if (dragDropActiveLocked()) {
125 Slog.w(TAG_WM, "Drag already in progress");
126 return null;
127 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900128
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900129 // TODO(multi-display): support other displays
130 final DisplayContent displayContent =
131 mService.getDefaultDisplayContentLocked();
132 final Display display = displayContent.getDisplay();
133
134 final SurfaceControl surface = new SurfaceControl.Builder(session)
135 .setName("drag surface")
136 .setSize(width, height)
137 .setFormat(PixelFormat.TRANSLUCENT)
138 .build();
139 surface.setLayerStack(display.getLayerStack());
140 float alpha = 1;
141 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
142 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
143 }
144 surface.setAlpha(alpha);
145
146 if (SHOW_TRANSACTIONS)
147 Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
148 outSurface.copyFrom(surface);
149 final IBinder winBinder = window.asBinder();
150 IBinder token = new Binder();
151 mDragState = new DragState(mService, token, surface, flags, winBinder);
152 mDragState.mPid = callerPid;
153 mDragState.mUid = callerUid;
154 mDragState.mOriginalAlpha = alpha;
155 token = mDragState.mToken = new Binder();
156
157 // 5 second timeout for this window to actually begin the drag
158 sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
159 return token;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900160 }
161 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900162 }
163
Daichi Hirono58e25e12017-10-25 15:48:08 +0900164 boolean performDrag(IWindow window, IBinder dragToken,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900165 int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
166 ClipData data) {
167 if (DEBUG_DRAG) {
168 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
169 }
170
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900171 synchronized (mWriteLock) {
172 synchronized (mService.mWindowMap) {
173 if (mDragState == null) {
174 Slog.w(TAG_WM, "No drag prepared");
175 throw new IllegalStateException("performDrag() without prepareDrag()");
176 }
177
178 if (dragToken != mDragState.mToken) {
179 Slog.w(TAG_WM, "Performing mismatched drag");
180 throw new IllegalStateException("performDrag() does not match prepareDrag()");
181 }
182
183 final WindowState callingWin = mService.windowForClientLocked(null, window, false);
184 if (callingWin == null) {
185 Slog.w(TAG_WM, "Bad requesting window " + window);
186 return false; // !!! TODO: throw here?
187 }
188
189 // !!! TODO: if input is not still focused on the initiating window, fail
190 // the drag initiation (e.g. an alarm window popped up just as the application
191 // called performDrag()
192
193 mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
194
195 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
196 // will let us eliminate the (touchX,touchY) parameters from the API.
197
198 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
199 // the actual drag event dispatch stuff in the dragstate
200
201 final DisplayContent displayContent = callingWin.getDisplayContent();
202 if (displayContent == null) {
203 return false;
204 }
205 Display display = displayContent.getDisplay();
206 mDragState.register(display);
207 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
208 mDragState.getInputChannel())) {
209 Slog.e(TAG_WM, "Unable to transfer touch focus");
210 mDragState.closeLocked();
211 return false;
212 }
213
214 mDragState.mDisplayContent = displayContent;
215 mDragState.mData = data;
216 mDragState.broadcastDragStartedLocked(touchX, touchY);
217 mDragState.overridePointerIconLocked(touchSource);
218
219 // remember the thumb offsets for later
220 mDragState.mThumbOffsetX = thumbCenterX;
221 mDragState.mThumbOffsetY = thumbCenterY;
222
223 // Make the surface visible at the proper location
224 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
225 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
226 mService.openSurfaceTransaction();
227 try {
228 surfaceControl.setPosition(touchX - thumbCenterX,
229 touchY - thumbCenterY);
230 surfaceControl.setLayer(mDragState.getDragLayerLocked());
231 surfaceControl.setLayerStack(display.getLayerStack());
232 surfaceControl.show();
233 } finally {
234 mService.closeSurfaceTransaction("performDrag");
235 if (SHOW_LIGHT_TRANSACTIONS) {
236 Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
237 }
238 }
239
240 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900241 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900242 }
243
244 return true; // success!
245 }
246
Daichi Hirono58e25e12017-10-25 15:48:08 +0900247 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900248 IBinder token = window.asBinder();
249 if (DEBUG_DRAG) {
250 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
251 }
252
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900253 synchronized (mWriteLock) {
254 synchronized (mService.mWindowMap) {
255 if (mDragState == null) {
256 // Most likely the drop recipient ANRed and we ended the drag
257 // out from under it. Log the issue and move on.
258 Slog.w(TAG_WM, "Drop result given but no drag in progress");
259 return;
260 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900261
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900262 if (mDragState.mToken != token) {
263 // We're in a drag, but the wrong window has responded.
264 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
265 throw new IllegalStateException("reportDropResult() by non-recipient");
266 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900267
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900268 // The right window has responded, even if it's no longer around,
269 // so be sure to halt the timeout even if the later WindowState
270 // lookup fails.
271 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
272 WindowState callingWin = mService.windowForClientLocked(null, window, false);
273 if (callingWin == null) {
274 Slog.w(TAG_WM, "Bad result-reporting window " + window);
275 return; // !!! TODO: throw here?
276 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900277
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900278
279 mDragState.mDragResult = consumed;
280 mDragState.endDragLocked();
281 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900282 }
283 }
284
Daichi Hirono58e25e12017-10-25 15:48:08 +0900285 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900286 if (DEBUG_DRAG) {
287 Slog.d(TAG_WM, "cancelDragAndDrop");
288 }
289
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900290 synchronized (mWriteLock) {
291 synchronized (mService.mWindowMap) {
292 if (mDragState == null) {
293 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
294 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
295 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900296
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900297 if (mDragState.mToken != dragToken) {
298 Slog.w(TAG_WM,
299 "cancelDragAndDrop() does not match prepareDrag()");
300 throw new IllegalStateException(
301 "cancelDragAndDrop() does not match prepareDrag()");
302 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900303
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900304 mDragState.mDragResult = false;
305 mDragState.cancelDragLocked();
306 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900307 }
308 }
309
Daichi Hirono768012e2017-10-30 10:05:37 +0900310 /**
311 * Handles motion events.
312 * @param keepHandling Whether if the drag operation is continuing or this is the last motion
313 * event.
314 * @param newX X coordinate value in dp in the screen coordinate
315 * @param newY Y coordinate value in dp in the screen coordinate
316 */
317 void handleMotionEvent(boolean keepHandling, float newX, float newY) {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900318 synchronized (mWriteLock) {
319 synchronized (mService.mWindowMap) {
320 if (!dragDropActiveLocked()) {
321 // The drag has ended but the clean-up message has not been processed by
322 // window manager. Drop events that occur after this until window manager
323 // has a chance to clean-up the input handle.
324 return;
325 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900326
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900327 if (keepHandling) {
328 mDragState.notifyMoveLocked(newX, newY);
329 } else {
330 mDragState.notifyDropLocked(newX, newY);
331 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900332 }
333 }
334 }
335
Daichi Hironodf5cf622017-09-11 14:59:26 +0900336 void dragRecipientEntered(IWindow window) {
337 if (DEBUG_DRAG) {
338 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
339 }
340 }
341
342 void dragRecipientExited(IWindow window) {
343 if (DEBUG_DRAG) {
344 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
345 }
346 }
347
Daichi Hirono58e25e12017-10-25 15:48:08 +0900348 /**
349 * Sends a message to the Handler managed by DragDropController.
350 */
351 void sendHandlerMessage(int what, Object arg) {
352 mHandler.obtainMessage(what, arg).sendToTarget();
353 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900354
Daichi Hirono58e25e12017-10-25 15:48:08 +0900355 /**
356 * Sends a timeout message to the Handler managed by DragDropController.
357 */
358 void sendTimeoutMessage(int what, Object arg) {
359 mHandler.removeMessages(what, arg);
360 final Message msg = mHandler.obtainMessage(what, arg);
361 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
362 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900363
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900364 /**
365 * Notifies the current drag state is closed.
366 */
367 void onDragStateClosedLocked(DragState dragState) {
368 if (mDragState != dragState) {
369 Slog.wtf(TAG_WM, "Unknown drag state is closed");
370 return;
371 }
372 mDragState = null;
373 }
374
Daichi Hirono58e25e12017-10-25 15:48:08 +0900375 private class DragHandler extends Handler {
376 /**
377 * Lock for window manager.
378 */
379 private final WindowManagerService mService;
380
381 DragHandler(WindowManagerService service, Looper looper) {
382 super(looper);
383 mService = service;
384 }
385
386 @Override
387 public void handleMessage(Message msg) {
388 switch (msg.what) {
389 case MSG_DRAG_START_TIMEOUT: {
390 IBinder win = (IBinder) msg.obj;
391 if (DEBUG_DRAG) {
392 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900393 }
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900394 synchronized (mWriteLock) {
395 synchronized (mService.mWindowMap) {
396 // !!! TODO: ANR the app that has failed to start the drag in time
397 if (mDragState != null) {
398 mDragState.closeLocked();
399 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900400 }
401 }
402 break;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900403 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900404
405 case MSG_DRAG_END_TIMEOUT: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900406 final IBinder win = (IBinder) msg.obj;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900407 if (DEBUG_DRAG) {
408 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
409 }
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900410 synchronized (mWriteLock) {
411 synchronized (mService.mWindowMap) {
412 // !!! TODO: ANR the drag-receiving app
413 if (mDragState != null) {
414 mDragState.mDragResult = false;
415 mDragState.endDragLocked();
416 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900417 }
418 }
419 break;
420 }
421
422 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
423 if (DEBUG_DRAG)
424 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900425 final DragState.InputInterceptor interceptor =
426 (DragState.InputInterceptor) msg.obj;
427 if (interceptor == null) return;
428 synchronized (mService.mWindowMap) {
429 interceptor.tearDown();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900430 }
431 break;
432 }
433
434 case MSG_ANIMATION_END: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900435 synchronized (mWriteLock) {
436 synchronized (mService.mWindowMap) {
437 if (mDragState == null) {
438 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
439 "plyaing animation");
440 return;
441 }
442 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900443 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900444 }
445 break;
446 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900447 }
448 }
449 }
450}