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