• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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