WindowManagerImpl.java revision 4478de3c02c1fb2f4f888e696ee1b13721e936d9
1/*
2 * Copyright (C) 2006 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.app.ActivityManager;
20import android.content.ComponentCallbacks2;
21import android.content.res.CompatibilityInfo;
22import android.content.res.Configuration;
23import android.graphics.PixelFormat;
24import android.opengl.ManagedEGLContext;
25import android.os.IBinder;
26import android.util.AndroidRuntimeException;
27import android.util.Log;
28import android.view.inputmethod.InputMethodManager;
29
30import java.io.FileDescriptor;
31import java.io.FileOutputStream;
32import java.io.PrintWriter;
33import java.util.HashMap;
34
35final class WindowLeaked extends AndroidRuntimeException {
36    public WindowLeaked(String msg) {
37        super(msg);
38    }
39}
40
41/**
42 * Low-level communication with the global system window manager.  It implements
43 * the ViewManager interface, allowing you to add any View subclass as a
44 * top-level window on the screen. Additional window manager specific layout
45 * parameters are defined for control over how windows are displayed.
46 * It also implemens the WindowManager interface, allowing you to control the
47 * displays attached to the device.
48 *
49 * <p>Applications will not normally use WindowManager directly, instead relying
50 * on the higher-level facilities in {@link android.app.Activity} and
51 * {@link android.app.Dialog}.
52 *
53 * <p>Even for low-level window manager access, it is almost never correct to use
54 * this class.  For example, {@link android.app.Activity#getWindowManager}
55 * provides a ViewManager for adding windows that are associated with that
56 * activity -- the window manager will not normally allow you to add arbitrary
57 * windows that are not associated with an activity.
58 *
59 * @hide
60 */
61public class WindowManagerImpl implements WindowManager {
62    /**
63     * The user is navigating with keys (not the touch screen), so
64     * navigational focus should be shown.
65     */
66    public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1;
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     * The window manager has changed the surface from the last call.
74     */
75    public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
76
77    /**
78     * Flag for relayout: the client will be later giving
79     * internal insets; as a result, the window will not impact other window
80     * layouts until the insets are given.
81     */
82    public static final int RELAYOUT_INSETS_PENDING = 0x1;
83
84    /**
85     * Flag for relayout: the client may be currently using the current surface,
86     * so if it is to be destroyed as a part of the relayout the destroy must
87     * be deferred until later.  The client will call performDeferredDestroy()
88     * when it is okay.
89     */
90    public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
91
92    public static final int ADD_FLAG_APP_VISIBLE = 0x2;
93    public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE;
94
95    public static final int ADD_OKAY = 0;
96    public static final int ADD_BAD_APP_TOKEN = -1;
97    public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
98    public static final int ADD_NOT_APP_TOKEN = -3;
99    public static final int ADD_APP_EXITING = -4;
100    public static final int ADD_DUPLICATE_ADD = -5;
101    public static final int ADD_STARTING_NOT_NEEDED = -6;
102    public static final int ADD_MULTIPLE_SINGLETON = -7;
103    public static final int ADD_PERMISSION_DENIED = -8;
104
105    private View[] mViews;
106    private ViewRootImpl[] mRoots;
107    private WindowManager.LayoutParams[] mParams;
108    private boolean mNeedsEglTerminate;
109
110    private final static Object sLock = new Object();
111    private final static WindowManagerImpl sWindowManager = new WindowManagerImpl();
112    private final static HashMap<CompatibilityInfo, WindowManager> sCompatWindowManagers
113            = new HashMap<CompatibilityInfo, WindowManager>();
114
115    static class CompatModeWrapper implements WindowManager {
116        private final WindowManagerImpl mWindowManager;
117        private final Display mDefaultDisplay;
118        private final CompatibilityInfoHolder mCompatibilityInfo;
119
120        CompatModeWrapper(WindowManager wm, CompatibilityInfoHolder ci) {
121            mWindowManager = wm instanceof CompatModeWrapper
122                    ? ((CompatModeWrapper)wm).mWindowManager : (WindowManagerImpl)wm;
123
124            // Use the original display if there is no compatibility mode
125            // to apply, or the underlying window manager is already a
126            // compatibility mode wrapper.  (We assume that if it is a
127            // wrapper, it is applying the same compatibility mode.)
128            if (ci == null) {
129                mDefaultDisplay = mWindowManager.getDefaultDisplay();
130            } else {
131                //mDefaultDisplay = mWindowManager.getDefaultDisplay();
132                mDefaultDisplay = Display.createCompatibleDisplay(
133                        mWindowManager.getDefaultDisplay().getDisplayId(), ci);
134            }
135
136            mCompatibilityInfo = ci;
137        }
138
139        @Override
140        public void addView(View view, android.view.ViewGroup.LayoutParams params) {
141            mWindowManager.addView(view, params, mCompatibilityInfo);
142        }
143
144        @Override
145        public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {
146            mWindowManager.updateViewLayout(view, params);
147
148        }
149
150        @Override
151        public void removeView(View view) {
152            mWindowManager.removeView(view);
153        }
154
155        @Override
156        public Display getDefaultDisplay() {
157            return mDefaultDisplay;
158        }
159
160        @Override
161        public void removeViewImmediate(View view) {
162            mWindowManager.removeViewImmediate(view);
163        }
164
165        @Override
166        public boolean isHardwareAccelerated() {
167            return mWindowManager.isHardwareAccelerated();
168        }
169
170    }
171
172    public static WindowManagerImpl getDefault() {
173        return sWindowManager;
174    }
175
176    public static WindowManager getDefault(CompatibilityInfo compatInfo) {
177        CompatibilityInfoHolder cih = new CompatibilityInfoHolder();
178        cih.set(compatInfo);
179        if (cih.getIfNeeded() == null) {
180            return sWindowManager;
181        }
182
183        synchronized (sLock) {
184            // NOTE: It would be cleaner to move the implementation of
185            // WindowManagerImpl into a static inner class, and have this
186            // public impl just call into that.  Then we can make multiple
187            // instances of WindowManagerImpl for compat mode rather than
188            // having to make wrappers.
189            WindowManager wm = sCompatWindowManagers.get(compatInfo);
190            if (wm == null) {
191                wm = new CompatModeWrapper(sWindowManager, cih);
192                sCompatWindowManagers.put(compatInfo, wm);
193            }
194            return wm;
195        }
196    }
197
198    public static WindowManager getDefault(CompatibilityInfoHolder compatInfo) {
199        return new CompatModeWrapper(sWindowManager, compatInfo);
200    }
201
202    public boolean isHardwareAccelerated() {
203        return false;
204    }
205
206    public void addView(View view) {
207        addView(view, new WindowManager.LayoutParams(
208            WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE));
209    }
210
211    public void addView(View view, ViewGroup.LayoutParams params) {
212        addView(view, params, null, false);
213    }
214
215    public void addView(View view, ViewGroup.LayoutParams params, CompatibilityInfoHolder cih) {
216        addView(view, params, cih, false);
217    }
218
219    private void addView(View view, ViewGroup.LayoutParams params,
220            CompatibilityInfoHolder cih, boolean nest) {
221        if (false) Log.v("WindowManager", "addView view=" + view);
222
223        if (!(params instanceof WindowManager.LayoutParams)) {
224            throw new IllegalArgumentException(
225                    "Params must be WindowManager.LayoutParams");
226        }
227
228        final WindowManager.LayoutParams wparams
229                = (WindowManager.LayoutParams)params;
230
231        ViewRootImpl root;
232        View panelParentView = null;
233
234        synchronized (this) {
235            // Here's an odd/questionable case: if someone tries to add a
236            // view multiple times, then we simply bump up a nesting count
237            // and they need to remove the view the corresponding number of
238            // times to have it actually removed from the window manager.
239            // This is useful specifically for the notification manager,
240            // which can continually add/remove the same view as a
241            // notification gets updated.
242            int index = findViewLocked(view, false);
243            if (index >= 0) {
244                if (!nest) {
245                    throw new IllegalStateException("View " + view
246                            + " has already been added to the window manager.");
247                }
248                root = mRoots[index];
249                root.mAddNesting++;
250                // Update layout parameters.
251                view.setLayoutParams(wparams);
252                root.setLayoutParams(wparams, true);
253                return;
254            }
255
256            // If this is a panel window, then find the window it is being
257            // attached to for future reference.
258            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
259                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
260                final int count = mViews != null ? mViews.length : 0;
261                for (int i=0; i<count; i++) {
262                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
263                        panelParentView = mViews[i];
264                    }
265                }
266            }
267
268            root = new ViewRootImpl(view.getContext());
269            root.mAddNesting = 1;
270            if (cih == null) {
271                root.mCompatibilityInfo = new CompatibilityInfoHolder();
272            } else {
273                root.mCompatibilityInfo = cih;
274            }
275
276            view.setLayoutParams(wparams);
277
278            if (mViews == null) {
279                index = 1;
280                mViews = new View[1];
281                mRoots = new ViewRootImpl[1];
282                mParams = new WindowManager.LayoutParams[1];
283            } else {
284                index = mViews.length + 1;
285                Object[] old = mViews;
286                mViews = new View[index];
287                System.arraycopy(old, 0, mViews, 0, index-1);
288                old = mRoots;
289                mRoots = new ViewRootImpl[index];
290                System.arraycopy(old, 0, mRoots, 0, index-1);
291                old = mParams;
292                mParams = new WindowManager.LayoutParams[index];
293                System.arraycopy(old, 0, mParams, 0, index-1);
294            }
295            index--;
296
297            mViews[index] = view;
298            mRoots[index] = root;
299            mParams[index] = wparams;
300        }
301        // do this last because it fires off messages to start doing things
302        root.setView(view, wparams, panelParentView);
303    }
304
305    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
306        if (!(params instanceof WindowManager.LayoutParams)) {
307            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
308        }
309
310        final WindowManager.LayoutParams wparams
311                = (WindowManager.LayoutParams)params;
312
313        view.setLayoutParams(wparams);
314
315        synchronized (this) {
316            int index = findViewLocked(view, true);
317            ViewRootImpl root = mRoots[index];
318            mParams[index] = wparams;
319            root.setLayoutParams(wparams, false);
320        }
321    }
322
323    public void removeView(View view) {
324        synchronized (this) {
325            int index = findViewLocked(view, true);
326            View curView = removeViewLocked(index);
327            if (curView == view) {
328                return;
329            }
330
331            throw new IllegalStateException("Calling with view " + view
332                    + " but the ViewAncestor is attached to " + curView);
333        }
334    }
335
336    public void removeViewImmediate(View view) {
337        synchronized (this) {
338            int index = findViewLocked(view, true);
339            ViewRootImpl root = mRoots[index];
340            View curView = root.getView();
341
342            root.mAddNesting = 0;
343
344            if (view != null) {
345                InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
346                if (imm != null) {
347                    imm.windowDismissed(mViews[index].getWindowToken());
348                }
349            }
350
351            root.die(true);
352            finishRemoveViewLocked(curView, index);
353            if (curView == view) {
354                return;
355            }
356
357            throw new IllegalStateException("Calling with view " + view
358                    + " but the ViewAncestor is attached to " + curView);
359        }
360    }
361
362    View removeViewLocked(int index) {
363        ViewRootImpl root = mRoots[index];
364        View view = root.getView();
365
366        // Don't really remove until we have matched all calls to add().
367        root.mAddNesting--;
368        if (root.mAddNesting > 0) {
369            return view;
370        }
371
372        if (view != null) {
373            InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
374            if (imm != null) {
375                imm.windowDismissed(mViews[index].getWindowToken());
376            }
377        }
378        root.die(false);
379        finishRemoveViewLocked(view, index);
380        return view;
381    }
382
383    void finishRemoveViewLocked(View view, int index) {
384        final int count = mViews.length;
385
386        // remove it from the list
387        View[] tmpViews = new View[count-1];
388        removeItem(tmpViews, mViews, index);
389        mViews = tmpViews;
390
391        ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1];
392        removeItem(tmpRoots, mRoots, index);
393        mRoots = tmpRoots;
394
395        WindowManager.LayoutParams[] tmpParams
396                = new WindowManager.LayoutParams[count-1];
397        removeItem(tmpParams, mParams, index);
398        mParams = tmpParams;
399
400        if (view != null) {
401            view.assignParent(null);
402            // func doesn't allow null...  does it matter if we clear them?
403            //view.setLayoutParams(null);
404        }
405    }
406
407    public void closeAll(IBinder token, String who, String what) {
408        synchronized (this) {
409            if (mViews == null)
410                return;
411
412            int count = mViews.length;
413            //Log.i("foo", "Closing all windows of " + token);
414            for (int i=0; i<count; i++) {
415                //Log.i("foo", "@ " + i + " token " + mParams[i].token
416                //        + " view " + mRoots[i].getView());
417                if (token == null || mParams[i].token == token) {
418                    ViewRootImpl root = mRoots[i];
419                    root.mAddNesting = 1;
420
421                    //Log.i("foo", "Force closing " + root);
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("WindowManager", leak.getMessage(), leak);
428                    }
429
430                    removeViewLocked(i);
431                    i--;
432                    count--;
433                }
434            }
435        }
436    }
437
438    /**
439     * @param level See {@link android.content.ComponentCallbacks}
440     *
441     * @hide
442     */
443    public void startTrimMemory(int level) {
444        if (HardwareRenderer.isAvailable()) {
445            // On low-end gfx devices we trim when memory is moderate;
446            // on high-end devices we do this when low.
447            if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
448                    || (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
449                            && !ActivityManager.isHighEndGfx(getDefaultDisplay()))) {
450                // Destroy all hardware surfaces and resources associated to
451                // known windows
452                synchronized (this) {
453                    if (mViews == null) return;
454                    int count = mViews.length;
455                    for (int i = 0; i < count; i++) {
456                        mRoots[i].terminateHardwareResources();
457                    }
458                }
459                // Force a full memory flush
460                mNeedsEglTerminate = true;
461                HardwareRenderer.startTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
462                return;
463            }
464
465            HardwareRenderer.startTrimMemory(level);
466        }
467    }
468
469    /**
470     * @hide
471     */
472    public void endTrimMemory() {
473        HardwareRenderer.endTrimMemory();
474
475        if (mNeedsEglTerminate) {
476            ManagedEGLContext.doTerminate();
477            mNeedsEglTerminate = false;
478        }
479    }
480
481    /**
482     * @hide
483     */
484    public void trimLocalMemory() {
485        synchronized (this) {
486            if (mViews == null) return;
487            int count = mViews.length;
488            for (int i = 0; i < count; i++) {
489                mRoots[i].destroyHardwareLayers();
490            }
491        }
492    }
493
494    /**
495     * @hide
496     */
497    public void dumpGfxInfo(FileDescriptor fd) {
498        FileOutputStream fout = new FileOutputStream(fd);
499        PrintWriter pw = new PrintWriter(fout);
500        try {
501            synchronized (this) {
502                if (mViews != null) {
503                    final int count = mViews.length;
504
505                    pw.println("Profile data in ms:");
506
507                    for (int i = 0; i < count; i++) {
508                        ViewRootImpl root = mRoots[i];
509                        HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer;
510                        if (renderer != null) {
511                            renderer.dumpGfxInfo(pw);
512                        }
513                    }
514
515                    pw.println("\nView hierarchy:");
516
517                    int viewsCount = 0;
518                    int displayListsSize = 0;
519                    int[] info = new int[2];
520
521                    for (int i = 0; i < count; i++) {
522                        ViewRootImpl root = mRoots[i];
523                        root.dumpGfxInfo(info);
524
525                        String name = root.getClass().getName() + '@' +
526                                Integer.toHexString(hashCode());
527                        pw.printf("  %s: %d views, %.2f kB (display lists)",
528                                name, info[0], info[1] / 1024.0f);
529                        HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer;
530                        if (renderer != null) {
531                            pw.printf(", %d frames rendered", renderer.getFrameCount());
532                        }
533                        pw.printf("\n");
534
535                        viewsCount += info[0];
536                        displayListsSize += info[1];
537                    }
538
539                    pw.printf("\nTotal ViewRootImpl: %d\n", count);
540                    pw.printf("Total Views:        %d\n", viewsCount);
541                    pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
542                }
543            }
544        } finally {
545            pw.flush();
546        }
547    }
548
549    public void setStoppedState(IBinder token, boolean stopped) {
550        synchronized (this) {
551            if (mViews == null)
552                return;
553            int count = mViews.length;
554            for (int i=0; i<count; i++) {
555                if (token == null || mParams[i].token == token) {
556                    ViewRootImpl root = mRoots[i];
557                    root.setStopped(stopped);
558                }
559            }
560        }
561    }
562
563    public void reportNewConfiguration(Configuration config) {
564        synchronized (this) {
565            int count = mViews.length;
566            config = new Configuration(config);
567            for (int i=0; i<count; i++) {
568                ViewRootImpl root = mRoots[i];
569                root.requestUpdateConfiguration(config);
570            }
571        }
572    }
573
574    public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
575        ViewParent vp = view.getParent();
576        while (vp != null && !(vp instanceof ViewRootImpl)) {
577            vp = vp.getParent();
578        }
579
580        if (vp == null) return null;
581
582        ViewRootImpl vr = (ViewRootImpl)vp;
583
584        int N = mRoots.length;
585        for (int i = 0; i < N; ++i) {
586            if (mRoots[i] == vr) {
587                return mParams[i];
588            }
589        }
590
591        return null;
592    }
593
594    public void closeAll() {
595        closeAll(null, null, null);
596    }
597
598    public Display getDefaultDisplay() {
599        return new Display(Display.DEFAULT_DISPLAY, null);
600    }
601
602    private static void removeItem(Object[] dst, Object[] src, int index) {
603        if (dst.length > 0) {
604            if (index > 0) {
605                System.arraycopy(src, 0, dst, 0, index);
606            }
607            if (index < dst.length) {
608                System.arraycopy(src, index+1, dst, index, src.length-index-1);
609            }
610        }
611    }
612
613    private int findViewLocked(View view, boolean required) {
614        synchronized (this) {
615            final int count = mViews != null ? mViews.length : 0;
616            for (int i=0; i<count; i++) {
617                if (mViews[i] == view) {
618                    return i;
619                }
620            }
621            if (required) {
622                throw new IllegalArgumentException(
623                        "View not attached to window manager");
624            }
625            return -1;
626        }
627    }
628}
629