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