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                    if (sWindowManagerService != null) {
171                        ValueAnimator.setDurationScale(
172                                sWindowManagerService.getCurrentAnimatorScale());
173                    }
174                } catch (RemoteException e) {
175                    throw e.rethrowFromSystemServer();
176                }
177            }
178            return sWindowManagerService;
179        }
180    }
181
182    public static IWindowSession getWindowSession() {
183        synchronized (WindowManagerGlobal.class) {
184            if (sWindowSession == null) {
185                try {
186                    InputMethodManager imm = InputMethodManager.getInstance();
187                    IWindowManager windowManager = getWindowManagerService();
188                    sWindowSession = windowManager.openSession(
189                            new IWindowSessionCallback.Stub() {
190                                @Override
191                                public void onAnimatorScaleChanged(float scale) {
192                                    ValueAnimator.setDurationScale(scale);
193                                }
194                            },
195                            imm.getClient(), imm.getInputContext());
196                } catch (RemoteException e) {
197                    throw e.rethrowFromSystemServer();
198                }
199            }
200            return sWindowSession;
201        }
202    }
203
204    public static IWindowSession peekWindowSession() {
205        synchronized (WindowManagerGlobal.class) {
206            return sWindowSession;
207        }
208    }
209
210    public String[] getViewRootNames() {
211        synchronized (mLock) {
212            final int numRoots = mRoots.size();
213            String[] mViewRoots = new String[numRoots];
214            for (int i = 0; i < numRoots; ++i) {
215                mViewRoots[i] = getWindowName(mRoots.get(i));
216            }
217            return mViewRoots;
218        }
219    }
220
221    public ArrayList<ViewRootImpl> getRootViews(IBinder token) {
222        ArrayList<ViewRootImpl> views = new ArrayList<>();
223        synchronized (mLock) {
224            final int numRoots = mRoots.size();
225            for (int i = 0; i < numRoots; ++i) {
226                WindowManager.LayoutParams params = mParams.get(i);
227                if (params.token == null) {
228                    continue;
229                }
230                if (params.token != token) {
231                    boolean isChild = false;
232                    if (params.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
233                            && params.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
234                        for (int j = 0 ; j < numRoots; ++j) {
235                            View viewj = mViews.get(j);
236                            WindowManager.LayoutParams paramsj = mParams.get(j);
237                            if (params.token == viewj.getWindowToken()
238                                    && paramsj.token == token) {
239                                isChild = true;
240                                break;
241                            }
242                        }
243                    }
244                    if (!isChild) {
245                        continue;
246                    }
247                }
248                views.add(mRoots.get(i));
249            }
250        }
251        return views;
252    }
253
254    public View getWindowView(IBinder windowToken) {
255        synchronized (mLock) {
256            final int numViews = mViews.size();
257            for (int i = 0; i < numViews; ++i) {
258                final View view = mViews.get(i);
259                if (view.getWindowToken() == windowToken) {
260                    return view;
261                }
262            }
263        }
264        return null;
265    }
266
267    public View getRootView(String name) {
268        synchronized (mLock) {
269            for (int i = mRoots.size() - 1; i >= 0; --i) {
270                final ViewRootImpl root = mRoots.get(i);
271                if (name.equals(getWindowName(root))) return root.getView();
272            }
273        }
274
275        return null;
276    }
277
278    public void addView(View view, ViewGroup.LayoutParams params,
279            Display display, Window parentWindow) {
280        if (view == null) {
281            throw new IllegalArgumentException("view must not be null");
282        }
283        if (display == null) {
284            throw new IllegalArgumentException("display must not be null");
285        }
286        if (!(params instanceof WindowManager.LayoutParams)) {
287            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
288        }
289
290        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
291        if (parentWindow != null) {
292            parentWindow.adjustLayoutParamsForSubWindow(wparams);
293        } else {
294            // If there's no parent, then hardware acceleration for this view is
295            // set from the application's hardware acceleration setting.
296            final Context context = view.getContext();
297            if (context != null
298                    && (context.getApplicationInfo().flags
299                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
300                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
301            }
302        }
303
304        ViewRootImpl root;
305        View panelParentView = null;
306
307        synchronized (mLock) {
308            // Start watching for system property changes.
309            if (mSystemPropertyUpdater == null) {
310                mSystemPropertyUpdater = new Runnable() {
311                    @Override public void run() {
312                        synchronized (mLock) {
313                            for (int i = mRoots.size() - 1; i >= 0; --i) {
314                                mRoots.get(i).loadSystemProperties();
315                            }
316                        }
317                    }
318                };
319                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
320            }
321
322            int index = findViewLocked(view, false);
323            if (index >= 0) {
324                if (mDyingViews.contains(view)) {
325                    // Don't wait for MSG_DIE to make it's way through root's queue.
326                    mRoots.get(index).doDie();
327                } else {
328                    throw new IllegalStateException("View " + view
329                            + " has already been added to the window manager.");
330                }
331                // The previous removeView() had not completed executing. Now it has.
332            }
333
334            // If this is a panel window, then find the window it is being
335            // attached to for future reference.
336            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
337                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
338                final int count = mViews.size();
339                for (int i = 0; i < count; i++) {
340                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
341                        panelParentView = mViews.get(i);
342                    }
343                }
344            }
345
346            root = new ViewRootImpl(view.getContext(), display);
347
348            view.setLayoutParams(wparams);
349
350            mViews.add(view);
351            mRoots.add(root);
352            mParams.add(wparams);
353
354            // do this last because it fires off messages to start doing things
355            try {
356                root.setView(view, wparams, panelParentView);
357            } catch (RuntimeException e) {
358                // BadTokenException or InvalidDisplayException, clean up.
359                if (index >= 0) {
360                    removeViewLocked(index, true);
361                }
362                throw e;
363            }
364        }
365    }
366
367    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
368        if (view == null) {
369            throw new IllegalArgumentException("view must not be null");
370        }
371        if (!(params instanceof WindowManager.LayoutParams)) {
372            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
373        }
374
375        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
376
377        view.setLayoutParams(wparams);
378
379        synchronized (mLock) {
380            int index = findViewLocked(view, true);
381            ViewRootImpl root = mRoots.get(index);
382            mParams.remove(index);
383            mParams.add(index, wparams);
384            root.setLayoutParams(wparams, false);
385        }
386    }
387
388    public void removeView(View view, boolean immediate) {
389        if (view == null) {
390            throw new IllegalArgumentException("view must not be null");
391        }
392
393        synchronized (mLock) {
394            int index = findViewLocked(view, true);
395            View curView = mRoots.get(index).getView();
396            removeViewLocked(index, immediate);
397            if (curView == view) {
398                return;
399            }
400
401            throw new IllegalStateException("Calling with view " + view
402                    + " but the ViewAncestor is attached to " + curView);
403        }
404    }
405
406    /**
407     * Remove all roots with specified token.
408     *
409     * @param token app or window token.
410     * @param who name of caller, used in logs.
411     * @param what type of caller, used in logs.
412     */
413    public void closeAll(IBinder token, String who, String what) {
414        closeAllExceptView(token, null /* view */, who, what);
415    }
416
417    /**
418     * Remove all roots with specified token, except maybe one view.
419     *
420     * @param token app or window token.
421     * @param view view that should be should be preserved along with it's root.
422     *             Pass null if everything should be removed.
423     * @param who name of caller, used in logs.
424     * @param what type of caller, used in logs.
425     */
426    public void closeAllExceptView(IBinder token, View view, String who, String what) {
427        synchronized (mLock) {
428            int count = mViews.size();
429            for (int i = 0; i < count; i++) {
430                if ((view == null || mViews.get(i) != view)
431                        && (token == null || mParams.get(i).token == token)) {
432                    ViewRootImpl root = mRoots.get(i);
433
434                    if (who != null) {
435                        WindowLeaked leak = new WindowLeaked(
436                                what + " " + who + " has leaked window "
437                                + root.getView() + " that was originally added here");
438                        leak.setStackTrace(root.getLocation().getStackTrace());
439                        Log.e(TAG, "", leak);
440                    }
441
442                    removeViewLocked(i, false);
443                }
444            }
445        }
446    }
447
448    private void removeViewLocked(int index, boolean immediate) {
449        ViewRootImpl root = mRoots.get(index);
450        View view = root.getView();
451
452        if (view != null) {
453            InputMethodManager imm = InputMethodManager.getInstance();
454            if (imm != null) {
455                imm.windowDismissed(mViews.get(index).getWindowToken());
456            }
457        }
458        boolean deferred = root.die(immediate);
459        if (view != null) {
460            view.assignParent(null);
461            if (deferred) {
462                mDyingViews.add(view);
463            }
464        }
465    }
466
467    void doRemoveView(ViewRootImpl root) {
468        synchronized (mLock) {
469            final int index = mRoots.indexOf(root);
470            if (index >= 0) {
471                mRoots.remove(index);
472                mParams.remove(index);
473                final View view = mViews.remove(index);
474                mDyingViews.remove(view);
475            }
476        }
477        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
478            doTrimForeground();
479        }
480    }
481
482    private int findViewLocked(View view, boolean required) {
483        final int index = mViews.indexOf(view);
484        if (required && index < 0) {
485            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
486        }
487        return index;
488    }
489
490    public static boolean shouldDestroyEglContext(int trimLevel) {
491        // On low-end gfx devices we trim when memory is moderate;
492        // on high-end devices we do this when low.
493        if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
494            return true;
495        }
496        if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
497                && !ActivityManager.isHighEndGfx()) {
498            return true;
499        }
500        return false;
501    }
502
503    public void trimMemory(int level) {
504        if (ThreadedRenderer.isAvailable()) {
505            if (shouldDestroyEglContext(level)) {
506                // Destroy all hardware surfaces and resources associated to
507                // known windows
508                synchronized (mLock) {
509                    for (int i = mRoots.size() - 1; i >= 0; --i) {
510                        mRoots.get(i).destroyHardwareResources();
511                    }
512                }
513                // Force a full memory flush
514                level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
515            }
516
517            ThreadedRenderer.trimMemory(level);
518
519            if (ThreadedRenderer.sTrimForeground) {
520                doTrimForeground();
521            }
522        }
523    }
524
525    public static void trimForeground() {
526        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
527            WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
528            wm.doTrimForeground();
529        }
530    }
531
532    private void doTrimForeground() {
533        boolean hasVisibleWindows = false;
534        synchronized (mLock) {
535            for (int i = mRoots.size() - 1; i >= 0; --i) {
536                final ViewRootImpl root = mRoots.get(i);
537                if (root.mView != null && root.getHostVisibility() == View.VISIBLE
538                        && root.mAttachInfo.mThreadedRenderer != null) {
539                    hasVisibleWindows = true;
540                } else {
541                    root.destroyHardwareResources();
542                }
543            }
544        }
545        if (!hasVisibleWindows) {
546            ThreadedRenderer.trimMemory(
547                    ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
548        }
549    }
550
551    public void dumpGfxInfo(FileDescriptor fd, String[] args) {
552        FileOutputStream fout = new FileOutputStream(fd);
553        PrintWriter pw = new FastPrintWriter(fout);
554        try {
555            synchronized (mLock) {
556                final int count = mViews.size();
557
558                pw.println("Profile data in ms:");
559
560                for (int i = 0; i < count; i++) {
561                    ViewRootImpl root = mRoots.get(i);
562                    String name = getWindowName(root);
563                    pw.printf("\n\t%s (visibility=%d)", name, root.getHostVisibility());
564
565                    ThreadedRenderer renderer =
566                            root.getView().mAttachInfo.mThreadedRenderer;
567                    if (renderer != null) {
568                        renderer.dumpGfxInfo(pw, fd, args);
569                    }
570                }
571
572                pw.println("\nView hierarchy:\n");
573
574                int viewsCount = 0;
575                int displayListsSize = 0;
576                int[] info = new int[2];
577
578                for (int i = 0; i < count; i++) {
579                    ViewRootImpl root = mRoots.get(i);
580                    root.dumpGfxInfo(info);
581
582                    String name = getWindowName(root);
583                    pw.printf("  %s\n  %d views, %.2f kB of display lists",
584                            name, info[0], info[1] / 1024.0f);
585                    pw.printf("\n\n");
586
587                    viewsCount += info[0];
588                    displayListsSize += info[1];
589                }
590
591                pw.printf("\nTotal ViewRootImpl: %d\n", count);
592                pw.printf("Total Views:        %d\n", viewsCount);
593                pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
594            }
595        } finally {
596            pw.flush();
597        }
598    }
599
600    private static String getWindowName(ViewRootImpl root) {
601        return root.mWindowAttributes.getTitle() + "/" +
602                root.getClass().getName() + '@' + Integer.toHexString(root.hashCode());
603    }
604
605    public void setStoppedState(IBinder token, boolean stopped) {
606        synchronized (mLock) {
607            int count = mViews.size();
608            for (int i = 0; i < count; i++) {
609                if (token == null || mParams.get(i).token == token) {
610                    ViewRootImpl root = mRoots.get(i);
611                    root.setWindowStopped(stopped);
612                }
613            }
614        }
615    }
616
617    public void reportNewConfiguration(Configuration config) {
618        synchronized (mLock) {
619            int count = mViews.size();
620            config = new Configuration(config);
621            for (int i=0; i < count; i++) {
622                ViewRootImpl root = mRoots.get(i);
623                root.requestUpdateConfiguration(config);
624            }
625        }
626    }
627
628    /** @hide */
629    public void changeCanvasOpacity(IBinder token, boolean opaque) {
630        if (token == null) {
631            return;
632        }
633        synchronized (mLock) {
634            for (int i = mParams.size() - 1; i >= 0; --i) {
635                if (mParams.get(i).token == token) {
636                    mRoots.get(i).changeCanvasOpacity(opaque);
637                    return;
638                }
639            }
640        }
641    }
642}
643
644final class WindowLeaked extends AndroidRuntimeException {
645    public WindowLeaked(String msg) {
646        super(msg);
647    }
648}
649