Home | History | Annotate | Download | only in view
      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.UnsupportedAppUsage;
     22 import android.app.ActivityManager;
     23 import android.content.ComponentCallbacks2;
     24 import android.content.Context;
     25 import android.content.pm.ApplicationInfo;
     26 import android.content.res.Configuration;
     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.util.ArrayList;
     43 
     44 /**
     45  * Provides low-level communication with the system window manager for
     46  * operations that are not associated with any particular context.
     47  *
     48  * This class is only used internally to implement global functions where
     49  * the caller already knows the display and relevant compatibility information
     50  * for the operation.  For most purposes, you should use {@link WindowManager} instead
     51  * since it is bound to a context.
     52  *
     53  * @see WindowManagerImpl
     54  * @hide
     55  */
     56 public final class WindowManagerGlobal {
     57     private static final String TAG = "WindowManager";
     58 
     59     /**
     60      * The user is navigating with keys (not the touch screen), so
     61      * navigational focus should be shown.
     62      */
     63     public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1;
     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 = 0x2;
     70 
     71     /**
     72      * The window manager has changed the surface from the last call.
     73      */
     74     public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
     75 
     76     /**
     77      * The window is being resized by dragging on the docked divider. The client should render
     78      * at (0, 0) and extend its background to the background frame passed into
     79      * {@link IWindow#resized}.
     80      */
     81     public static final int RELAYOUT_RES_DRAG_RESIZING_DOCKED = 0x8;
     82 
     83     /**
     84      * The window is being resized by dragging one of the window corners,
     85      * in this case the surface would be fullscreen-sized. The client should
     86      * render to the actual frame location (instead of (0,curScrollY)).
     87      */
     88     public static final int RELAYOUT_RES_DRAG_RESIZING_FREEFORM = 0x10;
     89 
     90     /**
     91      * The window manager has changed the size of the surface from the last call.
     92      */
     93     public static final int RELAYOUT_RES_SURFACE_RESIZED = 0x20;
     94 
     95     /**
     96      * In multi-window we force show the system bars. Because we don't want that the surface size
     97      * changes in this mode, we instead have a flag whether the system bar sizes should always be
     98      * consumed, so the app is treated like there is no virtual system bars at all.
     99      */
    100     public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 0x40;
    101 
    102     /**
    103      * Flag for relayout: the client will be later giving
    104      * internal insets; as a result, the window will not impact other window
    105      * layouts until the insets are given.
    106      */
    107     public static final int RELAYOUT_INSETS_PENDING = 0x1;
    108 
    109     /**
    110      * Flag for relayout: the client may be currently using the current surface,
    111      * so if it is to be destroyed as a part of the relayout the destroy must
    112      * be deferred until later.  The client will call performDeferredDestroy()
    113      * when it is okay.
    114      */
    115     public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
    116 
    117     public static final int ADD_FLAG_APP_VISIBLE = 0x2;
    118     public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE;
    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 
    138     @UnsupportedAppUsage
    139     private static WindowManagerGlobal sDefaultWindowManager;
    140     @UnsupportedAppUsage
    141     private static IWindowManager sWindowManagerService;
    142     @UnsupportedAppUsage
    143     private static IWindowSession sWindowSession;
    144 
    145     @UnsupportedAppUsage
    146     private final Object mLock = new Object();
    147 
    148     @UnsupportedAppUsage
    149     private final ArrayList<View> mViews = new ArrayList<View>();
    150     @UnsupportedAppUsage
    151     private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    152     @UnsupportedAppUsage
    153     private final ArrayList<WindowManager.LayoutParams> mParams =
    154             new ArrayList<WindowManager.LayoutParams>();
    155     private final ArraySet<View> mDyingViews = new ArraySet<View>();
    156 
    157     private Runnable mSystemPropertyUpdater;
    158 
    159     private WindowManagerGlobal() {
    160     }
    161 
    162     @UnsupportedAppUsage
    163     public static void initialize() {
    164         getWindowManagerService();
    165     }
    166 
    167     @UnsupportedAppUsage
    168     public static WindowManagerGlobal getInstance() {
    169         synchronized (WindowManagerGlobal.class) {
    170             if (sDefaultWindowManager == null) {
    171                 sDefaultWindowManager = new WindowManagerGlobal();
    172             }
    173             return sDefaultWindowManager;
    174         }
    175     }
    176 
    177     @UnsupportedAppUsage
    178     public static IWindowManager getWindowManagerService() {
    179         synchronized (WindowManagerGlobal.class) {
    180             if (sWindowManagerService == null) {
    181                 sWindowManagerService = IWindowManager.Stub.asInterface(
    182                         ServiceManager.getService("window"));
    183                 try {
    184                     if (sWindowManagerService != null) {
    185                         ValueAnimator.setDurationScale(
    186                                 sWindowManagerService.getCurrentAnimatorScale());
    187                     }
    188                 } catch (RemoteException e) {
    189                     throw e.rethrowFromSystemServer();
    190                 }
    191             }
    192             return sWindowManagerService;
    193         }
    194     }
    195 
    196     @UnsupportedAppUsage
    197     public static IWindowSession getWindowSession() {
    198         synchronized (WindowManagerGlobal.class) {
    199             if (sWindowSession == null) {
    200                 try {
    201                     // Emulate the legacy behavior.  The global instance of InputMethodManager
    202                     // was instantiated here.
    203                     // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
    204                     InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
    205                     IWindowManager windowManager = getWindowManagerService();
    206                     sWindowSession = windowManager.openSession(
    207                             new IWindowSessionCallback.Stub() {
    208                                 @Override
    209                                 public void onAnimatorScaleChanged(float scale) {
    210                                     ValueAnimator.setDurationScale(scale);
    211                                 }
    212                             });
    213                 } catch (RemoteException e) {
    214                     throw e.rethrowFromSystemServer();
    215                 }
    216             }
    217             return sWindowSession;
    218         }
    219     }
    220 
    221     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    222     public static IWindowSession peekWindowSession() {
    223         synchronized (WindowManagerGlobal.class) {
    224             return sWindowSession;
    225         }
    226     }
    227 
    228     @UnsupportedAppUsage
    229     public String[] getViewRootNames() {
    230         synchronized (mLock) {
    231             final int numRoots = mRoots.size();
    232             String[] mViewRoots = new String[numRoots];
    233             for (int i = 0; i < numRoots; ++i) {
    234                 mViewRoots[i] = getWindowName(mRoots.get(i));
    235             }
    236             return mViewRoots;
    237         }
    238     }
    239 
    240     @UnsupportedAppUsage
    241     public ArrayList<ViewRootImpl> getRootViews(IBinder token) {
    242         ArrayList<ViewRootImpl> views = new ArrayList<>();
    243         synchronized (mLock) {
    244             final int numRoots = mRoots.size();
    245             for (int i = 0; i < numRoots; ++i) {
    246                 WindowManager.LayoutParams params = mParams.get(i);
    247                 if (params.token == null) {
    248                     continue;
    249                 }
    250                 if (params.token != token) {
    251                     boolean isChild = false;
    252                     if (params.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
    253                             && params.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
    254                         for (int j = 0 ; j < numRoots; ++j) {
    255                             View viewj = mViews.get(j);
    256                             WindowManager.LayoutParams paramsj = mParams.get(j);
    257                             if (params.token == viewj.getWindowToken()
    258                                     && paramsj.token == token) {
    259                                 isChild = true;
    260                                 break;
    261                             }
    262                         }
    263                     }
    264                     if (!isChild) {
    265                         continue;
    266                     }
    267                 }
    268                 views.add(mRoots.get(i));
    269             }
    270         }
    271         return views;
    272     }
    273 
    274     /**
    275      * @return the list of all views attached to the global window manager
    276      */
    277     @NonNull
    278     public ArrayList<View> getWindowViews() {
    279         synchronized (mLock) {
    280             return new ArrayList<>(mViews);
    281         }
    282     }
    283 
    284     public View getWindowView(IBinder windowToken) {
    285         synchronized (mLock) {
    286             final int numViews = mViews.size();
    287             for (int i = 0; i < numViews; ++i) {
    288                 final View view = mViews.get(i);
    289                 if (view.getWindowToken() == windowToken) {
    290                     return view;
    291                 }
    292             }
    293         }
    294         return null;
    295     }
    296 
    297     @UnsupportedAppUsage
    298     public View getRootView(String name) {
    299         synchronized (mLock) {
    300             for (int i = mRoots.size() - 1; i >= 0; --i) {
    301                 final ViewRootImpl root = mRoots.get(i);
    302                 if (name.equals(getWindowName(root))) return root.getView();
    303             }
    304         }
    305 
    306         return null;
    307     }
    308 
    309     public void addView(View view, ViewGroup.LayoutParams params,
    310             Display display, Window parentWindow) {
    311         if (view == null) {
    312             throw new IllegalArgumentException("view must not be null");
    313         }
    314         if (display == null) {
    315             throw new IllegalArgumentException("display must not be null");
    316         }
    317         if (!(params instanceof WindowManager.LayoutParams)) {
    318             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    319         }
    320 
    321         final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    322         if (parentWindow != null) {
    323             parentWindow.adjustLayoutParamsForSubWindow(wparams);
    324         } else {
    325             // If there's no parent, then hardware acceleration for this view is
    326             // set from the application's hardware acceleration setting.
    327             final Context context = view.getContext();
    328             if (context != null
    329                     && (context.getApplicationInfo().flags
    330                             & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
    331                 wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
    332             }
    333         }
    334 
    335         ViewRootImpl root;
    336         View panelParentView = null;
    337 
    338         synchronized (mLock) {
    339             // Start watching for system property changes.
    340             if (mSystemPropertyUpdater == null) {
    341                 mSystemPropertyUpdater = new Runnable() {
    342                     @Override public void run() {
    343                         synchronized (mLock) {
    344                             for (int i = mRoots.size() - 1; i >= 0; --i) {
    345                                 mRoots.get(i).loadSystemProperties();
    346                             }
    347                         }
    348                     }
    349                 };
    350                 SystemProperties.addChangeCallback(mSystemPropertyUpdater);
    351             }
    352 
    353             int index = findViewLocked(view, false);
    354             if (index >= 0) {
    355                 if (mDyingViews.contains(view)) {
    356                     // Don't wait for MSG_DIE to make it's way through root's queue.
    357                     mRoots.get(index).doDie();
    358                 } else {
    359                     throw new IllegalStateException("View " + view
    360                             + " has already been added to the window manager.");
    361                 }
    362                 // The previous removeView() had not completed executing. Now it has.
    363             }
    364 
    365             // If this is a panel window, then find the window it is being
    366             // attached to for future reference.
    367             if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
    368                     wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
    369                 final int count = mViews.size();
    370                 for (int i = 0; i < count; i++) {
    371                     if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
    372                         panelParentView = mViews.get(i);
    373                     }
    374                 }
    375             }
    376 
    377             root = new ViewRootImpl(view.getContext(), display);
    378 
    379             view.setLayoutParams(wparams);
    380 
    381             mViews.add(view);
    382             mRoots.add(root);
    383             mParams.add(wparams);
    384 
    385             // do this last because it fires off messages to start doing things
    386             try {
    387                 root.setView(view, wparams, panelParentView);
    388             } catch (RuntimeException e) {
    389                 // BadTokenException or InvalidDisplayException, clean up.
    390                 if (index >= 0) {
    391                     removeViewLocked(index, true);
    392                 }
    393                 throw e;
    394             }
    395         }
    396     }
    397 
    398     public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    399         if (view == null) {
    400             throw new IllegalArgumentException("view must not be null");
    401         }
    402         if (!(params instanceof WindowManager.LayoutParams)) {
    403             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    404         }
    405 
    406         final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
    407 
    408         view.setLayoutParams(wparams);
    409 
    410         synchronized (mLock) {
    411             int index = findViewLocked(view, true);
    412             ViewRootImpl root = mRoots.get(index);
    413             mParams.remove(index);
    414             mParams.add(index, wparams);
    415             root.setLayoutParams(wparams, false);
    416         }
    417     }
    418 
    419     @UnsupportedAppUsage
    420     public void removeView(View view, boolean immediate) {
    421         if (view == null) {
    422             throw new IllegalArgumentException("view must not be null");
    423         }
    424 
    425         synchronized (mLock) {
    426             int index = findViewLocked(view, true);
    427             View curView = mRoots.get(index).getView();
    428             removeViewLocked(index, immediate);
    429             if (curView == view) {
    430                 return;
    431             }
    432 
    433             throw new IllegalStateException("Calling with view " + view
    434                     + " but the ViewAncestor is attached to " + curView);
    435         }
    436     }
    437 
    438     /**
    439      * Remove all roots with specified token.
    440      *
    441      * @param token app or window token.
    442      * @param who name of caller, used in logs.
    443      * @param what type of caller, used in logs.
    444      */
    445     public void closeAll(IBinder token, String who, String what) {
    446         closeAllExceptView(token, null /* view */, who, what);
    447     }
    448 
    449     /**
    450      * Remove all roots with specified token, except maybe one view.
    451      *
    452      * @param token app or window token.
    453      * @param view view that should be should be preserved along with it's root.
    454      *             Pass null if everything should be removed.
    455      * @param who name of caller, used in logs.
    456      * @param what type of caller, used in logs.
    457      */
    458     public void closeAllExceptView(IBinder token, View view, String who, String what) {
    459         synchronized (mLock) {
    460             int count = mViews.size();
    461             for (int i = 0; i < count; i++) {
    462                 if ((view == null || mViews.get(i) != view)
    463                         && (token == null || mParams.get(i).token == token)) {
    464                     ViewRootImpl root = mRoots.get(i);
    465 
    466                     if (who != null) {
    467                         WindowLeaked leak = new WindowLeaked(
    468                                 what + " " + who + " has leaked window "
    469                                 + root.getView() + " that was originally added here");
    470                         leak.setStackTrace(root.getLocation().getStackTrace());
    471                         Log.e(TAG, "", leak);
    472                     }
    473 
    474                     removeViewLocked(i, false);
    475                 }
    476             }
    477         }
    478     }
    479 
    480     private void removeViewLocked(int index, boolean immediate) {
    481         ViewRootImpl root = mRoots.get(index);
    482         View view = root.getView();
    483 
    484         if (view != null) {
    485             InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
    486             if (imm != null) {
    487                 imm.windowDismissed(mViews.get(index).getWindowToken());
    488             }
    489         }
    490         boolean deferred = root.die(immediate);
    491         if (view != null) {
    492             view.assignParent(null);
    493             if (deferred) {
    494                 mDyingViews.add(view);
    495             }
    496         }
    497     }
    498 
    499     void doRemoveView(ViewRootImpl root) {
    500         synchronized (mLock) {
    501             final int index = mRoots.indexOf(root);
    502             if (index >= 0) {
    503                 mRoots.remove(index);
    504                 mParams.remove(index);
    505                 final View view = mViews.remove(index);
    506                 mDyingViews.remove(view);
    507             }
    508         }
    509         if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
    510             doTrimForeground();
    511         }
    512     }
    513 
    514     private int findViewLocked(View view, boolean required) {
    515         final int index = mViews.indexOf(view);
    516         if (required && index < 0) {
    517             throw new IllegalArgumentException("View=" + view + " not attached to window manager");
    518         }
    519         return index;
    520     }
    521 
    522     public static boolean shouldDestroyEglContext(int trimLevel) {
    523         // On low-end gfx devices we trim when memory is moderate;
    524         // on high-end devices we do this when low.
    525         if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
    526             return true;
    527         }
    528         if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
    529                 && !ActivityManager.isHighEndGfx()) {
    530             return true;
    531         }
    532         return false;
    533     }
    534 
    535     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    536     public void trimMemory(int level) {
    537         if (ThreadedRenderer.isAvailable()) {
    538             if (shouldDestroyEglContext(level)) {
    539                 // Destroy all hardware surfaces and resources associated to
    540                 // known windows
    541                 synchronized (mLock) {
    542                     for (int i = mRoots.size() - 1; i >= 0; --i) {
    543                         mRoots.get(i).destroyHardwareResources();
    544                     }
    545                 }
    546                 // Force a full memory flush
    547                 level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
    548             }
    549 
    550             ThreadedRenderer.trimMemory(level);
    551 
    552             if (ThreadedRenderer.sTrimForeground) {
    553                 doTrimForeground();
    554             }
    555         }
    556     }
    557 
    558     public static void trimForeground() {
    559         if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
    560             WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
    561             wm.doTrimForeground();
    562         }
    563     }
    564 
    565     private void doTrimForeground() {
    566         boolean hasVisibleWindows = false;
    567         synchronized (mLock) {
    568             for (int i = mRoots.size() - 1; i >= 0; --i) {
    569                 final ViewRootImpl root = mRoots.get(i);
    570                 if (root.mView != null && root.getHostVisibility() == View.VISIBLE
    571                         && root.mAttachInfo.mThreadedRenderer != null) {
    572                     hasVisibleWindows = true;
    573                 } else {
    574                     root.destroyHardwareResources();
    575                 }
    576             }
    577         }
    578         if (!hasVisibleWindows) {
    579             ThreadedRenderer.trimMemory(
    580                     ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
    581         }
    582     }
    583 
    584     public void dumpGfxInfo(FileDescriptor fd, String[] args) {
    585         FileOutputStream fout = new FileOutputStream(fd);
    586         PrintWriter pw = new FastPrintWriter(fout);
    587         try {
    588             synchronized (mLock) {
    589                 final int count = mViews.size();
    590 
    591                 pw.println("Profile data in ms:");
    592 
    593                 for (int i = 0; i < count; i++) {
    594                     ViewRootImpl root = mRoots.get(i);
    595                     String name = getWindowName(root);
    596                     pw.printf("\n\t%s (visibility=%d)", name, root.getHostVisibility());
    597 
    598                     ThreadedRenderer renderer =
    599                             root.getView().mAttachInfo.mThreadedRenderer;
    600                     if (renderer != null) {
    601                         renderer.dumpGfxInfo(pw, fd, args);
    602                     }
    603                 }
    604 
    605                 pw.println("\nView hierarchy:\n");
    606 
    607                 int viewsCount = 0;
    608                 int displayListsSize = 0;
    609                 int[] info = new int[2];
    610 
    611                 for (int i = 0; i < count; i++) {
    612                     ViewRootImpl root = mRoots.get(i);
    613                     root.dumpGfxInfo(info);
    614 
    615                     String name = getWindowName(root);
    616                     pw.printf("  %s\n  %d views, %.2f kB of display lists",
    617                             name, info[0], info[1] / 1024.0f);
    618                     pw.printf("\n\n");
    619 
    620                     viewsCount += info[0];
    621                     displayListsSize += info[1];
    622                 }
    623 
    624                 pw.printf("\nTotal ViewRootImpl: %d\n", count);
    625                 pw.printf("Total Views:        %d\n", viewsCount);
    626                 pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
    627             }
    628         } finally {
    629             pw.flush();
    630         }
    631     }
    632 
    633     private static String getWindowName(ViewRootImpl root) {
    634         return root.mWindowAttributes.getTitle() + "/" +
    635                 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode());
    636     }
    637 
    638     public void setStoppedState(IBinder token, boolean stopped) {
    639         ArrayList<ViewRootImpl> nonCurrentThreadRoots = null;
    640         synchronized (mLock) {
    641             int count = mViews.size();
    642             for (int i = count - 1; i >= 0; i--) {
    643                 if (token == null || mParams.get(i).token == token) {
    644                     ViewRootImpl root = mRoots.get(i);
    645                     // Client might remove the view by "stopped" event.
    646                     if (root.mThread == Thread.currentThread()) {
    647                         root.setWindowStopped(stopped);
    648                     } else {
    649                         if (nonCurrentThreadRoots == null) {
    650                             nonCurrentThreadRoots = new ArrayList<>();
    651                         }
    652                         nonCurrentThreadRoots.add(root);
    653                     }
    654                     // Recursively forward stopped state to View's attached
    655                     // to this Window rather than the root application token,
    656                     // e.g. PopupWindow's.
    657                     setStoppedState(root.mAttachInfo.mWindowToken, stopped);
    658                 }
    659             }
    660         }
    661 
    662         // Update the stopped state synchronously to ensure the surface won't be used after server
    663         // side has destroyed it. This operation should be outside the lock to avoid any potential
    664         // paths from setWindowStopped to WindowManagerGlobal which may cause deadlocks.
    665         if (nonCurrentThreadRoots != null) {
    666             for (int i = nonCurrentThreadRoots.size() - 1; i >= 0; i--) {
    667                 ViewRootImpl root = nonCurrentThreadRoots.get(i);
    668                 root.mHandler.runWithScissors(() -> root.setWindowStopped(stopped), 0);
    669             }
    670         }
    671     }
    672 
    673     public void reportNewConfiguration(Configuration config) {
    674         synchronized (mLock) {
    675             int count = mViews.size();
    676             config = new Configuration(config);
    677             for (int i=0; i < count; i++) {
    678                 ViewRootImpl root = mRoots.get(i);
    679                 root.requestUpdateConfiguration(config);
    680             }
    681         }
    682     }
    683 
    684     /** @hide */
    685     public void changeCanvasOpacity(IBinder token, boolean opaque) {
    686         if (token == null) {
    687             return;
    688         }
    689         synchronized (mLock) {
    690             for (int i = mParams.size() - 1; i >= 0; --i) {
    691                 if (mParams.get(i).token == token) {
    692                     mRoots.get(i).changeCanvasOpacity(opaque);
    693                     return;
    694                 }
    695             }
    696         }
    697     }
    698 }
    699 
    700 final class WindowLeaked extends AndroidRuntimeException {
    701     @UnsupportedAppUsage
    702     public WindowLeaked(String msg) {
    703         super(msg);
    704     }
    705 }
    706