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