blob: bf97bd21e8b8d18a78d681f465219f16271121cd [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;
Daichi Hironodf5cf622017-09-11 14:59:26 +090021import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
22
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090023import android.annotation.NonNull;
Daichi Hironodf5cf622017-09-11 14:59:26 +090024import android.content.ClipData;
25import android.os.Binder;
Daichi Hirono58e25e12017-10-25 15:48:08 +090026import android.os.Handler;
Daichi Hironodf5cf622017-09-11 14:59:26 +090027import android.os.IBinder;
Daichi Hirono58e25e12017-10-25 15:48:08 +090028import android.os.Looper;
Daichi Hironodf5cf622017-09-11 14:59:26 +090029import android.os.Message;
30import android.util.Slog;
31import android.view.Display;
32import android.view.IWindow;
Daichi Hironodf5cf622017-09-11 14:59:26 +090033import android.view.SurfaceControl;
Daichi Hironoa1fb9be2017-12-18 17:02:54 +090034import android.view.SurfaceControl.Transaction;
Daichi Hironodf5cf622017-09-11 14:59:26 +090035import android.view.SurfaceSession;
36import android.view.View;
Adrian Roose99bc052017-11-20 17:55:31 +010037
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090038import com.android.internal.util.Preconditions;
Daichi Hirono768012e2017-10-30 10:05:37 +090039import com.android.server.input.InputWindowHandle;
Adrian Roose99bc052017-11-20 17:55:31 +010040import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
Daichi Hironobb28efb2017-11-21 15:11:01 +090041import java.util.concurrent.atomic.AtomicReference;
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 /**
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090069 * Callback which is used to sync drag state with the vendor-specific code.
70 */
Daichi Hironobb28efb2017-11-21 15:11:01 +090071 @NonNull private AtomicReference<IDragDropCallback> mCallback = new AtomicReference<>(
72 new IDragDropCallback() {});
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090073
Daichi Hirono76a26aa2017-09-11 15:13:38 +090074 boolean dragDropActiveLocked() {
75 return mDragState != null;
76 }
Daichi Hironodf5cf622017-09-11 14:59:26 +090077
Daichi Hirono768012e2017-10-30 10:05:37 +090078 InputWindowHandle getInputWindowHandleLocked() {
79 return mDragState.getInputWindowHandle();
80 }
81
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090082 void registerCallback(IDragDropCallback callback) {
83 Preconditions.checkNotNull(callback);
Daichi Hironobb28efb2017-11-21 15:11:01 +090084 mCallback.set(callback);
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090085 }
86
Daichi Hirono58e25e12017-10-25 15:48:08 +090087 DragDropController(WindowManagerService service, Looper looper) {
88 mService = service;
89 mHandler = new DragHandler(service, looper);
90 }
91
Daichi Hirono768012e2017-10-30 10:05:37 +090092 void sendDragStartedIfNeededLocked(WindowState window) {
93 mDragState.sendDragStartedIfNeededLocked(window);
94 }
95
Daichi Hirono58e25e12017-10-25 15:48:08 +090096 IBinder prepareDrag(SurfaceSession session, int callerPid,
Daichi Hironoa1fb9be2017-12-18 17:02:54 +090097 int callerUid, IWindow window, int flags, int width, int height) {
Daichi Hironodf5cf622017-09-11 14:59:26 +090098 if (DEBUG_DRAG) {
99 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
100 + " flags=" + Integer.toHexString(flags) + " win=" + window
101 + " asbinder=" + window.asBinder());
102 }
103
Daichi Hirono1821c5a2017-12-12 17:54:12 +0900104 if (width <= 0 || height <= 0) {
105 Slog.w(TAG_WM, "width and height of drag shadow must be positive");
106 return null;
107 }
108
Daichi Hironobb28efb2017-11-21 15:11:01 +0900109 synchronized (mService.mWindowMap) {
110 if (dragDropActiveLocked()) {
111 Slog.w(TAG_WM, "Drag already in progress");
112 return null;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900113 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900114
115 // TODO(multi-display): support other displays
Daichi Hironobb28efb2017-11-21 15:11:01 +0900116 float alpha = 1;
117 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
118 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
119 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900120 final IBinder winBinder = window.asBinder();
121 IBinder token = new Binder();
Daichi Hironoa1fb9be2017-12-18 17:02:54 +0900122 mDragState = new DragState(mService, this, token, /* surface */ null, flags, winBinder);
Daichi Hironobb28efb2017-11-21 15:11:01 +0900123 mDragState.mPid = callerPid;
124 mDragState.mUid = callerUid;
125 mDragState.mOriginalAlpha = alpha;
126 token = mDragState.mToken = new Binder();
127
128 // 5 second timeout for this window to actually begin the drag
129 sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
130 return token;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900131 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900132 }
133
Daichi Hironoa1fb9be2017-12-18 17:02:54 +0900134 boolean performDrag(SurfaceSession session, IWindow window, IBinder dragToken,
135 SurfaceControl surface, int touchSource, float touchX, float touchY, float thumbCenterX,
136 float thumbCenterY, ClipData data) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900137 if (DEBUG_DRAG) {
138 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
139 }
140
Daichi Hirono663e9a72017-11-10 13:51:02 +0900141 final boolean callbackResult = mCallback.get().prePerformDrag(window, dragToken,
142 touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data);
Daichi Hironobb28efb2017-11-21 15:11:01 +0900143 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900144 synchronized (mService.mWindowMap) {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900145 mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900146 try {
Daichi Hirono663e9a72017-11-10 13:51:02 +0900147 if (!callbackResult) {
148 return false;
149 }
150
151 Preconditions.checkState(
152 mDragState != null, "performDrag() without prepareDrag()");
153 Preconditions.checkState(
154 mDragState.mToken == dragToken,
155 "performDrag() does not match prepareDrag()");
156
157 final WindowState callingWin = mService.windowForClientLocked(
158 null, window, false);
159 if (callingWin == null) {
160 Slog.w(TAG_WM, "Bad requesting window " + window);
161 return false; // !!! TODO: throw here?
162 }
163
164 // !!! TODO: if input is not still focused on the initiating window, fail
165 // the drag initiation (e.g. an alarm window popped up just as the application
166 // called performDrag()
167
168 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
169 // will let us eliminate the (touchX,touchY) parameters from the API.
170
171 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
172 // the actual drag event dispatch stuff in the dragstate
173
174 final DisplayContent displayContent = callingWin.getDisplayContent();
175 if (displayContent == null) {
176 Slog.w(TAG_WM, "display content is null");
177 return false;
178 }
179
180 final Display display = displayContent.getDisplay();
181 mDragState.register(display);
182 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
183 mDragState.getInputChannel())) {
184 Slog.e(TAG_WM, "Unable to transfer touch focus");
185 return false;
186 }
187
Daichi Hironoa1fb9be2017-12-18 17:02:54 +0900188 mDragState.mSurfaceControl = surface;
189 surface = null;
Daichi Hirono663e9a72017-11-10 13:51:02 +0900190 mDragState.mDisplayContent = displayContent;
191 mDragState.mData = data;
192 mDragState.broadcastDragStartedLocked(touchX, touchY);
193 mDragState.overridePointerIconLocked(touchSource);
194 // remember the thumb offsets for later
195 mDragState.mThumbOffsetX = thumbCenterX;
196 mDragState.mThumbOffsetY = thumbCenterY;
197
198 // Make the surface visible at the proper location
199 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
200 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
Daichi Hironoa1fb9be2017-12-18 17:02:54 +0900201
202 final SurfaceControl.Transaction transaction =
203 callingWin.getPendingTransaction();
204 transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
205 transaction.setPosition(
206 surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY);
207 transaction.show(surfaceControl);
208 displayContent.reparentToOverlay(transaction, surfaceControl);
209 callingWin.scheduleAnimation();
210
211 if (SHOW_LIGHT_TRANSACTIONS) {
212 Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
Daichi Hirono663e9a72017-11-10 13:51:02 +0900213 }
214
215 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900216 } finally {
Daichi Hironoa1fb9be2017-12-18 17:02:54 +0900217 if (surface != null) {
218 surface.release();
219 }
Daichi Hirono663e9a72017-11-10 13:51:02 +0900220 if (mDragState != null && !mDragState.isInProgress()) {
221 mDragState.closeLocked();
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900222 }
223 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900224 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900225 return true; // success!
226 } finally {
227 mCallback.get().postPerformDrag();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900228 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900229 }
230
Daichi Hirono58e25e12017-10-25 15:48:08 +0900231 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900232 IBinder token = window.asBinder();
233 if (DEBUG_DRAG) {
234 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
235 }
236
Daichi Hironobb28efb2017-11-21 15:11:01 +0900237 mCallback.get().preReportDropResult(window, consumed);
238 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900239 synchronized (mService.mWindowMap) {
240 if (mDragState == null) {
241 // Most likely the drop recipient ANRed and we ended the drag
242 // out from under it. Log the issue and move on.
243 Slog.w(TAG_WM, "Drop result given but no drag in progress");
244 return;
245 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900246
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900247 if (mDragState.mToken != token) {
248 // We're in a drag, but the wrong window has responded.
249 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
250 throw new IllegalStateException("reportDropResult() by non-recipient");
251 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900252
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900253 // The right window has responded, even if it's no longer around,
254 // so be sure to halt the timeout even if the later WindowState
255 // lookup fails.
256 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
257 WindowState callingWin = mService.windowForClientLocked(null, window, false);
258 if (callingWin == null) {
259 Slog.w(TAG_WM, "Bad result-reporting window " + window);
260 return; // !!! TODO: throw here?
261 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900262
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900263 mDragState.mDragResult = consumed;
264 mDragState.endDragLocked();
265 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900266 } finally {
267 mCallback.get().postReportDropResult();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900268 }
269 }
270
Daichi Hirono58e25e12017-10-25 15:48:08 +0900271 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900272 if (DEBUG_DRAG) {
273 Slog.d(TAG_WM, "cancelDragAndDrop");
274 }
275
Daichi Hironobb28efb2017-11-21 15:11:01 +0900276 mCallback.get().preCancelDragAndDrop(dragToken);
277 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900278 synchronized (mService.mWindowMap) {
279 if (mDragState == null) {
280 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
281 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
282 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900283
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900284 if (mDragState.mToken != dragToken) {
285 Slog.w(TAG_WM,
286 "cancelDragAndDrop() does not match prepareDrag()");
287 throw new IllegalStateException(
288 "cancelDragAndDrop() does not match prepareDrag()");
289 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900290
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900291 mDragState.mDragResult = false;
292 mDragState.cancelDragLocked();
293 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900294 } finally {
295 mCallback.get().postCancelDragAndDrop();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900296 }
297 }
298
Daichi Hirono768012e2017-10-30 10:05:37 +0900299 /**
300 * Handles motion events.
301 * @param keepHandling Whether if the drag operation is continuing or this is the last motion
302 * event.
303 * @param newX X coordinate value in dp in the screen coordinate
304 * @param newY Y coordinate value in dp in the screen coordinate
305 */
306 void handleMotionEvent(boolean keepHandling, float newX, float newY) {
Daichi Hironobb28efb2017-11-21 15:11:01 +0900307 synchronized (mService.mWindowMap) {
308 if (!dragDropActiveLocked()) {
309 // The drag has ended but the clean-up message has not been processed by
310 // window manager. Drop events that occur after this until window manager
311 // has a chance to clean-up the input handle.
312 return;
313 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900314
Daichi Hironobb28efb2017-11-21 15:11:01 +0900315 if (keepHandling) {
316 mDragState.notifyMoveLocked(newX, newY);
317 } else {
318 mDragState.notifyDropLocked(newX, newY);
Daichi Hirono768012e2017-10-30 10:05:37 +0900319 }
320 }
321 }
322
Daichi Hironodf5cf622017-09-11 14:59:26 +0900323 void dragRecipientEntered(IWindow window) {
324 if (DEBUG_DRAG) {
325 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
326 }
327 }
328
329 void dragRecipientExited(IWindow window) {
330 if (DEBUG_DRAG) {
331 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
332 }
333 }
334
Daichi Hirono58e25e12017-10-25 15:48:08 +0900335 /**
336 * Sends a message to the Handler managed by DragDropController.
337 */
338 void sendHandlerMessage(int what, Object arg) {
339 mHandler.obtainMessage(what, arg).sendToTarget();
340 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900341
Daichi Hirono58e25e12017-10-25 15:48:08 +0900342 /**
343 * Sends a timeout message to the Handler managed by DragDropController.
344 */
345 void sendTimeoutMessage(int what, Object arg) {
346 mHandler.removeMessages(what, arg);
347 final Message msg = mHandler.obtainMessage(what, arg);
348 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
349 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900350
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900351 /**
352 * Notifies the current drag state is closed.
353 */
354 void onDragStateClosedLocked(DragState dragState) {
355 if (mDragState != dragState) {
356 Slog.wtf(TAG_WM, "Unknown drag state is closed");
357 return;
358 }
359 mDragState = null;
360 }
361
Daichi Hirono58e25e12017-10-25 15:48:08 +0900362 private class DragHandler extends Handler {
363 /**
364 * Lock for window manager.
365 */
366 private final WindowManagerService mService;
367
368 DragHandler(WindowManagerService service, Looper looper) {
369 super(looper);
370 mService = service;
371 }
372
373 @Override
374 public void handleMessage(Message msg) {
375 switch (msg.what) {
376 case MSG_DRAG_START_TIMEOUT: {
377 IBinder win = (IBinder) msg.obj;
378 if (DEBUG_DRAG) {
379 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900380 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900381
382 synchronized (mService.mWindowMap) {
383 // !!! TODO: ANR the app that has failed to start the drag in time
384 if (mDragState != null) {
385 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900386 }
387 }
388 break;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900389 }
Daichi Hirono58e25e12017-10-25 15:48:08 +0900390
391 case MSG_DRAG_END_TIMEOUT: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900392 final IBinder win = (IBinder) msg.obj;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900393 if (DEBUG_DRAG) {
394 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
395 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900396
397 synchronized (mService.mWindowMap) {
398 // !!! TODO: ANR the drag-receiving app
399 if (mDragState != null) {
400 mDragState.mDragResult = false;
401 mDragState.endDragLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900402 }
403 }
404 break;
405 }
406
407 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
408 if (DEBUG_DRAG)
409 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900410 final DragState.InputInterceptor interceptor =
411 (DragState.InputInterceptor) msg.obj;
412 if (interceptor == null) return;
413 synchronized (mService.mWindowMap) {
414 interceptor.tearDown();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900415 }
416 break;
417 }
418
419 case MSG_ANIMATION_END: {
Daichi Hironobb28efb2017-11-21 15:11:01 +0900420 synchronized (mService.mWindowMap) {
421 if (mDragState == null) {
422 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
423 "plyaing animation");
424 return;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900425 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900426 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900427 }
428 break;
429 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900430 }
431 }
432 }
433}