blob: 1a125d4bfae2716b6bc473e0c7c7a208f497798c [file] [log] [blame]
Dianne Hackborn6e1eb762011-02-17 16:07:28 -08001/*
2 * Copyright (C) 2011 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
Jeff Brown4532e612012-04-05 14:27:12 -070019import com.android.server.input.InputApplicationHandle;
20import com.android.server.input.InputWindowHandle;
Jeff Brown32cbc38552011-12-01 14:01:49 -080021import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080022import com.android.server.wm.WindowManagerService.H;
23
24import android.content.ClipData;
25import android.content.ClipDescription;
Craig Mautner39834192012-09-02 07:47:24 -070026import android.graphics.Point;
Vladislav Kaznacheevc7ad0a02015-03-19 10:13:31 -070027import android.graphics.Rect;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080028import android.graphics.Region;
29import android.os.IBinder;
30import android.os.Message;
31import android.os.Process;
32import android.os.RemoteException;
33import android.util.Slog;
Craig Mautner39834192012-09-02 07:47:24 -070034import android.view.Display;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080035import android.view.DragEvent;
36import android.view.InputChannel;
Mathias Agopian3866f0d2013-02-11 22:08:48 -080037import android.view.SurfaceControl;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080038import android.view.View;
39import android.view.WindowManager;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080040
41import java.util.ArrayList;
42
43/**
44 * Drag/drop state
45 */
46class DragState {
47 final WindowManagerService mService;
48 IBinder mToken;
Mathias Agopian29479eb2013-02-14 14:36:04 -080049 SurfaceControl mSurfaceControl;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080050 int mFlags;
51 IBinder mLocalWin;
52 ClipData mData;
53 ClipDescription mDataDescription;
54 boolean mDragResult;
55 float mCurrentX, mCurrentY;
56 float mThumbOffsetX, mThumbOffsetY;
57 InputChannel mServerChannel, mClientChannel;
Jeff Brown32cbc38552011-12-01 14:01:49 -080058 DragInputEventReceiver mInputEventReceiver;
Jeff Brownea426552011-07-18 16:53:48 -070059 InputApplicationHandle mDragApplicationHandle;
60 InputWindowHandle mDragWindowHandle;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080061 WindowState mTargetWindow;
62 ArrayList<WindowState> mNotifiedWindows;
63 boolean mDragInProgress;
Craig Mautner39834192012-09-02 07:47:24 -070064 Display mDisplay;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080065
66 private final Region mTmpRegion = new Region();
Vladislav Kaznacheevc7ad0a02015-03-19 10:13:31 -070067 private final Rect mTmpRect = new Rect();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080068
Mathias Agopian3866f0d2013-02-11 22:08:48 -080069 DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080070 int flags, IBinder localWin) {
71 mService = service;
72 mToken = token;
Mathias Agopian29479eb2013-02-14 14:36:04 -080073 mSurfaceControl = surface;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080074 mFlags = flags;
75 mLocalWin = localWin;
76 mNotifiedWindows = new ArrayList<WindowState>();
77 }
78
79 void reset() {
Mathias Agopian29479eb2013-02-14 14:36:04 -080080 if (mSurfaceControl != null) {
81 mSurfaceControl.destroy();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080082 }
Mathias Agopian29479eb2013-02-14 14:36:04 -080083 mSurfaceControl = null;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080084 mFlags = 0;
85 mLocalWin = null;
86 mToken = null;
87 mData = null;
88 mThumbOffsetX = mThumbOffsetY = 0;
89 mNotifiedWindows = null;
90 }
91
Craig Mautner3486b962012-08-27 11:31:24 -070092 /**
Craig Mautner39834192012-09-02 07:47:24 -070093 * @param display The Display that the window being dragged is on.
Craig Mautner3486b962012-08-27 11:31:24 -070094 */
Craig Mautner39834192012-09-02 07:47:24 -070095 void register(Display display) {
96 mDisplay = display;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080097 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
98 if (mClientChannel != null) {
99 Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
100 } else {
101 InputChannel[] channels = InputChannel.openInputChannelPair("drag");
102 mServerChannel = channels[0];
103 mClientChannel = channels[1];
104 mService.mInputManager.registerInputChannel(mServerChannel, null);
Jeff Brown32cbc38552011-12-01 14:01:49 -0800105 mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
106 mService.mH.getLooper());
Jeff Brownea426552011-07-18 16:53:48 -0700107
108 mDragApplicationHandle = new InputApplicationHandle(null);
109 mDragApplicationHandle.name = "drag";
110 mDragApplicationHandle.dispatchingTimeoutNanos =
111 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
112
Craig Mautner59c00972012-07-30 12:10:24 -0700113 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
Craig Mautner39834192012-09-02 07:47:24 -0700114 mDisplay.getDisplayId());
Jeff Brownea426552011-07-18 16:53:48 -0700115 mDragWindowHandle.name = "drag";
116 mDragWindowHandle.inputChannel = mServerChannel;
117 mDragWindowHandle.layer = getDragLayerLw();
118 mDragWindowHandle.layoutParamsFlags = 0;
119 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
120 mDragWindowHandle.dispatchingTimeoutNanos =
121 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
122 mDragWindowHandle.visible = true;
123 mDragWindowHandle.canReceiveKeys = false;
124 mDragWindowHandle.hasFocus = true;
125 mDragWindowHandle.hasWallpaper = false;
126 mDragWindowHandle.paused = false;
127 mDragWindowHandle.ownerPid = Process.myPid();
128 mDragWindowHandle.ownerUid = Process.myUid();
129 mDragWindowHandle.inputFeatures = 0;
130 mDragWindowHandle.scaleFactor = 1.0f;
131
132 // The drag window cannot receive new touches.
133 mDragWindowHandle.touchableRegion.setEmpty();
134
135 // The drag window covers the entire display
136 mDragWindowHandle.frameLeft = 0;
137 mDragWindowHandle.frameTop = 0;
Craig Mautner39834192012-09-02 07:47:24 -0700138 Point p = new Point();
139 mDisplay.getRealSize(p);
140 mDragWindowHandle.frameRight = p.x;
141 mDragWindowHandle.frameBottom = p.y;
Jeff Brown01a98dd2011-09-20 15:08:29 -0700142
143 // Pause rotations before a drag.
144 if (WindowManagerService.DEBUG_ORIENTATION) {
145 Slog.d(WindowManagerService.TAG, "Pausing rotation during drag");
146 }
147 mService.pauseRotationLocked();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800148 }
149 }
150
151 void unregister() {
152 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "unregistering drag input channel");
153 if (mClientChannel == null) {
154 Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel");
155 } else {
156 mService.mInputManager.unregisterInputChannel(mServerChannel);
Jeff Brown32cbc38552011-12-01 14:01:49 -0800157 mInputEventReceiver.dispose();
158 mInputEventReceiver = null;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800159 mClientChannel.dispose();
160 mServerChannel.dispose();
161 mClientChannel = null;
162 mServerChannel = null;
Jeff Browncc4f7db2011-08-30 20:34:48 -0700163
164 mDragWindowHandle = null;
165 mDragApplicationHandle = null;
Jeff Brown01a98dd2011-09-20 15:08:29 -0700166
167 // Resume rotations after a drag.
168 if (WindowManagerService.DEBUG_ORIENTATION) {
169 Slog.d(WindowManagerService.TAG, "Resuming rotation after drag");
170 }
171 mService.resumeRotationLocked();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800172 }
173 }
174
175 int getDragLayerLw() {
176 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
177 * WindowManagerService.TYPE_LAYER_MULTIPLIER
178 + WindowManagerService.TYPE_LAYER_OFFSET;
179 }
180
181 /* call out to each visible window/session informing it about the drag
182 */
183 void broadcastDragStartedLw(final float touchX, final float touchY) {
184 // Cache a base-class instance of the clip metadata so that parceling
185 // works correctly in calling out to the apps.
186 mDataDescription = (mData != null) ? mData.getDescription() : null;
187 mNotifiedWindows.clear();
188 mDragInProgress = true;
189
190 if (WindowManagerService.DEBUG_DRAG) {
191 Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
192 }
193
Craig Mautner5c0e78c2012-09-12 16:45:36 -0700194 final WindowList windows = mService.getWindowListLocked(mDisplay);
Craig Mautner2d5618c2012-10-18 13:55:47 -0700195 if (windows != null) {
196 final int N = windows.size();
197 for (int i = 0; i < N; i++) {
198 sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
199 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800200 }
201 }
202
203 /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
204 * designated window is potentially a drop recipient. There are race situations
205 * around DRAG_ENDED broadcast, so we make sure that once we've declared that
206 * the drag has ended, we never send out another DRAG_STARTED for this drag action.
207 *
208 * This method clones the 'event' parameter if it's being delivered to the same
209 * process, so it's safe for the caller to call recycle() on the event afterwards.
210 */
211 private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
212 ClipDescription desc) {
213 // Don't actually send the event if the drag is supposed to be pinned
214 // to the originating window but 'newWin' is not that window.
215 if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
216 final IBinder winBinder = newWin.mClient.asBinder();
217 if (winBinder != mLocalWin) {
218 if (WindowManagerService.DEBUG_DRAG) {
219 Slog.d(WindowManagerService.TAG, "Not dispatching local DRAG_STARTED to " + newWin);
220 }
221 return;
222 }
223 }
224
225 if (mDragInProgress && newWin.isPotentialDragTarget()) {
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700226 DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
227 touchX, touchY, null, desc, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800228 try {
229 newWin.mClient.dispatchDragEvent(event);
230 // track each window that we've notified that the drag is starting
231 mNotifiedWindows.add(newWin);
232 } catch (RemoteException e) {
233 Slog.w(WindowManagerService.TAG, "Unable to drag-start window " + newWin);
234 } finally {
235 // if the callee was local, the dispatch has already recycled the event
236 if (Process.myPid() != newWin.mSession.mPid) {
237 event.recycle();
238 }
239 }
240 }
241 }
242
243 /* helper - construct and send a DRAG_STARTED event only if the window has not
244 * previously been notified, i.e. it became visible after the drag operation
245 * was begun. This is a rare case.
246 */
247 void sendDragStartedIfNeededLw(WindowState newWin) {
248 if (mDragInProgress) {
249 // If we have sent the drag-started, we needn't do so again
250 for (WindowState ws : mNotifiedWindows) {
251 if (ws == newWin) {
252 return;
253 }
254 }
255 if (WindowManagerService.DEBUG_DRAG) {
256 Slog.d(WindowManagerService.TAG, "need to send DRAG_STARTED to new window " + newWin);
257 }
258 sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
259 }
260 }
261
262 void broadcastDragEndedLw() {
263 if (WindowManagerService.DEBUG_DRAG) {
264 Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
265 }
266 DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
267 0, 0, null, null, null, mDragResult);
268 for (WindowState ws: mNotifiedWindows) {
269 try {
270 ws.mClient.dispatchDragEvent(evt);
271 } catch (RemoteException e) {
272 Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws);
273 }
274 }
275 mNotifiedWindows.clear();
276 mDragInProgress = false;
277 evt.recycle();
278 }
279
280 void endDragLw() {
281 mService.mDragState.broadcastDragEndedLw();
282
283 // stop intercepting input
284 mService.mDragState.unregister();
285 mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
286
287 // free our resources and drop all the object references
288 mService.mDragState.reset();
289 mService.mDragState = null;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800290 }
291
292 void notifyMoveLw(float x, float y) {
293 final int myPid = Process.myPid();
294
295 // Move the surface to the given touch
Dianne Hackborn36991742011-10-11 21:35:26 -0700296 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
297 WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
Mathias Agopian3866f0d2013-02-11 22:08:48 -0800298 SurfaceControl.openTransaction();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800299 try {
Mathias Agopian29479eb2013-02-14 14:36:04 -0800300 mSurfaceControl.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800301 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DRAG "
Mathias Agopian29479eb2013-02-14 14:36:04 -0800302 + mSurfaceControl + ": pos=(" +
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800303 (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
304 } finally {
Mathias Agopian3866f0d2013-02-11 22:08:48 -0800305 SurfaceControl.closeTransaction();
Dianne Hackborn36991742011-10-11 21:35:26 -0700306 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
307 WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800308 }
309
310 // Tell the affected window
311 WindowState touchedWin = getTouchedWinAtPointLw(x, y);
312 if (touchedWin == null) {
313 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "No touched win at x=" + x + " y=" + y);
314 return;
315 }
316 if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
317 final IBinder touchedBinder = touchedWin.mClient.asBinder();
318 if (touchedBinder != mLocalWin) {
319 // This drag is pinned only to the originating window, but the drag
320 // point is outside that window. Pretend it's over empty space.
321 touchedWin = null;
322 }
323 }
324 try {
325 // have we dragged over a new window?
326 if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
327 if (WindowManagerService.DEBUG_DRAG) {
328 Slog.d(WindowManagerService.TAG, "sending DRAG_EXITED to " + mTargetWindow);
329 }
330 // force DRAG_EXITED_EVENT if appropriate
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700331 DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
332 x, y, null, null, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800333 mTargetWindow.mClient.dispatchDragEvent(evt);
334 if (myPid != mTargetWindow.mSession.mPid) {
335 evt.recycle();
336 }
337 }
338 if (touchedWin != null) {
339 if (false && WindowManagerService.DEBUG_DRAG) {
340 Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin);
341 }
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700342 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
343 x, y, null, null, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800344 touchedWin.mClient.dispatchDragEvent(evt);
345 if (myPid != touchedWin.mSession.mPid) {
346 evt.recycle();
347 }
348 }
349 } catch (RemoteException e) {
350 Slog.w(WindowManagerService.TAG, "can't send drag notification to windows");
351 }
352 mTargetWindow = touchedWin;
353 }
354
355 // Tell the drop target about the data. Returns 'true' if we can immediately
356 // dispatch the global drag-ended message, 'false' if we need to wait for a
357 // result from the recipient.
358 boolean notifyDropLw(float x, float y) {
359 WindowState touchedWin = getTouchedWinAtPointLw(x, y);
360 if (touchedWin == null) {
361 // "drop" outside a valid window -- no recipient to apply a
362 // timeout to, and we can send the drag-ended message immediately.
363 mDragResult = false;
364 return true;
365 }
366
367 if (WindowManagerService.DEBUG_DRAG) {
368 Slog.d(WindowManagerService.TAG, "sending DROP to " + touchedWin);
369 }
370 final int myPid = Process.myPid();
371 final IBinder token = touchedWin.mClient.asBinder();
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700372 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800373 null, null, mData, false);
374 try {
375 touchedWin.mClient.dispatchDragEvent(evt);
376
377 // 5 second timeout for this window to respond to the drop
378 mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token);
379 Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
380 mService.mH.sendMessageDelayed(msg, 5000);
381 } catch (RemoteException e) {
382 Slog.w(WindowManagerService.TAG, "can't send drop notification to win " + touchedWin);
383 return true;
384 } finally {
385 if (myPid != touchedWin.mSession.mPid) {
386 evt.recycle();
387 }
388 }
389 mToken = token;
390 return false;
391 }
392
393 // Find the visible, touch-deliverable window under the given point
394 private WindowState getTouchedWinAtPointLw(float xf, float yf) {
395 WindowState touchedWin = null;
396 final int x = (int) xf;
397 final int y = (int) yf;
Craig Mautner59c00972012-07-30 12:10:24 -0700398
Craig Mautner5c0e78c2012-09-12 16:45:36 -0700399 final WindowList windows = mService.getWindowListLocked(mDisplay);
Craig Mautner2d5618c2012-10-18 13:55:47 -0700400 if (windows == null) {
401 return null;
402 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800403 final int N = windows.size();
404 for (int i = N - 1; i >= 0; i--) {
405 WindowState child = windows.get(i);
406 final int flags = child.mAttrs.flags;
407 if (!child.isVisibleLw()) {
408 // not visible == don't tell about drags
409 continue;
410 }
411 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
412 // not touchable == don't tell about drags
413 continue;
414 }
415
Vladislav Kaznacheevc7ad0a02015-03-19 10:13:31 -0700416 child.getStackBounds(mTmpRect);
417 if (!mTmpRect.contains(x, y)) {
418 // outside of this window's activity stack == don't tell about drags
419 continue;
420 }
421
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800422 child.getTouchableRegion(mTmpRegion);
423
424 final int touchFlags = flags &
425 (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
426 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
427 if (mTmpRegion.contains(x, y) || touchFlags == 0) {
428 // Found it
429 touchedWin = child;
430 break;
431 }
432 }
433
434 return touchedWin;
435 }
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700436
437 private static DragEvent obtainDragEvent(WindowState win, int action,
438 float x, float y, Object localState,
439 ClipDescription description, ClipData data, boolean result) {
440 float winX = x - win.mFrame.left;
441 float winY = y - win.mFrame.top;
442 if (win.mEnforceSizeCompat) {
443 winX *= win.mGlobalScale;
444 winY *= win.mGlobalScale;
445 }
446 return DragEvent.obtain(action, winX, winY, localState, description, data, result);
447 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800448}