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