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