blob: a3c6167075d9b6e35a8c4774cfe307c239795eb4 [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 Hironodf5cf622017-09-11 14:59:26 +090027import android.os.IBinder;
28import android.os.Message;
29import android.util.Slog;
30import android.view.Display;
31import android.view.IWindow;
32import android.view.Surface;
Daichi Hironodf5cf622017-09-11 14:59:26 +090033import android.view.SurfaceControl;
34import android.view.SurfaceSession;
35import android.view.View;
36import com.android.server.wm.WindowManagerService.H;
37
38/**
39 * Managing drag and drop operations initiated by View#startDragAndDrop.
40 */
41class DragDropController {
42 private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
43 private static final long DRAG_TIMEOUT_MS = 5000;
Daichi Hirono76a26aa2017-09-11 15:13:38 +090044 DragState mDragState;
45
46 boolean dragDropActiveLocked() {
47 return mDragState != null;
48 }
Daichi Hironodf5cf622017-09-11 14:59:26 +090049
50 IBinder prepareDrag(WindowManagerService service, SurfaceSession session, int callerPid,
51 int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
52 if (DEBUG_DRAG) {
53 Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
54 + " flags=" + Integer.toHexString(flags) + " win=" + window
55 + " asbinder=" + window.asBinder());
56 }
57
58 IBinder token = null;
59
60 synchronized (service.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +090061 if (dragDropActiveLocked()) {
62 Slog.w(TAG_WM, "Drag already in progress");
63 return null;
Daichi Hironodf5cf622017-09-11 14:59:26 +090064 }
Daichi Hirono76a26aa2017-09-11 15:13:38 +090065
66 // TODO(multi-display): support other displays
67 final DisplayContent displayContent =
68 service.getDefaultDisplayContentLocked();
69 final Display display = displayContent.getDisplay();
70
71 final SurfaceControl surface = new SurfaceControl.Builder(session)
72 .setName("drag surface")
73 .setSize(width, height)
74 .setFormat(PixelFormat.TRANSLUCENT)
75 .build();
76 surface.setLayerStack(display.getLayerStack());
77 float alpha = 1;
78 if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
79 alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
80 }
81 surface.setAlpha(alpha);
82
83 if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
84 outSurface.copyFrom(surface);
85 final IBinder winBinder = window.asBinder();
86 token = new Binder();
87 mDragState = new DragState(service, token, surface, flags, winBinder);
88 mDragState.mPid = callerPid;
89 mDragState.mUid = callerUid;
90 mDragState.mOriginalAlpha = alpha;
91 token = mDragState.mToken = new Binder();
92
93 // 5 second timeout for this window to actually begin the drag
94 service.mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
95 Message msg = service.mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
96 service.mH.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
Daichi Hironodf5cf622017-09-11 14:59:26 +090097 }
98
99 return token;
100 }
101
102 boolean performDrag(WindowManagerService service, IWindow window, IBinder dragToken,
103 int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
104 ClipData data) {
105 if (DEBUG_DRAG) {
106 Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
107 }
108
109 synchronized (service.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900110 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900111 Slog.w(TAG_WM, "No drag prepared");
112 throw new IllegalStateException("performDrag() without prepareDrag()");
113 }
114
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900115 if (dragToken != mDragState.mToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900116 Slog.w(TAG_WM, "Performing mismatched drag");
117 throw new IllegalStateException("performDrag() does not match prepareDrag()");
118 }
119
120 final WindowState callingWin = service.windowForClientLocked(null, window, false);
121 if (callingWin == null) {
122 Slog.w(TAG_WM, "Bad requesting window " + window);
123 return false; // !!! TODO: throw here?
124 }
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 service.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
131
132 // !!! TODO: extract the current touch (x, y) in screen coordinates. That
133 // will let us eliminate the (touchX,touchY) parameters from the API.
134
135 // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
136 // the actual drag event dispatch stuff in the dragstate
137
138 final DisplayContent displayContent = callingWin.getDisplayContent();
139 if (displayContent == null) {
140 return false;
141 }
142 Display display = displayContent.getDisplay();
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900143 mDragState.register(display);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900144 if (!service.mInputManager.transferTouchFocus(callingWin.mInputChannel,
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900145 mDragState.getInputChannel())) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900146 Slog.e(TAG_WM, "Unable to transfer touch focus");
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900147 mDragState.unregister();
148 mDragState.reset();
149 mDragState = null;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900150 return false;
151 }
152
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900153 mDragState.mDisplayContent = displayContent;
154 mDragState.mData = data;
155 mDragState.broadcastDragStartedLw(touchX, touchY);
156 mDragState.overridePointerIconLw(touchSource);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900157
158 // remember the thumb offsets for later
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900159 mDragState.mThumbOffsetX = thumbCenterX;
160 mDragState.mThumbOffsetY = thumbCenterY;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900161
162 // Make the surface visible at the proper location
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900163 final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900164 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
165 TAG_WM, ">>> OPEN TRANSACTION performDrag");
166 service.openSurfaceTransaction();
167 try {
168 surfaceControl.setPosition(touchX - thumbCenterX,
169 touchY - thumbCenterY);
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900170 surfaceControl.setLayer(mDragState.getDragLayerLw());
Daichi Hironodf5cf622017-09-11 14:59:26 +0900171 surfaceControl.setLayerStack(display.getLayerStack());
172 surfaceControl.show();
173 } finally {
174 service.closeSurfaceTransaction();
175 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
176 TAG_WM, "<<< CLOSE TRANSACTION performDrag");
177 }
178
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900179 mDragState.notifyLocationLw(touchX, touchY);
Daichi Hironodf5cf622017-09-11 14:59:26 +0900180 }
181
182 return true; // success!
183 }
184
185 void reportDropResult(WindowManagerService service, IWindow window, boolean consumed) {
186 IBinder token = window.asBinder();
187 if (DEBUG_DRAG) {
188 Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
189 }
190
191 synchronized (service.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900192 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900193 // Most likely the drop recipient ANRed and we ended the drag
194 // out from under it. Log the issue and move on.
195 Slog.w(TAG_WM, "Drop result given but no drag in progress");
196 return;
197 }
198
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900199 if (mDragState.mToken != token) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900200 // We're in a drag, but the wrong window has responded.
201 Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
202 throw new IllegalStateException("reportDropResult() by non-recipient");
203 }
204
205 // The right window has responded, even if it's no longer around,
206 // so be sure to halt the timeout even if the later WindowState
207 // lookup fails.
208 service.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
209 WindowState callingWin = service.windowForClientLocked(null, window, false);
210 if (callingWin == null) {
211 Slog.w(TAG_WM, "Bad result-reporting window " + window);
212 return; // !!! TODO: throw here?
213 }
214
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900215 mDragState.mDragResult = consumed;
216 mDragState.endDragLw();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900217 }
218 }
219
220 void cancelDragAndDrop(WindowManagerService service, IBinder dragToken) {
221 if (DEBUG_DRAG) {
222 Slog.d(TAG_WM, "cancelDragAndDrop");
223 }
224
225 synchronized (service.mWindowMap) {
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900226 if (mDragState == null) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900227 Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
228 throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
229 }
230
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900231 if (mDragState.mToken != dragToken) {
Daichi Hironodf5cf622017-09-11 14:59:26 +0900232 Slog.w(TAG_WM,
233 "cancelDragAndDrop() does not match prepareDrag()");
234 throw new IllegalStateException(
235 "cancelDragAndDrop() does not match prepareDrag()");
236 }
237
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900238 mDragState.mDragResult = false;
239 mDragState.cancelDragLw();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900240 }
241 }
242
243 void dragRecipientEntered(IWindow window) {
244 if (DEBUG_DRAG) {
245 Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
246 }
247 }
248
249 void dragRecipientExited(IWindow window) {
250 if (DEBUG_DRAG) {
251 Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
252 }
253 }
254
255 void handleMessage(WindowManagerService service, Message msg) {
256 switch (msg.what) {
257 case H.DRAG_START_TIMEOUT: {
258 IBinder win = (IBinder) msg.obj;
259 if (DEBUG_DRAG) {
260 Slog.w(TAG_WM, "Timeout starting drag by win " + win);
261 }
262 synchronized (service.mWindowMap) {
263 // !!! TODO: ANR the app that has failed to start the drag in time
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900264 if (mDragState != null) {
265 mDragState.unregister();
266 mDragState.reset();
267 mDragState = null;
Daichi Hironodf5cf622017-09-11 14:59:26 +0900268 }
269 }
270 break;
271 }
272
273 case H.DRAG_END_TIMEOUT: {
274 IBinder win = (IBinder) msg.obj;
275 if (DEBUG_DRAG) {
276 Slog.w(TAG_WM, "Timeout ending drag to win " + win);
277 }
278 synchronized (service.mWindowMap) {
279 // !!! TODO: ANR the drag-receiving app
Daichi Hirono76a26aa2017-09-11 15:13:38 +0900280 if (mDragState != null) {
281 mDragState.mDragResult = false;
282 mDragState.endDragLw();
Daichi Hironodf5cf622017-09-11 14:59:26 +0900283 }
284 }
285 break;
286 }
287
288 case H.TEAR_DOWN_DRAG_AND_DROP_INPUT: {
289 if (DEBUG_DRAG)
290 Slog.d(TAG_WM, "Drag ending; tearing down input channel");
291 DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
292 if (interceptor != null) {
293 synchronized (service.mWindowMap) {
294 interceptor.tearDown();
295 }
296 }
297 break;
298 }
299 }
300 }
301}