WindowManagerImpl.java revision fee1c69a49d03b651e481983432ade94fc07650a
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 java.util.HashMap; 20 21import android.content.res.CompatibilityInfo; 22import android.graphics.PixelFormat; 23import android.os.IBinder; 24import android.util.AndroidRuntimeException; 25import android.util.Log; 26import android.view.WindowManager; 27import android.view.inputmethod.InputMethodManager; 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 ViewAncestor[] 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 WindowManager mWindowManager; 91 private final Display mDefaultDisplay; 92 93 CompatModeWrapper(WindowManager wm, CompatibilityInfo ci) { 94 mWindowManager = wm; 95 96 // Use the original display if there is no compatibility mode 97 // to apply, or the underlying window manager is already a 98 // compatibility mode wrapper. (We assume that if it is a 99 // wrapper, it is applying the same compatibility mode.) 100 if (ci == null || wm instanceof CompatModeWrapper 101 || (!ci.isScalingRequired() && ci.supportsScreen())) { 102 mDefaultDisplay = mWindowManager.getDefaultDisplay(); 103 } else { 104 //mDefaultDisplay = mWindowManager.getDefaultDisplay(); 105 mDefaultDisplay = Display.createCompatibleDisplay( 106 mWindowManager.getDefaultDisplay().getDisplayId(), ci); 107 } 108 } 109 110 @Override 111 public void addView(View view, android.view.ViewGroup.LayoutParams params) { 112 mWindowManager.addView(view, params); 113 } 114 115 @Override 116 public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) { 117 mWindowManager.updateViewLayout(view, params); 118 119 } 120 121 @Override 122 public void removeView(View view) { 123 mWindowManager.removeView(view); 124 } 125 126 @Override 127 public Display getDefaultDisplay() { 128 return mDefaultDisplay; 129 } 130 131 @Override 132 public void removeViewImmediate(View view) { 133 mWindowManager.removeViewImmediate(view); 134 } 135 136 @Override 137 public boolean isHardwareAccelerated() { 138 return mWindowManager.isHardwareAccelerated(); 139 } 140 141 } 142 143 public static WindowManagerImpl getDefault() { 144 return sWindowManager; 145 } 146 147 public static WindowManager getDefault(CompatibilityInfo compatInfo) { 148 if (compatInfo == null || (!compatInfo.isScalingRequired() 149 && compatInfo.supportsScreen())) { 150 return sWindowManager; 151 } 152 153 synchronized (sLock) { 154 // NOTE: It would be cleaner to move the implementation of 155 // WindowManagerImpl into a static inner class, and have this 156 // public impl just call into that. Then we can make multiple 157 // instances of WindowManagerImpl for compat mode rather than 158 // having to make wrappers. 159 WindowManager wm = sCompatWindowManagers.get(compatInfo); 160 if (wm == null) { 161 wm = new CompatModeWrapper(sWindowManager, compatInfo); 162 sCompatWindowManagers.put(compatInfo, wm); 163 } 164 return wm; 165 } 166 } 167 168 public boolean isHardwareAccelerated() { 169 return false; 170 } 171 172 public void addView(View view) 173 { 174 addView(view, new WindowManager.LayoutParams( 175 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE)); 176 } 177 178 public void addView(View view, ViewGroup.LayoutParams params) 179 { 180 addView(view, params, false); 181 } 182 183 public void addViewNesting(View view, ViewGroup.LayoutParams params) 184 { 185 addView(view, params, false); 186 } 187 188 private void addView(View view, ViewGroup.LayoutParams params, boolean nest) 189 { 190 if (false) Log.v("WindowManager", "addView view=" + view); 191 192 if (!(params instanceof WindowManager.LayoutParams)) { 193 throw new IllegalArgumentException( 194 "Params must be WindowManager.LayoutParams"); 195 } 196 197 final WindowManager.LayoutParams wparams 198 = (WindowManager.LayoutParams)params; 199 200 ViewAncestor root; 201 View panelParentView = null; 202 203 synchronized (this) { 204 // Here's an odd/questionable case: if someone tries to add a 205 // view multiple times, then we simply bump up a nesting count 206 // and they need to remove the view the corresponding number of 207 // times to have it actually removed from the window manager. 208 // This is useful specifically for the notification manager, 209 // which can continually add/remove the same view as a 210 // notification gets updated. 211 int index = findViewLocked(view, false); 212 if (index >= 0) { 213 if (!nest) { 214 throw new IllegalStateException("View " + view 215 + " has already been added to the window manager."); 216 } 217 root = mRoots[index]; 218 root.mAddNesting++; 219 // Update layout parameters. 220 view.setLayoutParams(wparams); 221 root.setLayoutParams(wparams, true); 222 return; 223 } 224 225 // If this is a panel window, then find the window it is being 226 // attached to for future reference. 227 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 228 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 229 final int count = mViews != null ? mViews.length : 0; 230 for (int i=0; i<count; i++) { 231 if (mRoots[i].mWindow.asBinder() == wparams.token) { 232 panelParentView = mViews[i]; 233 } 234 } 235 } 236 237 root = new ViewAncestor(view.getContext()); 238 root.mAddNesting = 1; 239 240 view.setLayoutParams(wparams); 241 242 if (mViews == null) { 243 index = 1; 244 mViews = new View[1]; 245 mRoots = new ViewAncestor[1]; 246 mParams = new WindowManager.LayoutParams[1]; 247 } else { 248 index = mViews.length + 1; 249 Object[] old = mViews; 250 mViews = new View[index]; 251 System.arraycopy(old, 0, mViews, 0, index-1); 252 old = mRoots; 253 mRoots = new ViewAncestor[index]; 254 System.arraycopy(old, 0, mRoots, 0, index-1); 255 old = mParams; 256 mParams = new WindowManager.LayoutParams[index]; 257 System.arraycopy(old, 0, mParams, 0, index-1); 258 } 259 index--; 260 261 mViews[index] = view; 262 mRoots[index] = root; 263 mParams[index] = wparams; 264 } 265 // do this last because it fires off messages to start doing things 266 root.setView(view, wparams, panelParentView); 267 } 268 269 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 270 if (!(params instanceof WindowManager.LayoutParams)) { 271 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 272 } 273 274 final WindowManager.LayoutParams wparams 275 = (WindowManager.LayoutParams)params; 276 277 view.setLayoutParams(wparams); 278 279 synchronized (this) { 280 int index = findViewLocked(view, true); 281 ViewAncestor root = mRoots[index]; 282 mParams[index] = wparams; 283 root.setLayoutParams(wparams, false); 284 } 285 } 286 287 public void removeView(View view) { 288 synchronized (this) { 289 int index = findViewLocked(view, true); 290 View curView = removeViewLocked(index); 291 if (curView == view) { 292 return; 293 } 294 295 throw new IllegalStateException("Calling with view " + view 296 + " but the ViewAncestor is attached to " + curView); 297 } 298 } 299 300 public void removeViewImmediate(View view) { 301 synchronized (this) { 302 int index = findViewLocked(view, true); 303 ViewAncestor root = mRoots[index]; 304 View curView = root.getView(); 305 306 root.mAddNesting = 0; 307 root.die(true); 308 finishRemoveViewLocked(curView, index); 309 if (curView == view) { 310 return; 311 } 312 313 throw new IllegalStateException("Calling with view " + view 314 + " but the ViewAncestor is attached to " + curView); 315 } 316 } 317 318 View removeViewLocked(int index) { 319 ViewAncestor root = mRoots[index]; 320 View view = root.getView(); 321 322 // Don't really remove until we have matched all calls to add(). 323 root.mAddNesting--; 324 if (root.mAddNesting > 0) { 325 return view; 326 } 327 328 InputMethodManager imm = InputMethodManager.getInstance(view.getContext()); 329 if (imm != null) { 330 imm.windowDismissed(mViews[index].getWindowToken()); 331 } 332 root.die(false); 333 finishRemoveViewLocked(view, index); 334 return view; 335 } 336 337 void finishRemoveViewLocked(View view, int index) { 338 final int count = mViews.length; 339 340 // remove it from the list 341 View[] tmpViews = new View[count-1]; 342 removeItem(tmpViews, mViews, index); 343 mViews = tmpViews; 344 345 ViewAncestor[] tmpRoots = new ViewAncestor[count-1]; 346 removeItem(tmpRoots, mRoots, index); 347 mRoots = tmpRoots; 348 349 WindowManager.LayoutParams[] tmpParams 350 = new WindowManager.LayoutParams[count-1]; 351 removeItem(tmpParams, mParams, index); 352 mParams = tmpParams; 353 354 view.assignParent(null); 355 // func doesn't allow null... does it matter if we clear them? 356 //view.setLayoutParams(null); 357 } 358 359 public void closeAll(IBinder token, String who, String what) { 360 synchronized (this) { 361 if (mViews == null) 362 return; 363 364 int count = mViews.length; 365 //Log.i("foo", "Closing all windows of " + token); 366 for (int i=0; i<count; i++) { 367 //Log.i("foo", "@ " + i + " token " + mParams[i].token 368 // + " view " + mRoots[i].getView()); 369 if (token == null || mParams[i].token == token) { 370 ViewAncestor root = mRoots[i]; 371 root.mAddNesting = 1; 372 373 //Log.i("foo", "Force closing " + root); 374 if (who != null) { 375 WindowLeaked leak = new WindowLeaked( 376 what + " " + who + " has leaked window " 377 + root.getView() + " that was originally added here"); 378 leak.setStackTrace(root.getLocation().getStackTrace()); 379 Log.e("WindowManager", leak.getMessage(), leak); 380 } 381 382 removeViewLocked(i); 383 i--; 384 count--; 385 } 386 } 387 } 388 } 389 390 public void setStoppedState(IBinder token, boolean stopped) { 391 synchronized (this) { 392 if (mViews == null) 393 return; 394 int count = mViews.length; 395 for (int i=0; i<count; i++) { 396 if (token == null || mParams[i].token == token) { 397 ViewAncestor root = mRoots[i]; 398 root.setStopped(stopped); 399 } 400 } 401 } 402 } 403 404 public WindowManager.LayoutParams getRootViewLayoutParameter(View view) { 405 ViewParent vp = view.getParent(); 406 while (vp != null && !(vp instanceof ViewAncestor)) { 407 vp = vp.getParent(); 408 } 409 410 if (vp == null) return null; 411 412 ViewAncestor vr = (ViewAncestor)vp; 413 414 int N = mRoots.length; 415 for (int i = 0; i < N; ++i) { 416 if (mRoots[i] == vr) { 417 return mParams[i]; 418 } 419 } 420 421 return null; 422 } 423 424 public void closeAll() { 425 closeAll(null, null, null); 426 } 427 428 public Display getDefaultDisplay() { 429 return new Display(Display.DEFAULT_DISPLAY, null); 430 } 431 432 private static void removeItem(Object[] dst, Object[] src, int index) 433 { 434 if (dst.length > 0) { 435 if (index > 0) { 436 System.arraycopy(src, 0, dst, 0, index); 437 } 438 if (index < dst.length) { 439 System.arraycopy(src, index+1, dst, index, src.length-index-1); 440 } 441 } 442 } 443 444 private int findViewLocked(View view, boolean required) 445 { 446 synchronized (this) { 447 final int count = mViews != null ? mViews.length : 0; 448 for (int i=0; i<count; i++) { 449 if (mViews[i] == view) { 450 return i; 451 } 452 } 453 if (required) { 454 throw new IllegalArgumentException( 455 "View not attached to window manager"); 456 } 457 return -1; 458 } 459 } 460} 461