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