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