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