WindowManagerGlobal.java revision 8f303ad97007a9b38d6d927353c1fba812879ae5
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.res.Configuration;
23import android.opengl.ManagedEGLContext;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.os.ServiceManager;
27import android.os.SystemProperties;
28import android.util.AndroidRuntimeException;
29import android.util.ArraySet;
30import android.util.Log;
31import android.view.inputmethod.InputMethodManager;
32
33import java.io.FileDescriptor;
34import java.io.FileOutputStream;
35import java.io.PrintWriter;
36import java.util.ArrayList;
37
38/**
39 * Provides low-level communication with the system window manager for
40 * operations that are not associated with any particular context.
41 *
42 * This class is only used internally to implement global functions where
43 * the caller already knows the display and relevant compatibility information
44 * for the operation.  For most purposes, you should use {@link WindowManager} instead
45 * since it is bound to a context.
46 *
47 * @see WindowManagerImpl
48 * @hide
49 */
50public final class WindowManagerGlobal {
51    private static final String TAG = "WindowManager";
52
53    /**
54     * The user is navigating with keys (not the touch screen), so
55     * navigational focus should be shown.
56     */
57    public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1;
58
59    /**
60     * This is the first time the window is being drawn,
61     * so the client must call drawingFinished() when done
62     */
63    public static final int RELAYOUT_RES_FIRST_TIME = 0x2;
64
65    /**
66     * The window manager has changed the surface from the last call.
67     */
68    public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
69
70    /**
71     * The window manager is currently animating.  It will call
72     * IWindow.doneAnimating() when done.
73     */
74    public static final int RELAYOUT_RES_ANIMATING = 0x8;
75
76    /**
77     * Flag for relayout: the client will be later giving
78     * internal insets; as a result, the window will not impact other window
79     * layouts until the insets are given.
80     */
81    public static final int RELAYOUT_INSETS_PENDING = 0x1;
82
83    /**
84     * Flag for relayout: the client may be currently using the current surface,
85     * so if it is to be destroyed as a part of the relayout the destroy must
86     * be deferred until later.  The client will call performDeferredDestroy()
87     * when it is okay.
88     */
89    public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
90
91    public static final int ADD_FLAG_APP_VISIBLE = 0x2;
92    public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE;
93
94    public static final int ADD_OKAY = 0;
95    public static final int ADD_BAD_APP_TOKEN = -1;
96    public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
97    public static final int ADD_NOT_APP_TOKEN = -3;
98    public static final int ADD_APP_EXITING = -4;
99    public static final int ADD_DUPLICATE_ADD = -5;
100    public static final int ADD_STARTING_NOT_NEEDED = -6;
101    public static final int ADD_MULTIPLE_SINGLETON = -7;
102    public static final int ADD_PERMISSION_DENIED = -8;
103    public static final int ADD_INVALID_DISPLAY = -9;
104
105    private static WindowManagerGlobal sDefaultWindowManager;
106    private static IWindowManager sWindowManagerService;
107    private static IWindowSession sWindowSession;
108
109    private final Object mLock = new Object();
110
111    private final ArrayList<View> mViews = new ArrayList<View>();
112    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
113    private final ArrayList<WindowManager.LayoutParams> mParams =
114            new ArrayList<WindowManager.LayoutParams>();
115    private final ArraySet<View> mDyingViews = new ArraySet<View>();
116    private boolean mNeedsEglTerminate;
117
118    private Runnable mSystemPropertyUpdater;
119
120    private WindowManagerGlobal() {
121    }
122
123    public static WindowManagerGlobal getInstance() {
124        synchronized (WindowManagerGlobal.class) {
125            if (sDefaultWindowManager == null) {
126                sDefaultWindowManager = new WindowManagerGlobal();
127            }
128            return sDefaultWindowManager;
129        }
130    }
131
132    public static IWindowManager getWindowManagerService() {
133        synchronized (WindowManagerGlobal.class) {
134            if (sWindowManagerService == null) {
135                sWindowManagerService = IWindowManager.Stub.asInterface(
136                        ServiceManager.getService("window"));
137            }
138            return sWindowManagerService;
139        }
140    }
141
142    public static IWindowSession getWindowSession() {
143        synchronized (WindowManagerGlobal.class) {
144            if (sWindowSession == null) {
145                try {
146                    InputMethodManager imm = InputMethodManager.getInstance();
147                    IWindowManager windowManager = getWindowManagerService();
148                    sWindowSession = windowManager.openSession(
149                            imm.getClient(), imm.getInputContext());
150                    float animatorScale = windowManager.getAnimationScale(2);
151                    ValueAnimator.setDurationScale(animatorScale);
152                } catch (RemoteException e) {
153                    Log.e(TAG, "Failed to open window session", e);
154                }
155            }
156            return sWindowSession;
157        }
158    }
159
160    public static IWindowSession peekWindowSession() {
161        synchronized (WindowManagerGlobal.class) {
162            return sWindowSession;
163        }
164    }
165
166    public String[] getViewRootNames() {
167        synchronized (mLock) {
168            final int numRoots = mRoots.size();
169            String[] mViewRoots = new String[numRoots];
170            for (int i = 0; i < numRoots; ++i) {
171                mViewRoots[i] = getWindowName(mRoots.get(i));
172            }
173            return mViewRoots;
174        }
175    }
176
177    public View getRootView(String name) {
178        synchronized (mLock) {
179            for (int i = mRoots.size() - 1; i >= 0; --i) {
180                final ViewRootImpl root = mRoots.get(i);
181                if (name.equals(getWindowName(root))) return root.getView();
182            }
183        }
184
185        return null;
186    }
187
188    public void addView(View view, ViewGroup.LayoutParams params,
189            Display display, Window parentWindow) {
190        if (view == null) {
191            throw new IllegalArgumentException("view must not be null");
192        }
193        if (display == null) {
194            throw new IllegalArgumentException("display must not be null");
195        }
196        if (!(params instanceof WindowManager.LayoutParams)) {
197            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
198        }
199
200        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
201        if (parentWindow != null) {
202            parentWindow.adjustLayoutParamsForSubWindow(wparams);
203        }
204
205        ViewRootImpl root;
206        View panelParentView = null;
207
208        synchronized (mLock) {
209            // Start watching for system property changes.
210            if (mSystemPropertyUpdater == null) {
211                mSystemPropertyUpdater = new Runnable() {
212                    @Override public void run() {
213                        synchronized (mLock) {
214                            for (int i = mRoots.size() - 1; i >= 0; --i) {
215                                mRoots.get(i).loadSystemProperties();
216                            }
217                        }
218                    }
219                };
220                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
221            }
222
223            int index = findViewLocked(view, false);
224            if (index >= 0) {
225                if (mDyingViews.contains(view)) {
226                    // Don't wait for MSG_DIE to make it's way through root's queue.
227                    mRoots.get(index).doDie();
228                } else {
229                    throw new IllegalStateException("View " + view
230                            + " has already been added to the window manager.");
231                }
232                // The previous removeView() had not completed executing. Now it has.
233            }
234
235            // If this is a panel window, then find the window it is being
236            // attached to for future reference.
237            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
238                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
239                final int count = mViews.size();
240                for (int i = 0; i < count; i++) {
241                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
242                        panelParentView = mViews.get(i);
243                    }
244                }
245            }
246
247            root = new ViewRootImpl(view.getContext(), display);
248
249            view.setLayoutParams(wparams);
250
251            mViews.add(view);
252            mRoots.add(root);
253            mParams.add(wparams);
254        }
255
256        // do this last because it fires off messages to start doing things
257        try {
258            root.setView(view, wparams, panelParentView);
259        } catch (RuntimeException e) {
260            // BadTokenException or InvalidDisplayException, clean up.
261            synchronized (mLock) {
262                final int index = findViewLocked(view, false);
263                if (index >= 0) {
264                    removeViewLocked(index, true);
265                }
266            }
267            throw e;
268        }
269    }
270
271    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
272        if (view == null) {
273            throw new IllegalArgumentException("view must not be null");
274        }
275        if (!(params instanceof WindowManager.LayoutParams)) {
276            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
277        }
278
279        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
280
281        view.setLayoutParams(wparams);
282
283        synchronized (mLock) {
284            int index = findViewLocked(view, true);
285            ViewRootImpl root = mRoots.get(index);
286            mParams.remove(index);
287            mParams.add(index, wparams);
288            root.setLayoutParams(wparams, false);
289        }
290    }
291
292    public void removeView(View view, boolean immediate) {
293        if (view == null) {
294            throw new IllegalArgumentException("view must not be null");
295        }
296
297        synchronized (mLock) {
298            int index = findViewLocked(view, true);
299            View curView = mRoots.get(index).getView();
300            removeViewLocked(index, immediate);
301            if (curView == view) {
302                return;
303            }
304
305            throw new IllegalStateException("Calling with view " + view
306                    + " but the ViewAncestor is attached to " + curView);
307        }
308    }
309
310    public void closeAll(IBinder token, String who, String what) {
311        synchronized (mLock) {
312            int count = mViews.size();
313            //Log.i("foo", "Closing all windows of " + token);
314            for (int i = 0; i < count; i++) {
315                //Log.i("foo", "@ " + i + " token " + mParams[i].token
316                //        + " view " + mRoots[i].getView());
317                if (token == null || mParams.get(i).token == token) {
318                    ViewRootImpl root = mRoots.get(i);
319
320                    //Log.i("foo", "Force closing " + root);
321                    if (who != null) {
322                        WindowLeaked leak = new WindowLeaked(
323                                what + " " + who + " has leaked window "
324                                + root.getView() + " that was originally added here");
325                        leak.setStackTrace(root.getLocation().getStackTrace());
326                        Log.e(TAG, "", leak);
327                    }
328
329                    removeViewLocked(i, false);
330                }
331            }
332        }
333    }
334
335    private void removeViewLocked(int index, boolean immediate) {
336        ViewRootImpl root = mRoots.get(index);
337        View view = root.getView();
338
339        if (view != null) {
340            InputMethodManager imm = InputMethodManager.getInstance();
341            if (imm != null) {
342                imm.windowDismissed(mViews.get(index).getWindowToken());
343            }
344        }
345        boolean deferred = root.die(immediate);
346        if (view != null) {
347            view.assignParent(null);
348            if (deferred) {
349                mDyingViews.add(view);
350            }
351        }
352    }
353
354    void doRemoveView(ViewRootImpl root) {
355        synchronized (mLock) {
356            final int index = mRoots.indexOf(root);
357            if (index >= 0) {
358                mRoots.remove(index);
359                mParams.remove(index);
360                final View view = mViews.remove(index);
361                mDyingViews.remove(view);
362            }
363        }
364    }
365
366    private int findViewLocked(View view, boolean required) {
367        final int index = mViews.indexOf(view);
368        if (required && index < 0) {
369            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
370        }
371        return index;
372    }
373
374    public void startTrimMemory(int level) {
375        if (HardwareRenderer.isAvailable()) {
376            // On low-end gfx devices we trim when memory is moderate;
377            // on high-end devices we do this when low.
378            if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
379                    || (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
380                            && !ActivityManager.isHighEndGfx())) {
381                // Destroy all hardware surfaces and resources associated to
382                // known windows
383                synchronized (mLock) {
384                    for (int i = mRoots.size() - 1; i >= 0; --i) {
385                        mRoots.get(i).terminateHardwareResources();
386                    }
387                }
388                // Force a full memory flush
389                mNeedsEglTerminate = true;
390                HardwareRenderer.startTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
391                return;
392            }
393
394            HardwareRenderer.startTrimMemory(level);
395        }
396    }
397
398    public void endTrimMemory() {
399        HardwareRenderer.endTrimMemory();
400
401        if (mNeedsEglTerminate) {
402            ManagedEGLContext.doTerminate();
403            mNeedsEglTerminate = false;
404        }
405    }
406
407    public void trimLocalMemory() {
408        synchronized (mLock) {
409            for (int i = mRoots.size() - 1; i >= 0; --i) {
410                mRoots.get(i).destroyHardwareLayers();
411            }
412        }
413    }
414
415    public void dumpGfxInfo(FileDescriptor fd) {
416        FileOutputStream fout = new FileOutputStream(fd);
417        PrintWriter pw = new PrintWriter(fout);
418        try {
419            synchronized (mLock) {
420                final int count = mViews.size();
421
422                pw.println("Profile data in ms:");
423
424                for (int i = 0; i < count; i++) {
425                    ViewRootImpl root = mRoots.get(i);
426                    String name = getWindowName(root);
427                    pw.printf("\n\t%s", name);
428
429                    HardwareRenderer renderer =
430                            root.getView().mAttachInfo.mHardwareRenderer;
431                    if (renderer != null) {
432                        renderer.dumpGfxInfo(pw);
433                    }
434                }
435
436                pw.println("\nView hierarchy:\n");
437
438                int viewsCount = 0;
439                int displayListsSize = 0;
440                int[] info = new int[2];
441
442                for (int i = 0; i < count; i++) {
443                    ViewRootImpl root = mRoots.get(i);
444                    root.dumpGfxInfo(info);
445
446                    String name = getWindowName(root);
447                    pw.printf("  %s\n  %d views, %.2f kB of display lists",
448                            name, info[0], info[1] / 1024.0f);
449                    HardwareRenderer renderer =
450                            root.getView().mAttachInfo.mHardwareRenderer;
451                    if (renderer != null) {
452                        pw.printf(", %d frames rendered", renderer.getFrameCount());
453                    }
454                    pw.printf("\n\n");
455
456                    viewsCount += info[0];
457                    displayListsSize += info[1];
458                }
459
460                pw.printf("\nTotal ViewRootImpl: %d\n", count);
461                pw.printf("Total Views:        %d\n", viewsCount);
462                pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
463            }
464        } finally {
465            pw.flush();
466        }
467    }
468
469    private static String getWindowName(ViewRootImpl root) {
470        return root.mWindowAttributes.getTitle() + "/" +
471                root.getClass().getName() + '@' + Integer.toHexString(root.hashCode());
472    }
473
474    public void setStoppedState(IBinder token, boolean stopped) {
475        synchronized (mLock) {
476            int count = mViews.size();
477            for (int i = 0; i < count; i++) {
478                if (token == null || mParams.get(i).token == token) {
479                    ViewRootImpl root = mRoots.get(i);
480                    root.setStopped(stopped);
481                }
482            }
483        }
484    }
485
486    public void reportNewConfiguration(Configuration config) {
487        synchronized (mLock) {
488            int count = mViews.size();
489            config = new Configuration(config);
490            for (int i=0; i < count; i++) {
491                ViewRootImpl root = mRoots.get(i);
492                root.requestUpdateConfiguration(config);
493            }
494        }
495    }
496}
497
498final class WindowLeaked extends AndroidRuntimeException {
499    public WindowLeaked(String msg) {
500        super(msg);
501    }
502}
503