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 WindowManagerGlobal()148 private WindowManagerGlobal() { 149 } 150 initialize()151 public static void initialize() { 152 getWindowManagerService(); 153 } 154 getInstance()155 public static WindowManagerGlobal getInstance() { 156 synchronized (WindowManagerGlobal.class) { 157 if (sDefaultWindowManager == null) { 158 sDefaultWindowManager = new WindowManagerGlobal(); 159 } 160 return sDefaultWindowManager; 161 } 162 } 163 getWindowManagerService()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 if (sWindowManagerService != null) { 171 ValueAnimator.setDurationScale( 172 sWindowManagerService.getCurrentAnimatorScale()); 173 } 174 } catch (RemoteException e) { 175 throw e.rethrowFromSystemServer(); 176 } 177 } 178 return sWindowManagerService; 179 } 180 } 181 getWindowSession()182 public static IWindowSession getWindowSession() { 183 synchronized (WindowManagerGlobal.class) { 184 if (sWindowSession == null) { 185 try { 186 InputMethodManager imm = InputMethodManager.getInstance(); 187 IWindowManager windowManager = getWindowManagerService(); 188 sWindowSession = windowManager.openSession( 189 new IWindowSessionCallback.Stub() { 190 @Override 191 public void onAnimatorScaleChanged(float scale) { 192 ValueAnimator.setDurationScale(scale); 193 } 194 }, 195 imm.getClient(), imm.getInputContext()); 196 } catch (RemoteException e) { 197 throw e.rethrowFromSystemServer(); 198 } 199 } 200 return sWindowSession; 201 } 202 } 203 peekWindowSession()204 public static IWindowSession peekWindowSession() { 205 synchronized (WindowManagerGlobal.class) { 206 return sWindowSession; 207 } 208 } 209 getViewRootNames()210 public String[] getViewRootNames() { 211 synchronized (mLock) { 212 final int numRoots = mRoots.size(); 213 String[] mViewRoots = new String[numRoots]; 214 for (int i = 0; i < numRoots; ++i) { 215 mViewRoots[i] = getWindowName(mRoots.get(i)); 216 } 217 return mViewRoots; 218 } 219 } 220 getRootViews(IBinder token)221 public ArrayList<ViewRootImpl> getRootViews(IBinder token) { 222 ArrayList<ViewRootImpl> views = new ArrayList<>(); 223 synchronized (mLock) { 224 final int numRoots = mRoots.size(); 225 for (int i = 0; i < numRoots; ++i) { 226 WindowManager.LayoutParams params = mParams.get(i); 227 if (params.token == null) { 228 continue; 229 } 230 if (params.token != token) { 231 boolean isChild = false; 232 if (params.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW 233 && params.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 234 for (int j = 0 ; j < numRoots; ++j) { 235 View viewj = mViews.get(j); 236 WindowManager.LayoutParams paramsj = mParams.get(j); 237 if (params.token == viewj.getWindowToken() 238 && paramsj.token == token) { 239 isChild = true; 240 break; 241 } 242 } 243 } 244 if (!isChild) { 245 continue; 246 } 247 } 248 views.add(mRoots.get(i)); 249 } 250 } 251 return views; 252 } 253 getWindowView(IBinder windowToken)254 public View getWindowView(IBinder windowToken) { 255 synchronized (mLock) { 256 final int numViews = mViews.size(); 257 for (int i = 0; i < numViews; ++i) { 258 final View view = mViews.get(i); 259 if (view.getWindowToken() == windowToken) { 260 return view; 261 } 262 } 263 } 264 return null; 265 } 266 getRootView(String name)267 public View getRootView(String name) { 268 synchronized (mLock) { 269 for (int i = mRoots.size() - 1; i >= 0; --i) { 270 final ViewRootImpl root = mRoots.get(i); 271 if (name.equals(getWindowName(root))) return root.getView(); 272 } 273 } 274 275 return null; 276 } 277 addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow)278 public void addView(View view, ViewGroup.LayoutParams params, 279 Display display, Window parentWindow) { 280 if (view == null) { 281 throw new IllegalArgumentException("view must not be null"); 282 } 283 if (display == null) { 284 throw new IllegalArgumentException("display must not be null"); 285 } 286 if (!(params instanceof WindowManager.LayoutParams)) { 287 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 288 } 289 290 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; 291 if (parentWindow != null) { 292 parentWindow.adjustLayoutParamsForSubWindow(wparams); 293 } else { 294 // If there's no parent, then hardware acceleration for this view is 295 // set from the application's hardware acceleration setting. 296 final Context context = view.getContext(); 297 if (context != null 298 && (context.getApplicationInfo().flags 299 & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { 300 wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 301 } 302 } 303 304 ViewRootImpl root; 305 View panelParentView = null; 306 307 synchronized (mLock) { 308 // Start watching for system property changes. 309 if (mSystemPropertyUpdater == null) { 310 mSystemPropertyUpdater = new Runnable() { 311 @Override public void run() { 312 synchronized (mLock) { 313 for (int i = mRoots.size() - 1; i >= 0; --i) { 314 mRoots.get(i).loadSystemProperties(); 315 } 316 } 317 } 318 }; 319 SystemProperties.addChangeCallback(mSystemPropertyUpdater); 320 } 321 322 int index = findViewLocked(view, false); 323 if (index >= 0) { 324 if (mDyingViews.contains(view)) { 325 // Don't wait for MSG_DIE to make it's way through root's queue. 326 mRoots.get(index).doDie(); 327 } else { 328 throw new IllegalStateException("View " + view 329 + " has already been added to the window manager."); 330 } 331 // The previous removeView() had not completed executing. Now it has. 332 } 333 334 // If this is a panel window, then find the window it is being 335 // attached to for future reference. 336 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 337 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 338 final int count = mViews.size(); 339 for (int i = 0; i < count; i++) { 340 if (mRoots.get(i).mWindow.asBinder() == wparams.token) { 341 panelParentView = mViews.get(i); 342 } 343 } 344 } 345 346 root = new ViewRootImpl(view.getContext(), display); 347 348 view.setLayoutParams(wparams); 349 350 mViews.add(view); 351 mRoots.add(root); 352 mParams.add(wparams); 353 354 // do this last because it fires off messages to start doing things 355 try { 356 root.setView(view, wparams, panelParentView); 357 } catch (RuntimeException e) { 358 // BadTokenException or InvalidDisplayException, clean up. 359 if (index >= 0) { 360 removeViewLocked(index, true); 361 } 362 throw e; 363 } 364 } 365 } 366 updateViewLayout(View view, ViewGroup.LayoutParams params)367 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 368 if (view == null) { 369 throw new IllegalArgumentException("view must not be null"); 370 } 371 if (!(params instanceof WindowManager.LayoutParams)) { 372 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 373 } 374 375 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 376 377 view.setLayoutParams(wparams); 378 379 synchronized (mLock) { 380 int index = findViewLocked(view, true); 381 ViewRootImpl root = mRoots.get(index); 382 mParams.remove(index); 383 mParams.add(index, wparams); 384 root.setLayoutParams(wparams, false); 385 } 386 } 387 removeView(View view, boolean immediate)388 public void removeView(View view, boolean immediate) { 389 if (view == null) { 390 throw new IllegalArgumentException("view must not be null"); 391 } 392 393 synchronized (mLock) { 394 int index = findViewLocked(view, true); 395 View curView = mRoots.get(index).getView(); 396 removeViewLocked(index, immediate); 397 if (curView == view) { 398 return; 399 } 400 401 throw new IllegalStateException("Calling with view " + view 402 + " but the ViewAncestor is attached to " + curView); 403 } 404 } 405 406 /** 407 * Remove all roots with specified token. 408 * 409 * @param token app or window token. 410 * @param who name of caller, used in logs. 411 * @param what type of caller, used in logs. 412 */ closeAll(IBinder token, String who, String what)413 public void closeAll(IBinder token, String who, String what) { 414 closeAllExceptView(token, null /* view */, who, what); 415 } 416 417 /** 418 * Remove all roots with specified token, except maybe one view. 419 * 420 * @param token app or window token. 421 * @param view view that should be should be preserved along with it's root. 422 * Pass null if everything should be removed. 423 * @param who name of caller, used in logs. 424 * @param what type of caller, used in logs. 425 */ closeAllExceptView(IBinder token, View view, String who, String what)426 public void closeAllExceptView(IBinder token, View view, String who, String what) { 427 synchronized (mLock) { 428 int count = mViews.size(); 429 for (int i = 0; i < count; i++) { 430 if ((view == null || mViews.get(i) != view) 431 && (token == null || mParams.get(i).token == token)) { 432 ViewRootImpl root = mRoots.get(i); 433 434 if (who != null) { 435 WindowLeaked leak = new WindowLeaked( 436 what + " " + who + " has leaked window " 437 + root.getView() + " that was originally added here"); 438 leak.setStackTrace(root.getLocation().getStackTrace()); 439 Log.e(TAG, "", leak); 440 } 441 442 removeViewLocked(i, false); 443 } 444 } 445 } 446 } 447 removeViewLocked(int index, boolean immediate)448 private void removeViewLocked(int index, boolean immediate) { 449 ViewRootImpl root = mRoots.get(index); 450 View view = root.getView(); 451 452 if (view != null) { 453 InputMethodManager imm = InputMethodManager.getInstance(); 454 if (imm != null) { 455 imm.windowDismissed(mViews.get(index).getWindowToken()); 456 } 457 } 458 boolean deferred = root.die(immediate); 459 if (view != null) { 460 view.assignParent(null); 461 if (deferred) { 462 mDyingViews.add(view); 463 } 464 } 465 } 466 doRemoveView(ViewRootImpl root)467 void doRemoveView(ViewRootImpl root) { 468 synchronized (mLock) { 469 final int index = mRoots.indexOf(root); 470 if (index >= 0) { 471 mRoots.remove(index); 472 mParams.remove(index); 473 final View view = mViews.remove(index); 474 mDyingViews.remove(view); 475 } 476 } 477 if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { 478 doTrimForeground(); 479 } 480 } 481 findViewLocked(View view, boolean required)482 private int findViewLocked(View view, boolean required) { 483 final int index = mViews.indexOf(view); 484 if (required && index < 0) { 485 throw new IllegalArgumentException("View=" + view + " not attached to window manager"); 486 } 487 return index; 488 } 489 shouldDestroyEglContext(int trimLevel)490 public static boolean shouldDestroyEglContext(int trimLevel) { 491 // On low-end gfx devices we trim when memory is moderate; 492 // on high-end devices we do this when low. 493 if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { 494 return true; 495 } 496 if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_MODERATE 497 && !ActivityManager.isHighEndGfx()) { 498 return true; 499 } 500 return false; 501 } 502 trimMemory(int level)503 public void trimMemory(int level) { 504 if (ThreadedRenderer.isAvailable()) { 505 if (shouldDestroyEglContext(level)) { 506 // Destroy all hardware surfaces and resources associated to 507 // known windows 508 synchronized (mLock) { 509 for (int i = mRoots.size() - 1; i >= 0; --i) { 510 mRoots.get(i).destroyHardwareResources(); 511 } 512 } 513 // Force a full memory flush 514 level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; 515 } 516 517 ThreadedRenderer.trimMemory(level); 518 519 if (ThreadedRenderer.sTrimForeground) { 520 doTrimForeground(); 521 } 522 } 523 } 524 trimForeground()525 public static void trimForeground() { 526 if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { 527 WindowManagerGlobal wm = WindowManagerGlobal.getInstance(); 528 wm.doTrimForeground(); 529 } 530 } 531 doTrimForeground()532 private void doTrimForeground() { 533 boolean hasVisibleWindows = false; 534 synchronized (mLock) { 535 for (int i = mRoots.size() - 1; i >= 0; --i) { 536 final ViewRootImpl root = mRoots.get(i); 537 if (root.mView != null && root.getHostVisibility() == View.VISIBLE 538 && root.mAttachInfo.mThreadedRenderer != null) { 539 hasVisibleWindows = true; 540 } else { 541 root.destroyHardwareResources(); 542 } 543 } 544 } 545 if (!hasVisibleWindows) { 546 ThreadedRenderer.trimMemory( 547 ComponentCallbacks2.TRIM_MEMORY_COMPLETE); 548 } 549 } 550 dumpGfxInfo(FileDescriptor fd, String[] args)551 public void dumpGfxInfo(FileDescriptor fd, String[] args) { 552 FileOutputStream fout = new FileOutputStream(fd); 553 PrintWriter pw = new FastPrintWriter(fout); 554 try { 555 synchronized (mLock) { 556 final int count = mViews.size(); 557 558 pw.println("Profile data in ms:"); 559 560 for (int i = 0; i < count; i++) { 561 ViewRootImpl root = mRoots.get(i); 562 String name = getWindowName(root); 563 pw.printf("\n\t%s (visibility=%d)", name, root.getHostVisibility()); 564 565 ThreadedRenderer renderer = 566 root.getView().mAttachInfo.mThreadedRenderer; 567 if (renderer != null) { 568 renderer.dumpGfxInfo(pw, fd, args); 569 } 570 } 571 572 pw.println("\nView hierarchy:\n"); 573 574 int viewsCount = 0; 575 int displayListsSize = 0; 576 int[] info = new int[2]; 577 578 for (int i = 0; i < count; i++) { 579 ViewRootImpl root = mRoots.get(i); 580 root.dumpGfxInfo(info); 581 582 String name = getWindowName(root); 583 pw.printf(" %s\n %d views, %.2f kB of display lists", 584 name, info[0], info[1] / 1024.0f); 585 pw.printf("\n\n"); 586 587 viewsCount += info[0]; 588 displayListsSize += info[1]; 589 } 590 591 pw.printf("\nTotal ViewRootImpl: %d\n", count); 592 pw.printf("Total Views: %d\n", viewsCount); 593 pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f); 594 } 595 } finally { 596 pw.flush(); 597 } 598 } 599 getWindowName(ViewRootImpl root)600 private static String getWindowName(ViewRootImpl root) { 601 return root.mWindowAttributes.getTitle() + "/" + 602 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode()); 603 } 604 setStoppedState(IBinder token, boolean stopped)605 public void setStoppedState(IBinder token, boolean stopped) { 606 synchronized (mLock) { 607 int count = mViews.size(); 608 for (int i = count - 1; i >= 0; i--) { 609 if (token == null || mParams.get(i).token == token) { 610 ViewRootImpl root = mRoots.get(i); 611 // Client might remove the view by "stopped" event. 612 root.setWindowStopped(stopped); 613 // Recursively forward stopped state to View's attached 614 // to this Window rather than the root application token, 615 // e.g. PopupWindow's. 616 setStoppedState(root.mAttachInfo.mWindowToken, stopped); 617 } 618 } 619 } 620 } 621 reportNewConfiguration(Configuration config)622 public void reportNewConfiguration(Configuration config) { 623 synchronized (mLock) { 624 int count = mViews.size(); 625 config = new Configuration(config); 626 for (int i=0; i < count; i++) { 627 ViewRootImpl root = mRoots.get(i); 628 root.requestUpdateConfiguration(config); 629 } 630 } 631 } 632 633 /** @hide */ changeCanvasOpacity(IBinder token, boolean opaque)634 public void changeCanvasOpacity(IBinder token, boolean opaque) { 635 if (token == null) { 636 return; 637 } 638 synchronized (mLock) { 639 for (int i = mParams.size() - 1; i >= 0; --i) { 640 if (mParams.get(i).token == token) { 641 mRoots.get(i).changeCanvasOpacity(opaque); 642 return; 643 } 644 } 645 } 646 } 647 } 648 649 final class WindowLeaked extends AndroidRuntimeException { WindowLeaked(String msg)650 public WindowLeaked(String msg) { 651 super(msg); 652 } 653 } 654