blob: 70908337dfd388b1a01de0104e41e7cfca2f7256 [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.
55 * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
56 * itself, thus the variable can be null after calling DragState's methods.
57 */
Daichi Hirono768012e2017-10-30 10:05:37 +090058 private DragState mDragState;
Daichi Hirono76a26aa2017-09-11 15:13:38 +090059
Daichi Hirono58e25e12017-10-25 15:48:08 +090060 private WindowManagerService mService;
61 private final Handler mHandler;
62
Daichi Hirono76a26aa2017-09-11 15:13:38 +090063 boolean dragDropActiveLocked() {
64 return mDragState != null;
65 }
Daichi Hironodf5cf622017-09-11 14:59:26 +090066
Daichi Hirono768012e2017-10-30 10:05:37 +090067 InputWindowHandle getInputWindowHandleLocked() {
68 return mDragState.getInputWindowHandle();
69 }
70
Daichi Hirono58e25e12017-10-25 15:48:08 +090071 DragDropController(WindowManagerService service, Looper looper) {
72 mService = service;
73 mHandler = new DragHandler(service, looper);
74 }
75
Daichi Hirono768012e2017-10-30 10:05:37 +090076 void sendDragStartedIfNeededLocked(WindowState window) {
77 mDragState.sendDragStartedIfNeededLocked(window);
78 }
79
Daichi Hirono58e25e12017-10-25 15:48:08 +090080 IBinder prepareDrag(SurfaceSession session, int callerPid,
Daichi Hironodf5cf622017-09-11 14:59:26 +090081 int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
82 if (DEBUG_DRAG) {
83 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
84 + " flags=" + Integer.toHexString(flags) + " win=" + window
85 + " asbinder=" + window.asBinder());
86 }
87
88 IBinder token = null;
89
Daichi Hirono58e25e12017-10-25 15:48:08 +090090 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +090091 if (dragDropActiveLocked()) {
92 Slog.w(TAG_WM, "Drag already in progress");
93 return null;
Daichi Hironodf5cf622017-09-11 14:59:26 +090094 }
Daichi Hirono76a26aa2017-09-11 15:13:38 +090095
96 // TODO(multi-display): support other displays
97 final DisplayContent displayContent =
Daichi Hirono58e25e12017-10-25 15:48:08 +090098 mService.getDefaultDisplayContentLocked();
Daichi Hirono76a26aa2017-09-11 15:13:38 +090099 final Display display = displayContent.getDisplay();
100
101 final SurfaceControl surface = new SurfaceControl.Builder(session)
102 .setName("drag surface")
103 .setSize(width, height)
104 .setFormat(PixelFormat.TRANSLUCENT)
105 .build();
106 surface.setLayerStack(display.getLayerStack());
107 float alpha = 1;
108 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
109 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
110 }
111 surface.setAlpha(alpha);
112
113 if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
114 outSurface.copyFrom(surface);
115 final IBinder winBinder = window.asBinder();
116 token = new Binder();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900117 mDragState = new DragState(mService, token, surface, flags, winBinder);
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900118 mDragState.mPid = callerPid;
119 mDragState.mUid = callerUid;
120 mDragState.mOriginalAlpha = alpha;
121 token = mDragState.mToken = new Binder();
122
123 // 5 second timeout for this window to actually begin the drag
Daichi Hirono58e25e12017-10-25 15:48:08 +0900124 sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900125 }
126
127 return token;
128 }
129
Daichi Hirono58e25e12017-10-25 15:48:08 +0900130 boolean performDrag(IWindow window, IBinder dragToken,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900131 int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
132 ClipData data) {
133 if (DEBUG_DRAG) {
134 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
135 }
136
Daichi Hirono58e25e12017-10-25 15:48:08 +0900137 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900138 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900139 Slog.w(TAG_WM, "No drag prepared");
140 throw new IllegalStateException("performDrag() without prepareDrag()");
141 }
142
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900143 if (dragToken != mDragState.mToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900144 Slog.w(TAG_WM, "Performing mismatched drag");
145 throw new IllegalStateException("performDrag() does not match prepareDrag()");
146 }
147
Daichi Hirono58e25e12017-10-25 15:48:08 +0900148 final WindowState callingWin = mService.windowForClientLocked(null, window, false);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900149 if (callingWin == null) {
150 Slog.w(TAG_WM, "Bad requesting window " + window);
151 return false; // !!! TODO: throw here?
152 }
153
154 // !!! TODO: if input is not still focused on the initiating window, fail
155 // the drag initiation (e.g. an alarm window popped up just as the application
156 // called performDrag()
157
Daichi Hirono58e25e12017-10-25 15:48:08 +0900158 mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
Daichi Hironodf5cf622017-09-11 14:59:26 +0900159
160 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
161 // will let us eliminate the (touchX,touchY) parameters from the API.
162
Daichi Hirono58e25e12017-10-25 15:48:08 +0900163 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
Daichi Hironodf5cf622017-09-11 14:59:26 +0900164 // the actual drag event dispatch stuff in the dragstate
165
166 final DisplayContent displayContent = callingWin.getDisplayContent();
167 if (displayContent == null) {
168 return false;
169 }
170 Display display = displayContent.getDisplay();
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900171 mDragState.register(display);
Daichi Hirono58e25e12017-10-25 15:48:08 +0900172 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900173 mDragState.getInputChannel())) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900174 Slog.e(TAG_WM, "Unable to transfer touch focus");
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900175 mDragState.closeLocked();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900176 return false;
177 }
178
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900179 mDragState.mDisplayContent = displayContent;
180 mDragState.mData = data;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900181 mDragState.broadcastDragStartedLocked(touchX, touchY);
182 mDragState.overridePointerIconLocked(touchSource);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900183
184 // remember the thumb offsets for later
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900185 mDragState.mThumbOffsetX = thumbCenterX;
186 mDragState.mThumbOffsetY = thumbCenterY;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900187
188 // Make the surface visible at the proper location
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900189 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900190 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
191 TAG_WM, ">>> OPEN TRANSACTION performDrag");
Daichi Hirono58e25e12017-10-25 15:48:08 +0900192 mService.openSurfaceTransaction();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900193 try {
194 surfaceControl.setPosition(touchX - thumbCenterX,
195 touchY - thumbCenterY);
Daichi Hirono58e25e12017-10-25 15:48:08 +0900196 surfaceControl.setLayer(mDragState.getDragLayerLocked());
Daichi Hironodf5cf622017-09-11 14:59:26 +0900197 surfaceControl.setLayerStack(display.getLayerStack());
198 surfaceControl.show();
199 } finally {
Daichi Hirono58e25e12017-10-25 15:48:08 +0900200 mService.closeSurfaceTransaction();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900201 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
202 TAG_WM, "<<< CLOSE TRANSACTION performDrag");
203 }
204
Daichi Hirono58e25e12017-10-25 15:48:08 +0900205 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900206 }
207
208 return true; // success!
209 }
210
Daichi Hirono58e25e12017-10-25 15:48:08 +0900211 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900212 IBinder token = window.asBinder();
213 if (DEBUG_DRAG) {
214 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
215 }
216
Daichi Hirono58e25e12017-10-25 15:48:08 +0900217 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900218 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900219 // Most likely the drop recipient ANRed and we ended the drag
220 // out from under it. Log the issue and move on.
221 Slog.w(TAG_WM, "Drop result given but no drag in progress");
222 return;
223 }
224
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900225 if (mDragState.mToken != token) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900226 // We're in a drag, but the wrong window has responded.
227 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
228 throw new IllegalStateException("reportDropResult() by non-recipient");
229 }
230
231 // The right window has responded, even if it's no longer around,
232 // so be sure to halt the timeout even if the later WindowState
233 // lookup fails.
Daichi Hirono58e25e12017-10-25 15:48:08 +0900234 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
235 WindowState callingWin = mService.windowForClientLocked(null, window, false);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900236 if (callingWin == null) {
237 Slog.w(TAG_WM, "Bad result-reporting window " + window);
238 return; // !!! TODO: throw here?
239 }
240
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900241 mDragState.mDragResult = consumed;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900242 mDragState.endDragLocked();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900243 }
244 }
245
Daichi Hirono58e25e12017-10-25 15:48:08 +0900246 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900247 if (DEBUG_DRAG) {
248 Slog.d(TAG_WM, "cancelDragAndDrop");
249 }
250
Daichi Hirono58e25e12017-10-25 15:48:08 +0900251 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900252 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900253 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
254 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
255 }
256
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900257 if (mDragState.mToken != dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900258 Slog.w(TAG_WM,
259 "cancelDragAndDrop() does not match prepareDrag()");
260 throw new IllegalStateException(
261 "cancelDragAndDrop() does not match prepareDrag()");
262 }
263
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900264 mDragState.mDragResult = false;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900265 mDragState.cancelDragLocked();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900266 }
267 }
268
Daichi Hirono768012e2017-10-30 10:05:37 +0900269 /**
270 * Handles motion events.
271 * @param keepHandling Whether if the drag operation is continuing or this is the last motion
272 * event.
273 * @param newX X coordinate value in dp in the screen coordinate
274 * @param newY Y coordinate value in dp in the screen coordinate
275 */
276 void handleMotionEvent(boolean keepHandling, float newX, float newY) {
277 synchronized (mService.mWindowMap) {
278 if (!dragDropActiveLocked()) {
279 // The drag has ended but the clean-up message has not been processed by
280 // window manager. Drop events that occur after this until window manager
281 // has a chance to clean-up the input handle.
282 return;
283 }
284
285 if (keepHandling) {
286 mDragState.notifyMoveLocked(newX, newY);
287 } else {
288 mDragState.notifyDropLocked(newX, newY);
289 }
290 }
291 }
292
Daichi Hironodf5cf622017-09-11 14:59:26 +0900293 void dragRecipientEntered(IWindow window) {
294 if (DEBUG_DRAG) {
295 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
296 }
297 }
298
299 void dragRecipientExited(IWindow window) {
300 if (DEBUG_DRAG) {
301 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
302 }
303 }
304
Daichi Hirono58e25e12017-10-25 15:48:08 +0900305 /**
306 * Sends a message to the Handler managed by DragDropController.
307 */
308 void sendHandlerMessage(int what, Object arg) {
309 mHandler.obtainMessage(what, arg).sendToTarget();
310 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900311
Daichi Hirono58e25e12017-10-25 15:48:08 +0900312 /**
313 * Sends a timeout message to the Handler managed by DragDropController.
314 */
315 void sendTimeoutMessage(int what, Object arg) {
316 mHandler.removeMessages(what, arg);
317 final Message msg = mHandler.obtainMessage(what, arg);
318 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
319 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900320
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900321 /**
322 * Notifies the current drag state is closed.
323 */
324 void onDragStateClosedLocked(DragState dragState) {
325 if (mDragState != dragState) {
326 Slog.wtf(TAG_WM, "Unknown drag state is closed");
327 return;
328 }
329 mDragState = null;
330 }
331
Daichi Hirono58e25e12017-10-25 15:48:08 +0900332 private class DragHandler extends Handler {
333 /**
334 * Lock for window manager.
335 */
336 private final WindowManagerService mService;
337
338 DragHandler(WindowManagerService service, Looper looper) {
339 super(looper);
340 mService = service;
341 }
342
343 @Override
344 public void handleMessage(Message msg) {
345 switch (msg.what) {
346 case MSG_DRAG_START_TIMEOUT: {
347 IBinder win = (IBinder) msg.obj;
348 if (DEBUG_DRAG) {
349 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900350 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900351 synchronized (mService.mWindowMap) {
352 // !!! TODO: ANR the app that has failed to start the drag in time
353 if (mDragState != null) {
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900354 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900355 }
356 }
357 break;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900358 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900359
360 case MSG_DRAG_END_TIMEOUT: {
361 IBinder win = (IBinder) msg.obj;
362 if (DEBUG_DRAG) {
363 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
364 }
365 synchronized (mService.mWindowMap) {
366 // !!! TODO: ANR the drag-receiving app
367 if (mDragState != null) {
368 mDragState.mDragResult = false;
369 mDragState.endDragLocked();
370 }
371 }
372 break;
373 }
374
375 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
376 if (DEBUG_DRAG)
377 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
378 DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
379 if (interceptor != null) {
380 synchronized (mService.mWindowMap) {
381 interceptor.tearDown();
382 }
383 }
384 break;
385 }
386
387 case MSG_ANIMATION_END: {
388 synchronized (mService.mWindowMap) {
389 if (mDragState == null) {
390 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
391 "plyaing animation");
392 return;
393 }
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900394 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900395 }
396 break;
397 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900398 }
399 }
400 }
401}