Session.java revision 0ec1328f85a08a610868856c688ebb8196c79c17
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 android.view.IWindowId;
20import android.view.IWindowSessionCallback;
21import com.android.internal.view.IInputContext;
22import com.android.internal.view.IInputMethodClient;
23import com.android.internal.view.IInputMethodManager;
24import com.android.server.wm.WindowManagerService.H;
25
26import android.content.ClipData;
27import android.content.Context;
28import android.content.res.Configuration;
29import android.graphics.Rect;
30import android.graphics.Region;
31import android.os.Binder;
32import android.os.Bundle;
33import android.os.IBinder;
34import android.os.Parcel;
35import android.os.Process;
36import android.os.RemoteException;
37import android.os.ServiceManager;
38import android.os.UserHandle;
39import android.util.Slog;
40import android.view.Display;
41import android.view.IWindow;
42import android.view.IWindowSession;
43import android.view.InputChannel;
44import android.view.Surface;
45import android.view.SurfaceControl;
46import android.view.SurfaceSession;
47import android.view.WindowManager;
48
49import 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 */
55final 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
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
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
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
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
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
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
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
186    public void remove(IWindow window) {
187        mService.removeWindow(this, window);
188    }
189
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
207    public void performDeferredDestroy(IWindow window) {
208        mService.performDeferredDestroyWindow(this, window);
209    }
210
211    public boolean outOfMemory(IWindow window) {
212        return mService.outOfMemoryWindow(this, window);
213    }
214
215    public void setTransparentRegion(IWindow window, Region region) {
216        mService.setTransparentRegionWindow(this, window, region);
217    }
218
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
225    public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
226        mService.getWindowDisplayFrame(this, window, outDisplayFrame);
227    }
228
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
235    public void setInTouchMode(boolean mode) {
236        synchronized(mService.mWindowMap) {
237            mService.mInTouchMode = mode;
238        }
239    }
240
241    public boolean getInTouchMode() {
242        synchronized(mService.mWindowMap) {
243            return mService.mInTouchMode;
244        }
245    }
246
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 */
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
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
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
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
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
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
416    public void wallpaperOffsetsComplete(IBinder window) {
417        mService.wallpaperOffsetsComplete(window);
418    }
419
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
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
446    public void wallpaperCommandComplete(IBinder window, Bundle result) {
447        mService.wallpaperCommandComplete(window, result);
448    }
449
450    public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
451            float dsdx, float dtdx, float dsdy, float dtdy) {
452        synchronized(mService.mWindowMap) {
453            long ident = Binder.clearCallingIdentity();
454            try {
455                mService.setUniverseTransformLocked(
456                        mService.windowForClientLocked(this, window, true),
457                        alpha, offx, offy, dsdx, dtdx, dsdy, dtdy);
458            } finally {
459                Binder.restoreCallingIdentity(ident);
460            }
461        }
462    }
463
464    public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
465        synchronized(mService.mWindowMap) {
466            final long identity = Binder.clearCallingIdentity();
467            try {
468                mService.onRectangleOnScreenRequested(token, rectangle);
469            } finally {
470                Binder.restoreCallingIdentity(identity);
471            }
472        }
473    }
474
475    public IWindowId getWindowId(IBinder window) {
476        return mService.getWindowId(window);
477    }
478
479    @Override
480    public void pokeDrawLock(IBinder window) {
481        final long identity = Binder.clearCallingIdentity();
482        try {
483            mService.pokeDrawLock(this, window);
484        } finally {
485            Binder.restoreCallingIdentity(identity);
486        }
487    }
488
489    void windowAddedLocked() {
490        if (mSurfaceSession == null) {
491            if (WindowManagerService.localLOGV) Slog.v(
492                WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession");
493            mSurfaceSession = new SurfaceSession();
494            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
495                    WindowManagerService.TAG, "  NEW SURFACE SESSION " + mSurfaceSession);
496            mService.mSessions.add(this);
497            if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
498                mService.dispatchNewAnimatorScaleLocked(this);
499            }
500        }
501        mNumWindow++;
502    }
503
504    void windowRemovedLocked() {
505        mNumWindow--;
506        killSessionLocked();
507    }
508
509    void killSessionLocked() {
510        if (mNumWindow <= 0 && mClientDead) {
511            mService.mSessions.remove(this);
512            if (mSurfaceSession != null) {
513                if (WindowManagerService.localLOGV) Slog.v(
514                    WindowManagerService.TAG, "Last window removed from " + this
515                    + ", destroying " + mSurfaceSession);
516                if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
517                        WindowManagerService.TAG, "  KILL SURFACE SESSION " + mSurfaceSession);
518                try {
519                    mSurfaceSession.kill();
520                } catch (Exception e) {
521                    Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session "
522                        + mSurfaceSession + " in session " + this
523                        + ": " + e.toString());
524                }
525                mSurfaceSession = null;
526            }
527        }
528    }
529
530    void dump(PrintWriter pw, String prefix) {
531        pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
532                pw.print(" mClientDead="); pw.print(mClientDead);
533                pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
534    }
535
536    @Override
537    public String toString() {
538        return mStringName;
539    }
540}
541