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.Nullable; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.content.Context; 24 import android.content.pm.ApplicationInfo; 25 import android.content.res.Configuration; 26 import android.graphics.HardwareRenderer; 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.lang.ref.WeakReference; 43 import java.util.ArrayList; 44 import java.util.WeakHashMap; 45 import java.util.concurrent.Executor; 46 import java.util.function.IntConsumer; 47 48 /** 49 * Provides low-level communication with the system window manager for 50 * operations that are not associated with any particular context. 51 * 52 * This class is only used internally to implement global functions where 53 * the caller already knows the display and relevant compatibility information 54 * for the operation. For most purposes, you should use {@link WindowManager} instead 55 * since it is bound to a context. 56 * 57 * @see WindowManagerImpl 58 * @hide 59 */ 60 public final class WindowManagerGlobal { 61 private static final String TAG = "WindowManager"; 62 63 private static boolean sUseBLASTAdapter = false; 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 = 1; 70 71 /** 72 * The window manager has changed the surface from the last call. 73 */ 74 public static final int RELAYOUT_RES_SURFACE_CHANGED = 1 << 1; 75 76 /** 77 * The window manager has changed the size of the surface from the last call. 78 */ 79 public static final int RELAYOUT_RES_SURFACE_RESIZED = 1 << 2; 80 81 /** 82 * In multi-window we force show the system bars. Because we don't want that the surface size 83 * changes in this mode, we instead have a flag whether the system bar sizes should always be 84 * consumed, so the app is treated like there is no virtual system bars at all. 85 */ 86 public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 1 << 3; 87 88 /** 89 * The window manager has told the window it cannot draw this frame and should retry again. 90 */ 91 public static final int RELAYOUT_RES_CANCEL_AND_REDRAW = 1 << 4; 92 93 /** 94 * Flag for relayout: the client will be later giving 95 * internal insets; as a result, the window will not impact other window 96 * layouts until the insets are given. 97 */ 98 public static final int RELAYOUT_INSETS_PENDING = 0x1; 99 100 public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1; 101 public static final int ADD_FLAG_APP_VISIBLE = 0x2; 102 public static final int ADD_FLAG_USE_BLAST = 0x8; 103 104 /** 105 * Like {@link #RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS}, but as a "hint" when adding the 106 * window. 107 */ 108 public static final int ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS = 0x4; 109 110 public static final int ADD_OKAY = 0; 111 public static final int ADD_BAD_APP_TOKEN = -1; 112 public static final int ADD_BAD_SUBWINDOW_TOKEN = -2; 113 public static final int ADD_NOT_APP_TOKEN = -3; 114 public static final int ADD_APP_EXITING = -4; 115 public static final int ADD_DUPLICATE_ADD = -5; 116 public static final int ADD_STARTING_NOT_NEEDED = -6; 117 public static final int ADD_MULTIPLE_SINGLETON = -7; 118 public static final int ADD_PERMISSION_DENIED = -8; 119 public static final int ADD_INVALID_DISPLAY = -9; 120 public static final int ADD_INVALID_TYPE = -10; 121 public static final int ADD_INVALID_USER = -11; 122 123 @UnsupportedAppUsage 124 private static WindowManagerGlobal sDefaultWindowManager; 125 @UnsupportedAppUsage 126 private static IWindowManager sWindowManagerService; 127 @UnsupportedAppUsage 128 private static IWindowSession sWindowSession; 129 130 @UnsupportedAppUsage 131 private final Object mLock = new Object(); 132 133 @UnsupportedAppUsage 134 private final ArrayList<View> mViews = new ArrayList<View>(); 135 @UnsupportedAppUsage 136 private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); 137 @UnsupportedAppUsage 138 private final ArrayList<WindowManager.LayoutParams> mParams = 139 new ArrayList<WindowManager.LayoutParams>(); 140 private final ArraySet<View> mDyingViews = new ArraySet<View>(); 141 142 private final ArrayList<ViewRootImpl> mWindowlessRoots = new ArrayList<ViewRootImpl>(); 143 144 /** A context token only has one remote registration to system. */ 145 private WeakHashMap<IBinder, ProposedRotationListenerDelegate> mProposedRotationListenerMap; 146 147 private Runnable mSystemPropertyUpdater; 148 WindowManagerGlobal()149 private WindowManagerGlobal() { 150 } 151 152 @UnsupportedAppUsage initialize()153 public static void initialize() { 154 getWindowManagerService(); 155 } 156 157 @UnsupportedAppUsage getInstance()158 public static WindowManagerGlobal getInstance() { 159 synchronized (WindowManagerGlobal.class) { 160 if (sDefaultWindowManager == null) { 161 sDefaultWindowManager = new WindowManagerGlobal(); 162 } 163 return sDefaultWindowManager; 164 } 165 } 166 167 @UnsupportedAppUsage getWindowManagerService()168 public static IWindowManager getWindowManagerService() { 169 synchronized (WindowManagerGlobal.class) { 170 if (sWindowManagerService == null) { 171 sWindowManagerService = IWindowManager.Stub.asInterface( 172 ServiceManager.getService("window")); 173 try { 174 if (sWindowManagerService != null) { 175 ValueAnimator.setDurationScale( 176 sWindowManagerService.getCurrentAnimatorScale()); 177 sUseBLASTAdapter = sWindowManagerService.useBLAST(); 178 } 179 } catch (RemoteException e) { 180 throw e.rethrowFromSystemServer(); 181 } 182 } 183 return sWindowManagerService; 184 } 185 } 186 187 @UnsupportedAppUsage getWindowSession()188 public static IWindowSession getWindowSession() { 189 synchronized (WindowManagerGlobal.class) { 190 if (sWindowSession == null) { 191 try { 192 // Emulate the legacy behavior. The global instance of InputMethodManager 193 // was instantiated here. 194 // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage 195 InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary(); 196 IWindowManager windowManager = getWindowManagerService(); 197 sWindowSession = windowManager.openSession( 198 new IWindowSessionCallback.Stub() { 199 @Override 200 public void onAnimatorScaleChanged(float scale) { 201 ValueAnimator.setDurationScale(scale); 202 } 203 }); 204 } catch (RemoteException e) { 205 throw e.rethrowFromSystemServer(); 206 } 207 } 208 return sWindowSession; 209 } 210 } 211 212 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) peekWindowSession()213 public static IWindowSession peekWindowSession() { 214 synchronized (WindowManagerGlobal.class) { 215 return sWindowSession; 216 } 217 } 218 219 /** 220 * Whether or not to use BLAST for ViewRootImpl 221 */ useBLAST()222 public static boolean useBLAST() { 223 return sUseBLASTAdapter; 224 } 225 226 @UnsupportedAppUsage getViewRootNames()227 public String[] getViewRootNames() { 228 synchronized (mLock) { 229 final int numRoots = mRoots.size(); 230 final int windowlessRoots = mWindowlessRoots.size(); 231 String[] mViewRoots = new String[numRoots + windowlessRoots]; 232 for (int i = 0; i < numRoots; ++i) { 233 mViewRoots[i] = getWindowName(mRoots.get(i)); 234 } 235 for (int i = 0; i < windowlessRoots; ++i) { 236 mViewRoots[i + numRoots] = getWindowName(mWindowlessRoots.get(i)); 237 } 238 return mViewRoots; 239 } 240 } 241 242 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getRootViews(IBinder token)243 public ArrayList<ViewRootImpl> getRootViews(IBinder token) { 244 ArrayList<ViewRootImpl> views = new ArrayList<>(); 245 synchronized (mLock) { 246 final int numRoots = mRoots.size(); 247 for (int i = 0; i < numRoots; ++i) { 248 WindowManager.LayoutParams params = mParams.get(i); 249 if (params.token == null) { 250 continue; 251 } 252 if (params.token != token) { 253 boolean isChild = false; 254 if (params.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW 255 && params.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 256 for (int j = 0 ; j < numRoots; ++j) { 257 View viewj = mViews.get(j); 258 WindowManager.LayoutParams paramsj = mParams.get(j); 259 if (params.token == viewj.getWindowToken() 260 && paramsj.token == token) { 261 isChild = true; 262 break; 263 } 264 } 265 } 266 if (!isChild) { 267 continue; 268 } 269 } 270 views.add(mRoots.get(i)); 271 } 272 } 273 return views; 274 } 275 276 /** 277 * @return the list of all views attached to the global window manager 278 */ 279 @NonNull getWindowViews()280 public ArrayList<View> getWindowViews() { 281 synchronized (mLock) { 282 return new ArrayList<>(mViews); 283 } 284 } 285 getWindowView(IBinder windowToken)286 public View getWindowView(IBinder windowToken) { 287 synchronized (mLock) { 288 final int numViews = mViews.size(); 289 for (int i = 0; i < numViews; ++i) { 290 final View view = mViews.get(i); 291 if (view.getWindowToken() == windowToken) { 292 return view; 293 } 294 } 295 } 296 return null; 297 } 298 299 @UnsupportedAppUsage getRootView(String name)300 public View getRootView(String name) { 301 synchronized (mLock) { 302 for (int i = mRoots.size() - 1; i >= 0; --i) { 303 final ViewRootImpl root = mRoots.get(i); 304 if (name.equals(getWindowName(root))) return root.getView(); 305 } 306 for (int i = mWindowlessRoots.size() - 1; i >= 0; --i) { 307 final ViewRootImpl root = mWindowlessRoots.get(i); 308 if (name.equals(getWindowName(root))) return root.getView(); 309 } 310 } 311 312 return null; 313 } 314 addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId)315 public void addView(View view, ViewGroup.LayoutParams params, 316 Display display, Window parentWindow, int userId) { 317 if (view == null) { 318 throw new IllegalArgumentException("view must not be null"); 319 } 320 if (display == null) { 321 throw new IllegalArgumentException("display must not be null"); 322 } 323 if (!(params instanceof WindowManager.LayoutParams)) { 324 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 325 } 326 327 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; 328 if (parentWindow != null) { 329 parentWindow.adjustLayoutParamsForSubWindow(wparams); 330 } else { 331 // If there's no parent, then hardware acceleration for this view is 332 // set from the application's hardware acceleration setting. 333 final Context context = view.getContext(); 334 if (context != null 335 && (context.getApplicationInfo().flags 336 & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { 337 wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 338 } 339 } 340 341 ViewRootImpl root; 342 View panelParentView = null; 343 344 synchronized (mLock) { 345 // Start watching for system property changes. 346 if (mSystemPropertyUpdater == null) { 347 mSystemPropertyUpdater = new Runnable() { 348 @Override public void run() { 349 synchronized (mLock) { 350 for (int i = mRoots.size() - 1; i >= 0; --i) { 351 mRoots.get(i).loadSystemProperties(); 352 } 353 } 354 } 355 }; 356 SystemProperties.addChangeCallback(mSystemPropertyUpdater); 357 } 358 359 int index = findViewLocked(view, false); 360 if (index >= 0) { 361 if (mDyingViews.contains(view)) { 362 // Don't wait for MSG_DIE to make it's way through root's queue. 363 mRoots.get(index).doDie(); 364 } else { 365 throw new IllegalStateException("View " + view 366 + " has already been added to the window manager."); 367 } 368 // The previous removeView() had not completed executing. Now it has. 369 } 370 371 // If this is a panel window, then find the window it is being 372 // attached to for future reference. 373 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 374 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 375 final int count = mViews.size(); 376 for (int i = 0; i < count; i++) { 377 if (mRoots.get(i).mWindow.asBinder() == wparams.token) { 378 panelParentView = mViews.get(i); 379 } 380 } 381 } 382 383 IWindowSession windowlessSession = null; 384 // If there is a parent set, but we can't find it, it may be coming 385 // from a SurfaceControlViewHost hierarchy. 386 if (wparams.token != null && panelParentView == null) { 387 for (int i = 0; i < mWindowlessRoots.size(); i++) { 388 ViewRootImpl maybeParent = mWindowlessRoots.get(i); 389 if (maybeParent.getWindowToken() == wparams.token) { 390 windowlessSession = maybeParent.getWindowSession(); 391 break; 392 } 393 } 394 } 395 396 if (windowlessSession == null) { 397 root = new ViewRootImpl(view.getContext(), display); 398 } else { 399 root = new ViewRootImpl(view.getContext(), display, 400 windowlessSession, new WindowlessWindowLayout()); 401 } 402 403 view.setLayoutParams(wparams); 404 405 mViews.add(view); 406 mRoots.add(root); 407 mParams.add(wparams); 408 409 // do this last because it fires off messages to start doing things 410 try { 411 root.setView(view, wparams, panelParentView, userId); 412 } catch (RuntimeException e) { 413 final int viewIndex = (index >= 0) ? index : (mViews.size() - 1); 414 // BadTokenException or InvalidDisplayException, clean up. 415 if (viewIndex >= 0) { 416 removeViewLocked(viewIndex, true); 417 } 418 throw e; 419 } 420 } 421 } 422 updateViewLayout(View view, ViewGroup.LayoutParams params)423 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 424 if (view == null) { 425 throw new IllegalArgumentException("view must not be null"); 426 } 427 if (!(params instanceof WindowManager.LayoutParams)) { 428 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 429 } 430 431 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 432 433 view.setLayoutParams(wparams); 434 435 synchronized (mLock) { 436 int index = findViewLocked(view, true); 437 ViewRootImpl root = mRoots.get(index); 438 mParams.remove(index); 439 mParams.add(index, wparams); 440 root.setLayoutParams(wparams, false); 441 } 442 } 443 444 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) removeView(View view, boolean immediate)445 public void removeView(View view, boolean immediate) { 446 if (view == null) { 447 throw new IllegalArgumentException("view must not be null"); 448 } 449 450 synchronized (mLock) { 451 int index = findViewLocked(view, true); 452 View curView = mRoots.get(index).getView(); 453 removeViewLocked(index, immediate); 454 if (curView == view) { 455 return; 456 } 457 458 throw new IllegalStateException("Calling with view " + view 459 + " but the ViewAncestor is attached to " + curView); 460 } 461 } 462 463 /** 464 * Remove all roots with specified token. 465 * 466 * @param token app or window token. 467 * @param who name of caller, used in logs. 468 * @param what type of caller, used in logs. 469 */ closeAll(IBinder token, String who, String what)470 public void closeAll(IBinder token, String who, String what) { 471 closeAllExceptView(token, null /* view */, who, what); 472 } 473 474 /** 475 * Remove all roots with specified token, except maybe one view. 476 * 477 * @param token app or window token. 478 * @param view view that should be should be preserved along with it's root. 479 * Pass null if everything should be removed. 480 * @param who name of caller, used in logs. 481 * @param what type of caller, used in logs. 482 */ closeAllExceptView(IBinder token, View view, String who, String what)483 public void closeAllExceptView(IBinder token, View view, String who, String what) { 484 synchronized (mLock) { 485 int count = mViews.size(); 486 for (int i = 0; i < count; i++) { 487 if ((view == null || mViews.get(i) != view) 488 && (token == null || mParams.get(i).token == token)) { 489 ViewRootImpl root = mRoots.get(i); 490 491 if (who != null) { 492 WindowLeaked leak = new WindowLeaked( 493 what + " " + who + " has leaked window " 494 + root.getView() + " that was originally added here"); 495 leak.setStackTrace(root.getLocation().getStackTrace()); 496 Log.e(TAG, "", leak); 497 } 498 499 removeViewLocked(i, false); 500 } 501 } 502 } 503 } 504 removeViewLocked(int index, boolean immediate)505 private void removeViewLocked(int index, boolean immediate) { 506 ViewRootImpl root = mRoots.get(index); 507 View view = root.getView(); 508 509 if (root != null) { 510 root.getImeFocusController().onWindowDismissed(); 511 } 512 boolean deferred = root.die(immediate); 513 if (view != null) { 514 view.assignParent(null); 515 if (deferred) { 516 mDyingViews.add(view); 517 } 518 } 519 } 520 doRemoveView(ViewRootImpl root)521 void doRemoveView(ViewRootImpl root) { 522 boolean allViewsRemoved; 523 synchronized (mLock) { 524 final int index = mRoots.indexOf(root); 525 if (index >= 0) { 526 mRoots.remove(index); 527 mParams.remove(index); 528 final View view = mViews.remove(index); 529 mDyingViews.remove(view); 530 } 531 allViewsRemoved = mRoots.isEmpty(); 532 } 533 534 // If we don't have any views anymore in our process, we no longer need the 535 // InsetsAnimationThread to save some resources. 536 if (allViewsRemoved) { 537 InsetsAnimationThread.release(); 538 } 539 } 540 findViewLocked(View view, boolean required)541 private int findViewLocked(View view, boolean required) { 542 final int index = mViews.indexOf(view); 543 if (required && index < 0) { 544 throw new IllegalArgumentException("View=" + view + " not attached to window manager"); 545 } 546 return index; 547 } 548 549 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) trimMemory(int level)550 public void trimMemory(int level) { 551 ThreadedRenderer.trimMemory(level); 552 } 553 554 /** @hide */ trimCaches(@ardwareRenderer.CacheTrimLevel int level)555 public void trimCaches(@HardwareRenderer.CacheTrimLevel int level) { 556 ThreadedRenderer.trimCaches(level); 557 } 558 dumpGfxInfo(FileDescriptor fd, String[] args)559 public void dumpGfxInfo(FileDescriptor fd, String[] args) { 560 FileOutputStream fout = new FileOutputStream(fd); 561 PrintWriter pw = new FastPrintWriter(fout); 562 try { 563 synchronized (mLock) { 564 final int count = mViews.size(); 565 566 pw.println("Profile data in ms:"); 567 568 for (int i = 0; i < count; i++) { 569 ViewRootImpl root = mRoots.get(i); 570 String name = getWindowName(root); 571 pw.printf("\n\t%s (visibility=%d)", name, root.getHostVisibility()); 572 573 ThreadedRenderer renderer = 574 root.getView().mAttachInfo.mThreadedRenderer; 575 if (renderer != null) { 576 renderer.dumpGfxInfo(pw, fd, args); 577 } 578 } 579 580 pw.println("\nView hierarchy:\n"); 581 582 ViewRootImpl.GfxInfo totals = new ViewRootImpl.GfxInfo(); 583 584 for (int i = 0; i < count; i++) { 585 ViewRootImpl root = mRoots.get(i); 586 ViewRootImpl.GfxInfo info = root.getGfxInfo(); 587 totals.add(info); 588 589 String name = getWindowName(root); 590 pw.printf(" %s\n %d views, %.2f kB of render nodes", 591 name, info.viewCount, info.renderNodeMemoryUsage / 1024.f); 592 pw.printf("\n\n"); 593 } 594 595 pw.printf("\nTotal %-15s: %d\n", "ViewRootImpl", count); 596 pw.printf("Total %-15s: %d\n", "attached Views", totals.viewCount); 597 pw.printf("Total %-15s: %.2f kB (used) / %.2f kB (capacity)\n\n", "RenderNode", 598 totals.renderNodeMemoryUsage / 1024.0f, 599 totals.renderNodeMemoryAllocated / 1024.0f); 600 } 601 } finally { 602 pw.flush(); 603 } 604 } 605 getWindowName(ViewRootImpl root)606 private static String getWindowName(ViewRootImpl root) { 607 return root.mWindowAttributes.getTitle() + "/" + 608 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode()); 609 } 610 setStoppedState(IBinder token, boolean stopped)611 public void setStoppedState(IBinder token, boolean stopped) { 612 ArrayList<ViewRootImpl> nonCurrentThreadRoots = null; 613 synchronized (mLock) { 614 int count = mViews.size(); 615 for (int i = count - 1; i >= 0; i--) { 616 if (token == null || mParams.get(i).token == token) { 617 ViewRootImpl root = mRoots.get(i); 618 // Client might remove the view by "stopped" event. 619 if (root.mThread == Thread.currentThread()) { 620 root.setWindowStopped(stopped); 621 } else { 622 if (nonCurrentThreadRoots == null) { 623 nonCurrentThreadRoots = new ArrayList<>(); 624 } 625 nonCurrentThreadRoots.add(root); 626 } 627 // Recursively forward stopped state to View's attached 628 // to this Window rather than the root application token, 629 // e.g. PopupWindow's. 630 setStoppedState(root.mAttachInfo.mWindowToken, stopped); 631 } 632 } 633 } 634 635 // Update the stopped state synchronously to ensure the surface won't be used after server 636 // side has destroyed it. This operation should be outside the lock to avoid any potential 637 // paths from setWindowStopped to WindowManagerGlobal which may cause deadlocks. 638 if (nonCurrentThreadRoots != null) { 639 for (int i = nonCurrentThreadRoots.size() - 1; i >= 0; i--) { 640 ViewRootImpl root = nonCurrentThreadRoots.get(i); 641 root.mHandler.runWithScissors(() -> root.setWindowStopped(stopped), 0); 642 } 643 } 644 } 645 reportNewConfiguration(Configuration config)646 public void reportNewConfiguration(Configuration config) { 647 synchronized (mLock) { 648 int count = mViews.size(); 649 config = new Configuration(config); 650 for (int i=0; i < count; i++) { 651 ViewRootImpl root = mRoots.get(i); 652 root.requestUpdateConfiguration(config); 653 } 654 } 655 } 656 657 /** @hide */ changeCanvasOpacity(IBinder token, boolean opaque)658 public void changeCanvasOpacity(IBinder token, boolean opaque) { 659 if (token == null) { 660 return; 661 } 662 synchronized (mLock) { 663 for (int i = mParams.size() - 1; i >= 0; --i) { 664 if (mParams.get(i).token == token) { 665 mRoots.get(i).changeCanvasOpacity(opaque); 666 return; 667 } 668 } 669 } 670 } 671 672 /** @hide */ 673 @Nullable mirrorWallpaperSurface(int displayId)674 public SurfaceControl mirrorWallpaperSurface(int displayId) { 675 try { 676 return getWindowManagerService().mirrorWallpaperSurface(displayId); 677 } catch (RemoteException e) { 678 throw e.rethrowFromSystemServer(); 679 } 680 } 681 682 /** Registers the listener to the context token and returns the current proposed rotation. */ registerProposedRotationListener(IBinder contextToken, Executor executor, IntConsumer listener)683 public void registerProposedRotationListener(IBinder contextToken, Executor executor, 684 IntConsumer listener) { 685 ProposedRotationListenerDelegate delegate; 686 synchronized (mLock) { 687 if (mProposedRotationListenerMap == null) { 688 mProposedRotationListenerMap = new WeakHashMap<>(1); 689 } 690 delegate = mProposedRotationListenerMap.get(contextToken); 691 final ProposedRotationListenerDelegate existingDelegate = delegate; 692 if (delegate == null) { 693 mProposedRotationListenerMap.put(contextToken, 694 delegate = new ProposedRotationListenerDelegate()); 695 } 696 if (!delegate.add(executor, listener)) { 697 // Duplicated listener. 698 return; 699 } 700 if (existingDelegate != null) { 701 executor.execute(() -> listener.accept(existingDelegate.mLastRotation)); 702 return; 703 } 704 } 705 try { 706 final int currentRotation = getWindowManagerService().registerProposedRotationListener( 707 contextToken, delegate); 708 delegate.onRotationChanged(currentRotation); 709 } catch (RemoteException e) { 710 throw e.rethrowFromSystemServer(); 711 } 712 } 713 714 /** Unregisters the proposed rotation listener of the given token. */ unregisterProposedRotationListener(IBinder contextToken, IntConsumer listener)715 public void unregisterProposedRotationListener(IBinder contextToken, IntConsumer listener) { 716 final ProposedRotationListenerDelegate delegate; 717 synchronized (mLock) { 718 if (mProposedRotationListenerMap == null) { 719 return; 720 } 721 delegate = mProposedRotationListenerMap.get(contextToken); 722 if (delegate == null) { 723 return; 724 } 725 if (delegate.remove(listener)) { 726 // The delegate becomes empty. 727 mProposedRotationListenerMap.remove(contextToken); 728 } else { 729 // The delegate still contains other listeners. 730 return; 731 } 732 } 733 try { 734 getWindowManagerService().removeRotationWatcher(delegate); 735 } catch (RemoteException e) { 736 e.rethrowFromSystemServer(); 737 } 738 } 739 740 private static class ProposedRotationListenerDelegate extends IRotationWatcher.Stub { 741 static class ListenerWrapper { 742 final Executor mExecutor; 743 final WeakReference<IntConsumer> mListener; 744 ListenerWrapper(Executor executor, IntConsumer listener)745 ListenerWrapper(Executor executor, IntConsumer listener) { 746 mExecutor = executor; 747 mListener = new WeakReference<>(listener); 748 } 749 } 750 751 /** The registered listeners. */ 752 private final ArrayList<ListenerWrapper> mListeners = new ArrayList<>(1); 753 /** A thread-safe copy of registered listeners for dispatching events. */ 754 private volatile ListenerWrapper[] mListenerArray; 755 int mLastRotation; 756 add(Executor executor, IntConsumer listener)757 boolean add(Executor executor, IntConsumer listener) { 758 for (int i = mListeners.size() - 1; i >= 0; i--) { 759 if (mListeners.get(i).mListener.get() == listener) { 760 // Ignore adding duplicated listener. 761 return false; 762 } 763 } 764 mListeners.add(new ListenerWrapper(executor, listener)); 765 mListenerArray = mListeners.toArray(new ListenerWrapper[0]); 766 return true; 767 } 768 remove(IntConsumer listener)769 boolean remove(IntConsumer listener) { 770 for (int i = mListeners.size() - 1; i >= 0; i--) { 771 if (mListeners.get(i).mListener.get() == listener) { 772 mListeners.remove(i); 773 mListenerArray = mListeners.toArray(new ListenerWrapper[0]); 774 return mListeners.isEmpty(); 775 } 776 } 777 return false; 778 } 779 780 @Override onRotationChanged(int rotation)781 public void onRotationChanged(int rotation) { 782 mLastRotation = rotation; 783 boolean alive = false; 784 for (ListenerWrapper listenerWrapper : mListenerArray) { 785 final IntConsumer listener = listenerWrapper.mListener.get(); 786 if (listener != null) { 787 listenerWrapper.mExecutor.execute(() -> listener.accept(rotation)); 788 alive = true; 789 } 790 } 791 if (!alive) { 792 // Unregister if there is no strong reference. 793 try { 794 getWindowManagerService().removeRotationWatcher(this); 795 } catch (RemoteException e) { 796 e.rethrowFromSystemServer(); 797 } 798 } 799 } 800 } 801 802 /** @hide */ addWindowlessRoot(ViewRootImpl impl)803 public void addWindowlessRoot(ViewRootImpl impl) { 804 synchronized (mLock) { 805 mWindowlessRoots.add(impl); 806 } 807 } 808 809 /** @hide */ removeWindowlessRoot(ViewRootImpl impl)810 public void removeWindowlessRoot(ViewRootImpl impl) { 811 synchronized (mLock) { 812 mWindowlessRoots.remove(impl); 813 } 814 } 815 setRecentsAppBehindSystemBars(boolean behindSystemBars)816 public void setRecentsAppBehindSystemBars(boolean behindSystemBars) { 817 try { 818 getWindowManagerService().setRecentsAppBehindSystemBars(behindSystemBars); 819 } catch (RemoteException e) { 820 throw e.rethrowFromSystemServer(); 821 } 822 } 823 } 824 825 final class WindowLeaked extends AndroidRuntimeException { 826 @UnsupportedAppUsage WindowLeaked(String msg)827 public WindowLeaked(String msg) { 828 super(msg); 829 } 830 } 831