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