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