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