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