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 static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
     20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
     21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
     22 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
     23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     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.IWindowId;
     42 import android.view.IWindowSession;
     43 import android.view.IWindowSessionCallback;
     44 import android.view.InputChannel;
     45 import android.view.Surface;
     46 import android.view.SurfaceControl;
     47 import android.view.SurfaceSession;
     48 import android.view.WindowManager;
     49 
     50 import com.android.internal.view.IInputContext;
     51 import com.android.internal.view.IInputMethodClient;
     52 import com.android.internal.view.IInputMethodManager;
     53 import com.android.server.wm.WindowManagerService.H;
     54 
     55 import 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  */
     61 final 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