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