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