WindowManagerImpl.java revision 211370fd943cf26905001b38b8b1791851b26adc
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
109    private final static Object sLock = new Object();
110    private final static WindowManagerImpl sWindowManager = new WindowManagerImpl();
111    private final static HashMap<CompatibilityInfo, WindowManager> sCompatWindowManagers
112            = new HashMap<CompatibilityInfo, WindowManager>();
113
114    static class CompatModeWrapper implements WindowManager {
115        private final WindowManagerImpl mWindowManager;
116        private final Display mDefaultDisplay;
117        private final CompatibilityInfoHolder mCompatibilityInfo;
118
119        CompatModeWrapper(WindowManager wm, CompatibilityInfoHolder ci) {
120            mWindowManager = wm instanceof CompatModeWrapper
121                    ? ((CompatModeWrapper)wm).mWindowManager : (WindowManagerImpl)wm;
122
123            // Use the original display if there is no compatibility mode
124            // to apply, or the underlying window manager is already a
125            // compatibility mode wrapper.  (We assume that if it is a
126            // wrapper, it is applying the same compatibility mode.)
127            if (ci == null) {
128                mDefaultDisplay = mWindowManager.getDefaultDisplay();
129            } else {
130                //mDefaultDisplay = mWindowManager.getDefaultDisplay();
131                mDefaultDisplay = Display.createCompatibleDisplay(
132                        mWindowManager.getDefaultDisplay().getDisplayId(), ci);
133            }
134
135            mCompatibilityInfo = ci;
136        }
137
138        @Override
139        public void addView(View view, android.view.ViewGroup.LayoutParams params) {
140            mWindowManager.addView(view, params, mCompatibilityInfo);
141        }
142
143        @Override
144        public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {
145            mWindowManager.updateViewLayout(view, params);
146
147        }
148
149        @Override
150        public void removeView(View view) {
151            mWindowManager.removeView(view);
152        }
153
154        @Override
155        public Display getDefaultDisplay() {
156            return mDefaultDisplay;
157        }
158
159        @Override
160        public void removeViewImmediate(View view) {
161            mWindowManager.removeViewImmediate(view);
162        }
163
164        @Override
165        public boolean isHardwareAccelerated() {
166            return mWindowManager.isHardwareAccelerated();
167        }
168
169    }
170
171    public static WindowManagerImpl getDefault() {
172        return sWindowManager;
173    }
174
175    public static WindowManager getDefault(CompatibilityInfo compatInfo) {
176        CompatibilityInfoHolder cih = new CompatibilityInfoHolder();
177        cih.set(compatInfo);
178        if (cih.getIfNeeded() == null) {
179            return sWindowManager;
180        }
181
182        synchronized (sLock) {
183            // NOTE: It would be cleaner to move the implementation of
184            // WindowManagerImpl into a static inner class, and have this
185            // public impl just call into that.  Then we can make multiple
186            // instances of WindowManagerImpl for compat mode rather than
187            // having to make wrappers.
188            WindowManager wm = sCompatWindowManagers.get(compatInfo);
189            if (wm == null) {
190                wm = new CompatModeWrapper(sWindowManager, cih);
191                sCompatWindowManagers.put(compatInfo, wm);
192            }
193            return wm;
194        }
195    }
196
197    public static WindowManager getDefault(CompatibilityInfoHolder compatInfo) {
198        return new CompatModeWrapper(sWindowManager, compatInfo);
199    }
200
201    public boolean isHardwareAccelerated() {
202        return false;
203    }
204
205    public void addView(View view) {
206        addView(view, new WindowManager.LayoutParams(
207            WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE));
208    }
209
210    public void addView(View view, ViewGroup.LayoutParams params) {
211        addView(view, params, null, false);
212    }
213
214    public void addView(View view, ViewGroup.LayoutParams params, CompatibilityInfoHolder cih) {
215        addView(view, params, cih, false);
216    }
217
218    private void addView(View view, ViewGroup.LayoutParams params,
219            CompatibilityInfoHolder cih, boolean nest) {
220        if (false) Log.v("WindowManager", "addView view=" + view);
221
222        if (!(params instanceof WindowManager.LayoutParams)) {
223            throw new IllegalArgumentException(
224                    "Params must be WindowManager.LayoutParams");
225        }
226
227        final WindowManager.LayoutParams wparams
228                = (WindowManager.LayoutParams)params;
229
230        ViewRootImpl root;
231        View panelParentView = null;
232
233        synchronized (this) {
234            // Here's an odd/questionable case: if someone tries to add a
235            // view multiple times, then we simply bump up a nesting count
236            // and they need to remove the view the corresponding number of
237            // times to have it actually removed from the window manager.
238            // This is useful specifically for the notification manager,
239            // which can continually add/remove the same view as a
240            // notification gets updated.
241            int index = findViewLocked(view, false);
242            if (index >= 0) {
243                if (!nest) {
244                    throw new IllegalStateException("View " + view
245                            + " has already been added to the window manager.");
246                }
247                root = mRoots[index];
248                root.mAddNesting++;
249                // Update layout parameters.
250                view.setLayoutParams(wparams);
251                root.setLayoutParams(wparams, true);
252                return;
253            }
254
255            // If this is a panel window, then find the window it is being
256            // attached to for future reference.
257            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
258                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
259                final int count = mViews != null ? mViews.length : 0;
260                for (int i=0; i<count; i++) {
261                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
262                        panelParentView = mViews[i];
263                    }
264                }
265            }
266
267            root = new ViewRootImpl(view.getContext());
268            root.mAddNesting = 1;
269            if (cih == null) {
270                root.mCompatibilityInfo = new CompatibilityInfoHolder();
271            } else {
272                root.mCompatibilityInfo = cih;
273            }
274
275            view.setLayoutParams(wparams);
276
277            if (mViews == null) {
278                index = 1;
279                mViews = new View[1];
280                mRoots = new ViewRootImpl[1];
281                mParams = new WindowManager.LayoutParams[1];
282            } else {
283                index = mViews.length + 1;
284                Object[] old = mViews;
285                mViews = new View[index];
286                System.arraycopy(old, 0, mViews, 0, index-1);
287                old = mRoots;
288                mRoots = new ViewRootImpl[index];
289                System.arraycopy(old, 0, mRoots, 0, index-1);
290                old = mParams;
291                mParams = new WindowManager.LayoutParams[index];
292                System.arraycopy(old, 0, mParams, 0, index-1);
293            }
294            index--;
295
296            mViews[index] = view;
297            mRoots[index] = root;
298            mParams[index] = wparams;
299        }
300        // do this last because it fires off messages to start doing things
301        root.setView(view, wparams, panelParentView);
302    }
303
304    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
305        if (!(params instanceof WindowManager.LayoutParams)) {
306            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
307        }
308
309        final WindowManager.LayoutParams wparams
310                = (WindowManager.LayoutParams)params;
311
312        view.setLayoutParams(wparams);
313
314        synchronized (this) {
315            int index = findViewLocked(view, true);
316            ViewRootImpl root = mRoots[index];
317            mParams[index] = wparams;
318            root.setLayoutParams(wparams, false);
319        }
320    }
321
322    public void removeView(View view) {
323        synchronized (this) {
324            int index = findViewLocked(view, true);
325            View curView = removeViewLocked(index);
326            if (curView == view) {
327                return;
328            }
329
330            throw new IllegalStateException("Calling with view " + view
331                    + " but the ViewAncestor is attached to " + curView);
332        }
333    }
334
335    public void removeViewImmediate(View view) {
336        synchronized (this) {
337            int index = findViewLocked(view, true);
338            ViewRootImpl root = mRoots[index];
339            View curView = root.getView();
340
341            root.mAddNesting = 0;
342            root.die(true);
343            finishRemoveViewLocked(curView, index);
344            if (curView == view) {
345                return;
346            }
347
348            throw new IllegalStateException("Calling with view " + view
349                    + " but the ViewAncestor is attached to " + curView);
350        }
351    }
352
353    View removeViewLocked(int index) {
354        ViewRootImpl root = mRoots[index];
355        View view = root.getView();
356
357        // Don't really remove until we have matched all calls to add().
358        root.mAddNesting--;
359        if (root.mAddNesting > 0) {
360            return view;
361        }
362
363        if (view != null) {
364            InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
365            if (imm != null) {
366                imm.windowDismissed(mViews[index].getWindowToken());
367            }
368        }
369        root.die(false);
370        finishRemoveViewLocked(view, index);
371        return view;
372    }
373
374    void finishRemoveViewLocked(View view, int index) {
375        final int count = mViews.length;
376
377        // remove it from the list
378        View[] tmpViews = new View[count-1];
379        removeItem(tmpViews, mViews, index);
380        mViews = tmpViews;
381
382        ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1];
383        removeItem(tmpRoots, mRoots, index);
384        mRoots = tmpRoots;
385
386        WindowManager.LayoutParams[] tmpParams
387                = new WindowManager.LayoutParams[count-1];
388        removeItem(tmpParams, mParams, index);
389        mParams = tmpParams;
390
391        if (view != null) {
392            view.assignParent(null);
393            // func doesn't allow null...  does it matter if we clear them?
394            //view.setLayoutParams(null);
395        }
396    }
397
398    public void closeAll(IBinder token, String who, String what) {
399        synchronized (this) {
400            if (mViews == null)
401                return;
402
403            int count = mViews.length;
404            //Log.i("foo", "Closing all windows of " + token);
405            for (int i=0; i<count; i++) {
406                //Log.i("foo", "@ " + i + " token " + mParams[i].token
407                //        + " view " + mRoots[i].getView());
408                if (token == null || mParams[i].token == token) {
409                    ViewRootImpl root = mRoots[i];
410                    root.mAddNesting = 1;
411
412                    //Log.i("foo", "Force closing " + root);
413                    if (who != null) {
414                        WindowLeaked leak = new WindowLeaked(
415                                what + " " + who + " has leaked window "
416                                + root.getView() + " that was originally added here");
417                        leak.setStackTrace(root.getLocation().getStackTrace());
418                        Log.e("WindowManager", leak.getMessage(), leak);
419                    }
420
421                    removeViewLocked(i);
422                    i--;
423                    count--;
424                }
425            }
426        }
427    }
428
429    /**
430     * @param level See {@link android.content.ComponentCallbacks}
431     */
432    public void trimMemory(int level) {
433        if (HardwareRenderer.isAvailable()) {
434            switch (level) {
435                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
436                case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
437                    // On low and medium end gfx devices
438                    if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
439                        // Force a full memory flush
440                        HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
441                        // Destroy all hardware surfaces and resources associated to
442                        // known windows
443                        synchronized (this) {
444                            if (mViews == null) return;
445                            int count = mViews.length;
446                            for (int i = 0; i < count; i++) {
447                                mRoots[i].terminateHardwareResources();
448                            }
449                        }
450                        // Terminate the hardware renderer to free all resources
451                        ManagedEGLContext.doTerminate();
452                        break;
453                    }
454                    // high end gfx devices fall through to next case
455                default:
456                    HardwareRenderer.trimMemory(level);
457            }
458        }
459    }
460
461    /**
462     * @hide
463     */
464    public void trimLocalMemory() {
465        synchronized (this) {
466            if (mViews == null) return;
467            int count = mViews.length;
468            for (int i = 0; i < count; i++) {
469                mRoots[i].destroyHardwareLayers();
470            }
471        }
472    }
473
474    /**
475     * @hide
476     */
477    public void dumpGfxInfo(FileDescriptor fd) {
478        FileOutputStream fout = new FileOutputStream(fd);
479        PrintWriter pw = new PrintWriter(fout);
480        try {
481            synchronized (this) {
482                if (mViews != null) {
483                    pw.println("View hierarchy:");
484
485                    final int count = mViews.length;
486
487                    int viewsCount = 0;
488                    int displayListsSize = 0;
489                    int[] info = new int[2];
490
491                    for (int i = 0; i < count; i++) {
492                        ViewRootImpl root = mRoots[i];
493                        root.dumpGfxInfo(info);
494
495                        String name = root.getClass().getName() + '@' +
496                                Integer.toHexString(hashCode());
497                        pw.printf("  %s: %d views, %.2f kB (display lists)\n",
498                                name, info[0], info[1] / 1024.0f);
499
500                        viewsCount += info[0];
501                        displayListsSize += info[1];
502                    }
503
504                    pw.printf("\nTotal ViewRootImpl: %d\n", count);
505                    pw.printf("Total Views:        %d\n", viewsCount);
506                    pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
507                }
508            }
509        } finally {
510            pw.flush();
511        }
512    }
513
514    public void setStoppedState(IBinder token, boolean stopped) {
515        synchronized (this) {
516            if (mViews == null)
517                return;
518            int count = mViews.length;
519            for (int i=0; i<count; i++) {
520                if (token == null || mParams[i].token == token) {
521                    ViewRootImpl root = mRoots[i];
522                    root.setStopped(stopped);
523                }
524            }
525        }
526    }
527
528    public void reportNewConfiguration(Configuration config) {
529        synchronized (this) {
530            int count = mViews.length;
531            config = new Configuration(config);
532            for (int i=0; i<count; i++) {
533                ViewRootImpl root = mRoots[i];
534                root.requestUpdateConfiguration(config);
535            }
536        }
537    }
538
539    public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
540        ViewParent vp = view.getParent();
541        while (vp != null && !(vp instanceof ViewRootImpl)) {
542            vp = vp.getParent();
543        }
544
545        if (vp == null) return null;
546
547        ViewRootImpl vr = (ViewRootImpl)vp;
548
549        int N = mRoots.length;
550        for (int i = 0; i < N; ++i) {
551            if (mRoots[i] == vr) {
552                return mParams[i];
553            }
554        }
555
556        return null;
557    }
558
559    public void closeAll() {
560        closeAll(null, null, null);
561    }
562
563    public Display getDefaultDisplay() {
564        return new Display(Display.DEFAULT_DISPLAY, null);
565    }
566
567    private static void removeItem(Object[] dst, Object[] src, int index) {
568        if (dst.length > 0) {
569            if (index > 0) {
570                System.arraycopy(src, 0, dst, 0, index);
571            }
572            if (index < dst.length) {
573                System.arraycopy(src, index+1, dst, index, src.length-index-1);
574            }
575        }
576    }
577
578    private int findViewLocked(View view, boolean required) {
579        synchronized (this) {
580            final int count = mViews != null ? mViews.length : 0;
581            for (int i=0; i<count; i++) {
582                if (mViews[i] == view) {
583                    return i;
584                }
585            }
586            if (required) {
587                throw new IllegalArgumentException(
588                        "View not attached to window manager");
589            }
590            return -1;
591        }
592    }
593}
594