blob: 72fc180a6aae79e6aa5089e5fab395474feff532 [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;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080027import android.graphics.Region;
28import android.os.IBinder;
29import android.os.Message;
30import android.os.Process;
31import android.os.RemoteException;
32import android.util.Slog;
Craig Mautner39834192012-09-02 07:47:24 -070033import android.view.Display;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080034import android.view.DragEvent;
35import android.view.InputChannel;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080036import android.view.Surface;
37import android.view.View;
38import android.view.WindowManager;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080039
40import java.util.ArrayList;
41
42/**
43 * Drag/drop state
44 */
45class DragState {
46 final WindowManagerService mService;
47 IBinder mToken;
48 Surface mSurface;
49 int mFlags;
50 IBinder mLocalWin;
51 ClipData mData;
52 ClipDescription mDataDescription;
53 boolean mDragResult;
54 float mCurrentX, mCurrentY;
55 float mThumbOffsetX, mThumbOffsetY;
56 InputChannel mServerChannel, mClientChannel;
Jeff Brown32cbc38552011-12-01 14:01:49 -080057 DragInputEventReceiver mInputEventReceiver;
Jeff Brownea426552011-07-18 16:53:48 -070058 InputApplicationHandle mDragApplicationHandle;
59 InputWindowHandle mDragWindowHandle;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080060 WindowState mTargetWindow;
61 ArrayList<WindowState> mNotifiedWindows;
62 boolean mDragInProgress;
Craig Mautner39834192012-09-02 07:47:24 -070063 Display mDisplay;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080064
65 private final Region mTmpRegion = new Region();
66
67 DragState(WindowManagerService service, IBinder token, Surface surface,
68 int flags, IBinder localWin) {
69 mService = service;
70 mToken = token;
71 mSurface = surface;
72 mFlags = flags;
73 mLocalWin = localWin;
74 mNotifiedWindows = new ArrayList<WindowState>();
75 }
76
77 void reset() {
78 if (mSurface != null) {
79 mSurface.destroy();
80 }
81 mSurface = null;
82 mFlags = 0;
83 mLocalWin = null;
84 mToken = null;
85 mData = null;
86 mThumbOffsetX = mThumbOffsetY = 0;
87 mNotifiedWindows = null;
88 }
89
Craig Mautner3486b962012-08-27 11:31:24 -070090 /**
Craig Mautner39834192012-09-02 07:47:24 -070091 * @param display The Display that the window being dragged is on.
Craig Mautner3486b962012-08-27 11:31:24 -070092 */
Craig Mautner39834192012-09-02 07:47:24 -070093 void register(Display display) {
94 mDisplay = display;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -080095 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
96 if (mClientChannel != null) {
97 Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
98 } else {
99 InputChannel[] channels = InputChannel.openInputChannelPair("drag");
100 mServerChannel = channels[0];
101 mClientChannel = channels[1];
102 mService.mInputManager.registerInputChannel(mServerChannel, null);
Jeff Brown32cbc38552011-12-01 14:01:49 -0800103 mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
104 mService.mH.getLooper());
Jeff Brownea426552011-07-18 16:53:48 -0700105
106 mDragApplicationHandle = new InputApplicationHandle(null);
107 mDragApplicationHandle.name = "drag";
108 mDragApplicationHandle.dispatchingTimeoutNanos =
109 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
110
Craig Mautner59c00972012-07-30 12:10:24 -0700111 mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
Craig Mautner39834192012-09-02 07:47:24 -0700112 mDisplay.getDisplayId());
Jeff Brownea426552011-07-18 16:53:48 -0700113 mDragWindowHandle.name = "drag";
114 mDragWindowHandle.inputChannel = mServerChannel;
115 mDragWindowHandle.layer = getDragLayerLw();
116 mDragWindowHandle.layoutParamsFlags = 0;
117 mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
118 mDragWindowHandle.dispatchingTimeoutNanos =
119 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
120 mDragWindowHandle.visible = true;
121 mDragWindowHandle.canReceiveKeys = false;
122 mDragWindowHandle.hasFocus = true;
123 mDragWindowHandle.hasWallpaper = false;
124 mDragWindowHandle.paused = false;
125 mDragWindowHandle.ownerPid = Process.myPid();
126 mDragWindowHandle.ownerUid = Process.myUid();
127 mDragWindowHandle.inputFeatures = 0;
128 mDragWindowHandle.scaleFactor = 1.0f;
129
130 // The drag window cannot receive new touches.
131 mDragWindowHandle.touchableRegion.setEmpty();
132
133 // The drag window covers the entire display
134 mDragWindowHandle.frameLeft = 0;
135 mDragWindowHandle.frameTop = 0;
Craig Mautner39834192012-09-02 07:47:24 -0700136 Point p = new Point();
137 mDisplay.getRealSize(p);
138 mDragWindowHandle.frameRight = p.x;
139 mDragWindowHandle.frameBottom = p.y;
Jeff Brown01a98dd2011-09-20 15:08:29 -0700140
141 // Pause rotations before a drag.
142 if (WindowManagerService.DEBUG_ORIENTATION) {
143 Slog.d(WindowManagerService.TAG, "Pausing rotation during drag");
144 }
145 mService.pauseRotationLocked();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800146 }
147 }
148
149 void unregister() {
150 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "unregistering drag input channel");
151 if (mClientChannel == null) {
152 Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel");
153 } else {
154 mService.mInputManager.unregisterInputChannel(mServerChannel);
Jeff Brown32cbc38552011-12-01 14:01:49 -0800155 mInputEventReceiver.dispose();
156 mInputEventReceiver = null;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800157 mClientChannel.dispose();
158 mServerChannel.dispose();
159 mClientChannel = null;
160 mServerChannel = null;
Jeff Browncc4f7db2011-08-30 20:34:48 -0700161
162 mDragWindowHandle = null;
163 mDragApplicationHandle = null;
Jeff Brown01a98dd2011-09-20 15:08:29 -0700164
165 // Resume rotations after a drag.
166 if (WindowManagerService.DEBUG_ORIENTATION) {
167 Slog.d(WindowManagerService.TAG, "Resuming rotation after drag");
168 }
169 mService.resumeRotationLocked();
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800170 }
171 }
172
173 int getDragLayerLw() {
174 return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
175 * WindowManagerService.TYPE_LAYER_MULTIPLIER
176 + WindowManagerService.TYPE_LAYER_OFFSET;
177 }
178
179 /* call out to each visible window/session informing it about the drag
180 */
181 void broadcastDragStartedLw(final float touchX, final float touchY) {
182 // Cache a base-class instance of the clip metadata so that parceling
183 // works correctly in calling out to the apps.
184 mDataDescription = (mData != null) ? mData.getDescription() : null;
185 mNotifiedWindows.clear();
186 mDragInProgress = true;
187
188 if (WindowManagerService.DEBUG_DRAG) {
189 Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
190 }
191
Craig Mautner5c0e78c2012-09-12 16:45:36 -0700192 final WindowList windows = mService.getWindowListLocked(mDisplay);
Craig Mautner2d5618c2012-10-18 13:55:47 -0700193 if (windows != null) {
194 final int N = windows.size();
195 for (int i = 0; i < N; i++) {
196 sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
197 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800198 }
199 }
200
201 /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
202 * designated window is potentially a drop recipient. There are race situations
203 * around DRAG_ENDED broadcast, so we make sure that once we've declared that
204 * the drag has ended, we never send out another DRAG_STARTED for this drag action.
205 *
206 * This method clones the 'event' parameter if it's being delivered to the same
207 * process, so it's safe for the caller to call recycle() on the event afterwards.
208 */
209 private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
210 ClipDescription desc) {
211 // Don't actually send the event if the drag is supposed to be pinned
212 // to the originating window but 'newWin' is not that window.
213 if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
214 final IBinder winBinder = newWin.mClient.asBinder();
215 if (winBinder != mLocalWin) {
216 if (WindowManagerService.DEBUG_DRAG) {
217 Slog.d(WindowManagerService.TAG, "Not dispatching local DRAG_STARTED to " + newWin);
218 }
219 return;
220 }
221 }
222
223 if (mDragInProgress && newWin.isPotentialDragTarget()) {
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700224 DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
225 touchX, touchY, null, desc, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800226 try {
227 newWin.mClient.dispatchDragEvent(event);
228 // track each window that we've notified that the drag is starting
229 mNotifiedWindows.add(newWin);
230 } catch (RemoteException e) {
231 Slog.w(WindowManagerService.TAG, "Unable to drag-start window " + newWin);
232 } finally {
233 // if the callee was local, the dispatch has already recycled the event
234 if (Process.myPid() != newWin.mSession.mPid) {
235 event.recycle();
236 }
237 }
238 }
239 }
240
241 /* helper - construct and send a DRAG_STARTED event only if the window has not
242 * previously been notified, i.e. it became visible after the drag operation
243 * was begun. This is a rare case.
244 */
245 void sendDragStartedIfNeededLw(WindowState newWin) {
246 if (mDragInProgress) {
247 // If we have sent the drag-started, we needn't do so again
248 for (WindowState ws : mNotifiedWindows) {
249 if (ws == newWin) {
250 return;
251 }
252 }
253 if (WindowManagerService.DEBUG_DRAG) {
254 Slog.d(WindowManagerService.TAG, "need to send DRAG_STARTED to new window " + newWin);
255 }
256 sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
257 }
258 }
259
260 void broadcastDragEndedLw() {
261 if (WindowManagerService.DEBUG_DRAG) {
262 Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
263 }
264 DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
265 0, 0, null, null, null, mDragResult);
266 for (WindowState ws: mNotifiedWindows) {
267 try {
268 ws.mClient.dispatchDragEvent(evt);
269 } catch (RemoteException e) {
270 Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws);
271 }
272 }
273 mNotifiedWindows.clear();
274 mDragInProgress = false;
275 evt.recycle();
276 }
277
278 void endDragLw() {
279 mService.mDragState.broadcastDragEndedLw();
280
281 // stop intercepting input
282 mService.mDragState.unregister();
283 mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
284
285 // free our resources and drop all the object references
286 mService.mDragState.reset();
287 mService.mDragState = null;
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800288 }
289
290 void notifyMoveLw(float x, float y) {
291 final int myPid = Process.myPid();
292
293 // Move the surface to the given touch
Dianne Hackborn36991742011-10-11 21:35:26 -0700294 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
295 WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800296 Surface.openTransaction();
297 try {
Dianne Hackbornd040edb2011-08-31 12:47:58 -0700298 mSurface.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800299 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DRAG "
300 + mSurface + ": pos=(" +
301 (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
302 } finally {
303 Surface.closeTransaction();
Dianne Hackborn36991742011-10-11 21:35:26 -0700304 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
305 WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800306 }
307
308 // Tell the affected window
309 WindowState touchedWin = getTouchedWinAtPointLw(x, y);
310 if (touchedWin == null) {
311 if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "No touched win at x=" + x + " y=" + y);
312 return;
313 }
314 if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
315 final IBinder touchedBinder = touchedWin.mClient.asBinder();
316 if (touchedBinder != mLocalWin) {
317 // This drag is pinned only to the originating window, but the drag
318 // point is outside that window. Pretend it's over empty space.
319 touchedWin = null;
320 }
321 }
322 try {
323 // have we dragged over a new window?
324 if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
325 if (WindowManagerService.DEBUG_DRAG) {
326 Slog.d(WindowManagerService.TAG, "sending DRAG_EXITED to " + mTargetWindow);
327 }
328 // force DRAG_EXITED_EVENT if appropriate
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700329 DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
330 x, y, null, null, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800331 mTargetWindow.mClient.dispatchDragEvent(evt);
332 if (myPid != mTargetWindow.mSession.mPid) {
333 evt.recycle();
334 }
335 }
336 if (touchedWin != null) {
337 if (false && WindowManagerService.DEBUG_DRAG) {
338 Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin);
339 }
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700340 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
341 x, y, null, null, null, false);
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800342 touchedWin.mClient.dispatchDragEvent(evt);
343 if (myPid != touchedWin.mSession.mPid) {
344 evt.recycle();
345 }
346 }
347 } catch (RemoteException e) {
348 Slog.w(WindowManagerService.TAG, "can't send drag notification to windows");
349 }
350 mTargetWindow = touchedWin;
351 }
352
353 // Tell the drop target about the data. Returns 'true' if we can immediately
354 // dispatch the global drag-ended message, 'false' if we need to wait for a
355 // result from the recipient.
356 boolean notifyDropLw(float x, float y) {
357 WindowState touchedWin = getTouchedWinAtPointLw(x, y);
358 if (touchedWin == null) {
359 // "drop" outside a valid window -- no recipient to apply a
360 // timeout to, and we can send the drag-ended message immediately.
361 mDragResult = false;
362 return true;
363 }
364
365 if (WindowManagerService.DEBUG_DRAG) {
366 Slog.d(WindowManagerService.TAG, "sending DROP to " + touchedWin);
367 }
368 final int myPid = Process.myPid();
369 final IBinder token = touchedWin.mClient.asBinder();
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700370 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800371 null, null, mData, false);
372 try {
373 touchedWin.mClient.dispatchDragEvent(evt);
374
375 // 5 second timeout for this window to respond to the drop
376 mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token);
377 Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
378 mService.mH.sendMessageDelayed(msg, 5000);
379 } catch (RemoteException e) {
380 Slog.w(WindowManagerService.TAG, "can't send drop notification to win " + touchedWin);
381 return true;
382 } finally {
383 if (myPid != touchedWin.mSession.mPid) {
384 evt.recycle();
385 }
386 }
387 mToken = token;
388 return false;
389 }
390
391 // Find the visible, touch-deliverable window under the given point
392 private WindowState getTouchedWinAtPointLw(float xf, float yf) {
393 WindowState touchedWin = null;
394 final int x = (int) xf;
395 final int y = (int) yf;
Craig Mautner59c00972012-07-30 12:10:24 -0700396
Craig Mautner5c0e78c2012-09-12 16:45:36 -0700397 final WindowList windows = mService.getWindowListLocked(mDisplay);
Craig Mautner2d5618c2012-10-18 13:55:47 -0700398 if (windows == null) {
399 return null;
400 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800401 final int N = windows.size();
402 for (int i = N - 1; i >= 0; i--) {
403 WindowState child = windows.get(i);
404 final int flags = child.mAttrs.flags;
405 if (!child.isVisibleLw()) {
406 // not visible == don't tell about drags
407 continue;
408 }
409 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
410 // not touchable == don't tell about drags
411 continue;
412 }
413
414 child.getTouchableRegion(mTmpRegion);
415
416 final int touchFlags = flags &
417 (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
418 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
419 if (mTmpRegion.contains(x, y) || touchFlags == 0) {
420 // Found it
421 touchedWin = child;
422 break;
423 }
424 }
425
426 return touchedWin;
427 }
Dianne Hackbornffb3d932011-05-17 17:44:51 -0700428
429 private static DragEvent obtainDragEvent(WindowState win, int action,
430 float x, float y, Object localState,
431 ClipDescription description, ClipData data, boolean result) {
432 float winX = x - win.mFrame.left;
433 float winY = y - win.mFrame.top;
434 if (win.mEnforceSizeCompat) {
435 winX *= win.mGlobalScale;
436 winY *= win.mGlobalScale;
437 }
438 return DragEvent.obtain(action, winX, winY, localState, description, data, result);
439 }
Dianne Hackborn6e1eb762011-02-17 16:07:28 -0800440}