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