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