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