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