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, null /* outOutsets */, outInputChannel);
162     }
163 
164     @Override
addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel)165     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
166             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
167             Rect outOutsets, InputChannel outInputChannel) {
168         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
169                 outContentInsets, outStableInsets, outOutsets, 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 /* outOutsets */, 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, Rect outsets, 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, Rect outsets, Configuration
194                     outConfig,
195             Surface outSurface) {
196         if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from "
197                 + Binder.getCallingPid());
198         int res = mService.relayoutWindow(this, window, seq, attrs,
199                 requestedWidth, requestedHeight, viewFlags, flags,
200                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
201                 outStableInsets, outsets, outConfig, outSurface);
202         if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to "
203                 + Binder.getCallingPid());
204         return res;
205     }
206 
performDeferredDestroy(IWindow window)207     public void performDeferredDestroy(IWindow window) {
208         mService.performDeferredDestroyWindow(this, window);
209     }
210 
outOfMemory(IWindow window)211     public boolean outOfMemory(IWindow window) {
212         return mService.outOfMemoryWindow(this, window);
213     }
214 
setTransparentRegion(IWindow window, Region region)215     public void setTransparentRegion(IWindow window, Region region) {
216         mService.setTransparentRegionWindow(this, window, region);
217     }
218 
setInsets(IWindow window, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableArea)219     public void setInsets(IWindow window, int touchableInsets,
220             Rect contentInsets, Rect visibleInsets, Region touchableArea) {
221         mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
222                 visibleInsets, touchableArea);
223     }
224 
getDisplayFrame(IWindow window, Rect outDisplayFrame)225     public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
226         mService.getWindowDisplayFrame(this, window, outDisplayFrame);
227     }
228 
finishDrawing(IWindow window)229     public void finishDrawing(IWindow window) {
230         if (WindowManagerService.localLOGV) Slog.v(
231             WindowManagerService.TAG, "IWindow finishDrawing called for " + window);
232         mService.finishDrawingWindow(this, window);
233     }
234 
setInTouchMode(boolean mode)235     public void setInTouchMode(boolean mode) {
236         synchronized(mService.mWindowMap) {
237             mService.mInTouchMode = mode;
238         }
239     }
240 
getInTouchMode()241     public boolean getInTouchMode() {
242         synchronized(mService.mWindowMap) {
243             return mService.mInTouchMode;
244         }
245     }
246 
performHapticFeedback(IWindow window, int effectId, boolean always)247     public boolean performHapticFeedback(IWindow window, int effectId,
248             boolean always) {
249         synchronized(mService.mWindowMap) {
250             long ident = Binder.clearCallingIdentity();
251             try {
252                 return mService.mPolicy.performHapticFeedbackLw(
253                         mService.windowForClientLocked(this, window, true),
254                         effectId, always);
255             } finally {
256                 Binder.restoreCallingIdentity(ident);
257             }
258         }
259     }
260 
261     /* Drag/drop */
prepareDrag(IWindow window, int flags, int width, int height, Surface outSurface)262     public IBinder prepareDrag(IWindow window, int flags,
263             int width, int height, Surface outSurface) {
264         return mService.prepareDragSurface(window, mSurfaceSession, flags,
265                 width, height, outSurface);
266     }
267 
performDrag(IWindow window, IBinder dragToken, float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data)268     public boolean performDrag(IWindow window, IBinder dragToken,
269             float touchX, float touchY, float thumbCenterX, float thumbCenterY,
270             ClipData data) {
271         if (WindowManagerService.DEBUG_DRAG) {
272             Slog.d(WindowManagerService.TAG, "perform drag: win=" + window + " data=" + data);
273         }
274 
275         synchronized (mService.mWindowMap) {
276             if (mService.mDragState == null) {
277                 Slog.w(WindowManagerService.TAG, "No drag prepared");
278                 throw new IllegalStateException("performDrag() without prepareDrag()");
279             }
280 
281             if (dragToken != mService.mDragState.mToken) {
282                 Slog.w(WindowManagerService.TAG, "Performing mismatched drag");
283                 throw new IllegalStateException("performDrag() does not match prepareDrag()");
284             }
285 
286             WindowState callingWin = mService.windowForClientLocked(null, window, false);
287             if (callingWin == null) {
288                 Slog.w(WindowManagerService.TAG, "Bad requesting window " + window);
289                 return false;  // !!! TODO: throw here?
290             }
291 
292             // !!! TODO: if input is not still focused on the initiating window, fail
293             // the drag initiation (e.g. an alarm window popped up just as the application
294             // called performDrag()
295 
296             mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
297 
298             // !!! TODO: extract the current touch (x, y) in screen coordinates.  That
299             // will let us eliminate the (touchX,touchY) parameters from the API.
300 
301             // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
302             // the actual drag event dispatch stuff in the dragstate
303 
304             final DisplayContent displayContent = callingWin.getDisplayContent();
305             if (displayContent == null) {
306                return false;
307             }
308             Display display = displayContent.getDisplay();
309             mService.mDragState.register(display);
310             mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
311             if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
312                     mService.mDragState.mServerChannel)) {
313                 Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus");
314                 mService.mDragState.unregister();
315                 mService.mDragState = null;
316                 mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
317                 return false;
318             }
319 
320             mService.mDragState.mData = data;
321             mService.mDragState.mCurrentX = touchX;
322             mService.mDragState.mCurrentY = touchY;
323             mService.mDragState.broadcastDragStartedLw(touchX, touchY);
324 
325             // remember the thumb offsets for later
326             mService.mDragState.mThumbOffsetX = thumbCenterX;
327             mService.mDragState.mThumbOffsetY = thumbCenterY;
328 
329             // Make the surface visible at the proper location
330             final SurfaceControl surfaceControl = mService.mDragState.mSurfaceControl;
331             if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
332                     WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
333             SurfaceControl.openTransaction();
334             try {
335                 surfaceControl.setPosition(touchX - thumbCenterX,
336                         touchY - thumbCenterY);
337                 surfaceControl.setAlpha(.7071f);
338                 surfaceControl.setLayer(mService.mDragState.getDragLayerLw());
339                 surfaceControl.setLayerStack(display.getLayerStack());
340                 surfaceControl.show();
341             } finally {
342                 SurfaceControl.closeTransaction();
343                 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
344                         WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag");
345             }
346         }
347 
348         return true;    // success!
349     }
350 
reportDropResult(IWindow window, boolean consumed)351     public void reportDropResult(IWindow window, boolean consumed) {
352         IBinder token = window.asBinder();
353         if (WindowManagerService.DEBUG_DRAG) {
354             Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token);
355         }
356 
357         synchronized (mService.mWindowMap) {
358             long ident = Binder.clearCallingIdentity();
359             try {
360                 if (mService.mDragState == null) {
361                     // Most likely the drop recipient ANRed and we ended the drag
362                     // out from under it.  Log the issue and move on.
363                     Slog.w(WindowManagerService.TAG, "Drop result given but no drag in progress");
364                     return;
365                 }
366 
367                 if (mService.mDragState.mToken != token) {
368                     // We're in a drag, but the wrong window has responded.
369                     Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window);
370                     throw new IllegalStateException("reportDropResult() by non-recipient");
371                 }
372 
373                 // The right window has responded, even if it's no longer around,
374                 // so be sure to halt the timeout even if the later WindowState
375                 // lookup fails.
376                 mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
377                 WindowState callingWin = mService.windowForClientLocked(null, window, false);
378                 if (callingWin == null) {
379                     Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window);
380                     return;  // !!! TODO: throw here?
381                 }
382 
383                 mService.mDragState.mDragResult = consumed;
384                 mService.mDragState.endDragLw();
385             } finally {
386                 Binder.restoreCallingIdentity(ident);
387             }
388         }
389     }
390 
dragRecipientEntered(IWindow window)391     public void dragRecipientEntered(IWindow window) {
392         if (WindowManagerService.DEBUG_DRAG) {
393             Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder());
394         }
395     }
396 
dragRecipientExited(IWindow window)397     public void dragRecipientExited(IWindow window) {
398         if (WindowManagerService.DEBUG_DRAG) {
399             Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder());
400         }
401     }
402 
setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep)403     public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
404         synchronized(mService.mWindowMap) {
405             long ident = Binder.clearCallingIdentity();
406             try {
407                 mService.setWindowWallpaperPositionLocked(
408                         mService.windowForClientLocked(this, window, true),
409                         x, y, xStep, yStep);
410             } finally {
411                 Binder.restoreCallingIdentity(ident);
412             }
413         }
414     }
415 
wallpaperOffsetsComplete(IBinder window)416     public void wallpaperOffsetsComplete(IBinder window) {
417         mService.wallpaperOffsetsComplete(window);
418     }
419 
setWallpaperDisplayOffset(IBinder window, int x, int y)420     public void setWallpaperDisplayOffset(IBinder window, int x, int y) {
421         synchronized(mService.mWindowMap) {
422             long ident = Binder.clearCallingIdentity();
423             try {
424                 mService.setWindowWallpaperDisplayOffsetLocked(
425                         mService.windowForClientLocked(this, window, true), x, y);
426             } finally {
427                 Binder.restoreCallingIdentity(ident);
428             }
429         }
430     }
431 
sendWallpaperCommand(IBinder window, String action, int x, int y, int z, Bundle extras, boolean sync)432     public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
433             int z, Bundle extras, boolean sync) {
434         synchronized(mService.mWindowMap) {
435             long ident = Binder.clearCallingIdentity();
436             try {
437                 return mService.sendWindowWallpaperCommandLocked(
438                         mService.windowForClientLocked(this, window, true),
439                         action, x, y, z, extras, sync);
440             } finally {
441                 Binder.restoreCallingIdentity(ident);
442             }
443         }
444     }
445 
wallpaperCommandComplete(IBinder window, Bundle result)446     public void wallpaperCommandComplete(IBinder window, Bundle result) {
447         mService.wallpaperCommandComplete(window, result);
448     }
449 
onRectangleOnScreenRequested(IBinder token, Rect rectangle)450     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
451         synchronized(mService.mWindowMap) {
452             final long identity = Binder.clearCallingIdentity();
453             try {
454                 mService.onRectangleOnScreenRequested(token, rectangle);
455             } finally {
456                 Binder.restoreCallingIdentity(identity);
457             }
458         }
459     }
460 
getWindowId(IBinder window)461     public IWindowId getWindowId(IBinder window) {
462         return mService.getWindowId(window);
463     }
464 
465     @Override
pokeDrawLock(IBinder window)466     public void pokeDrawLock(IBinder window) {
467         final long identity = Binder.clearCallingIdentity();
468         try {
469             mService.pokeDrawLock(this, window);
470         } finally {
471             Binder.restoreCallingIdentity(identity);
472         }
473     }
474 
windowAddedLocked()475     void windowAddedLocked() {
476         if (mSurfaceSession == null) {
477             if (WindowManagerService.localLOGV) Slog.v(
478                 WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession");
479             mSurfaceSession = new SurfaceSession();
480             if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
481                     WindowManagerService.TAG, "  NEW SURFACE SESSION " + mSurfaceSession);
482             mService.mSessions.add(this);
483             if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
484                 mService.dispatchNewAnimatorScaleLocked(this);
485             }
486         }
487         mNumWindow++;
488     }
489 
windowRemovedLocked()490     void windowRemovedLocked() {
491         mNumWindow--;
492         killSessionLocked();
493     }
494 
killSessionLocked()495     void killSessionLocked() {
496         if (mNumWindow <= 0 && mClientDead) {
497             mService.mSessions.remove(this);
498             if (mSurfaceSession != null) {
499                 if (WindowManagerService.localLOGV) Slog.v(
500                     WindowManagerService.TAG, "Last window removed from " + this
501                     + ", destroying " + mSurfaceSession);
502                 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
503                         WindowManagerService.TAG, "  KILL SURFACE SESSION " + mSurfaceSession);
504                 try {
505                     mSurfaceSession.kill();
506                 } catch (Exception e) {
507                     Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session "
508                         + mSurfaceSession + " in session " + this
509                         + ": " + e.toString());
510                 }
511                 mSurfaceSession = null;
512             }
513         }
514     }
515 
dump(PrintWriter pw, String prefix)516     void dump(PrintWriter pw, String prefix) {
517         pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
518                 pw.print(" mClientDead="); pw.print(mClientDead);
519                 pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
520     }
521 
522     @Override
toString()523     public String toString() {
524         return mStringName;
525     }
526 }
527