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