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 android.view.IWindowId;
20 import android.view.IWindowSessionCallback;
21 import com.android.internal.view.IInputContext;
22 import com.android.internal.view.IInputMethodClient;
23 import com.android.internal.view.IInputMethodManager;
24 import com.android.server.wm.WindowManagerService.H;
25 
26 import android.content.ClipData;
27 import android.content.Context;
28 import android.content.res.Configuration;
29 import android.graphics.Rect;
30 import android.graphics.Region;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.IBinder;
34 import android.os.Parcel;
35 import android.os.Process;
36 import android.os.RemoteException;
37 import android.os.ServiceManager;
38 import android.os.UserHandle;
39 import android.util.Slog;
40 import android.view.Display;
41 import android.view.IWindow;
42 import android.view.IWindowSession;
43 import android.view.InputChannel;
44 import android.view.Surface;
45 import android.view.SurfaceControl;
46 import android.view.SurfaceSession;
47 import android.view.WindowManager;
48 
49 import java.io.PrintWriter;
50 
51 /**
52  * This class represents an active client session.  There is generally one
53  * Session object per process that is interacting with the window manager.
54  */
55 final class Session extends IWindowSession.Stub
56         implements IBinder.DeathRecipient {
57     final WindowManagerService mService;
58     final IWindowSessionCallback mCallback;
59     final IInputMethodClient mClient;
60     final IInputContext mInputContext;
61     final int mUid;
62     final int mPid;
63     final String mStringName;
64     SurfaceSession mSurfaceSession;
65     int mNumWindow = 0;
66     boolean mClientDead = false;
67     float mLastReportedAnimatorScale;
68 
Session(WindowManagerService service, IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext)69     public Session(WindowManagerService service, IWindowSessionCallback callback,
70             IInputMethodClient client, IInputContext inputContext) {
71         mService = service;
72         mCallback = callback;
73         mClient = client;
74         mInputContext = inputContext;
75         mUid = Binder.getCallingUid();
76         mPid = Binder.getCallingPid();
77         mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
78         StringBuilder sb = new StringBuilder();
79         sb.append("Session{");
80         sb.append(Integer.toHexString(System.identityHashCode(this)));
81         sb.append(" ");
82         sb.append(mPid);
83         if (mUid < Process.FIRST_APPLICATION_UID) {
84             sb.append(":");
85             sb.append(mUid);
86         } else {
87             sb.append(":u");
88             sb.append(UserHandle.getUserId(mUid));
89             sb.append('a');
90             sb.append(UserHandle.getAppId(mUid));
91         }
92         sb.append("}");
93         mStringName = sb.toString();
94 
95         synchronized (mService.mWindowMap) {
96             if (mService.mInputMethodManager == null && mService.mHaveInputMethods) {
97                 IBinder b = ServiceManager.getService(
98                         Context.INPUT_METHOD_SERVICE);
99                 mService.mInputMethodManager = IInputMethodManager.Stub.asInterface(b);
100             }
101         }
102         long ident = Binder.clearCallingIdentity();
103         try {
104             // Note: it is safe to call in to the input method manager
105             // here because we are not holding our lock.
106             if (mService.mInputMethodManager != null) {
107                 mService.mInputMethodManager.addClient(client, inputContext,
108                         mUid, mPid);
109             } else {
110                 client.setUsingInputMethod(false);
111             }
112             client.asBinder().linkToDeath(this, 0);
113         } catch (RemoteException e) {
114             // The caller has died, so we can just forget about this.
115             try {
116                 if (mService.mInputMethodManager != null) {
117                     mService.mInputMethodManager.removeClient(client);
118                 }
119             } catch (RemoteException ee) {
120             }
121         } finally {
122             Binder.restoreCallingIdentity(ident);
123         }
124     }
125 
126     @Override
onTransact(int code, Parcel data, Parcel reply, int flags)127     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
128             throws RemoteException {
129         try {
130             return super.onTransact(code, data, reply, flags);
131         } catch (RuntimeException e) {
132             // Log all 'real' exceptions thrown to the caller
133             if (!(e instanceof SecurityException)) {
134                 Slog.wtf(WindowManagerService.TAG, "Window Session Crash", e);
135             }
136             throw e;
137         }
138     }
139 
binderDied()140     public void binderDied() {
141         // Note: it is safe to call in to the input method manager
142         // here because we are not holding our lock.
143         try {
144             if (mService.mInputMethodManager != null) {
145                 mService.mInputMethodManager.removeClient(mClient);
146             }
147         } catch (RemoteException e) {
148         }
149         synchronized(mService.mWindowMap) {
150             mClient.asBinder().unlinkToDeath(this, 0);
151             mClientDead = true;
152             killSessionLocked();
153         }
154     }
155 
156     @Override
add(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel)157     public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
158             int viewVisibility, Rect outContentInsets, Rect outStableInsets,
159             InputChannel outInputChannel) {
160         return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
161                 outContentInsets, outStableInsets, outInputChannel);
162     }
163 
164     @Override
addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel)165     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
166             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
167             InputChannel outInputChannel) {
168         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
169                 outContentInsets, outStableInsets, outInputChannel);
170     }
171 
172     @Override
addWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, Rect outStableInsets)173     public int addWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
174             int viewVisibility, Rect outContentInsets, Rect outStableInsets) {
175         return addToDisplayWithoutInputChannel(window, seq, attrs, viewVisibility,
176                 Display.DEFAULT_DISPLAY, outContentInsets, outStableInsets);
177     }
178 
179     @Override
addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets)180     public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
181             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
182         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
183             outContentInsets, outStableInsets, null);
184     }
185 
remove(IWindow window)186     public void remove(IWindow window) {
187         mService.removeWindow(this, window);
188     }
189 
relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig, Surface outSurface)190     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
191             int requestedWidth, int requestedHeight, int viewFlags,
192             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
193             Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
194             Surface outSurface) {
195         if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from "
196                 + Binder.getCallingPid());
197         int res = mService.relayoutWindow(this, window, seq, attrs,
198                 requestedWidth, requestedHeight, viewFlags, flags,
199                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
200                 outStableInsets, outConfig, outSurface);
201         if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to "
202                 + Binder.getCallingPid());
203         return res;
204     }
205 
performDeferredDestroy(IWindow window)206     public void performDeferredDestroy(IWindow window) {
207         mService.performDeferredDestroyWindow(this, window);
208     }
209 
outOfMemory(IWindow window)210     public boolean outOfMemory(IWindow window) {
211         return mService.outOfMemoryWindow(this, window);
212     }
213 
setTransparentRegion(IWindow window, Region region)214     public void setTransparentRegion(IWindow window, Region region) {
215         mService.setTransparentRegionWindow(this, window, region);
216     }
217 
setInsets(IWindow window, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableArea)218     public void setInsets(IWindow window, int touchableInsets,
219             Rect contentInsets, Rect visibleInsets, Region touchableArea) {
220         mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
221                 visibleInsets, touchableArea);
222     }
223 
getDisplayFrame(IWindow window, Rect outDisplayFrame)224     public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
225         mService.getWindowDisplayFrame(this, window, outDisplayFrame);
226     }
227 
finishDrawing(IWindow window)228     public void finishDrawing(IWindow window) {
229         if (WindowManagerService.localLOGV) Slog.v(
230             WindowManagerService.TAG, "IWindow finishDrawing called for " + window);
231         mService.finishDrawingWindow(this, window);
232     }
233 
setInTouchMode(boolean mode)234     public void setInTouchMode(boolean mode) {
235         synchronized(mService.mWindowMap) {
236             mService.mInTouchMode = mode;
237         }
238     }
239 
getInTouchMode()240     public boolean getInTouchMode() {
241         synchronized(mService.mWindowMap) {
242             return mService.mInTouchMode;
243         }
244     }
245 
performHapticFeedback(IWindow window, int effectId, boolean always)246     public boolean performHapticFeedback(IWindow window, int effectId,
247             boolean always) {
248         synchronized(mService.mWindowMap) {
249             long ident = Binder.clearCallingIdentity();
250             try {
251                 return mService.mPolicy.performHapticFeedbackLw(
252                         mService.windowForClientLocked(this, window, true),
253                         effectId, always);
254             } finally {
255                 Binder.restoreCallingIdentity(ident);
256             }
257         }
258     }
259 
260     /* Drag/drop */
prepareDrag(IWindow window, int flags, int width, int height, Surface outSurface)261     public IBinder prepareDrag(IWindow window, int flags,
262             int width, int height, Surface outSurface) {
263         return mService.prepareDragSurface(window, mSurfaceSession, flags,
264                 width, height, outSurface);
265     }
266 
performDrag(IWindow window, IBinder dragToken, float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data)267     public boolean performDrag(IWindow window, IBinder dragToken,
268             float touchX, float touchY, float thumbCenterX, float thumbCenterY,
269             ClipData data) {
270         if (WindowManagerService.DEBUG_DRAG) {
271             Slog.d(WindowManagerService.TAG, "perform drag: win=" + window + " data=" + data);
272         }
273 
274         synchronized (mService.mWindowMap) {
275             if (mService.mDragState == null) {
276                 Slog.w(WindowManagerService.TAG, "No drag prepared");
277                 throw new IllegalStateException("performDrag() without prepareDrag()");
278             }
279 
280             if (dragToken != mService.mDragState.mToken) {
281                 Slog.w(WindowManagerService.TAG, "Performing mismatched drag");
282                 throw new IllegalStateException("performDrag() does not match prepareDrag()");
283             }
284 
285             WindowState callingWin = mService.windowForClientLocked(null, window, false);
286             if (callingWin == null) {
287                 Slog.w(WindowManagerService.TAG, "Bad requesting window " + window);
288                 return false;  // !!! TODO: throw here?
289             }
290 
291             // !!! TODO: if input is not still focused on the initiating window, fail
292             // the drag initiation (e.g. an alarm window popped up just as the application
293             // called performDrag()
294 
295             mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
296 
297             // !!! TODO: extract the current touch (x, y) in screen coordinates.  That
298             // will let us eliminate the (touchX,touchY) parameters from the API.
299 
300             // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
301             // the actual drag event dispatch stuff in the dragstate
302 
303             final DisplayContent displayContent = callingWin.getDisplayContent();
304             if (displayContent == null) {
305                return false;
306             }
307             Display display = displayContent.getDisplay();
308             mService.mDragState.register(display);
309             mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
310             if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
311                     mService.mDragState.mServerChannel)) {
312                 Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus");
313                 mService.mDragState.unregister();
314                 mService.mDragState = null;
315                 mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
316                 return false;
317             }
318 
319             mService.mDragState.mData = data;
320             mService.mDragState.mCurrentX = touchX;
321             mService.mDragState.mCurrentY = touchY;
322             mService.mDragState.broadcastDragStartedLw(touchX, touchY);
323 
324             // remember the thumb offsets for later
325             mService.mDragState.mThumbOffsetX = thumbCenterX;
326             mService.mDragState.mThumbOffsetY = thumbCenterY;
327 
328             // Make the surface visible at the proper location
329             final SurfaceControl surfaceControl = mService.mDragState.mSurfaceControl;
330             if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
331                     WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
332             SurfaceControl.openTransaction();
333             try {
334                 surfaceControl.setPosition(touchX - thumbCenterX,
335                         touchY - thumbCenterY);
336                 surfaceControl.setAlpha(.7071f);
337                 surfaceControl.setLayer(mService.mDragState.getDragLayerLw());
338                 surfaceControl.setLayerStack(display.getLayerStack());
339                 surfaceControl.show();
340             } finally {
341                 SurfaceControl.closeTransaction();
342                 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
343                         WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag");
344             }
345         }
346 
347         return true;    // success!
348     }
349 
reportDropResult(IWindow window, boolean consumed)350     public void reportDropResult(IWindow window, boolean consumed) {
351         IBinder token = window.asBinder();
352         if (WindowManagerService.DEBUG_DRAG) {
353             Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token);
354         }
355 
356         synchronized (mService.mWindowMap) {
357             long ident = Binder.clearCallingIdentity();
358             try {
359                 if (mService.mDragState == null) {
360                     // Most likely the drop recipient ANRed and we ended the drag
361                     // out from under it.  Log the issue and move on.
362                     Slog.w(WindowManagerService.TAG, "Drop result given but no drag in progress");
363                     return;
364                 }
365 
366                 if (mService.mDragState.mToken != token) {
367                     // We're in a drag, but the wrong window has responded.
368                     Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window);
369                     throw new IllegalStateException("reportDropResult() by non-recipient");
370                 }
371 
372                 // The right window has responded, even if it's no longer around,
373                 // so be sure to halt the timeout even if the later WindowState
374                 // lookup fails.
375                 mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
376                 WindowState callingWin = mService.windowForClientLocked(null, window, false);
377                 if (callingWin == null) {
378                     Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window);
379                     return;  // !!! TODO: throw here?
380                 }
381 
382                 mService.mDragState.mDragResult = consumed;
383                 mService.mDragState.endDragLw();
384             } finally {
385                 Binder.restoreCallingIdentity(ident);
386             }
387         }
388     }
389 
dragRecipientEntered(IWindow window)390     public void dragRecipientEntered(IWindow window) {
391         if (WindowManagerService.DEBUG_DRAG) {
392             Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder());
393         }
394     }
395 
dragRecipientExited(IWindow window)396     public void dragRecipientExited(IWindow window) {
397         if (WindowManagerService.DEBUG_DRAG) {
398             Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder());
399         }
400     }
401 
setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep)402     public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
403         synchronized(mService.mWindowMap) {
404             long ident = Binder.clearCallingIdentity();
405             try {
406                 mService.setWindowWallpaperPositionLocked(
407                         mService.windowForClientLocked(this, window, true),
408                         x, y, xStep, yStep);
409             } finally {
410                 Binder.restoreCallingIdentity(ident);
411             }
412         }
413     }
414 
wallpaperOffsetsComplete(IBinder window)415     public void wallpaperOffsetsComplete(IBinder window) {
416         mService.wallpaperOffsetsComplete(window);
417     }
418 
setWallpaperDisplayOffset(IBinder window, int x, int y)419     public void setWallpaperDisplayOffset(IBinder window, int x, int y) {
420         synchronized(mService.mWindowMap) {
421             long ident = Binder.clearCallingIdentity();
422             try {
423                 mService.setWindowWallpaperDisplayOffsetLocked(
424                         mService.windowForClientLocked(this, window, true), x, y);
425             } finally {
426                 Binder.restoreCallingIdentity(ident);
427             }
428         }
429     }
430 
sendWallpaperCommand(IBinder window, String action, int x, int y, int z, Bundle extras, boolean sync)431     public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
432             int z, Bundle extras, boolean sync) {
433         synchronized(mService.mWindowMap) {
434             long ident = Binder.clearCallingIdentity();
435             try {
436                 return mService.sendWindowWallpaperCommandLocked(
437                         mService.windowForClientLocked(this, window, true),
438                         action, x, y, z, extras, sync);
439             } finally {
440                 Binder.restoreCallingIdentity(ident);
441             }
442         }
443     }
444 
wallpaperCommandComplete(IBinder window, Bundle result)445     public void wallpaperCommandComplete(IBinder window, Bundle result) {
446         mService.wallpaperCommandComplete(window, result);
447     }
448 
setUniverseTransform(IBinder window, float alpha, float offx, float offy, float dsdx, float dtdx, float dsdy, float dtdy)449     public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
450             float dsdx, float dtdx, float dsdy, float dtdy) {
451         synchronized(mService.mWindowMap) {
452             long ident = Binder.clearCallingIdentity();
453             try {
454                 mService.setUniverseTransformLocked(
455                         mService.windowForClientLocked(this, window, true),
456                         alpha, offx, offy, dsdx, dtdx, dsdy, dtdy);
457             } finally {
458                 Binder.restoreCallingIdentity(ident);
459             }
460         }
461     }
462 
onRectangleOnScreenRequested(IBinder token, Rect rectangle)463     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
464         synchronized(mService.mWindowMap) {
465             final long identity = Binder.clearCallingIdentity();
466             try {
467                 mService.onRectangleOnScreenRequested(token, rectangle);
468             } finally {
469                 Binder.restoreCallingIdentity(identity);
470             }
471         }
472     }
473 
getWindowId(IBinder window)474     public IWindowId getWindowId(IBinder window) {
475         return mService.getWindowId(window);
476     }
477 
windowAddedLocked()478     void windowAddedLocked() {
479         if (mSurfaceSession == null) {
480             if (WindowManagerService.localLOGV) Slog.v(
481                 WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession");
482             mSurfaceSession = new SurfaceSession();
483             if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
484                     WindowManagerService.TAG, "  NEW SURFACE SESSION " + mSurfaceSession);
485             mService.mSessions.add(this);
486             if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
487                 mService.dispatchNewAnimatorScaleLocked(this);
488             }
489         }
490         mNumWindow++;
491     }
492 
windowRemovedLocked()493     void windowRemovedLocked() {
494         mNumWindow--;
495         killSessionLocked();
496     }
497 
killSessionLocked()498     void killSessionLocked() {
499         if (mNumWindow <= 0 && mClientDead) {
500             mService.mSessions.remove(this);
501             if (mSurfaceSession != null) {
502                 if (WindowManagerService.localLOGV) Slog.v(
503                     WindowManagerService.TAG, "Last window removed from " + this
504                     + ", destroying " + mSurfaceSession);
505                 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
506                         WindowManagerService.TAG, "  KILL SURFACE SESSION " + mSurfaceSession);
507                 try {
508                     mSurfaceSession.kill();
509                 } catch (Exception e) {
510                     Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session "
511                         + mSurfaceSession + " in session " + this
512                         + ": " + e.toString());
513                 }
514                 mSurfaceSession = null;
515             }
516         }
517     }
518 
dump(PrintWriter pw, String prefix)519     void dump(PrintWriter pw, String prefix) {
520         pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
521                 pw.print(" mClientDead="); pw.print(mClientDead);
522                 pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
523     }
524 
525     @Override
toString()526     public String toString() {
527         return mStringName;
528     }
529 }