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