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