blob: 91478d0d1f2ffda0cec0db2e5109dbb77f593974 [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 Hironobb28efb2017-11-21 15:11:01 +090043import java.util.concurrent.atomic.AtomicReference;
Daichi Hironodf5cf622017-09-11 14:59:26 +090044
45/**
46 * Managing drag and drop operations initiated by View#startDragAndDrop.
47 */
48class DragDropController {
49 private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
50 private static final long DRAG_TIMEOUT_MS = 5000;
Daichi Hirono58e25e12017-10-25 15:48:08 +090051
52 // Messages for Handler.
53 private static final int MSG_DRAG_START_TIMEOUT = 0;
54 static final int MSG_DRAG_END_TIMEOUT = 1;
55 static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2;
56 static final int MSG_ANIMATION_END = 3;
57
Daichi Hirono3bae0b02017-10-27 13:05:13 +090058 /**
59 * Drag state per operation.
Daichi Hirono7a5d0072017-10-30 10:07:24 +090060 * Needs a lock of {@code WindowManagerService#mWindowMap} to read this. Needs both locks of
61 * {@code mWriteLock} and {@code WindowManagerService#mWindowMap} to update this.
Daichi Hirono3bae0b02017-10-27 13:05:13 +090062 * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
63 * itself, thus the variable can be null after calling DragState's methods.
64 */
Daichi Hirono768012e2017-10-30 10:05:37 +090065 private DragState mDragState;
Daichi Hirono76a26aa2017-09-11 15:13:38 +090066
Daichi Hirono58e25e12017-10-25 15:48:08 +090067 private WindowManagerService mService;
68 private final Handler mHandler;
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090069
Daichi Hirono7a5d0072017-10-30 10:07:24 +090070 /**
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090071 * Callback which is used to sync drag state with the vendor-specific code.
72 */
Daichi Hironobb28efb2017-11-21 15:11:01 +090073 @NonNull private AtomicReference<IDragDropCallback> mCallback = new AtomicReference<>(
74 new IDragDropCallback() {});
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090075
Daichi Hirono76a26aa2017-09-11 15:13:38 +090076 boolean dragDropActiveLocked() {
77 return mDragState != null;
78 }
Daichi Hironodf5cf622017-09-11 14:59:26 +090079
Daichi Hirono768012e2017-10-30 10:05:37 +090080 InputWindowHandle getInputWindowHandleLocked() {
81 return mDragState.getInputWindowHandle();
82 }
83
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090084 void registerCallback(IDragDropCallback callback) {
85 Preconditions.checkNotNull(callback);
Daichi Hironobb28efb2017-11-21 15:11:01 +090086 mCallback.set(callback);
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090087 }
88
Daichi Hirono58e25e12017-10-25 15:48:08 +090089 DragDropController(WindowManagerService service, Looper looper) {
90 mService = service;
91 mHandler = new DragHandler(service, looper);
92 }
93
Daichi Hirono768012e2017-10-30 10:05:37 +090094 void sendDragStartedIfNeededLocked(WindowState window) {
95 mDragState.sendDragStartedIfNeededLocked(window);
96 }
97
Daichi Hirono58e25e12017-10-25 15:48:08 +090098 IBinder prepareDrag(SurfaceSession session, int callerPid,
Daichi Hironodf5cf622017-09-11 14:59:26 +090099 int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
100 if (DEBUG_DRAG) {
101 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
102 + " flags=" + Integer.toHexString(flags) + " win=" + window
103 + " asbinder=" + window.asBinder());
104 }
105
Daichi Hironobb28efb2017-11-21 15:11:01 +0900106 synchronized (mService.mWindowMap) {
107 if (dragDropActiveLocked()) {
108 Slog.w(TAG_WM, "Drag already in progress");
109 return null;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900110 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900111
112 // TODO(multi-display): support other displays
113 final DisplayContent displayContent =
114 mService.getDefaultDisplayContentLocked();
115 final Display display = displayContent.getDisplay();
116
117 final SurfaceControl surface = new SurfaceControl.Builder(session)
118 .setName("drag surface")
119 .setSize(width, height)
120 .setFormat(PixelFormat.TRANSLUCENT)
121 .build();
122 surface.setLayerStack(display.getLayerStack());
123 float alpha = 1;
124 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
125 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
126 }
127 surface.setAlpha(alpha);
128
129 if (SHOW_TRANSACTIONS)
130 Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
131 outSurface.copyFrom(surface);
132 final IBinder winBinder = window.asBinder();
133 IBinder token = new Binder();
134 mDragState = new DragState(mService, token, surface, flags, winBinder);
135 mDragState.mPid = callerPid;
136 mDragState.mUid = callerUid;
137 mDragState.mOriginalAlpha = alpha;
138 token = mDragState.mToken = new Binder();
139
140 // 5 second timeout for this window to actually begin the drag
141 sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
142 return token;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900143 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900144 }
145
Daichi Hirono58e25e12017-10-25 15:48:08 +0900146 boolean performDrag(IWindow window, IBinder dragToken,
Daichi Hironodf5cf622017-09-11 14:59:26 +0900147 int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
148 ClipData data) {
149 if (DEBUG_DRAG) {
150 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
151 }
152
Daichi Hirono663e9a72017-11-10 13:51:02 +0900153 final boolean callbackResult = mCallback.get().prePerformDrag(window, dragToken,
154 touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data);
Daichi Hironobb28efb2017-11-21 15:11:01 +0900155 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900156 synchronized (mService.mWindowMap) {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900157 mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900158 try {
Daichi Hirono663e9a72017-11-10 13:51:02 +0900159 if (!callbackResult) {
160 return false;
161 }
162
163 Preconditions.checkState(
164 mDragState != null, "performDrag() without prepareDrag()");
165 Preconditions.checkState(
166 mDragState.mToken == dragToken,
167 "performDrag() does not match prepareDrag()");
168
169 final WindowState callingWin = mService.windowForClientLocked(
170 null, window, false);
171 if (callingWin == null) {
172 Slog.w(TAG_WM, "Bad requesting window " + window);
173 return false; // !!! TODO: throw here?
174 }
175
176 // !!! TODO: if input is not still focused on the initiating window, fail
177 // the drag initiation (e.g. an alarm window popped up just as the application
178 // called performDrag()
179
180 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
181 // will let us eliminate the (touchX,touchY) parameters from the API.
182
183 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
184 // the actual drag event dispatch stuff in the dragstate
185
186 final DisplayContent displayContent = callingWin.getDisplayContent();
187 if (displayContent == null) {
188 Slog.w(TAG_WM, "display content is null");
189 return false;
190 }
191
192 final Display display = displayContent.getDisplay();
193 mDragState.register(display);
194 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
195 mDragState.getInputChannel())) {
196 Slog.e(TAG_WM, "Unable to transfer touch focus");
197 return false;
198 }
199
200 mDragState.mDisplayContent = displayContent;
201 mDragState.mData = data;
202 mDragState.broadcastDragStartedLocked(touchX, touchY);
203 mDragState.overridePointerIconLocked(touchSource);
204 // remember the thumb offsets for later
205 mDragState.mThumbOffsetX = thumbCenterX;
206 mDragState.mThumbOffsetY = thumbCenterY;
207
208 // Make the surface visible at the proper location
209 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
210 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
211 mService.openSurfaceTransaction();
212 try {
213 surfaceControl.setPosition(touchX - thumbCenterX,
214 touchY - thumbCenterY);
215 surfaceControl.setLayer(mDragState.getDragLayerLocked());
216 surfaceControl.setLayerStack(display.getLayerStack());
217 surfaceControl.show();
218 } finally {
219 mService.closeSurfaceTransaction("performDrag");
220 if (SHOW_LIGHT_TRANSACTIONS) {
221 Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
222 }
223 }
224
225 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900226 } finally {
Daichi Hirono663e9a72017-11-10 13:51:02 +0900227 if (mDragState != null && !mDragState.isInProgress()) {
228 mDragState.closeLocked();
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900229 }
230 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900231 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900232 return true; // success!
233 } finally {
234 mCallback.get().postPerformDrag();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900235 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900236 }
237
Daichi Hirono58e25e12017-10-25 15:48:08 +0900238 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900239 IBinder token = window.asBinder();
240 if (DEBUG_DRAG) {
241 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
242 }
243
Daichi Hironobb28efb2017-11-21 15:11:01 +0900244 mCallback.get().preReportDropResult(window, consumed);
245 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900246 synchronized (mService.mWindowMap) {
247 if (mDragState == null) {
248 // Most likely the drop recipient ANRed and we ended the drag
249 // out from under it. Log the issue and move on.
250 Slog.w(TAG_WM, "Drop result given but no drag in progress");
251 return;
252 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900253
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900254 if (mDragState.mToken != token) {
255 // We're in a drag, but the wrong window has responded.
256 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
257 throw new IllegalStateException("reportDropResult() by non-recipient");
258 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900259
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900260 // The right window has responded, even if it's no longer around,
261 // so be sure to halt the timeout even if the later WindowState
262 // lookup fails.
263 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
264 WindowState callingWin = mService.windowForClientLocked(null, window, false);
265 if (callingWin == null) {
266 Slog.w(TAG_WM, "Bad result-reporting window " + window);
267 return; // !!! TODO: throw here?
268 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900269
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900270 mDragState.mDragResult = consumed;
271 mDragState.endDragLocked();
272 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900273 } finally {
274 mCallback.get().postReportDropResult();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900275 }
276 }
277
Daichi Hirono58e25e12017-10-25 15:48:08 +0900278 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900279 if (DEBUG_DRAG) {
280 Slog.d(TAG_WM, "cancelDragAndDrop");
281 }
282
Daichi Hironobb28efb2017-11-21 15:11:01 +0900283 mCallback.get().preCancelDragAndDrop(dragToken);
284 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900285 synchronized (mService.mWindowMap) {
286 if (mDragState == null) {
287 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
288 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
289 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900290
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900291 if (mDragState.mToken != dragToken) {
292 Slog.w(TAG_WM,
293 "cancelDragAndDrop() does not match prepareDrag()");
294 throw new IllegalStateException(
295 "cancelDragAndDrop() does not match prepareDrag()");
296 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900297
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900298 mDragState.mDragResult = false;
299 mDragState.cancelDragLocked();
300 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900301 } finally {
302 mCallback.get().postCancelDragAndDrop();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900303 }
304 }
305
Daichi Hirono768012e2017-10-30 10:05:37 +0900306 /**
307 * Handles motion events.
308 * @param keepHandling Whether if the drag operation is continuing or this is the last motion
309 * event.
310 * @param newX X coordinate value in dp in the screen coordinate
311 * @param newY Y coordinate value in dp in the screen coordinate
312 */
313 void handleMotionEvent(boolean keepHandling, float newX, float newY) {
Daichi Hironobb28efb2017-11-21 15:11:01 +0900314 synchronized (mService.mWindowMap) {
315 if (!dragDropActiveLocked()) {
316 // The drag has ended but the clean-up message has not been processed by
317 // window manager. Drop events that occur after this until window manager
318 // has a chance to clean-up the input handle.
319 return;
320 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900321
Daichi Hironobb28efb2017-11-21 15:11:01 +0900322 if (keepHandling) {
323 mDragState.notifyMoveLocked(newX, newY);
324 } else {
325 mDragState.notifyDropLocked(newX, newY);
Daichi Hirono768012e2017-10-30 10:05:37 +0900326 }
327 }
328 }
329
Daichi Hironodf5cf622017-09-11 14:59:26 +0900330 void dragRecipientEntered(IWindow window) {
331 if (DEBUG_DRAG) {
332 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
333 }
334 }
335
336 void dragRecipientExited(IWindow window) {
337 if (DEBUG_DRAG) {
338 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
339 }
340 }
341
Daichi Hirono58e25e12017-10-25 15:48:08 +0900342 /**
343 * Sends a message to the Handler managed by DragDropController.
344 */
345 void sendHandlerMessage(int what, Object arg) {
346 mHandler.obtainMessage(what, arg).sendToTarget();
347 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900348
Daichi Hirono58e25e12017-10-25 15:48:08 +0900349 /**
350 * Sends a timeout message to the Handler managed by DragDropController.
351 */
352 void sendTimeoutMessage(int what, Object arg) {
353 mHandler.removeMessages(what, arg);
354 final Message msg = mHandler.obtainMessage(what, arg);
355 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
356 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900357
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900358 /**
359 * Notifies the current drag state is closed.
360 */
361 void onDragStateClosedLocked(DragState dragState) {
362 if (mDragState != dragState) {
363 Slog.wtf(TAG_WM, "Unknown drag state is closed");
364 return;
365 }
366 mDragState = null;
367 }
368
Daichi Hirono58e25e12017-10-25 15:48:08 +0900369 private class DragHandler extends Handler {
370 /**
371 * Lock for window manager.
372 */
373 private final WindowManagerService mService;
374
375 DragHandler(WindowManagerService service, Looper looper) {
376 super(looper);
377 mService = service;
378 }
379
380 @Override
381 public void handleMessage(Message msg) {
382 switch (msg.what) {
383 case MSG_DRAG_START_TIMEOUT: {
384 IBinder win = (IBinder) msg.obj;
385 if (DEBUG_DRAG) {
386 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900387 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900388
389 synchronized (mService.mWindowMap) {
390 // !!! TODO: ANR the app that has failed to start the drag in time
391 if (mDragState != null) {
392 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900393 }
394 }
395 break;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900396 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900397
398 case MSG_DRAG_END_TIMEOUT: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900399 final IBinder win = (IBinder) msg.obj;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900400 if (DEBUG_DRAG) {
401 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
402 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900403
404 synchronized (mService.mWindowMap) {
405 // !!! TODO: ANR the drag-receiving app
406 if (mDragState != null) {
407 mDragState.mDragResult = false;
408 mDragState.endDragLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900409 }
410 }
411 break;
412 }
413
414 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
415 if (DEBUG_DRAG)
416 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900417 final DragState.InputInterceptor interceptor =
418 (DragState.InputInterceptor) msg.obj;
419 if (interceptor == null) return;
420 synchronized (mService.mWindowMap) {
421 interceptor.tearDown();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900422 }
423 break;
424 }
425
426 case MSG_ANIMATION_END: {
Daichi Hironobb28efb2017-11-21 15:11:01 +0900427 synchronized (mService.mWindowMap) {
428 if (mDragState == null) {
429 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
430 "plyaing animation");
431 return;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900432 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900433 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900434 }
435 break;
436 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900437 }
438 }
439 }
440}