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