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