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