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