blob: fc370d930cf37a340fcc3e91b66de7a5198c3a74 [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.
Daichi Hironofabca092017-12-19 15:02:50 +090051 static final int MSG_DRAG_END_TIMEOUT = 0;
52 static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 1;
53 static final int MSG_ANIMATION_END = 2;
Daichi Hirono58e25e12017-10-25 15:48:08 +090054
Daichi Hirono3bae0b02017-10-27 13:05:13 +090055 /**
56 * Drag state per operation.
Daichi Hirono7a5d0072017-10-30 10:07:24 +090057 * Needs a lock of {@code WindowManagerService#mWindowMap} to read this. Needs both locks of
58 * {@code mWriteLock} and {@code WindowManagerService#mWindowMap} to update this.
Daichi Hirono3bae0b02017-10-27 13:05:13 +090059 * The variable is cleared by {@code #onDragStateClosedLocked} which is invoked by DragState
60 * itself, thus the variable can be null after calling DragState's methods.
61 */
Daichi Hirono768012e2017-10-30 10:05:37 +090062 private DragState mDragState;
Daichi Hirono76a26aa2017-09-11 15:13:38 +090063
Daichi Hirono58e25e12017-10-25 15:48:08 +090064 private WindowManagerService mService;
65 private final Handler mHandler;
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090066
Daichi Hirono7a5d0072017-10-30 10:07:24 +090067 /**
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090068 * Callback which is used to sync drag state with the vendor-specific code.
69 */
Daichi Hironobb28efb2017-11-21 15:11:01 +090070 @NonNull private AtomicReference<IDragDropCallback> mCallback = new AtomicReference<>(
71 new IDragDropCallback() {});
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090072
Daichi Hirono76a26aa2017-09-11 15:13:38 +090073 boolean dragDropActiveLocked() {
74 return mDragState != null;
75 }
Daichi Hironodf5cf622017-09-11 14:59:26 +090076
Daichi Hirono768012e2017-10-30 10:05:37 +090077 InputWindowHandle getInputWindowHandleLocked() {
78 return mDragState.getInputWindowHandle();
79 }
80
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090081 void registerCallback(IDragDropCallback callback) {
82 Preconditions.checkNotNull(callback);
Daichi Hironobb28efb2017-11-21 15:11:01 +090083 mCallback.set(callback);
Daichi Hirono3c6c95e2017-09-13 12:23:57 +090084 }
85
Daichi Hirono58e25e12017-10-25 15:48:08 +090086 DragDropController(WindowManagerService service, Looper looper) {
87 mService = service;
88 mHandler = new DragHandler(service, looper);
89 }
90
Daichi Hirono768012e2017-10-30 10:05:37 +090091 void sendDragStartedIfNeededLocked(WindowState window) {
92 mDragState.sendDragStartedIfNeededLocked(window);
93 }
94
Daichi Hironofabca092017-12-19 15:02:50 +090095 IBinder performDrag(SurfaceSession session, int callerPid, int callerUid, IWindow window,
96 int flags, SurfaceControl surface, int touchSource, float touchX, float touchY,
97 float thumbCenterX, float thumbCenterY, ClipData data) {
Daichi Hironodf5cf622017-09-11 14:59:26 +090098 if (DEBUG_DRAG) {
Daichi Hironofabca092017-12-19 15:02:50 +090099 Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" +
100 Integer.toHexString(flags) + " data=" + data);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900101 }
102
Daichi Hironofabca092017-12-19 15:02:50 +0900103 final IBinder dragToken = new Binder();
Daichi Hirono663e9a72017-11-10 13:51:02 +0900104 final boolean callbackResult = mCallback.get().prePerformDrag(window, dragToken,
105 touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data);
Daichi Hironobb28efb2017-11-21 15:11:01 +0900106 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900107 synchronized (mService.mWindowMap) {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900108 try {
Daichi Hirono663e9a72017-11-10 13:51:02 +0900109 if (!callbackResult) {
Daichi Hironofabca092017-12-19 15:02:50 +0900110 Slog.w(TAG_WM, "IDragDropCallback rejects the performDrag request");
111 return null;
Daichi Hirono663e9a72017-11-10 13:51:02 +0900112 }
113
Daichi Hironofabca092017-12-19 15:02:50 +0900114 if (dragDropActiveLocked()) {
115 Slog.w(TAG_WM, "Drag already in progress");
116 return null;
117 }
Daichi Hirono663e9a72017-11-10 13:51:02 +0900118
119 final WindowState callingWin = mService.windowForClientLocked(
120 null, window, false);
121 if (callingWin == null) {
122 Slog.w(TAG_WM, "Bad requesting window " + window);
Daichi Hironofabca092017-12-19 15:02:50 +0900123 return null; // !!! TODO: throw here?
Daichi Hirono663e9a72017-11-10 13:51:02 +0900124 }
125
126 // !!! TODO: if input is not still focused on the initiating window, fail
127 // the drag initiation (e.g. an alarm window popped up just as the application
128 // called performDrag()
129
130 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
131 // will let us eliminate the (touchX,touchY) parameters from the API.
132
133 // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
134 // the actual drag event dispatch stuff in the dragstate
135
Daichi Hironofabca092017-12-19 15:02:50 +0900136 // !!! TODO(multi-display): support other displays
137
Daichi Hirono663e9a72017-11-10 13:51:02 +0900138 final DisplayContent displayContent = callingWin.getDisplayContent();
139 if (displayContent == null) {
140 Slog.w(TAG_WM, "display content is null");
Daichi Hironofabca092017-12-19 15:02:50 +0900141 return null;
Daichi Hirono663e9a72017-11-10 13:51:02 +0900142 }
143
Daichi Hironofabca092017-12-19 15:02:50 +0900144 final float alpha = (flags & View.DRAG_FLAG_OPAQUE) == 0 ?
145 DRAG_SHADOW_ALPHA_TRANSPARENT : 1;
146 final IBinder winBinder = window.asBinder();
147 IBinder token = new Binder();
148 mDragState = new DragState(mService, this, token, surface, flags, winBinder);
149 surface = null;
150 mDragState.mPid = callerPid;
151 mDragState.mUid = callerUid;
152 mDragState.mOriginalAlpha = alpha;
153 mDragState.mToken = dragToken;
154
Daichi Hirono663e9a72017-11-10 13:51:02 +0900155 final Display display = displayContent.getDisplay();
Daichi Hirono4a545172018-02-01 10:59:48 +0900156 if (!mCallback.get().registerInputChannel(
157 mDragState, display, mService.mInputManager,
158 callingWin.mInputChannel)) {
Daichi Hirono663e9a72017-11-10 13:51:02 +0900159 Slog.e(TAG_WM, "Unable to transfer touch focus");
Daichi Hironofabca092017-12-19 15:02:50 +0900160 return null;
Daichi Hirono663e9a72017-11-10 13:51:02 +0900161 }
162
163 mDragState.mDisplayContent = displayContent;
164 mDragState.mData = data;
165 mDragState.broadcastDragStartedLocked(touchX, touchY);
166 mDragState.overridePointerIconLocked(touchSource);
167 // remember the thumb offsets for later
168 mDragState.mThumbOffsetX = thumbCenterX;
169 mDragState.mThumbOffsetY = thumbCenterY;
170
171 // Make the surface visible at the proper location
172 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
173 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
Daichi Hironoa1fb9be2017-12-18 17:02:54 +0900174
175 final SurfaceControl.Transaction transaction =
176 callingWin.getPendingTransaction();
177 transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
178 transaction.setPosition(
179 surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY);
180 transaction.show(surfaceControl);
181 displayContent.reparentToOverlay(transaction, surfaceControl);
182 callingWin.scheduleAnimation();
183
184 if (SHOW_LIGHT_TRANSACTIONS) {
185 Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
Daichi Hirono663e9a72017-11-10 13:51:02 +0900186 }
187
188 mDragState.notifyLocationLocked(touchX, touchY);
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900189 } finally {
Daichi Hironoa1fb9be2017-12-18 17:02:54 +0900190 if (surface != null) {
191 surface.release();
192 }
Daichi Hirono663e9a72017-11-10 13:51:02 +0900193 if (mDragState != null && !mDragState.isInProgress()) {
194 mDragState.closeLocked();
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900195 }
196 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900197 }
Daichi Hironofabca092017-12-19 15:02:50 +0900198 return dragToken; // success!
Daichi Hironobb28efb2017-11-21 15:11:01 +0900199 } finally {
200 mCallback.get().postPerformDrag();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900201 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900202 }
203
Daichi Hirono58e25e12017-10-25 15:48:08 +0900204 void reportDropResult(IWindow window, boolean consumed) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900205 IBinder token = window.asBinder();
206 if (DEBUG_DRAG) {
207 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
208 }
209
Daichi Hironobb28efb2017-11-21 15:11:01 +0900210 mCallback.get().preReportDropResult(window, consumed);
211 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900212 synchronized (mService.mWindowMap) {
213 if (mDragState == null) {
214 // Most likely the drop recipient ANRed and we ended the drag
215 // out from under it. Log the issue and move on.
216 Slog.w(TAG_WM, "Drop result given but no drag in progress");
217 return;
218 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900219
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900220 if (mDragState.mToken != token) {
221 // We're in a drag, but the wrong window has responded.
222 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
223 throw new IllegalStateException("reportDropResult() by non-recipient");
224 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900225
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900226 // The right window has responded, even if it's no longer around,
227 // so be sure to halt the timeout even if the later WindowState
228 // lookup fails.
229 mHandler.removeMessages(MSG_DRAG_END_TIMEOUT, window.asBinder());
230 WindowState callingWin = mService.windowForClientLocked(null, window, false);
231 if (callingWin == null) {
232 Slog.w(TAG_WM, "Bad result-reporting window " + window);
233 return; // !!! TODO: throw here?
234 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900235
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900236 mDragState.mDragResult = consumed;
237 mDragState.endDragLocked();
238 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900239 } finally {
240 mCallback.get().postReportDropResult();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900241 }
242 }
243
Daichi Hirono58e25e12017-10-25 15:48:08 +0900244 void cancelDragAndDrop(IBinder dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900245 if (DEBUG_DRAG) {
246 Slog.d(TAG_WM, "cancelDragAndDrop");
247 }
248
Daichi Hironobb28efb2017-11-21 15:11:01 +0900249 mCallback.get().preCancelDragAndDrop(dragToken);
250 try {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900251 synchronized (mService.mWindowMap) {
252 if (mDragState == null) {
253 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
254 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
255 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900256
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900257 if (mDragState.mToken != dragToken) {
258 Slog.w(TAG_WM,
259 "cancelDragAndDrop() does not match prepareDrag()");
260 throw new IllegalStateException(
261 "cancelDragAndDrop() does not match prepareDrag()");
262 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900263
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900264 mDragState.mDragResult = false;
265 mDragState.cancelDragLocked();
266 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900267 } finally {
268 mCallback.get().postCancelDragAndDrop();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900269 }
270 }
271
Daichi Hirono768012e2017-10-30 10:05:37 +0900272 /**
273 * Handles motion events.
274 * @param keepHandling Whether if the drag operation is continuing or this is the last motion
275 * event.
276 * @param newX X coordinate value in dp in the screen coordinate
277 * @param newY Y coordinate value in dp in the screen coordinate
278 */
279 void handleMotionEvent(boolean keepHandling, float newX, float newY) {
Daichi Hironobb28efb2017-11-21 15:11:01 +0900280 synchronized (mService.mWindowMap) {
281 if (!dragDropActiveLocked()) {
282 // The drag has ended but the clean-up message has not been processed by
283 // window manager. Drop events that occur after this until window manager
284 // has a chance to clean-up the input handle.
285 return;
286 }
Daichi Hirono768012e2017-10-30 10:05:37 +0900287
Daichi Hironobb28efb2017-11-21 15:11:01 +0900288 if (keepHandling) {
289 mDragState.notifyMoveLocked(newX, newY);
290 } else {
291 mDragState.notifyDropLocked(newX, newY);
Daichi Hirono768012e2017-10-30 10:05:37 +0900292 }
293 }
294 }
295
Daichi Hironodf5cf622017-09-11 14:59:26 +0900296 void dragRecipientEntered(IWindow window) {
297 if (DEBUG_DRAG) {
298 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
299 }
300 }
301
302 void dragRecipientExited(IWindow window) {
303 if (DEBUG_DRAG) {
304 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
305 }
306 }
307
Daichi Hirono58e25e12017-10-25 15:48:08 +0900308 /**
309 * Sends a message to the Handler managed by DragDropController.
310 */
311 void sendHandlerMessage(int what, Object arg) {
312 mHandler.obtainMessage(what, arg).sendToTarget();
313 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900314
Daichi Hirono58e25e12017-10-25 15:48:08 +0900315 /**
316 * Sends a timeout message to the Handler managed by DragDropController.
317 */
318 void sendTimeoutMessage(int what, Object arg) {
319 mHandler.removeMessages(what, arg);
320 final Message msg = mHandler.obtainMessage(what, arg);
321 mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
322 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900323
Daichi Hirono3bae0b02017-10-27 13:05:13 +0900324 /**
325 * Notifies the current drag state is closed.
326 */
327 void onDragStateClosedLocked(DragState dragState) {
328 if (mDragState != dragState) {
329 Slog.wtf(TAG_WM, "Unknown drag state is closed");
330 return;
331 }
332 mDragState = null;
333 }
334
Daichi Hirono58e25e12017-10-25 15:48:08 +0900335 private class DragHandler extends Handler {
336 /**
337 * Lock for window manager.
338 */
339 private final WindowManagerService mService;
340
341 DragHandler(WindowManagerService service, Looper looper) {
342 super(looper);
343 mService = service;
344 }
345
346 @Override
347 public void handleMessage(Message msg) {
348 switch (msg.what) {
Daichi Hirono58e25e12017-10-25 15:48:08 +0900349 case MSG_DRAG_END_TIMEOUT: {
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900350 final IBinder win = (IBinder) msg.obj;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900351 if (DEBUG_DRAG) {
352 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
353 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900354
355 synchronized (mService.mWindowMap) {
356 // !!! TODO: ANR the drag-receiving app
357 if (mDragState != null) {
358 mDragState.mDragResult = false;
359 mDragState.endDragLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900360 }
361 }
362 break;
363 }
364
365 case MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT: {
366 if (DEBUG_DRAG)
367 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
Daichi Hirono7a5d0072017-10-30 10:07:24 +0900368 final DragState.InputInterceptor interceptor =
369 (DragState.InputInterceptor) msg.obj;
370 if (interceptor == null) return;
371 synchronized (mService.mWindowMap) {
372 interceptor.tearDown();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900373 }
374 break;
375 }
376
377 case MSG_ANIMATION_END: {
Daichi Hironobb28efb2017-11-21 15:11:01 +0900378 synchronized (mService.mWindowMap) {
379 if (mDragState == null) {
380 Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
381 "plyaing animation");
382 return;
Daichi Hirono58e25e12017-10-25 15:48:08 +0900383 }
Daichi Hironobb28efb2017-11-21 15:11:01 +0900384 mDragState.closeLocked();
Daichi Hirono58e25e12017-10-25 15:48:08 +0900385 }
386 break;
387 }
Daichi Hironodf5cf622017-09-11 14:59:26 +0900388 }
389 }
390 }
391}