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