WindowManagerImpl.java revision a8b9defade5b937d4ad64f9aff4bca792298f43c
1/* 2 * Copyright (C) 2006 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 android.view; 18 19import android.app.ActivityManager; 20import android.content.ComponentCallbacks2; 21import android.content.res.Configuration; 22import android.opengl.ManagedEGLContext; 23import android.os.IBinder; 24import android.os.ServiceManager; 25import android.os.SystemProperties; 26import android.util.AndroidRuntimeException; 27import android.util.Log; 28import android.view.inputmethod.InputMethodManager; 29 30import java.io.FileDescriptor; 31import java.io.FileOutputStream; 32import java.io.PrintWriter; 33 34final class WindowLeaked extends AndroidRuntimeException { 35 public WindowLeaked(String msg) { 36 super(msg); 37 } 38} 39 40/** 41 * Low-level communication with the global system window manager. It implements 42 * the ViewManager interface, allowing you to add any View subclass as a 43 * top-level window on the screen. Additional window manager specific layout 44 * parameters are defined for control over how windows are displayed. 45 * It also implements the WindowManager interface, allowing you to control the 46 * displays attached to the device. 47 * 48 * <p>Applications will not normally use WindowManager directly, instead relying 49 * on the higher-level facilities in {@link android.app.Activity} and 50 * {@link android.app.Dialog}. 51 * 52 * <p>Even for low-level window manager access, it is almost never correct to use 53 * this class. For example, {@link android.app.Activity#getWindowManager} 54 * provides a ViewManager for adding windows that are associated with that 55 * activity -- the window manager will not normally allow you to add arbitrary 56 * windows that are not associated with an activity. 57 * 58 * @hide 59 */ 60public class WindowManagerImpl implements WindowManager { 61 private static final String TAG = "WindowManager"; 62 63 /** 64 * The user is navigating with keys (not the touch screen), so 65 * navigational focus should be shown. 66 */ 67 public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1; 68 69 /** 70 * This is the first time the window is being drawn, 71 * so the client must call drawingFinished() when done 72 */ 73 public static final int RELAYOUT_RES_FIRST_TIME = 0x2; 74 75 /** 76 * The window manager has changed the surface from the last call. 77 */ 78 public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4; 79 80 /** 81 * The window manager is currently animating. It will call 82 * IWindow.doneAnimating() when done. 83 */ 84 public static final int RELAYOUT_RES_ANIMATING = 0x8; 85 86 /** 87 * Flag for relayout: the client will be later giving 88 * internal insets; as a result, the window will not impact other window 89 * layouts until the insets are given. 90 */ 91 public static final int RELAYOUT_INSETS_PENDING = 0x1; 92 93 /** 94 * Flag for relayout: the client may be currently using the current surface, 95 * so if it is to be destroyed as a part of the relayout the destroy must 96 * be deferred until later. The client will call performDeferredDestroy() 97 * when it is okay. 98 */ 99 public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2; 100 101 public static final int ADD_FLAG_APP_VISIBLE = 0x2; 102 public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE; 103 104 public static final int ADD_OKAY = 0; 105 public static final int ADD_BAD_APP_TOKEN = -1; 106 public static final int ADD_BAD_SUBWINDOW_TOKEN = -2; 107 public static final int ADD_NOT_APP_TOKEN = -3; 108 public static final int ADD_APP_EXITING = -4; 109 public static final int ADD_DUPLICATE_ADD = -5; 110 public static final int ADD_STARTING_NOT_NEEDED = -6; 111 public static final int ADD_MULTIPLE_SINGLETON = -7; 112 public static final int ADD_PERMISSION_DENIED = -8; 113 114 private static WindowManagerImpl sDefaultWindowManager; 115 private static IWindowManager sWindowManagerService; 116 117 private final WindowManagerState mState; 118 private final Window mParentWindow; 119 private final CompatibilityInfoHolder mCompatibilityInfo; 120 private final Display mDefaultDisplay; 121 122 private WindowManagerImpl(WindowManagerState state, Window parentWindow, 123 CompatibilityInfoHolder compatibilityInfo) { 124 mState = state; 125 mParentWindow = parentWindow; 126 mCompatibilityInfo = compatibilityInfo; 127 mDefaultDisplay = mState.getDefaultDisplay(mCompatibilityInfo); 128 } 129 130 public static WindowManagerImpl getDefault() { 131 synchronized (WindowManagerImpl.class) { 132 if (sDefaultWindowManager == null) { 133 sDefaultWindowManager = new WindowManagerImpl( 134 new WindowManagerState(), null, null); 135 } 136 return sDefaultWindowManager; 137 } 138 } 139 140 public static IWindowManager getWindowManagerService() { 141 synchronized (WindowManagerImpl.class) { 142 if (sWindowManagerService == null) { 143 sWindowManagerService = IWindowManager.Stub.asInterface( 144 ServiceManager.getService("window")); 145 } 146 return sWindowManagerService; 147 } 148 } 149 150 public WindowManagerImpl makeLocal(Window parentWindow) { 151 return new WindowManagerImpl(mState, parentWindow, parentWindow.getCompatibilityInfo()); 152 } 153 154 public WindowManagerImpl makeCompatible(CompatibilityInfoHolder compatInfo) { 155 if (compatInfo == mCompatibilityInfo) { 156 return this; 157 } 158 if (compatInfo == null && mParentWindow == null) { 159 return getDefault(); 160 } 161 return new WindowManagerImpl(mState, mParentWindow, compatInfo); 162 } 163 164 @Override 165 public void addView(View view, ViewGroup.LayoutParams params) { 166 mState.addView(view, params, mParentWindow, mCompatibilityInfo); 167 } 168 169 @Override 170 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 171 mState.updateViewLayout(view, params); 172 } 173 174 @Override 175 public void removeView(View view) { 176 mState.removeView(view, false); 177 } 178 179 @Override 180 public void removeViewImmediate(View view) { 181 mState.removeView(view, true); 182 } 183 184 @Override 185 public Display getDefaultDisplay() { 186 return mDefaultDisplay; 187 } 188 189 public void closeAll(IBinder token, String who, String what) { 190 mState.closeAll(token, who, what); 191 } 192 193 public void startTrimMemory(int level) { 194 mState.startTrimMemory(level); 195 } 196 197 public void endTrimMemory() { 198 mState.endTrimMemory(); 199 } 200 201 public void trimLocalMemory() { 202 mState.trimLocalMemory(); 203 } 204 205 public void dumpGfxInfo(FileDescriptor fd) { 206 mState.dumpGfxInfo(fd); 207 } 208 209 public void setStoppedState(IBinder token, boolean stopped) { 210 mState.setStoppedState(token, stopped); 211 } 212 213 public void reportNewConfiguration(Configuration config) { 214 mState.reportNewConfiguration(config); 215 } 216 217 static final class WindowManagerState { 218 private final Display mDefaultDisplay; 219 220 private View[] mViews; 221 private ViewRootImpl[] mRoots; 222 private WindowManager.LayoutParams[] mParams; 223 private boolean mNeedsEglTerminate; 224 225 private Runnable mSystemPropertyUpdater; 226 227 public WindowManagerState() { 228 mDefaultDisplay = new Display(Display.DEFAULT_DISPLAY, null); 229 } 230 231 public Display getDefaultDisplay(CompatibilityInfoHolder compatInfo) { 232 if (compatInfo == null) { 233 return mDefaultDisplay; 234 } 235 return new Display(Display.DEFAULT_DISPLAY, compatInfo); 236 } 237 238 public void addView(View view, ViewGroup.LayoutParams params, Window parentWindow, 239 CompatibilityInfoHolder cih) { 240 if (!(params instanceof WindowManager.LayoutParams)) { 241 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 242 } 243 244 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 245 if (parentWindow != null) { 246 parentWindow.adjustLayoutParamsForSubWindow(wparams); 247 } 248 249 ViewRootImpl root; 250 View panelParentView = null; 251 252 synchronized (this) { 253 // Start watching for system property changes. 254 if (mSystemPropertyUpdater == null) { 255 mSystemPropertyUpdater = new Runnable() { 256 @Override public void run() { 257 synchronized (this) { 258 synchronized (this) { 259 for (ViewRootImpl root : mRoots) { 260 root.loadSystemProperties(); 261 } 262 } 263 } 264 } 265 }; 266 SystemProperties.addChangeCallback(mSystemPropertyUpdater); 267 } 268 269 int index = findViewLocked(view, false); 270 if (index >= 0) { 271 throw new IllegalStateException("View " + view 272 + " has already been added to the window manager."); 273 } 274 275 // If this is a panel window, then find the window it is being 276 // attached to for future reference. 277 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 278 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 279 final int count = mViews != null ? mViews.length : 0; 280 for (int i=0; i<count; i++) { 281 if (mRoots[i].mWindow.asBinder() == wparams.token) { 282 panelParentView = mViews[i]; 283 } 284 } 285 } 286 287 root = new ViewRootImpl(view.getContext()); 288 if (cih == null) { 289 root.mCompatibilityInfo = new CompatibilityInfoHolder(); 290 } else { 291 root.mCompatibilityInfo = cih; 292 } 293 294 view.setLayoutParams(wparams); 295 296 if (mViews == null) { 297 index = 1; 298 mViews = new View[1]; 299 mRoots = new ViewRootImpl[1]; 300 mParams = new WindowManager.LayoutParams[1]; 301 } else { 302 index = mViews.length + 1; 303 Object[] old = mViews; 304 mViews = new View[index]; 305 System.arraycopy(old, 0, mViews, 0, index-1); 306 old = mRoots; 307 mRoots = new ViewRootImpl[index]; 308 System.arraycopy(old, 0, mRoots, 0, index-1); 309 old = mParams; 310 mParams = new WindowManager.LayoutParams[index]; 311 System.arraycopy(old, 0, mParams, 0, index-1); 312 } 313 index--; 314 315 mViews[index] = view; 316 mRoots[index] = root; 317 mParams[index] = wparams; 318 } 319 320 // do this last because it fires off messages to start doing things 321 root.setView(view, wparams, panelParentView); 322 } 323 324 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 325 if (!(params instanceof WindowManager.LayoutParams)) { 326 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 327 } 328 329 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 330 331 view.setLayoutParams(wparams); 332 333 synchronized (this) { 334 int index = findViewLocked(view, true); 335 ViewRootImpl root = mRoots[index]; 336 mParams[index] = wparams; 337 root.setLayoutParams(wparams, false); 338 } 339 } 340 341 public void removeView(View view, boolean immediate) { 342 synchronized (this) { 343 int index = findViewLocked(view, true); 344 View curView = removeViewLocked(index, immediate); 345 if (curView == view) { 346 return; 347 } 348 349 throw new IllegalStateException("Calling with view " + view 350 + " but the ViewAncestor is attached to " + curView); 351 } 352 } 353 354 public void closeAll(IBinder token, String who, String what) { 355 synchronized (this) { 356 if (mViews == null) 357 return; 358 359 int count = mViews.length; 360 //Log.i("foo", "Closing all windows of " + token); 361 for (int i=0; i<count; i++) { 362 //Log.i("foo", "@ " + i + " token " + mParams[i].token 363 // + " view " + mRoots[i].getView()); 364 if (token == null || mParams[i].token == token) { 365 ViewRootImpl root = mRoots[i]; 366 367 //Log.i("foo", "Force closing " + root); 368 if (who != null) { 369 WindowLeaked leak = new WindowLeaked( 370 what + " " + who + " has leaked window " 371 + root.getView() + " that was originally added here"); 372 leak.setStackTrace(root.getLocation().getStackTrace()); 373 Log.e(TAG, leak.getMessage(), leak); 374 } 375 376 removeViewLocked(i, false); 377 i--; 378 count--; 379 } 380 } 381 } 382 } 383 384 private View removeViewLocked(int index, boolean immediate) { 385 ViewRootImpl root = mRoots[index]; 386 View view = root.getView(); 387 388 if (view != null) { 389 InputMethodManager imm = InputMethodManager.getInstance(view.getContext()); 390 if (imm != null) { 391 imm.windowDismissed(mViews[index].getWindowToken()); 392 } 393 } 394 root.die(immediate); 395 396 final int count = mViews.length; 397 398 // remove it from the list 399 View[] tmpViews = new View[count-1]; 400 removeItem(tmpViews, mViews, index); 401 mViews = tmpViews; 402 403 ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1]; 404 removeItem(tmpRoots, mRoots, index); 405 mRoots = tmpRoots; 406 407 WindowManager.LayoutParams[] tmpParams 408 = new WindowManager.LayoutParams[count-1]; 409 removeItem(tmpParams, mParams, index); 410 mParams = tmpParams; 411 412 if (view != null) { 413 view.assignParent(null); 414 // func doesn't allow null... does it matter if we clear them? 415 //view.setLayoutParams(null); 416 } 417 return view; 418 } 419 420 private static void removeItem(Object[] dst, Object[] src, int index) { 421 if (dst.length > 0) { 422 if (index > 0) { 423 System.arraycopy(src, 0, dst, 0, index); 424 } 425 if (index < dst.length) { 426 System.arraycopy(src, index+1, dst, index, src.length-index-1); 427 } 428 } 429 } 430 431 private int findViewLocked(View view, boolean required) { 432 synchronized (this) { 433 if (mViews != null) { 434 final int count = mViews.length; 435 for (int i = 0; i < count; i++) { 436 if (mViews[i] == view) { 437 return i; 438 } 439 } 440 } 441 if (required) { 442 throw new IllegalArgumentException("View not attached to window manager"); 443 } 444 return -1; 445 } 446 } 447 448 public void startTrimMemory(int level) { 449 if (HardwareRenderer.isAvailable()) { 450 // On low-end gfx devices we trim when memory is moderate; 451 // on high-end devices we do this when low. 452 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE 453 || (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE 454 && !ActivityManager.isHighEndGfx(mDefaultDisplay))) { 455 // Destroy all hardware surfaces and resources associated to 456 // known windows 457 synchronized (this) { 458 if (mViews == null) return; 459 int count = mViews.length; 460 for (int i = 0; i < count; i++) { 461 mRoots[i].terminateHardwareResources(); 462 } 463 } 464 // Force a full memory flush 465 mNeedsEglTerminate = true; 466 HardwareRenderer.startTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE); 467 return; 468 } 469 470 HardwareRenderer.startTrimMemory(level); 471 } 472 } 473 474 public void endTrimMemory() { 475 HardwareRenderer.endTrimMemory(); 476 477 if (mNeedsEglTerminate) { 478 ManagedEGLContext.doTerminate(); 479 mNeedsEglTerminate = false; 480 } 481 } 482 483 public void trimLocalMemory() { 484 synchronized (this) { 485 if (mViews == null) return; 486 int count = mViews.length; 487 for (int i = 0; i < count; i++) { 488 mRoots[i].destroyHardwareLayers(); 489 } 490 } 491 } 492 493 public void dumpGfxInfo(FileDescriptor fd) { 494 FileOutputStream fout = new FileOutputStream(fd); 495 PrintWriter pw = new PrintWriter(fout); 496 try { 497 synchronized (this) { 498 if (mViews != null) { 499 final int count = mViews.length; 500 501 pw.println("Profile data in ms:"); 502 503 for (int i = 0; i < count; i++) { 504 ViewRootImpl root = mRoots[i]; 505 String name = getWindowName(root); 506 pw.printf("\n\t%s", name); 507 508 HardwareRenderer renderer = 509 root.getView().mAttachInfo.mHardwareRenderer; 510 if (renderer != null) { 511 renderer.dumpGfxInfo(pw); 512 } 513 } 514 515 pw.println("\nView hierarchy:\n"); 516 517 int viewsCount = 0; 518 int displayListsSize = 0; 519 int[] info = new int[2]; 520 521 for (int i = 0; i < count; i++) { 522 ViewRootImpl root = mRoots[i]; 523 root.dumpGfxInfo(info); 524 525 String name = getWindowName(root); 526 pw.printf(" %s\n %d views, %.2f kB of display lists", 527 name, info[0], info[1] / 1024.0f); 528 HardwareRenderer renderer = 529 root.getView().mAttachInfo.mHardwareRenderer; 530 if (renderer != null) { 531 pw.printf(", %d frames rendered", renderer.getFrameCount()); 532 } 533 pw.printf("\n\n"); 534 535 viewsCount += info[0]; 536 displayListsSize += info[1]; 537 } 538 539 pw.printf("\nTotal ViewRootImpl: %d\n", count); 540 pw.printf("Total Views: %d\n", viewsCount); 541 pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f); 542 } 543 } 544 } finally { 545 pw.flush(); 546 } 547 } 548 549 private static String getWindowName(ViewRootImpl root) { 550 return root.mWindowAttributes.getTitle() + "/" + 551 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode()); 552 } 553 554 public void setStoppedState(IBinder token, boolean stopped) { 555 synchronized (this) { 556 if (mViews != null) { 557 int count = mViews.length; 558 for (int i=0; i < count; i++) { 559 if (token == null || mParams[i].token == token) { 560 ViewRootImpl root = mRoots[i]; 561 root.setStopped(stopped); 562 } 563 } 564 } 565 } 566 } 567 568 public void reportNewConfiguration(Configuration config) { 569 synchronized (this) { 570 if (mViews != null) { 571 int count = mViews.length; 572 config = new Configuration(config); 573 for (int i=0; i < count; i++) { 574 ViewRootImpl root = mRoots[i]; 575 root.requestUpdateConfiguration(config); 576 } 577 } 578 } 579 } 580 } 581} 582