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