WindowManagerGlobal.java revision 98365d7663cbd82979a5700faf0050220b01084d
1/* 2 * Copyright (C) 2012 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.animation.ValueAnimator; 20import android.app.ActivityManager; 21import android.content.ComponentCallbacks2; 22import android.content.res.Configuration; 23import android.opengl.ManagedEGLContext; 24import android.os.IBinder; 25import android.os.Looper; 26import android.os.RemoteException; 27import android.os.ServiceManager; 28import android.os.SystemProperties; 29import android.util.AndroidRuntimeException; 30import android.util.Log; 31import android.view.inputmethod.InputMethodManager; 32 33import java.io.FileDescriptor; 34import java.io.FileOutputStream; 35import java.io.PrintWriter; 36 37/** 38 * Provides low-level communication with the system window manager for 39 * operations that are not associated with any particular context. 40 * 41 * This class is only used internally to implement global functions where 42 * the caller already knows the display and relevant compatibility information 43 * for the operation. For most purposes, you should use {@link WindowManager} instead 44 * since it is bound to a context. 45 * 46 * @see WindowManagerImpl 47 * @hide 48 */ 49public final class WindowManagerGlobal { 50 private static final String TAG = "WindowManager"; 51 52 /** 53 * The user is navigating with keys (not the touch screen), so 54 * navigational focus should be shown. 55 */ 56 public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1; 57 58 /** 59 * This is the first time the window is being drawn, 60 * so the client must call drawingFinished() when done 61 */ 62 public static final int RELAYOUT_RES_FIRST_TIME = 0x2; 63 64 /** 65 * The window manager has changed the surface from the last call. 66 */ 67 public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4; 68 69 /** 70 * The window manager is currently animating. It will call 71 * IWindow.doneAnimating() when done. 72 */ 73 public static final int RELAYOUT_RES_ANIMATING = 0x8; 74 75 /** 76 * Flag for relayout: the client will be later giving 77 * internal insets; as a result, the window will not impact other window 78 * layouts until the insets are given. 79 */ 80 public static final int RELAYOUT_INSETS_PENDING = 0x1; 81 82 /** 83 * Flag for relayout: the client may be currently using the current surface, 84 * so if it is to be destroyed as a part of the relayout the destroy must 85 * be deferred until later. The client will call performDeferredDestroy() 86 * when it is okay. 87 */ 88 public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2; 89 90 public static final int ADD_FLAG_APP_VISIBLE = 0x2; 91 public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE; 92 93 public static final int ADD_OKAY = 0; 94 public static final int ADD_BAD_APP_TOKEN = -1; 95 public static final int ADD_BAD_SUBWINDOW_TOKEN = -2; 96 public static final int ADD_NOT_APP_TOKEN = -3; 97 public static final int ADD_APP_EXITING = -4; 98 public static final int ADD_DUPLICATE_ADD = -5; 99 public static final int ADD_STARTING_NOT_NEEDED = -6; 100 public static final int ADD_MULTIPLE_SINGLETON = -7; 101 public static final int ADD_PERMISSION_DENIED = -8; 102 103 private static WindowManagerGlobal sDefaultWindowManager; 104 private static IWindowManager sWindowManagerService; 105 private static IWindowSession sWindowSession; 106 107 private final Object mLock = new Object(); 108 109 private View[] mViews; 110 private ViewRootImpl[] mRoots; 111 private WindowManager.LayoutParams[] mParams; 112 private boolean mNeedsEglTerminate; 113 114 private Runnable mSystemPropertyUpdater; 115 116 private WindowManagerGlobal() { 117 } 118 119 public static WindowManagerGlobal getInstance() { 120 synchronized (WindowManagerGlobal.class) { 121 if (sDefaultWindowManager == null) { 122 sDefaultWindowManager = new WindowManagerGlobal(); 123 } 124 return sDefaultWindowManager; 125 } 126 } 127 128 public static IWindowManager getWindowManagerService() { 129 synchronized (WindowManagerGlobal.class) { 130 if (sWindowManagerService == null) { 131 sWindowManagerService = IWindowManager.Stub.asInterface( 132 ServiceManager.getService("window")); 133 } 134 return sWindowManagerService; 135 } 136 } 137 138 public static IWindowSession getWindowSession(Looper mainLooper) { 139 synchronized (WindowManagerGlobal.class) { 140 if (sWindowSession == null) { 141 try { 142 InputMethodManager imm = InputMethodManager.getInstance(mainLooper); 143 IWindowManager windowManager = getWindowManagerService(); 144 sWindowSession = windowManager.openSession( 145 imm.getClient(), imm.getInputContext()); 146 float animatorScale = windowManager.getAnimationScale(2); 147 ValueAnimator.setDurationScale(animatorScale); 148 } catch (RemoteException e) { 149 Log.e(TAG, "Failed to open window session", e); 150 } 151 } 152 return sWindowSession; 153 } 154 } 155 156 public static IWindowSession peekWindowSession() { 157 synchronized (WindowManagerGlobal.class) { 158 return sWindowSession; 159 } 160 } 161 162 public void addView(View view, ViewGroup.LayoutParams params, 163 Display display, Window parentWindow) { 164 if (view == null) { 165 throw new IllegalArgumentException("view must not be null"); 166 } 167 if (display == null) { 168 throw new IllegalArgumentException("display must not be null"); 169 } 170 if (!(params instanceof WindowManager.LayoutParams)) { 171 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 172 } 173 174 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 175 if (parentWindow != null) { 176 parentWindow.adjustLayoutParamsForSubWindow(wparams); 177 } 178 179 ViewRootImpl root; 180 View panelParentView = null; 181 182 synchronized (mLock) { 183 // Start watching for system property changes. 184 if (mSystemPropertyUpdater == null) { 185 mSystemPropertyUpdater = new Runnable() { 186 @Override public void run() { 187 synchronized (mLock) { 188 for (ViewRootImpl root : mRoots) { 189 root.loadSystemProperties(); 190 } 191 } 192 } 193 }; 194 SystemProperties.addChangeCallback(mSystemPropertyUpdater); 195 } 196 197 int index = findViewLocked(view, false); 198 if (index >= 0) { 199 throw new IllegalStateException("View " + view 200 + " has already been added to the window manager."); 201 } 202 203 // If this is a panel window, then find the window it is being 204 // attached to for future reference. 205 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 206 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 207 final int count = mViews != null ? mViews.length : 0; 208 for (int i=0; i<count; i++) { 209 if (mRoots[i].mWindow.asBinder() == wparams.token) { 210 panelParentView = mViews[i]; 211 } 212 } 213 } 214 215 root = new ViewRootImpl(view.getContext(), display); 216 217 view.setLayoutParams(wparams); 218 219 if (mViews == null) { 220 index = 1; 221 mViews = new View[1]; 222 mRoots = new ViewRootImpl[1]; 223 mParams = new WindowManager.LayoutParams[1]; 224 } else { 225 index = mViews.length + 1; 226 Object[] old = mViews; 227 mViews = new View[index]; 228 System.arraycopy(old, 0, mViews, 0, index-1); 229 old = mRoots; 230 mRoots = new ViewRootImpl[index]; 231 System.arraycopy(old, 0, mRoots, 0, index-1); 232 old = mParams; 233 mParams = new WindowManager.LayoutParams[index]; 234 System.arraycopy(old, 0, mParams, 0, index-1); 235 } 236 index--; 237 238 mViews[index] = view; 239 mRoots[index] = root; 240 mParams[index] = wparams; 241 } 242 243 // do this last because it fires off messages to start doing things 244 root.setView(view, wparams, panelParentView); 245 } 246 247 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 248 if (view == null) { 249 throw new IllegalArgumentException("view must not be null"); 250 } 251 if (!(params instanceof WindowManager.LayoutParams)) { 252 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 253 } 254 255 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 256 257 view.setLayoutParams(wparams); 258 259 synchronized (mLock) { 260 int index = findViewLocked(view, true); 261 ViewRootImpl root = mRoots[index]; 262 mParams[index] = wparams; 263 root.setLayoutParams(wparams, false); 264 } 265 } 266 267 public void removeView(View view, boolean immediate) { 268 if (view == null) { 269 throw new IllegalArgumentException("view must not be null"); 270 } 271 272 synchronized (mLock) { 273 int index = findViewLocked(view, true); 274 View curView = removeViewLocked(index, immediate); 275 if (curView == view) { 276 return; 277 } 278 279 throw new IllegalStateException("Calling with view " + view 280 + " but the ViewAncestor is attached to " + curView); 281 } 282 } 283 284 public void closeAll(IBinder token, String who, String what) { 285 synchronized (mLock) { 286 if (mViews == null) 287 return; 288 289 int count = mViews.length; 290 //Log.i("foo", "Closing all windows of " + token); 291 for (int i=0; i<count; i++) { 292 //Log.i("foo", "@ " + i + " token " + mParams[i].token 293 // + " view " + mRoots[i].getView()); 294 if (token == null || mParams[i].token == token) { 295 ViewRootImpl root = mRoots[i]; 296 297 //Log.i("foo", "Force closing " + root); 298 if (who != null) { 299 WindowLeaked leak = new WindowLeaked( 300 what + " " + who + " has leaked window " 301 + root.getView() + " that was originally added here"); 302 leak.setStackTrace(root.getLocation().getStackTrace()); 303 Log.e(TAG, leak.getMessage(), leak); 304 } 305 306 removeViewLocked(i, false); 307 i--; 308 count--; 309 } 310 } 311 } 312 } 313 314 private View removeViewLocked(int index, boolean immediate) { 315 ViewRootImpl root = mRoots[index]; 316 View view = root.getView(); 317 318 if (view != null) { 319 InputMethodManager imm = InputMethodManager.getInstance(view.getContext()); 320 if (imm != null) { 321 imm.windowDismissed(mViews[index].getWindowToken()); 322 } 323 } 324 root.die(immediate); 325 326 final int count = mViews.length; 327 328 // remove it from the list 329 View[] tmpViews = new View[count-1]; 330 removeItem(tmpViews, mViews, index); 331 mViews = tmpViews; 332 333 ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1]; 334 removeItem(tmpRoots, mRoots, index); 335 mRoots = tmpRoots; 336 337 WindowManager.LayoutParams[] tmpParams 338 = new WindowManager.LayoutParams[count-1]; 339 removeItem(tmpParams, mParams, index); 340 mParams = tmpParams; 341 342 if (view != null) { 343 view.assignParent(null); 344 // func doesn't allow null... does it matter if we clear them? 345 //view.setLayoutParams(null); 346 } 347 return view; 348 } 349 350 private static void removeItem(Object[] dst, Object[] src, int index) { 351 if (dst.length > 0) { 352 if (index > 0) { 353 System.arraycopy(src, 0, dst, 0, index); 354 } 355 if (index < dst.length) { 356 System.arraycopy(src, index+1, dst, index, src.length-index-1); 357 } 358 } 359 } 360 361 private int findViewLocked(View view, boolean required) { 362 synchronized (mLock) { 363 if (mViews != null) { 364 final int count = mViews.length; 365 for (int i = 0; i < count; i++) { 366 if (mViews[i] == view) { 367 return i; 368 } 369 } 370 } 371 if (required) { 372 throw new IllegalArgumentException("View not attached to window manager"); 373 } 374 return -1; 375 } 376 } 377 378 public void startTrimMemory(int level) { 379 if (HardwareRenderer.isAvailable()) { 380 // On low-end gfx devices we trim when memory is moderate; 381 // on high-end devices we do this when low. 382 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE 383 || (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE 384 && !ActivityManager.isHighEndGfx())) { 385 // Destroy all hardware surfaces and resources associated to 386 // known windows 387 synchronized (mLock) { 388 if (mViews == null) return; 389 int count = mViews.length; 390 for (int i = 0; i < count; i++) { 391 mRoots[i].terminateHardwareResources(); 392 } 393 } 394 // Force a full memory flush 395 mNeedsEglTerminate = true; 396 HardwareRenderer.startTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE); 397 return; 398 } 399 400 HardwareRenderer.startTrimMemory(level); 401 } 402 } 403 404 public void endTrimMemory() { 405 HardwareRenderer.endTrimMemory(); 406 407 if (mNeedsEglTerminate) { 408 ManagedEGLContext.doTerminate(); 409 mNeedsEglTerminate = false; 410 } 411 } 412 413 public void trimLocalMemory() { 414 synchronized (mLock) { 415 if (mViews == null) return; 416 int count = mViews.length; 417 for (int i = 0; i < count; i++) { 418 mRoots[i].destroyHardwareLayers(); 419 } 420 } 421 } 422 423 public void dumpGfxInfo(FileDescriptor fd) { 424 FileOutputStream fout = new FileOutputStream(fd); 425 PrintWriter pw = new PrintWriter(fout); 426 try { 427 synchronized (mLock) { 428 if (mViews != null) { 429 final int count = mViews.length; 430 431 pw.println("Profile data in ms:"); 432 433 for (int i = 0; i < count; i++) { 434 ViewRootImpl root = mRoots[i]; 435 String name = getWindowName(root); 436 pw.printf("\n\t%s", name); 437 438 HardwareRenderer renderer = 439 root.getView().mAttachInfo.mHardwareRenderer; 440 if (renderer != null) { 441 renderer.dumpGfxInfo(pw); 442 } 443 } 444 445 pw.println("\nView hierarchy:\n"); 446 447 int viewsCount = 0; 448 int displayListsSize = 0; 449 int[] info = new int[2]; 450 451 for (int i = 0; i < count; i++) { 452 ViewRootImpl root = mRoots[i]; 453 root.dumpGfxInfo(info); 454 455 String name = getWindowName(root); 456 pw.printf(" %s\n %d views, %.2f kB of display lists", 457 name, info[0], info[1] / 1024.0f); 458 HardwareRenderer renderer = 459 root.getView().mAttachInfo.mHardwareRenderer; 460 if (renderer != null) { 461 pw.printf(", %d frames rendered", renderer.getFrameCount()); 462 } 463 pw.printf("\n\n"); 464 465 viewsCount += info[0]; 466 displayListsSize += info[1]; 467 } 468 469 pw.printf("\nTotal ViewRootImpl: %d\n", count); 470 pw.printf("Total Views: %d\n", viewsCount); 471 pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f); 472 } 473 } 474 } finally { 475 pw.flush(); 476 } 477 } 478 479 private static String getWindowName(ViewRootImpl root) { 480 return root.mWindowAttributes.getTitle() + "/" + 481 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode()); 482 } 483 484 public void setStoppedState(IBinder token, boolean stopped) { 485 synchronized (mLock) { 486 if (mViews != null) { 487 int count = mViews.length; 488 for (int i=0; i < count; i++) { 489 if (token == null || mParams[i].token == token) { 490 ViewRootImpl root = mRoots[i]; 491 root.setStopped(stopped); 492 } 493 } 494 } 495 } 496 } 497 498 public void reportNewConfiguration(Configuration config) { 499 synchronized (mLock) { 500 if (mViews != null) { 501 int count = mViews.length; 502 config = new Configuration(config); 503 for (int i=0; i < count; i++) { 504 ViewRootImpl root = mRoots[i]; 505 root.requestUpdateConfiguration(config); 506 } 507 } 508 } 509 } 510} 511 512final class WindowLeaked extends AndroidRuntimeException { 513 public WindowLeaked(String msg) { 514 super(msg); 515 } 516} 517