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