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 static android.view.Display.INVALID_DISPLAY; 20 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 21 22 import android.animation.ValueAnimator; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.compat.annotation.UnsupportedAppUsage; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.content.res.Configuration; 29 import android.graphics.HardwareRenderer; 30 import android.os.Binder; 31 import android.os.Build; 32 import android.os.IBinder; 33 import android.os.Looper; 34 import android.os.RemoteException; 35 import android.os.ServiceManager; 36 import android.os.SystemProperties; 37 import android.util.AndroidRuntimeException; 38 import android.util.ArrayMap; 39 import android.util.ArraySet; 40 import android.util.Log; 41 import android.util.Pair; 42 import android.util.SparseArray; 43 import android.view.inputmethod.InputMethodManager; 44 import android.window.ITrustedPresentationListener; 45 import android.window.InputTransferToken; 46 import android.window.TrustedPresentationThresholds; 47 48 import com.android.internal.annotations.GuardedBy; 49 import com.android.internal.util.FastPrintWriter; 50 51 import java.io.FileDescriptor; 52 import java.io.FileOutputStream; 53 import java.io.PrintWriter; 54 import java.lang.ref.WeakReference; 55 import java.util.ArrayList; 56 import java.util.WeakHashMap; 57 import java.util.concurrent.Executor; 58 import java.util.function.Consumer; 59 import java.util.function.IntConsumer; 60 61 /** 62 * Provides low-level communication with the system window manager for 63 * operations that are not associated with any particular context. 64 * 65 * This class is only used internally to implement global functions where 66 * the caller already knows the display and relevant compatibility information 67 * for the operation. For most purposes, you should use {@link WindowManager} instead 68 * since it is bound to a context. 69 * 70 * @see WindowManagerImpl 71 * @hide 72 */ 73 public final class WindowManagerGlobal { 74 private static final String TAG = "WindowManager"; 75 76 /** 77 * This is the first time the window is being drawn, 78 * so the client must call drawingFinished() when done 79 */ 80 public static final int RELAYOUT_RES_FIRST_TIME = 1; 81 82 /** 83 * The window manager has changed the surface from the last call. 84 */ 85 public static final int RELAYOUT_RES_SURFACE_CHANGED = 1 << 1; 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 = 1 << 2; 91 92 /** 93 * In multi-window we force show the system bars. Because we don't want that the surface size 94 * changes in this mode, we instead have a flag whether the system bar sizes should always be 95 * consumed, so the app is treated like there is no virtual system bars at all. 96 */ 97 public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 1 << 3; 98 99 /** 100 * The window manager has told the window it cannot draw this frame and should retry again. 101 */ 102 public static final int RELAYOUT_RES_CANCEL_AND_REDRAW = 1 << 4; 103 104 /** 105 * Flag for relayout: the client will be later giving 106 * internal insets; as a result, the window will not impact other window 107 * layouts until the insets are given. 108 */ 109 public static final int RELAYOUT_INSETS_PENDING = 0x1; 110 111 public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1; 112 public static final int ADD_FLAG_APP_VISIBLE = 0x2; 113 114 /** 115 * Like {@link #RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS}, but as a "hint" when adding the 116 * window. 117 */ 118 public static final int ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS = 0x4; 119 120 public static final int ADD_OKAY = 0; 121 public static final int ADD_BAD_APP_TOKEN = -1; 122 public static final int ADD_BAD_SUBWINDOW_TOKEN = -2; 123 public static final int ADD_NOT_APP_TOKEN = -3; 124 public static final int ADD_APP_EXITING = -4; 125 public static final int ADD_DUPLICATE_ADD = -5; 126 public static final int ADD_STARTING_NOT_NEEDED = -6; 127 public static final int ADD_MULTIPLE_SINGLETON = -7; 128 public static final int ADD_PERMISSION_DENIED = -8; 129 public static final int ADD_INVALID_DISPLAY = -9; 130 public static final int ADD_INVALID_TYPE = -10; 131 public static final int ADD_INVALID_USER = -11; 132 133 @UnsupportedAppUsage 134 private static WindowManagerGlobal sDefaultWindowManager; 135 @UnsupportedAppUsage 136 private static IWindowManager sWindowManagerService; 137 @UnsupportedAppUsage 138 private static IWindowSession sWindowSession; 139 140 @UnsupportedAppUsage 141 private final Object mLock = new Object(); 142 143 @UnsupportedAppUsage 144 private final ArrayList<View> mViews = new ArrayList<View>(); 145 @UnsupportedAppUsage 146 private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); 147 @UnsupportedAppUsage 148 private final ArrayList<WindowManager.LayoutParams> mParams = 149 new ArrayList<WindowManager.LayoutParams>(); 150 private final ArraySet<View> mDyingViews = new ArraySet<View>(); 151 152 private final ArrayList<ViewRootImpl> mWindowlessRoots = new ArrayList<ViewRootImpl>(); 153 154 /** A context token only has one remote registration to system. */ 155 private WeakHashMap<IBinder, ProposedRotationListenerDelegate> mProposedRotationListenerMap; 156 157 private Runnable mSystemPropertyUpdater; 158 159 private final TrustedPresentationListener mTrustedPresentationListener = 160 new TrustedPresentationListener(); 161 162 @GuardedBy("mSurfaceControlInputReceivers") 163 private final SparseArray<SurfaceControlInputReceiverInfo> 164 mSurfaceControlInputReceivers = new SparseArray<>(); 165 WindowManagerGlobal()166 private WindowManagerGlobal() { 167 } 168 169 @UnsupportedAppUsage initialize()170 public static void initialize() { 171 getWindowManagerService(); 172 } 173 174 @UnsupportedAppUsage getInstance()175 public static WindowManagerGlobal getInstance() { 176 synchronized (WindowManagerGlobal.class) { 177 if (sDefaultWindowManager == null) { 178 sDefaultWindowManager = new WindowManagerGlobal(); 179 } 180 return sDefaultWindowManager; 181 } 182 } 183 184 /** 185 * Sets {@link com.android.server.wm.WindowManagerService} for the system process. 186 * <p> 187 * It is needed to prevent possible deadlock. A possible scenario is: 188 * In system process, WMS holds {@link com.android.server.wm.WindowManagerGlobalLock} to call 189 * {@code WindowManagerGlobal} APIs and wait to lock {@code WindowManagerGlobal} itself 190 * (i.e. call {@link #getWindowManagerService()} in the global lock), while 191 * another component may lock {@code WindowManagerGlobal} and wait to lock 192 * {@link com.android.server.wm.WindowManagerGlobalLock}(i.e call {@link #addView} in the 193 * system process, which calls to {@link com.android.server.wm.WindowManagerService} API 194 * directly). 195 */ setWindowManagerServiceForSystemProcess(@onNull IWindowManager wms)196 public static void setWindowManagerServiceForSystemProcess(@NonNull IWindowManager wms) { 197 sWindowManagerService = wms; 198 } 199 200 @Nullable 201 @UnsupportedAppUsage getWindowManagerService()202 public static IWindowManager getWindowManagerService() { 203 if (sWindowManagerService != null) { 204 // Use WMS directly without locking WMGlobal to prevent deadlock. 205 return sWindowManagerService; 206 } 207 synchronized (WindowManagerGlobal.class) { 208 if (sWindowManagerService == null) { 209 sWindowManagerService = IWindowManager.Stub.asInterface( 210 ServiceManager.getService("window")); 211 try { 212 // Can be null if this is called before WindowManagerService is initialized. 213 if (sWindowManagerService != null) { 214 ValueAnimator.setDurationScale( 215 sWindowManagerService.getCurrentAnimatorScale()); 216 } 217 } catch (RemoteException e) { 218 throw e.rethrowFromSystemServer(); 219 } 220 } 221 return sWindowManagerService; 222 } 223 } 224 225 @UnsupportedAppUsage getWindowSession()226 public static IWindowSession getWindowSession() { 227 synchronized (WindowManagerGlobal.class) { 228 if (sWindowSession == null) { 229 try { 230 // Emulate the legacy behavior. The global instance of InputMethodManager 231 // was instantiated here. 232 // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage 233 InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary(); 234 IWindowManager windowManager = getWindowManagerService(); 235 sWindowSession = windowManager.openSession( 236 new IWindowSessionCallback.Stub() { 237 @Override 238 public void onAnimatorScaleChanged(float scale) { 239 ValueAnimator.setDurationScale(scale); 240 } 241 }); 242 } catch (RemoteException e) { 243 throw e.rethrowFromSystemServer(); 244 } 245 } 246 return sWindowSession; 247 } 248 } 249 250 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) peekWindowSession()251 public static IWindowSession peekWindowSession() { 252 synchronized (WindowManagerGlobal.class) { 253 return sWindowSession; 254 } 255 } 256 257 @UnsupportedAppUsage getViewRootNames()258 public String[] getViewRootNames() { 259 synchronized (mLock) { 260 final int numRoots = mRoots.size(); 261 final int windowlessRoots = mWindowlessRoots.size(); 262 String[] mViewRoots = new String[numRoots + windowlessRoots]; 263 for (int i = 0; i < numRoots; ++i) { 264 mViewRoots[i] = getWindowName(mRoots.get(i)); 265 } 266 for (int i = 0; i < windowlessRoots; ++i) { 267 mViewRoots[i + numRoots] = getWindowName(mWindowlessRoots.get(i)); 268 } 269 return mViewRoots; 270 } 271 } 272 273 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getRootViews(IBinder token)274 public ArrayList<ViewRootImpl> getRootViews(IBinder token) { 275 ArrayList<ViewRootImpl> views = new ArrayList<>(); 276 synchronized (mLock) { 277 final int numRoots = mRoots.size(); 278 for (int i = 0; i < numRoots; ++i) { 279 WindowManager.LayoutParams params = mParams.get(i); 280 if (params.token == null) { 281 continue; 282 } 283 if (params.token != token) { 284 boolean isChild = false; 285 if (params.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW 286 && params.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 287 for (int j = 0 ; j < numRoots; ++j) { 288 View viewj = mViews.get(j); 289 WindowManager.LayoutParams paramsj = mParams.get(j); 290 if (params.token == viewj.getWindowToken() 291 && paramsj.token == token) { 292 isChild = true; 293 break; 294 } 295 } 296 } 297 if (!isChild) { 298 continue; 299 } 300 } 301 views.add(mRoots.get(i)); 302 } 303 } 304 return views; 305 } 306 307 /** 308 * @return the list of all views attached to the global window manager 309 */ 310 @NonNull getWindowViews()311 public ArrayList<View> getWindowViews() { 312 synchronized (mLock) { 313 return new ArrayList<>(mViews); 314 } 315 } 316 getWindowView(IBinder windowToken)317 public View getWindowView(IBinder windowToken) { 318 synchronized (mLock) { 319 final int numViews = mViews.size(); 320 for (int i = 0; i < numViews; ++i) { 321 final View view = mViews.get(i); 322 if (view.getWindowToken() == windowToken) { 323 return view; 324 } 325 } 326 } 327 return null; 328 } 329 330 @UnsupportedAppUsage getRootView(String name)331 public View getRootView(String name) { 332 synchronized (mLock) { 333 for (int i = mRoots.size() - 1; i >= 0; --i) { 334 final ViewRootImpl root = mRoots.get(i); 335 if (name.equals(getWindowName(root))) return root.getView(); 336 } 337 for (int i = mWindowlessRoots.size() - 1; i >= 0; --i) { 338 final ViewRootImpl root = mWindowlessRoots.get(i); 339 if (name.equals(getWindowName(root))) return root.getView(); 340 } 341 } 342 343 return null; 344 } 345 addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId)346 public void addView(View view, ViewGroup.LayoutParams params, 347 Display display, Window parentWindow, int userId) { 348 if (view == null) { 349 throw new IllegalArgumentException("view must not be null"); 350 } 351 if (display == null) { 352 throw new IllegalArgumentException("display must not be null"); 353 } 354 if (!(params instanceof WindowManager.LayoutParams)) { 355 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 356 } 357 358 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; 359 if (parentWindow != null) { 360 parentWindow.adjustLayoutParamsForSubWindow(wparams); 361 } else { 362 // If there's no parent, then hardware acceleration for this view is 363 // set from the application's hardware acceleration setting. 364 final Context context = view.getContext(); 365 if (context != null 366 && (context.getApplicationInfo().flags 367 & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { 368 wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 369 } 370 } 371 372 ViewRootImpl root; 373 View panelParentView = null; 374 375 synchronized (mLock) { 376 // Start watching for system property changes. 377 if (mSystemPropertyUpdater == null) { 378 mSystemPropertyUpdater = new Runnable() { 379 @Override public void run() { 380 synchronized (mLock) { 381 for (int i = mRoots.size() - 1; i >= 0; --i) { 382 mRoots.get(i).loadSystemProperties(); 383 } 384 } 385 } 386 }; 387 SystemProperties.addChangeCallback(mSystemPropertyUpdater); 388 } 389 390 int index = findViewLocked(view, false); 391 if (index >= 0) { 392 if (mDyingViews.contains(view)) { 393 // Don't wait for MSG_DIE to make it's way through root's queue. 394 mRoots.get(index).doDie(); 395 } else { 396 throw new IllegalStateException("View " + view 397 + " has already been added to the window manager."); 398 } 399 // The previous removeView() had not completed executing. Now it has. 400 } 401 402 // If this is a panel window, then find the window it is being 403 // attached to for future reference. 404 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 405 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 406 final int count = mViews.size(); 407 for (int i = 0; i < count; i++) { 408 if (mRoots.get(i).mWindow.asBinder() == wparams.token) { 409 panelParentView = mViews.get(i); 410 } 411 } 412 } 413 414 IWindowSession windowlessSession = null; 415 // If there is a parent set, but we can't find it, it may be coming 416 // from a SurfaceControlViewHost hierarchy. 417 if (wparams.token != null && panelParentView == null) { 418 for (int i = 0; i < mWindowlessRoots.size(); i++) { 419 ViewRootImpl maybeParent = mWindowlessRoots.get(i); 420 if (maybeParent.getWindowToken() == wparams.token) { 421 windowlessSession = maybeParent.getWindowSession(); 422 break; 423 } 424 } 425 } 426 427 if (windowlessSession == null) { 428 root = new ViewRootImpl(view.getContext(), display); 429 } else { 430 root = new ViewRootImpl(view.getContext(), display, 431 windowlessSession, new WindowlessWindowLayout()); 432 } 433 434 view.setLayoutParams(wparams); 435 436 mViews.add(view); 437 mRoots.add(root); 438 mParams.add(wparams); 439 440 // do this last because it fires off messages to start doing things 441 try { 442 root.setView(view, wparams, panelParentView, userId); 443 } catch (RuntimeException e) { 444 final int viewIndex = (index >= 0) ? index : (mViews.size() - 1); 445 // BadTokenException or InvalidDisplayException, clean up. 446 if (viewIndex >= 0) { 447 removeViewLocked(viewIndex, true); 448 } 449 throw e; 450 } 451 } 452 } 453 updateViewLayout(View view, ViewGroup.LayoutParams params)454 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 455 if (view == null) { 456 throw new IllegalArgumentException("view must not be null"); 457 } 458 if (!(params instanceof WindowManager.LayoutParams)) { 459 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 460 } 461 462 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 463 464 view.setLayoutParams(wparams); 465 466 synchronized (mLock) { 467 int index = findViewLocked(view, true); 468 ViewRootImpl root = mRoots.get(index); 469 mParams.remove(index); 470 mParams.add(index, wparams); 471 root.setLayoutParams(wparams, false); 472 } 473 } 474 475 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) removeView(View view, boolean immediate)476 public void removeView(View view, boolean immediate) { 477 if (view == null) { 478 throw new IllegalArgumentException("view must not be null"); 479 } 480 481 synchronized (mLock) { 482 int index = findViewLocked(view, true); 483 View curView = mRoots.get(index).getView(); 484 removeViewLocked(index, immediate); 485 if (curView == view) { 486 return; 487 } 488 489 throw new IllegalStateException("Calling with view " + view 490 + " but the ViewAncestor is attached to " + curView); 491 } 492 } 493 494 /** 495 * Remove all roots with specified token. 496 * 497 * @param token app or window token. 498 * @param who name of caller, used in logs. 499 * @param what type of caller, used in logs. 500 */ closeAll(IBinder token, String who, String what)501 public void closeAll(IBinder token, String who, String what) { 502 closeAllExceptView(token, null /* view */, who, what); 503 } 504 505 /** 506 * Remove all roots with specified token, except maybe one view. 507 * 508 * @param token app or window token. 509 * @param view view that should be should be preserved along with it's root. 510 * Pass null if everything should be removed. 511 * @param who name of caller, used in logs. 512 * @param what type of caller, used in logs. 513 */ closeAllExceptView(IBinder token, View view, String who, String what)514 public void closeAllExceptView(IBinder token, View view, String who, String what) { 515 synchronized (mLock) { 516 int count = mViews.size(); 517 for (int i = 0; i < count; i++) { 518 if ((view == null || mViews.get(i) != view) 519 && (token == null || mParams.get(i).token == token)) { 520 ViewRootImpl root = mRoots.get(i); 521 522 if (who != null) { 523 WindowLeaked leak = new WindowLeaked( 524 what + " " + who + " has leaked window " 525 + root.getView() + " that was originally added here"); 526 leak.setStackTrace(root.getLocation().getStackTrace()); 527 Log.e(TAG, "", leak); 528 } 529 530 removeViewLocked(i, false); 531 } 532 } 533 } 534 } 535 removeViewLocked(int index, boolean immediate)536 private void removeViewLocked(int index, boolean immediate) { 537 ViewRootImpl root = mRoots.get(index); 538 View view = root.getView(); 539 540 if (root != null) { 541 root.getImeFocusController().onWindowDismissed(); 542 } 543 boolean deferred = root.die(immediate); 544 if (view != null) { 545 view.assignParent(null); 546 if (deferred) { 547 mDyingViews.add(view); 548 } 549 } 550 } 551 doRemoveView(ViewRootImpl root)552 void doRemoveView(ViewRootImpl root) { 553 boolean allViewsRemoved; 554 synchronized (mLock) { 555 final int index = mRoots.indexOf(root); 556 if (index >= 0) { 557 mRoots.remove(index); 558 mParams.remove(index); 559 final View view = mViews.remove(index); 560 mDyingViews.remove(view); 561 } 562 allViewsRemoved = mRoots.isEmpty(); 563 } 564 565 // If we don't have any views anymore in our process, we no longer need the 566 // InsetsAnimationThread to save some resources. 567 if (allViewsRemoved) { 568 InsetsAnimationThread.release(); 569 } 570 } 571 findViewLocked(View view, boolean required)572 private int findViewLocked(View view, boolean required) { 573 final int index = mViews.indexOf(view); 574 if (required && index < 0) { 575 throw new IllegalArgumentException("View=" + view + " not attached to window manager"); 576 } 577 return index; 578 } 579 580 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) trimMemory(int level)581 public void trimMemory(int level) { 582 ThreadedRenderer.trimMemory(level); 583 } 584 585 /** @hide */ trimCaches(@ardwareRenderer.CacheTrimLevel int level)586 public void trimCaches(@HardwareRenderer.CacheTrimLevel int level) { 587 ThreadedRenderer.trimCaches(level); 588 } 589 dumpGfxInfo(FileDescriptor fd, String[] args)590 public void dumpGfxInfo(FileDescriptor fd, String[] args) { 591 FileOutputStream fout = new FileOutputStream(fd); 592 PrintWriter pw = new FastPrintWriter(fout); 593 try { 594 synchronized (mLock) { 595 final int count = mViews.size(); 596 597 pw.println("Profile data in ms:"); 598 599 for (int i = 0; i < count; i++) { 600 ViewRootImpl root = mRoots.get(i); 601 String name = getWindowName(root); 602 pw.printf("\n\t%s (visibility=%d)", name, root.getHostVisibility()); 603 604 ThreadedRenderer renderer = 605 root.getView().mAttachInfo.mThreadedRenderer; 606 if (renderer != null) { 607 renderer.dumpGfxInfo(pw, fd, args); 608 } 609 } 610 611 pw.println("\nView hierarchy:\n"); 612 613 ViewRootImpl.GfxInfo totals = new ViewRootImpl.GfxInfo(); 614 615 for (int i = 0; i < count; i++) { 616 ViewRootImpl root = mRoots.get(i); 617 ViewRootImpl.GfxInfo info = root.getGfxInfo(); 618 totals.add(info); 619 620 String name = getWindowName(root); 621 pw.printf(" %s\n %d views, %.2f kB of render nodes", 622 name, info.viewCount, info.renderNodeMemoryUsage / 1024.f); 623 pw.printf("\n\n"); 624 } 625 626 pw.printf("\nTotal %-15s: %d\n", "ViewRootImpl", count); 627 pw.printf("Total %-15s: %d\n", "attached Views", totals.viewCount); 628 pw.printf("Total %-15s: %.2f kB (used) / %.2f kB (capacity)\n\n", "RenderNode", 629 totals.renderNodeMemoryUsage / 1024.0f, 630 totals.renderNodeMemoryAllocated / 1024.0f); 631 } 632 } finally { 633 pw.flush(); 634 } 635 } 636 getWindowName(ViewRootImpl root)637 private static String getWindowName(ViewRootImpl root) { 638 return root.mWindowAttributes.getTitle() + "/" + 639 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode()); 640 } 641 setStoppedState(IBinder token, boolean stopped)642 public void setStoppedState(IBinder token, boolean stopped) { 643 ArrayList<ViewRootImpl> nonCurrentThreadRoots = null; 644 synchronized (mLock) { 645 int count = mViews.size(); 646 for (int i = count - 1; i >= 0; i--) { 647 if (token == null || mParams.get(i).token == token) { 648 ViewRootImpl root = mRoots.get(i); 649 // Client might remove the view by "stopped" event. 650 if (root.mThread == Thread.currentThread()) { 651 root.setWindowStopped(stopped); 652 } else { 653 if (nonCurrentThreadRoots == null) { 654 nonCurrentThreadRoots = new ArrayList<>(); 655 } 656 nonCurrentThreadRoots.add(root); 657 } 658 // Recursively forward stopped state to View's attached 659 // to this Window rather than the root application token, 660 // e.g. PopupWindow's. 661 setStoppedState(root.mAttachInfo.mWindowToken, stopped); 662 } 663 } 664 } 665 666 // Update the stopped state synchronously to ensure the surface won't be used after server 667 // side has destroyed it. This operation should be outside the lock to avoid any potential 668 // paths from setWindowStopped to WindowManagerGlobal which may cause deadlocks. 669 if (nonCurrentThreadRoots != null) { 670 for (int i = nonCurrentThreadRoots.size() - 1; i >= 0; i--) { 671 ViewRootImpl root = nonCurrentThreadRoots.get(i); 672 root.mHandler.runWithScissors(() -> root.setWindowStopped(stopped), 0); 673 } 674 } 675 } 676 reportNewConfiguration(Configuration config)677 public void reportNewConfiguration(Configuration config) { 678 synchronized (mLock) { 679 int count = mViews.size(); 680 config = new Configuration(config); 681 for (int i=0; i < count; i++) { 682 ViewRootImpl root = mRoots.get(i); 683 root.requestUpdateConfiguration(config); 684 } 685 } 686 } 687 688 /** @hide */ changeCanvasOpacity(IBinder token, boolean opaque)689 public void changeCanvasOpacity(IBinder token, boolean opaque) { 690 if (token == null) { 691 return; 692 } 693 synchronized (mLock) { 694 for (int i = mParams.size() - 1; i >= 0; --i) { 695 if (mParams.get(i).token == token) { 696 mRoots.get(i).changeCanvasOpacity(opaque); 697 return; 698 } 699 } 700 } 701 } 702 703 /** @hide */ 704 @Nullable mirrorWallpaperSurface(int displayId)705 public SurfaceControl mirrorWallpaperSurface(int displayId) { 706 try { 707 return getWindowManagerService().mirrorWallpaperSurface(displayId); 708 } catch (RemoteException e) { 709 throw e.rethrowFromSystemServer(); 710 } 711 } 712 713 /** Registers the listener to the context token and returns the current proposed rotation. */ registerProposedRotationListener(IBinder contextToken, Executor executor, IntConsumer listener)714 public void registerProposedRotationListener(IBinder contextToken, Executor executor, 715 IntConsumer listener) { 716 ProposedRotationListenerDelegate delegate; 717 synchronized (mLock) { 718 if (mProposedRotationListenerMap == null) { 719 mProposedRotationListenerMap = new WeakHashMap<>(1); 720 } 721 delegate = mProposedRotationListenerMap.get(contextToken); 722 final ProposedRotationListenerDelegate existingDelegate = delegate; 723 if (delegate == null) { 724 mProposedRotationListenerMap.put(contextToken, 725 delegate = new ProposedRotationListenerDelegate()); 726 } 727 if (!delegate.add(executor, listener)) { 728 // Duplicated listener. 729 return; 730 } 731 if (existingDelegate != null) { 732 executor.execute(() -> listener.accept(existingDelegate.mLastRotation)); 733 return; 734 } 735 } 736 try { 737 final int currentRotation = getWindowManagerService().registerProposedRotationListener( 738 contextToken, delegate); 739 delegate.onRotationChanged(currentRotation); 740 } catch (RemoteException e) { 741 throw e.rethrowFromSystemServer(); 742 } 743 } 744 745 /** Unregisters the proposed rotation listener of the given token. */ unregisterProposedRotationListener(IBinder contextToken, IntConsumer listener)746 public void unregisterProposedRotationListener(IBinder contextToken, IntConsumer listener) { 747 final ProposedRotationListenerDelegate delegate; 748 synchronized (mLock) { 749 if (mProposedRotationListenerMap == null) { 750 return; 751 } 752 delegate = mProposedRotationListenerMap.get(contextToken); 753 if (delegate == null) { 754 return; 755 } 756 if (delegate.remove(listener)) { 757 // The delegate becomes empty. 758 mProposedRotationListenerMap.remove(contextToken); 759 } else { 760 // The delegate still contains other listeners. 761 return; 762 } 763 } 764 try { 765 getWindowManagerService().removeRotationWatcher(delegate); 766 } catch (RemoteException e) { 767 e.rethrowFromSystemServer(); 768 } 769 } 770 771 private static class ProposedRotationListenerDelegate extends IRotationWatcher.Stub { 772 static class ListenerWrapper { 773 final Executor mExecutor; 774 final WeakReference<IntConsumer> mListener; 775 ListenerWrapper(Executor executor, IntConsumer listener)776 ListenerWrapper(Executor executor, IntConsumer listener) { 777 mExecutor = executor; 778 mListener = new WeakReference<>(listener); 779 } 780 } 781 782 /** The registered listeners. */ 783 private final ArrayList<ListenerWrapper> mListeners = new ArrayList<>(1); 784 /** A thread-safe copy of registered listeners for dispatching events. */ 785 private volatile ListenerWrapper[] mListenerArray; 786 int mLastRotation; 787 add(Executor executor, IntConsumer listener)788 boolean add(Executor executor, IntConsumer listener) { 789 for (int i = mListeners.size() - 1; i >= 0; i--) { 790 if (mListeners.get(i).mListener.get() == listener) { 791 // Ignore adding duplicated listener. 792 return false; 793 } 794 } 795 mListeners.add(new ListenerWrapper(executor, listener)); 796 mListenerArray = mListeners.toArray(new ListenerWrapper[0]); 797 return true; 798 } 799 remove(IntConsumer listener)800 boolean remove(IntConsumer listener) { 801 for (int i = mListeners.size() - 1; i >= 0; i--) { 802 if (mListeners.get(i).mListener.get() == listener) { 803 mListeners.remove(i); 804 mListenerArray = mListeners.toArray(new ListenerWrapper[0]); 805 return mListeners.isEmpty(); 806 } 807 } 808 return false; 809 } 810 811 @Override onRotationChanged(int rotation)812 public void onRotationChanged(int rotation) { 813 mLastRotation = rotation; 814 boolean alive = false; 815 for (ListenerWrapper listenerWrapper : mListenerArray) { 816 final IntConsumer listener = listenerWrapper.mListener.get(); 817 if (listener != null) { 818 listenerWrapper.mExecutor.execute(() -> listener.accept(rotation)); 819 alive = true; 820 } 821 } 822 if (!alive) { 823 // Unregister if there is no strong reference. 824 try { 825 getWindowManagerService().removeRotationWatcher(this); 826 } catch (RemoteException e) { 827 e.rethrowFromSystemServer(); 828 } 829 } 830 } 831 } 832 registerTrustedPresentationListener(@onNull IBinder window, @NonNull TrustedPresentationThresholds thresholds, Executor executor, @NonNull Consumer<Boolean> listener)833 public void registerTrustedPresentationListener(@NonNull IBinder window, 834 @NonNull TrustedPresentationThresholds thresholds, Executor executor, 835 @NonNull Consumer<Boolean> listener) { 836 mTrustedPresentationListener.addListener(window, thresholds, listener, executor); 837 } 838 unregisterTrustedPresentationListener(@onNull Consumer<Boolean> listener)839 public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { 840 mTrustedPresentationListener.removeListener(listener); 841 } 842 createInputChannel(@onNull IBinder clientToken, @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, @Nullable InputTransferToken inputTransferToken)843 private static InputChannel createInputChannel(@NonNull IBinder clientToken, 844 @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, 845 @Nullable InputTransferToken inputTransferToken) { 846 InputChannel inputChannel = new InputChannel(); 847 try { 848 // TODO (b/329860681): Use INVALID_DISPLAY for now because the displayId will be 849 // selected in SurfaceFlinger. This should be cleaned up so grantInputChannel doesn't 850 // take in a displayId at all 851 WindowManagerGlobal.getWindowSession().grantInputChannel(INVALID_DISPLAY, 852 surfaceControl, clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, 853 inputTransferToken, surfaceControl.getName(), inputChannel); 854 } catch (RemoteException e) { 855 Log.e(TAG, "Failed to create input channel", e); 856 e.rethrowAsRuntimeException(); 857 } 858 return inputChannel; 859 } 860 removeInputChannel(IBinder clientToken)861 private static void removeInputChannel(IBinder clientToken) { 862 try { 863 WindowManagerGlobal.getWindowSession().remove(clientToken); 864 } catch (RemoteException e) { 865 Log.e(TAG, "Failed to remove input channel", e); 866 e.rethrowAsRuntimeException(); 867 } 868 } 869 registerBatchedSurfaceControlInputReceiver( @onNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver)870 InputTransferToken registerBatchedSurfaceControlInputReceiver( 871 @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, 872 @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { 873 IBinder clientToken = new Binder(); 874 InputTransferToken inputTransferToken = new InputTransferToken(); 875 InputChannel inputChannel = createInputChannel(clientToken, hostToken, 876 surfaceControl, inputTransferToken); 877 878 synchronized (mSurfaceControlInputReceivers) { 879 mSurfaceControlInputReceivers.put(surfaceControl.getLayerId(), 880 new SurfaceControlInputReceiverInfo(clientToken, 881 new BatchedInputEventReceiver(inputChannel, choreographer.getLooper(), 882 choreographer) { 883 @Override 884 public void onInputEvent(InputEvent event) { 885 boolean handled = receiver.onInputEvent(event); 886 finishInputEvent(event, handled); 887 } 888 })); 889 } 890 return inputTransferToken; 891 } 892 registerUnbatchedSurfaceControlInputReceiver( @onNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver)893 InputTransferToken registerUnbatchedSurfaceControlInputReceiver( 894 @NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl, 895 @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { 896 IBinder clientToken = new Binder(); 897 InputTransferToken inputTransferToken = new InputTransferToken(); 898 InputChannel inputChannel = createInputChannel(clientToken, hostToken, 899 surfaceControl, inputTransferToken); 900 901 synchronized (mSurfaceControlInputReceivers) { 902 mSurfaceControlInputReceivers.put(surfaceControl.getLayerId(), 903 new SurfaceControlInputReceiverInfo(clientToken, 904 new InputEventReceiver(inputChannel, looper) { 905 @Override 906 public void onInputEvent(InputEvent event) { 907 boolean handled = receiver.onInputEvent(event); 908 finishInputEvent(event, handled); 909 } 910 })); 911 } 912 return inputTransferToken; 913 } 914 unregisterSurfaceControlInputReceiver(@onNull SurfaceControl surfaceControl)915 void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) { 916 SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo; 917 synchronized (mSurfaceControlInputReceivers) { 918 surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.removeReturnOld( 919 surfaceControl.getLayerId()); 920 } 921 922 if (surfaceControlInputReceiverInfo == null) { 923 Log.w(TAG, "No registered input event receiver with sc: " + surfaceControl); 924 return; 925 } 926 removeInputChannel(surfaceControlInputReceiverInfo.mClientToken); 927 928 surfaceControlInputReceiverInfo.mInputEventReceiver.dispose(); 929 } 930 getSurfaceControlInputClientToken(@onNull SurfaceControl surfaceControl)931 IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) { 932 SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo; 933 synchronized (mSurfaceControlInputReceivers) { 934 surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.get( 935 surfaceControl.getLayerId()); 936 } 937 938 if (surfaceControlInputReceiverInfo == null) { 939 Log.w(TAG, "No registered input event receiver with sc: " + surfaceControl); 940 return null; 941 } 942 return surfaceControlInputReceiverInfo.mClientToken; 943 } 944 transferTouchGesture(@onNull InputTransferToken transferFromToken, @NonNull InputTransferToken transferToToken)945 boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken, 946 @NonNull InputTransferToken transferToToken) { 947 try { 948 return getWindowManagerService().transferTouchGesture(transferFromToken, 949 transferToToken); 950 } catch (RemoteException e) { 951 e.rethrowAsRuntimeException(); 952 } 953 return false; 954 } 955 956 private final class TrustedPresentationListener extends 957 ITrustedPresentationListener.Stub { 958 private static int sId = 0; 959 private final ArrayMap<Consumer<Boolean>, Pair<Integer, Executor>> mListeners = 960 new ArrayMap<>(); 961 962 private final Object mTplLock = new Object(); 963 addListener(IBinder window, TrustedPresentationThresholds thresholds, Consumer<Boolean> listener, Executor executor)964 private void addListener(IBinder window, TrustedPresentationThresholds thresholds, 965 Consumer<Boolean> listener, Executor executor) { 966 synchronized (mTplLock) { 967 if (mListeners.containsKey(listener)) { 968 Log.i(TAG, "Updating listener " + listener + " thresholds to " + thresholds); 969 removeListener(listener); 970 } 971 int id = sId++; 972 mListeners.put(listener, new Pair<>(id, executor)); 973 try { 974 WindowManagerGlobal.getWindowManagerService() 975 .registerTrustedPresentationListener(window, this, thresholds, id); 976 } catch (RemoteException e) { 977 e.rethrowFromSystemServer(); 978 } 979 } 980 } 981 removeListener(Consumer<Boolean> listener)982 private void removeListener(Consumer<Boolean> listener) { 983 synchronized (mTplLock) { 984 var removedListener = mListeners.remove(listener); 985 if (removedListener == null) { 986 Log.i(TAG, "listener " + listener + " does not exist."); 987 return; 988 } 989 990 try { 991 WindowManagerGlobal.getWindowManagerService() 992 .unregisterTrustedPresentationListener(this, removedListener.first); 993 } catch (RemoteException e) { 994 e.rethrowFromSystemServer(); 995 } 996 } 997 } 998 999 @Override onTrustedPresentationChanged(int[] inTrustedStateListenerIds, int[] outOfTrustedStateListenerIds)1000 public void onTrustedPresentationChanged(int[] inTrustedStateListenerIds, 1001 int[] outOfTrustedStateListenerIds) { 1002 ArrayList<Runnable> firedListeners = new ArrayList<>(); 1003 synchronized (mTplLock) { 1004 mListeners.forEach((listener, idExecutorPair) -> { 1005 final var listenerId = idExecutorPair.first; 1006 final var executor = idExecutorPair.second; 1007 for (int id : inTrustedStateListenerIds) { 1008 if (listenerId == id) { 1009 firedListeners.add(() -> executor.execute( 1010 () -> listener.accept(/*presentationState*/true))); 1011 } 1012 } 1013 for (int id : outOfTrustedStateListenerIds) { 1014 if (listenerId == id) { 1015 firedListeners.add(() -> executor.execute( 1016 () -> listener.accept(/*presentationState*/false))); 1017 } 1018 } 1019 }); 1020 } 1021 for (int i = 0; i < firedListeners.size(); i++) { 1022 firedListeners.get(i).run(); 1023 } 1024 } 1025 } 1026 1027 /** @hide */ addWindowlessRoot(ViewRootImpl impl)1028 public void addWindowlessRoot(ViewRootImpl impl) { 1029 synchronized (mLock) { 1030 mWindowlessRoots.add(impl); 1031 } 1032 } 1033 1034 /** @hide */ removeWindowlessRoot(ViewRootImpl impl)1035 public void removeWindowlessRoot(ViewRootImpl impl) { 1036 synchronized (mLock) { 1037 mWindowlessRoots.remove(impl); 1038 } 1039 } 1040 setRecentsAppBehindSystemBars(boolean behindSystemBars)1041 public void setRecentsAppBehindSystemBars(boolean behindSystemBars) { 1042 try { 1043 getWindowManagerService().setRecentsAppBehindSystemBars(behindSystemBars); 1044 } catch (RemoteException e) { 1045 throw e.rethrowFromSystemServer(); 1046 } 1047 } 1048 1049 private static class SurfaceControlInputReceiverInfo { 1050 final IBinder mClientToken; 1051 final InputEventReceiver mInputEventReceiver; 1052 SurfaceControlInputReceiverInfo(IBinder clientToken, InputEventReceiver inputEventReceiver)1053 private SurfaceControlInputReceiverInfo(IBinder clientToken, 1054 InputEventReceiver inputEventReceiver) { 1055 mClientToken = clientToken; 1056 mInputEventReceiver = inputEventReceiver; 1057 } 1058 } 1059 } 1060 1061 final class WindowLeaked extends AndroidRuntimeException { 1062 @UnsupportedAppUsage WindowLeaked(String msg)1063 public WindowLeaked(String msg) { 1064 super(msg); 1065 } 1066 } 1067