blob: b6f0f6a13454363cd814bed278cb97e9949dca75 [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 Hirono3bae0b02017-10-27 13:05:13 +090052 /**
53 * Drag state per operation.
54 * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
55 * itself, thus the variable can be null after calling DragState's methods.
56 */
Daichi Hirono76a26aa2017-09-11 15:13:38 +090057 DragState mDragState;
58
Daichi Hirono58e25e12017-10-25 15:48:08 +090059 private WindowManagerService mService;
60 private final Handler mHandler;
61
Daichi Hirono76a26aa2017-09-11 15:13:38 +090062 boolean dragDropActiveLocked() {
63 return mDragState != null;
64 }
Daichi Hironodf5cf622017-09-11 14:59:26 +090065
Daichi Hirono58e25e12017-10-25 15:48:08 +090066 DragDropController(WindowManagerService service, Looper looper) {
67 mService = service;
68 mHandler = new DragHandler(service, looper);
69 }
70
71 IBinder prepareDrag(SurfaceSession session, int callerPid,
Daichi Hironodf5cf622017-09-11 14:59:26 +090072 int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
73 if (DEBUG_DRAG) {
74 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
75 + " flags=" + Integer.toHexString(flags) + " win=" + window
76 + " asbinder=" + window.asBinder());
77 }
78
79 IBinder token = null;
80
Daichi Hirono58e25e12017-10-25 15:48:08 +090081 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +090082 if (dragDropActiveLocked()) {
83 Slog.w(TAG_WM, "Drag already in progress");
84 return null;
Daichi Hironodf5cf622017-09-11 14:59:26 +090085 }
Daichi Hirono76a26aa2017-09-11 15:13:38 +090086
87 // TODO(multi-display): support other displays
88 final DisplayContent displayContent =
Daichi Hirono58e25e12017-10-25 15:48:08 +090089 mService.getDefaultDisplayContentLocked();
Daichi Hirono76a26aa2017-09-11 15:13:38 +090090 final Display display = displayContent.getDisplay();
91
92 final SurfaceControl surface = new SurfaceControl.Builder(session)
93 .setName("drag surface")
94 .setSize(width, height)
95 .setFormat(PixelFormat.TRANSLUCENT)
96 .build();
97 surface.setLayerStack(display.getLayerStack());
98 float alpha = 1;
99 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
100 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
101 }
102 surface.setAlpha(alpha);
103
104 if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
105 outSurface.copyFrom(surface);
106 final IBinder winBinder = window.asBinder();
107 token = new Binder();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900108 mDragState = new DragState(mService, token, surface, flags, winBinder);
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900109 mDragState.mPid = callerPid;
110 mDragState.mUid = callerUid;
111 mDragState.mOriginalAlpha = alpha;
112 token = mDragState.mToken = new Binder();
113
114 // 5 second timeout for this window to actually begin the drag
Daichi Hirono58e25e12017-10-25 15:48:08 +0900115 sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900116 }
117
118 return token;
119 }
120
Daichi Hirono58e25e12017-10-25 15:48:08 +0900121 boolean performDrag(IWindow window, IBinder dragToken,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900122 int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
123 ClipData data) {
124 if (DEBUG_DRAG) {
125 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
126 }
127
Daichi Hirono58e25e12017-10-25 15:48:08 +0900128 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900129 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900130 Slog.w(TAG_WM, "No drag prepared");
131 throw new IllegalStateException("performDrag() without prepareDrag()");
132 }
133
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900134 if (dragToken != mDragState.mToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900135 Slog.w(TAG_WM, "Performing mismatched drag");
136 throw new IllegalStateException("performDrag() does not match prepareDrag()");
137 }
138
Daichi Hirono58e25e12017-10-25 15:48:08 +0900139 final WindowState callingWin = mService.windowForClientLocked(null, window, false);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900140 if (callingWin == null) {
141 Slog.w(TAG_WM, "Bad requesting window " + window);
142 return false; // !!! TODO: throw here?
143 }
144
145 // !!! TODO: if input is not still focused on the initiating window, fail
146 // the drag initiation (e.g. an alarm window popped up just as the application
147 // called performDrag()
148
Daichi Hirono58e25e12017-10-25 15:48:08 +0900149 mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
Daichi Hironodf5cf622017-09-11 14:59:26 +0900150
151 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
152 // will let us eliminate the (touchX,touchY) parameters from the API.
153
Daichi Hirono58e25e12017-10-25 15:48:08 +0900154 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
Daichi Hironodf5cf622017-09-11 14:59:26 +0900155 // the actual drag event dispatch stuff in the dragstate
156
157 final DisplayContent displayContent = callingWin.getDisplayContent();
158 if (displayContent == null) {
159 return false;
160 }
161 Display display = displayContent.getDisplay();
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900162 mDragState.register(display);
Daichi Hirono58e25e12017-10-25 15:48:08 +0900163 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900164 mDragState.getInputChannel())) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900165 Slog.e(TAG_WM, "Unable to transfer touch focus");
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900166 mDragState.closeLocked();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900167 return false;
168 }
169
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900170 mDragState.mDisplayContent = displayContent;
171 mDragState.mData = data;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900172 mDragState.broadcastDragStartedLocked(touchX, touchY);
173 mDragState.overridePointerIconLocked(touchSource);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900174
175 // remember the thumb offsets for later
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900176 mDragState.mThumbOffsetX = thumbCenterX;
177 mDragState.mThumbOffsetY = thumbCenterY;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900178
179 // Make the surface visible at the proper location
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900180 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900181 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
182 TAG_WM, ">>> OPEN TRANSACTION performDrag");
Daichi Hirono58e25e12017-10-25 15:48:08 +0900183 mService.openSurfaceTransaction();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900184 try {
185 surfaceControl.setPosition(touchX - thumbCenterX,
186 touchY - thumbCenterY);
Daichi Hirono58e25e12017-10-25 15:48:08 +0900187 surfaceControl.setLayer(mDragState.getDragLayerLocked());
Daichi Hironodf5cf622017-09-11 14:59:26 +0900188 surfaceControl.setLayerStack(display.getLayerStack());
189 surfaceControl.show();
190 } finally {
Daichi Hirono58e25e12017-10-25 15:48:08 +0900191 mService.closeSurfaceTransaction();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900192 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
193 TAG_WM, "<<< CLOSE TRANSACTION performDrag");
194 }
195
Daichi Hirono58e25e12017-10-25 15:48:08 +0900196 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900197 }
198
199 return true; // success!
200 }
201
Daichi Hirono58e25e12017-10-25 15:48:08 +0900202 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900203 IBinder token = window.asBinder();
204 if (DEBUG_DRAG) {
205 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
206 }
207
Daichi Hirono58e25e12017-10-25 15:48:08 +0900208 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900209 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900210 // Most likely the drop recipient ANRed and we ended the drag
211 // out from under it. Log the issue and move on.
212 Slog.w(TAG_WM, "Drop result given but no drag in progress");
213 return;
214 }
215
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900216 if (mDragState.mToken != token) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900217 // We're in a drag, but the wrong window has responded.
218 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
219 throw new IllegalStateException("reportDropResult() by non-recipient");
220 }
221
222 // The right window has responded, even if it's no longer around,
223 // so be sure to halt the timeout even if the later WindowState
224 // lookup fails.
Daichi Hirono58e25e12017-10-25 15:48:08 +0900225 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
226 WindowState callingWin = mService.windowForClientLocked(null, window, false);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900227 if (callingWin == null) {
228 Slog.w(TAG_WM, "Bad result-reporting window " + window);
229 return; // !!! TODO: throw here?
230 }
231
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900232 mDragState.mDragResult = consumed;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900233 mDragState.endDragLocked();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900234 }
235 }
236
Daichi Hirono58e25e12017-10-25 15:48:08 +0900237 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900238 if (DEBUG_DRAG) {
239 Slog.d(TAG_WM, "cancelDragAndDrop");
240 }
241
Daichi Hirono58e25e12017-10-25 15:48:08 +0900242 synchronized (mService.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900243 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900244 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
245 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
246 }
247
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900248 if (mDragState.mToken != dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900249 Slog.w(TAG_WM,
250 "cancelDragAndDrop() does not match prepareDrag()");
251 throw new IllegalStateException(
252 "cancelDragAndDrop() does not match prepareDrag()");
253 }
254
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900255 mDragState.mDragResult = false;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900256 mDragState.cancelDragLocked();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900257 }
258 }
259
260 void dragRecipientEntered(IWindow window) {
261 if (DEBUG_DRAG) {
262 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
263 }
264 }
265
266 void dragRecipientExited(IWindow window) {
267 if (DEBUG_DRAG) {
268 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
269 }
270 }
271
Daichi Hirono58e25e12017-10-25 15:48:08 +0900272 /**
273 * Sends a message to the Handler managed by DragDropController.
274 */
275 void sendHandlerMessage(int what, Object arg) {
276 mHandler.obtainMessage(what, arg).sendToTarget();
277 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900278
Daichi Hirono58e25e12017-10-25 15:48:08 +0900279 /**
280 * Sends a timeout message to the Handler managed by DragDropController.
281 */
282 void sendTimeoutMessage(int what, Object arg) {
283 mHandler.removeMessages(what, arg);
284 final Message msg = mHandler.obtainMessage(what, arg);
285 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
286 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900287
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900288 /**
289 * Notifies the current drag state is closed.
290 */
291 void onDragStateClosedLocked(DragState dragState) {
292 if (mDragState != dragState) {
293 Slog.wtf(TAG_WM, "Unknown drag state is closed");
294 return;
295 }
296 mDragState = null;
297 }
298
Daichi Hirono58e25e12017-10-25 15:48:08 +0900299 private class DragHandler extends Handler {
300 /**
301 * Lock for window manager.
302 */
303 private final WindowManagerService mService;
304
305 DragHandler(WindowManagerService service, Looper looper) {
306 super(looper);
307 mService = service;
308 }
309
310 @Override
311 public void handleMessage(Message msg) {
312 switch (msg.what) {
313 case MSG_DRAG_START_TIMEOUT: {
314 IBinder win = (IBinder) msg.obj;
315 if (DEBUG_DRAG) {
316 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900317 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900318 synchronized (mService.mWindowMap) {
319 // !!! TODO: ANR the app that has failed to start the drag in time
320 if (mDragState != null) {
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900321 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900322 }
323 }
324 break;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900325 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900326
327 case MSG_DRAG_END_TIMEOUT: {
328 IBinder win = (IBinder) msg.obj;
329 if (DEBUG_DRAG) {
330 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
331 }
332 synchronized (mService.mWindowMap) {
333 // !!! TODO: ANR the drag-receiving app
334 if (mDragState != null) {
335 mDragState.mDragResult = false;
336 mDragState.endDragLocked();
337 }
338 }
339 break;
340 }
341
342 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
343 if (DEBUG_DRAG)
344 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
345 DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
346 if (interceptor != null) {
347 synchronized (mService.mWindowMap) {
348 interceptor.tearDown();
349 }
350 }
351 break;
352 }
353
354 case MSG_ANIMATION_END: {
355 synchronized (mService.mWindowMap) {
356 if (mDragState == null) {
357 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
358 "plyaing animation");
359 return;
360 }
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900361 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900362 }
363 break;
364 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900365 }
366 }
367 }
368}