Session.java revision df88d73092c62a1a3cd2b2056ca63ae2e70cc238
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            final DisplayContent displayContent = callingWin.getDisplayContent();
297            if (displayContent == null) {
298               return false;
299            }
300            Display display = displayContent.getDisplay();
301            mService.mDragState.register(display);
302            mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
303            if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
304                    mService.mDragState.mServerChannel)) {
305                Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus");
306                mService.mDragState.unregister();
307                mService.mDragState = null;
308                mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
309                return false;
310            }
311
312            mService.mDragState.mData = data;
313            mService.mDragState.mCurrentX = touchX;
314            mService.mDragState.mCurrentY = touchY;
315            mService.mDragState.broadcastDragStartedLw(touchX, touchY);
316
317            // remember the thumb offsets for later
318            mService.mDragState.mThumbOffsetX = thumbCenterX;
319            mService.mDragState.mThumbOffsetY = thumbCenterY;
320
321            // Make the surface visible at the proper location
322            final SurfaceControl surfaceControl = mService.mDragState.mSurfaceControl;
323            if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
324                    WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
325            SurfaceControl.openTransaction();
326            try {
327                surfaceControl.setPosition(touchX - thumbCenterX,
328                        touchY - thumbCenterY);
329                surfaceControl.setAlpha(.7071f);
330                surfaceControl.setLayer(mService.mDragState.getDragLayerLw());
331                surfaceControl.setLayerStack(display.getLayerStack());
332                surfaceControl.show();
333            } finally {
334                SurfaceControl.closeTransaction();
335                if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
336                        WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag");
337            }
338        }
339
340        return true;    // success!
341    }
342
343    public void reportDropResult(IWindow window, boolean consumed) {
344        IBinder token = window.asBinder();
345        if (WindowManagerService.DEBUG_DRAG) {
346            Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token);
347        }
348
349        synchronized (mService.mWindowMap) {
350            long ident = Binder.clearCallingIdentity();
351            try {
352                if (mService.mDragState == null) {
353                    // Most likely the drop recipient ANRed and we ended the drag
354                    // out from under it.  Log the issue and move on.
355                    Slog.w(WindowManagerService.TAG, "Drop result given but no drag in progress");
356                    return;
357                }
358
359                if (mService.mDragState.mToken != token) {
360                    // We're in a drag, but the wrong window has responded.
361                    Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window);
362                    throw new IllegalStateException("reportDropResult() by non-recipient");
363                }
364
365                // The right window has responded, even if it's no longer around,
366                // so be sure to halt the timeout even if the later WindowState
367                // lookup fails.
368                mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
369                WindowState callingWin = mService.windowForClientLocked(null, window, false);
370                if (callingWin == null) {
371                    Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window);
372                    return;  // !!! TODO: throw here?
373                }
374
375                mService.mDragState.mDragResult = consumed;
376                mService.mDragState.endDragLw();
377            } finally {
378                Binder.restoreCallingIdentity(ident);
379            }
380        }
381    }
382
383    public void dragRecipientEntered(IWindow window) {
384        if (WindowManagerService.DEBUG_DRAG) {
385            Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder());
386        }
387    }
388
389    public void dragRecipientExited(IWindow window) {
390        if (WindowManagerService.DEBUG_DRAG) {
391            Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder());
392        }
393    }
394
395    public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
396        synchronized(mService.mWindowMap) {
397            long ident = Binder.clearCallingIdentity();
398            try {
399                mService.setWindowWallpaperPositionLocked(
400                        mService.windowForClientLocked(this, window, true),
401                        x, y, xStep, yStep);
402            } finally {
403                Binder.restoreCallingIdentity(ident);
404            }
405        }
406    }
407
408    public void wallpaperOffsetsComplete(IBinder window) {
409        mService.wallpaperOffsetsComplete(window);
410    }
411
412    public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
413            int z, Bundle extras, boolean sync) {
414        synchronized(mService.mWindowMap) {
415            long ident = Binder.clearCallingIdentity();
416            try {
417                return mService.sendWindowWallpaperCommandLocked(
418                        mService.windowForClientLocked(this, window, true),
419                        action, x, y, z, extras, sync);
420            } finally {
421                Binder.restoreCallingIdentity(ident);
422            }
423        }
424    }
425
426    public void wallpaperCommandComplete(IBinder window, Bundle result) {
427        mService.wallpaperCommandComplete(window, result);
428    }
429
430    public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
431            float dsdx, float dtdx, float dsdy, float dtdy) {
432        synchronized(mService.mWindowMap) {
433            long ident = Binder.clearCallingIdentity();
434            try {
435                mService.setUniverseTransformLocked(
436                        mService.windowForClientLocked(this, window, true),
437                        alpha, offx, offy, dsdx, dtdx, dsdy, dtdy);
438            } finally {
439                Binder.restoreCallingIdentity(ident);
440            }
441        }
442    }
443
444    public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
445        synchronized(mService.mWindowMap) {
446            final long identity = Binder.clearCallingIdentity();
447            try {
448                mService.onRectangleOnScreenRequested(token, rectangle, immediate);
449            } finally {
450                Binder.restoreCallingIdentity(identity);
451            }
452        }
453    }
454
455    public IWindowId getWindowId(IBinder window) {
456        return mService.getWindowId(window);
457    }
458
459    void windowAddedLocked() {
460        if (mSurfaceSession == null) {
461            if (WindowManagerService.localLOGV) Slog.v(
462                WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession");
463            mSurfaceSession = new SurfaceSession();
464            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
465                    WindowManagerService.TAG, "  NEW SURFACE SESSION " + mSurfaceSession);
466            mService.mSessions.add(this);
467        }
468        mNumWindow++;
469    }
470
471    void windowRemovedLocked() {
472        mNumWindow--;
473        killSessionLocked();
474    }
475
476    void killSessionLocked() {
477        if (mNumWindow <= 0 && mClientDead) {
478            mService.mSessions.remove(this);
479            if (mSurfaceSession != null) {
480                if (WindowManagerService.localLOGV) Slog.v(
481                    WindowManagerService.TAG, "Last window removed from " + this
482                    + ", destroying " + mSurfaceSession);
483                if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
484                        WindowManagerService.TAG, "  KILL SURFACE SESSION " + mSurfaceSession);
485                try {
486                    mSurfaceSession.kill();
487                } catch (Exception e) {
488                    Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session "
489                        + mSurfaceSession + " in session " + this
490                        + ": " + e.toString());
491                }
492                mSurfaceSession = null;
493            }
494        }
495    }
496
497    void dump(PrintWriter pw, String prefix) {
498        pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
499                pw.print(" mClientDead="); pw.print(mClientDead);
500                pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
501    }
502
503    @Override
504    public String toString() {
505        return mStringName;
506    }
507}