WindowManagerGlobal.java revision 0d1c27a713cb49de8f6f4fd0a129baa883153921
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 void initialize() { 128 getWindowManagerService(); 129 } 130 131 public static WindowManagerGlobal getInstance() { 132 synchronized (WindowManagerGlobal.class) { 133 if (sDefaultWindowManager == null) { 134 sDefaultWindowManager = new WindowManagerGlobal(); 135 } 136 return sDefaultWindowManager; 137 } 138 } 139 140 public static IWindowManager getWindowManagerService() { 141 synchronized (WindowManagerGlobal.class) { 142 if (sWindowManagerService == null) { 143 sWindowManagerService = IWindowManager.Stub.asInterface( 144 ServiceManager.getService("window")); 145 try { 146 sWindowManagerService = getWindowManagerService(); 147 ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale()); 148 } catch (RemoteException e) { 149 Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e); 150 } 151 } 152 return sWindowManagerService; 153 } 154 } 155 156 public static IWindowSession getWindowSession() { 157 synchronized (WindowManagerGlobal.class) { 158 if (sWindowSession == null) { 159 try { 160 InputMethodManager imm = InputMethodManager.getInstance(); 161 IWindowManager windowManager = getWindowManagerService(); 162 sWindowSession = windowManager.openSession( 163 new IWindowSessionCallback.Stub() { 164 @Override 165 public void onAnimatorScaleChanged(float scale) { 166 ValueAnimator.setDurationScale(scale); 167 } 168 }, 169 imm.getClient(), imm.getInputContext()); 170 } catch (RemoteException e) { 171 Log.e(TAG, "Failed to open window session", e); 172 } 173 } 174 return sWindowSession; 175 } 176 } 177 178 public static IWindowSession peekWindowSession() { 179 synchronized (WindowManagerGlobal.class) { 180 return sWindowSession; 181 } 182 } 183 184 /** 185 * Sets the default token to use in {@link #addView} when no parent window 186 * token is available and no token has been explicitly set in the view's 187 * layout params. 188 * 189 * @param token Default window token to apply to added views. 190 */ 191 public void setDefaultToken(IBinder token) { 192 mDefaultToken = token; 193 } 194 195 public String[] getViewRootNames() { 196 synchronized (mLock) { 197 final int numRoots = mRoots.size(); 198 String[] mViewRoots = new String[numRoots]; 199 for (int i = 0; i < numRoots; ++i) { 200 mViewRoots[i] = getWindowName(mRoots.get(i)); 201 } 202 return mViewRoots; 203 } 204 } 205 206 public View getRootView(String name) { 207 synchronized (mLock) { 208 for (int i = mRoots.size() - 1; i >= 0; --i) { 209 final ViewRootImpl root = mRoots.get(i); 210 if (name.equals(getWindowName(root))) return root.getView(); 211 } 212 } 213 214 return null; 215 } 216 217 public void addView(View view, ViewGroup.LayoutParams params, 218 Display display, Window parentWindow) { 219 if (view == null) { 220 throw new IllegalArgumentException("view must not be null"); 221 } 222 if (display == null) { 223 throw new IllegalArgumentException("display must not be null"); 224 } 225 if (!(params instanceof WindowManager.LayoutParams)) { 226 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 227 } 228 229 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 230 if (parentWindow != null) { 231 parentWindow.adjustLayoutParamsForSubWindow(wparams); 232 } else { 233 // If there's no parent and we're running on L or above (or in the 234 // system context), assume we want hardware acceleration. 235 final Context context = view.getContext(); 236 if (context != null 237 && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { 238 wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 239 } 240 } 241 242 if (wparams.token == null && mDefaultToken != null) { 243 wparams.token = mDefaultToken; 244 } 245 246 ViewRootImpl root; 247 View panelParentView = null; 248 249 synchronized (mLock) { 250 // Start watching for system property changes. 251 if (mSystemPropertyUpdater == null) { 252 mSystemPropertyUpdater = new Runnable() { 253 @Override public void run() { 254 synchronized (mLock) { 255 for (int i = mRoots.size() - 1; i >= 0; --i) { 256 mRoots.get(i).loadSystemProperties(); 257 } 258 } 259 } 260 }; 261 SystemProperties.addChangeCallback(mSystemPropertyUpdater); 262 } 263 264 int index = findViewLocked(view, false); 265 if (index >= 0) { 266 if (mDyingViews.contains(view)) { 267 // Don't wait for MSG_DIE to make it's way through root's queue. 268 mRoots.get(index).doDie(); 269 } else { 270 throw new IllegalStateException("View " + view 271 + " has already been added to the window manager."); 272 } 273 // The previous removeView() had not completed executing. Now it has. 274 } 275 276 // If this is a panel window, then find the window it is being 277 // attached to for future reference. 278 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 279 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 280 final int count = mViews.size(); 281 for (int i = 0; i < count; i++) { 282 if (mRoots.get(i).mWindow.asBinder() == wparams.token) { 283 panelParentView = mViews.get(i); 284 } 285 } 286 } 287 288 root = new ViewRootImpl(view.getContext(), display); 289 290 view.setLayoutParams(wparams); 291 292 mViews.add(view); 293 mRoots.add(root); 294 mParams.add(wparams); 295 } 296 297 // do this last because it fires off messages to start doing things 298 try { 299 root.setView(view, wparams, panelParentView); 300 } catch (RuntimeException e) { 301 // BadTokenException or InvalidDisplayException, clean up. 302 synchronized (mLock) { 303 final int index = findViewLocked(view, false); 304 if (index >= 0) { 305 removeViewLocked(index, true); 306 } 307 } 308 throw e; 309 } 310 } 311 312 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 313 if (view == null) { 314 throw new IllegalArgumentException("view must not be null"); 315 } 316 if (!(params instanceof WindowManager.LayoutParams)) { 317 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 318 } 319 320 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 321 322 view.setLayoutParams(wparams); 323 324 synchronized (mLock) { 325 int index = findViewLocked(view, true); 326 ViewRootImpl root = mRoots.get(index); 327 mParams.remove(index); 328 mParams.add(index, wparams); 329 root.setLayoutParams(wparams, false); 330 } 331 } 332 333 public void removeView(View view, boolean immediate) { 334 if (view == null) { 335 throw new IllegalArgumentException("view must not be null"); 336 } 337 338 synchronized (mLock) { 339 int index = findViewLocked(view, true); 340 View curView = mRoots.get(index).getView(); 341 removeViewLocked(index, immediate); 342 if (curView == view) { 343 return; 344 } 345 346 throw new IllegalStateException("Calling with view " + view 347 + " but the ViewAncestor is attached to " + curView); 348 } 349 } 350 351 public void closeAll(IBinder token, String who, String what) { 352 synchronized (mLock) { 353 int count = mViews.size(); 354 //Log.i("foo", "Closing all windows of " + token); 355 for (int i = 0; i < count; i++) { 356 //Log.i("foo", "@ " + i + " token " + mParams[i].token 357 // + " view " + mRoots[i].getView()); 358 if (token == null || mParams.get(i).token == token) { 359 ViewRootImpl root = mRoots.get(i); 360 361 //Log.i("foo", "Force closing " + root); 362 if (who != null) { 363 WindowLeaked leak = new WindowLeaked( 364 what + " " + who + " has leaked window " 365 + root.getView() + " that was originally added here"); 366 leak.setStackTrace(root.getLocation().getStackTrace()); 367 Log.e(TAG, "", leak); 368 } 369 370 removeViewLocked(i, false); 371 } 372 } 373 } 374 } 375 376 private void removeViewLocked(int index, boolean immediate) { 377 ViewRootImpl root = mRoots.get(index); 378 View view = root.getView(); 379 380 if (view != null) { 381 InputMethodManager imm = InputMethodManager.getInstance(); 382 if (imm != null) { 383 imm.windowDismissed(mViews.get(index).getWindowToken()); 384 } 385 } 386 boolean deferred = root.die(immediate); 387 if (view != null) { 388 view.assignParent(null); 389 if (deferred) { 390 mDyingViews.add(view); 391 } 392 } 393 } 394 395 void doRemoveView(ViewRootImpl root) { 396 synchronized (mLock) { 397 final int index = mRoots.indexOf(root); 398 if (index >= 0) { 399 mRoots.remove(index); 400 mParams.remove(index); 401 final View view = mViews.remove(index); 402 mDyingViews.remove(view); 403 } 404 } 405 if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) { 406 doTrimForeground(); 407 } 408 } 409 410 private int findViewLocked(View view, boolean required) { 411 final int index = mViews.indexOf(view); 412 if (required && index < 0) { 413 throw new IllegalArgumentException("View=" + view + " not attached to window manager"); 414 } 415 return index; 416 } 417 418 public static boolean shouldDestroyEglContext(int trimLevel) { 419 // On low-end gfx devices we trim when memory is moderate; 420 // on high-end devices we do this when low. 421 if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { 422 return true; 423 } 424 if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_MODERATE 425 && !ActivityManager.isHighEndGfx()) { 426 return true; 427 } 428 return false; 429 } 430 431 public void trimMemory(int level) { 432 if (HardwareRenderer.isAvailable()) { 433 if (shouldDestroyEglContext(level)) { 434 // Destroy all hardware surfaces and resources associated to 435 // known windows 436 synchronized (mLock) { 437 for (int i = mRoots.size() - 1; i >= 0; --i) { 438 mRoots.get(i).destroyHardwareResources(); 439 } 440 } 441 // Force a full memory flush 442 level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; 443 } 444 445 HardwareRenderer.trimMemory(level); 446 447 if (HardwareRenderer.sTrimForeground) { 448 doTrimForeground(); 449 } 450 } 451 } 452 453 public static void trimForeground() { 454 if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) { 455 WindowManagerGlobal wm = WindowManagerGlobal.getInstance(); 456 wm.doTrimForeground(); 457 } 458 } 459 460 private void doTrimForeground() { 461 boolean hasVisibleWindows = false; 462 synchronized (mLock) { 463 for (int i = mRoots.size() - 1; i >= 0; --i) { 464 final ViewRootImpl root = mRoots.get(i); 465 if (root.mView != null && root.getHostVisibility() == View.VISIBLE 466 && root.mAttachInfo.mHardwareRenderer != null) { 467 hasVisibleWindows = true; 468 } else { 469 root.destroyHardwareResources(); 470 } 471 } 472 } 473 if (!hasVisibleWindows) { 474 HardwareRenderer.trimMemory( 475 ComponentCallbacks2.TRIM_MEMORY_COMPLETE); 476 } 477 } 478 479 public void dumpGfxInfo(FileDescriptor fd) { 480 FileOutputStream fout = new FileOutputStream(fd); 481 PrintWriter pw = new FastPrintWriter(fout); 482 try { 483 synchronized (mLock) { 484 final int count = mViews.size(); 485 486 pw.println("Profile data in ms:"); 487 488 for (int i = 0; i < count; i++) { 489 ViewRootImpl root = mRoots.get(i); 490 String name = getWindowName(root); 491 pw.printf("\n\t%s (visibility=%d)", name, root.getHostVisibility()); 492 493 HardwareRenderer renderer = 494 root.getView().mAttachInfo.mHardwareRenderer; 495 if (renderer != null) { 496 renderer.dumpGfxInfo(pw, fd); 497 } 498 } 499 500 pw.println("\nView hierarchy:\n"); 501 502 int viewsCount = 0; 503 int displayListsSize = 0; 504 int[] info = new int[2]; 505 506 for (int i = 0; i < count; i++) { 507 ViewRootImpl root = mRoots.get(i); 508 root.dumpGfxInfo(info); 509 510 String name = getWindowName(root); 511 pw.printf(" %s\n %d views, %.2f kB of display lists", 512 name, info[0], info[1] / 1024.0f); 513 pw.printf("\n\n"); 514 515 viewsCount += info[0]; 516 displayListsSize += info[1]; 517 } 518 519 pw.printf("\nTotal ViewRootImpl: %d\n", count); 520 pw.printf("Total Views: %d\n", viewsCount); 521 pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f); 522 } 523 } finally { 524 pw.flush(); 525 } 526 } 527 528 private static String getWindowName(ViewRootImpl root) { 529 return root.mWindowAttributes.getTitle() + "/" + 530 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode()); 531 } 532 533 public void setStoppedState(IBinder token, boolean stopped) { 534 synchronized (mLock) { 535 int count = mViews.size(); 536 for (int i = 0; i < count; i++) { 537 if (token == null || mParams.get(i).token == token) { 538 ViewRootImpl root = mRoots.get(i); 539 root.setStopped(stopped); 540 } 541 } 542 } 543 } 544 545 public void reportNewConfiguration(Configuration config) { 546 synchronized (mLock) { 547 int count = mViews.size(); 548 config = new Configuration(config); 549 for (int i=0; i < count; i++) { 550 ViewRootImpl root = mRoots.get(i); 551 root.requestUpdateConfiguration(config); 552 } 553 } 554 } 555 556 /** @hide */ 557 public void changeCanvasOpacity(IBinder token, boolean opaque) { 558 if (token == null) { 559 return; 560 } 561 synchronized (mLock) { 562 for (int i = mParams.size() - 1; i >= 0; --i) { 563 if (mParams.get(i).token == token) { 564 mRoots.get(i).changeCanvasOpacity(opaque); 565 return; 566 } 567 } 568 } 569 } 570} 571 572final class WindowLeaked extends AndroidRuntimeException { 573 public WindowLeaked(String msg) { 574 super(msg); 575 } 576} 577