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