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 }