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