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