Session.java revision 14a9f2b9d23976b7aae5330b56c633a03181c710
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 Display display = callingWin.mDisplayContent.getDisplay(); 284 mService.mDragState.register(display); 285 mService.mInputMonitor.updateInputWindowsLw(true /*force*/); 286 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, 287 mService.mDragState.mServerChannel)) { 288 Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus"); 289 mService.mDragState.unregister(); 290 mService.mDragState = null; 291 mService.mInputMonitor.updateInputWindowsLw(true /*force*/); 292 return false; 293 } 294 295 mService.mDragState.mData = data; 296 mService.mDragState.mCurrentX = touchX; 297 mService.mDragState.mCurrentY = touchY; 298 mService.mDragState.broadcastDragStartedLw(touchX, touchY); 299 300 // remember the thumb offsets for later 301 mService.mDragState.mThumbOffsetX = thumbCenterX; 302 mService.mDragState.mThumbOffsetY = thumbCenterY; 303 304 // Make the surface visible at the proper location 305 final Surface surface = mService.mDragState.mSurface; 306 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i( 307 WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag"); 308 Surface.openTransaction(); 309 try { 310 surface.setPosition(touchX - thumbCenterX, 311 touchY - thumbCenterY); 312 surface.setAlpha(.7071f); 313 surface.setLayer(mService.mDragState.getDragLayerLw()); 314 surface.setLayerStack(display.getLayerStack()); 315 surface.show(); 316 } finally { 317 Surface.closeTransaction(); 318 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i( 319 WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag"); 320 } 321 } 322 323 return true; // success! 324 } 325 326 public void reportDropResult(IWindow window, boolean consumed) { 327 IBinder token = window.asBinder(); 328 if (WindowManagerService.DEBUG_DRAG) { 329 Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token); 330 } 331 332 synchronized (mService.mWindowMap) { 333 long ident = Binder.clearCallingIdentity(); 334 try { 335 if (mService.mDragState == null) { 336 // Most likely the drop recipient ANRed and we ended the drag 337 // out from under it. Log the issue and move on. 338 Slog.w(WindowManagerService.TAG, "Drop result given but no drag in progress"); 339 return; 340 } 341 342 if (mService.mDragState.mToken != token) { 343 // We're in a drag, but the wrong window has responded. 344 Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window); 345 throw new IllegalStateException("reportDropResult() by non-recipient"); 346 } 347 348 // The right window has responded, even if it's no longer around, 349 // so be sure to halt the timeout even if the later WindowState 350 // lookup fails. 351 mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder()); 352 WindowState callingWin = mService.windowForClientLocked(null, window, false); 353 if (callingWin == null) { 354 Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window); 355 return; // !!! TODO: throw here? 356 } 357 358 mService.mDragState.mDragResult = consumed; 359 mService.mDragState.endDragLw(); 360 } finally { 361 Binder.restoreCallingIdentity(ident); 362 } 363 } 364 } 365 366 public void dragRecipientEntered(IWindow window) { 367 if (WindowManagerService.DEBUG_DRAG) { 368 Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder()); 369 } 370 } 371 372 public void dragRecipientExited(IWindow window) { 373 if (WindowManagerService.DEBUG_DRAG) { 374 Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder()); 375 } 376 } 377 378 public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) { 379 synchronized(mService.mWindowMap) { 380 long ident = Binder.clearCallingIdentity(); 381 try { 382 mService.setWindowWallpaperPositionLocked( 383 mService.windowForClientLocked(this, window, true), 384 x, y, xStep, yStep); 385 } finally { 386 Binder.restoreCallingIdentity(ident); 387 } 388 } 389 } 390 391 public void wallpaperOffsetsComplete(IBinder window) { 392 mService.wallpaperOffsetsComplete(window); 393 } 394 395 public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, 396 int z, Bundle extras, boolean sync) { 397 synchronized(mService.mWindowMap) { 398 long ident = Binder.clearCallingIdentity(); 399 try { 400 return mService.sendWindowWallpaperCommandLocked( 401 mService.windowForClientLocked(this, window, true), 402 action, x, y, z, extras, sync); 403 } finally { 404 Binder.restoreCallingIdentity(ident); 405 } 406 } 407 } 408 409 public void wallpaperCommandComplete(IBinder window, Bundle result) { 410 mService.wallpaperCommandComplete(window, result); 411 } 412 413 public void setUniverseTransform(IBinder window, float alpha, float offx, float offy, 414 float dsdx, float dtdx, float dsdy, float dtdy) { 415 synchronized(mService.mWindowMap) { 416 long ident = Binder.clearCallingIdentity(); 417 try { 418 mService.setUniverseTransformLocked( 419 mService.windowForClientLocked(this, window, true), 420 alpha, offx, offy, dsdx, dtdx, dsdy, dtdy); 421 } finally { 422 Binder.restoreCallingIdentity(ident); 423 } 424 } 425 } 426 427 public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) { 428 synchronized(mService.mWindowMap) { 429 final long identity = Binder.clearCallingIdentity(); 430 try { 431 mService.onRectangleOnScreenRequested(token, rectangle, immediate); 432 } finally { 433 Binder.restoreCallingIdentity(identity); 434 } 435 } 436 } 437 438 void windowAddedLocked() { 439 if (mSurfaceSession == null) { 440 if (WindowManagerService.localLOGV) Slog.v( 441 WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession"); 442 mSurfaceSession = new SurfaceSession(); 443 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( 444 WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession); 445 mService.mSessions.add(this); 446 } 447 mNumWindow++; 448 } 449 450 void windowRemovedLocked() { 451 mNumWindow--; 452 killSessionLocked(); 453 } 454 455 void killSessionLocked() { 456 if (mNumWindow <= 0 && mClientDead) { 457 mService.mSessions.remove(this); 458 if (mSurfaceSession != null) { 459 if (WindowManagerService.localLOGV) Slog.v( 460 WindowManagerService.TAG, "Last window removed from " + this 461 + ", destroying " + mSurfaceSession); 462 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( 463 WindowManagerService.TAG, " KILL SURFACE SESSION " + mSurfaceSession); 464 try { 465 mSurfaceSession.kill(); 466 } catch (Exception e) { 467 Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session " 468 + mSurfaceSession + " in session " + this 469 + ": " + e.toString()); 470 } 471 mSurfaceSession = null; 472 } 473 } 474 } 475 476 void dump(PrintWriter pw, String prefix) { 477 pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); 478 pw.print(" mClientDead="); pw.print(mClientDead); 479 pw.print(" mSurfaceSession="); pw.println(mSurfaceSession); 480 } 481 482 @Override 483 public String toString() { 484 return mStringName; 485 } 486}