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