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