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