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 Session(WindowManagerService service, IInputMethodClient client, IInputContext inputContext)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 onTransact(int code, Parcel data, Parcel reply, int flags)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 binderDied()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 add(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel)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 addWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets)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 remove(IWindow window)148 public void remove(IWindow window) { 149 mService.removeWindow(this, window); 150 } 151 relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, boolean insetsPending, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface)152 public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, 153 int requestedWidth, int requestedHeight, int viewFlags, 154 boolean insetsPending, 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, insetsPending, 160 outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); 161 if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to " 162 + Binder.getCallingPid()); 163 return res; 164 } 165 outOfMemory(IWindow window)166 public boolean outOfMemory(IWindow window) { 167 return mService.outOfMemoryWindow(this, window); 168 } 169 setTransparentRegion(IWindow window, Region region)170 public void setTransparentRegion(IWindow window, Region region) { 171 mService.setTransparentRegionWindow(this, window, region); 172 } 173 setInsets(IWindow window, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableArea)174 public void setInsets(IWindow window, int touchableInsets, 175 Rect contentInsets, Rect visibleInsets, Region touchableArea) { 176 mService.setInsetsWindow(this, window, touchableInsets, contentInsets, 177 visibleInsets, touchableArea); 178 } 179 getDisplayFrame(IWindow window, Rect outDisplayFrame)180 public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { 181 mService.getWindowDisplayFrame(this, window, outDisplayFrame); 182 } 183 finishDrawing(IWindow window)184 public void finishDrawing(IWindow window) { 185 if (WindowManagerService.localLOGV) Slog.v( 186 WindowManagerService.TAG, "IWindow finishDrawing called for " + window); 187 mService.finishDrawingWindow(this, window); 188 } 189 setInTouchMode(boolean mode)190 public void setInTouchMode(boolean mode) { 191 synchronized(mService.mWindowMap) { 192 mService.mInTouchMode = mode; 193 } 194 } 195 getInTouchMode()196 public boolean getInTouchMode() { 197 synchronized(mService.mWindowMap) { 198 return mService.mInTouchMode; 199 } 200 } 201 performHapticFeedback(IWindow window, int effectId, boolean always)202 public boolean performHapticFeedback(IWindow window, int effectId, 203 boolean always) { 204 synchronized(mService.mWindowMap) { 205 long ident = Binder.clearCallingIdentity(); 206 try { 207 return mService.mPolicy.performHapticFeedbackLw( 208 mService.windowForClientLocked(this, window, true), 209 effectId, always); 210 } finally { 211 Binder.restoreCallingIdentity(ident); 212 } 213 } 214 } 215 216 /* Drag/drop */ prepareDrag(IWindow window, int flags, int width, int height, Surface outSurface)217 public IBinder prepareDrag(IWindow window, int flags, 218 int width, int height, Surface outSurface) { 219 return mService.prepareDragSurface(window, mSurfaceSession, flags, 220 width, height, outSurface); 221 } 222 performDrag(IWindow window, IBinder dragToken, float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data)223 public boolean performDrag(IWindow window, IBinder dragToken, 224 float touchX, float touchY, float thumbCenterX, float thumbCenterY, 225 ClipData data) { 226 if (WindowManagerService.DEBUG_DRAG) { 227 Slog.d(WindowManagerService.TAG, "perform drag: win=" + window + " data=" + data); 228 } 229 230 synchronized (mService.mWindowMap) { 231 if (mService.mDragState == null) { 232 Slog.w(WindowManagerService.TAG, "No drag prepared"); 233 throw new IllegalStateException("performDrag() without prepareDrag()"); 234 } 235 236 if (dragToken != mService.mDragState.mToken) { 237 Slog.w(WindowManagerService.TAG, "Performing mismatched drag"); 238 throw new IllegalStateException("performDrag() does not match prepareDrag()"); 239 } 240 241 WindowState callingWin = mService.windowForClientLocked(null, window, false); 242 if (callingWin == null) { 243 Slog.w(WindowManagerService.TAG, "Bad requesting window " + window); 244 return false; // !!! TODO: throw here? 245 } 246 247 // !!! TODO: if input is not still focused on the initiating window, fail 248 // the drag initiation (e.g. an alarm window popped up just as the application 249 // called performDrag() 250 251 mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder()); 252 253 // !!! TODO: extract the current touch (x, y) in screen coordinates. That 254 // will let us eliminate the (touchX,touchY) parameters from the API. 255 256 // !!! FIXME: put all this heavy stuff onto the mH looper, as well as 257 // the actual drag event dispatch stuff in the dragstate 258 259 mService.mDragState.register(); 260 mService.mInputMonitor.updateInputWindowsLw(true /*force*/); 261 if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, 262 mService.mDragState.mServerChannel)) { 263 Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus"); 264 mService.mDragState.unregister(); 265 mService.mDragState = null; 266 mService.mInputMonitor.updateInputWindowsLw(true /*force*/); 267 return false; 268 } 269 270 mService.mDragState.mData = data; 271 mService.mDragState.mCurrentX = touchX; 272 mService.mDragState.mCurrentY = touchY; 273 mService.mDragState.broadcastDragStartedLw(touchX, touchY); 274 275 // remember the thumb offsets for later 276 mService.mDragState.mThumbOffsetX = thumbCenterX; 277 mService.mDragState.mThumbOffsetY = thumbCenterY; 278 279 // Make the surface visible at the proper location 280 final Surface surface = mService.mDragState.mSurface; 281 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i( 282 WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag"); 283 Surface.openTransaction(); 284 try { 285 surface.setPosition(touchX - thumbCenterX, 286 touchY - thumbCenterY); 287 surface.setAlpha(.7071f); 288 surface.setLayer(mService.mDragState.getDragLayerLw()); 289 surface.show(); 290 } finally { 291 Surface.closeTransaction(); 292 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i( 293 WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag"); 294 } 295 } 296 297 return true; // success! 298 } 299 reportDropResult(IWindow window, boolean consumed)300 public void reportDropResult(IWindow window, boolean consumed) { 301 IBinder token = window.asBinder(); 302 if (WindowManagerService.DEBUG_DRAG) { 303 Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token); 304 } 305 306 synchronized (mService.mWindowMap) { 307 long ident = Binder.clearCallingIdentity(); 308 try { 309 if (mService.mDragState == null || mService.mDragState.mToken != token) { 310 Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window); 311 throw new IllegalStateException("reportDropResult() by non-recipient"); 312 } 313 314 // The right window has responded, even if it's no longer around, 315 // so be sure to halt the timeout even if the later WindowState 316 // lookup fails. 317 mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder()); 318 WindowState callingWin = mService.windowForClientLocked(null, window, false); 319 if (callingWin == null) { 320 Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window); 321 return; // !!! TODO: throw here? 322 } 323 324 mService.mDragState.mDragResult = consumed; 325 mService.mDragState.endDragLw(); 326 } finally { 327 Binder.restoreCallingIdentity(ident); 328 } 329 } 330 } 331 dragRecipientEntered(IWindow window)332 public void dragRecipientEntered(IWindow window) { 333 if (WindowManagerService.DEBUG_DRAG) { 334 Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder()); 335 } 336 } 337 dragRecipientExited(IWindow window)338 public void dragRecipientExited(IWindow window) { 339 if (WindowManagerService.DEBUG_DRAG) { 340 Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder()); 341 } 342 } 343 setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep)344 public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) { 345 synchronized(mService.mWindowMap) { 346 long ident = Binder.clearCallingIdentity(); 347 try { 348 mService.setWindowWallpaperPositionLocked( 349 mService.windowForClientLocked(this, window, true), 350 x, y, xStep, yStep); 351 } finally { 352 Binder.restoreCallingIdentity(ident); 353 } 354 } 355 } 356 wallpaperOffsetsComplete(IBinder window)357 public void wallpaperOffsetsComplete(IBinder window) { 358 mService.wallpaperOffsetsComplete(window); 359 } 360 sendWallpaperCommand(IBinder window, String action, int x, int y, int z, Bundle extras, boolean sync)361 public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, 362 int z, Bundle extras, boolean sync) { 363 synchronized(mService.mWindowMap) { 364 long ident = Binder.clearCallingIdentity(); 365 try { 366 return mService.sendWindowWallpaperCommandLocked( 367 mService.windowForClientLocked(this, window, true), 368 action, x, y, z, extras, sync); 369 } finally { 370 Binder.restoreCallingIdentity(ident); 371 } 372 } 373 } 374 wallpaperCommandComplete(IBinder window, Bundle result)375 public void wallpaperCommandComplete(IBinder window, Bundle result) { 376 mService.wallpaperCommandComplete(window, result); 377 } 378 windowAddedLocked()379 void windowAddedLocked() { 380 if (mSurfaceSession == null) { 381 if (WindowManagerService.localLOGV) Slog.v( 382 WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession"); 383 mSurfaceSession = new SurfaceSession(); 384 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( 385 WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession); 386 mService.mSessions.add(this); 387 } 388 mNumWindow++; 389 } 390 windowRemovedLocked()391 void windowRemovedLocked() { 392 mNumWindow--; 393 killSessionLocked(); 394 } 395 killSessionLocked()396 void killSessionLocked() { 397 if (mNumWindow <= 0 && mClientDead) { 398 mService.mSessions.remove(this); 399 if (mSurfaceSession != null) { 400 if (WindowManagerService.localLOGV) Slog.v( 401 WindowManagerService.TAG, "Last window removed from " + this 402 + ", destroying " + mSurfaceSession); 403 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( 404 WindowManagerService.TAG, " KILL SURFACE SESSION " + mSurfaceSession); 405 try { 406 mSurfaceSession.kill(); 407 } catch (Exception e) { 408 Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session " 409 + mSurfaceSession + " in session " + this 410 + ": " + e.toString()); 411 } 412 mSurfaceSession = null; 413 } 414 } 415 } 416 dump(PrintWriter pw, String prefix)417 void dump(PrintWriter pw, String prefix) { 418 pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); 419 pw.print(" mClientDead="); pw.print(mClientDead); 420 pw.print(" mSurfaceSession="); pw.println(mSurfaceSession); 421 } 422 423 @Override toString()424 public String toString() { 425 return mStringName; 426 } 427 }