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