WindowManagerImpl.java revision fee1c69a49d03b651e481983432ade94fc07650a
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 java.util.HashMap;
20
21import android.content.res.CompatibilityInfo;
22import android.graphics.PixelFormat;
23import android.os.IBinder;
24import android.util.AndroidRuntimeException;
25import android.util.Log;
26import android.view.WindowManager;
27import android.view.inputmethod.InputMethodManager;
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 ViewAncestor[] 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 WindowManager mWindowManager;
91        private final Display mDefaultDisplay;
92
93        CompatModeWrapper(WindowManager wm, CompatibilityInfo ci) {
94            mWindowManager = wm;
95
96            // Use the original display if there is no compatibility mode
97            // to apply, or the underlying window manager is already a
98            // compatibility mode wrapper.  (We assume that if it is a
99            // wrapper, it is applying the same compatibility mode.)
100            if (ci == null || wm instanceof CompatModeWrapper
101                    || (!ci.isScalingRequired() && ci.supportsScreen())) {
102                mDefaultDisplay = mWindowManager.getDefaultDisplay();
103            } else {
104                //mDefaultDisplay = mWindowManager.getDefaultDisplay();
105                mDefaultDisplay = Display.createCompatibleDisplay(
106                        mWindowManager.getDefaultDisplay().getDisplayId(), ci);
107            }
108        }
109
110        @Override
111        public void addView(View view, android.view.ViewGroup.LayoutParams params) {
112            mWindowManager.addView(view, params);
113        }
114
115        @Override
116        public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {
117            mWindowManager.updateViewLayout(view, params);
118
119        }
120
121        @Override
122        public void removeView(View view) {
123            mWindowManager.removeView(view);
124        }
125
126        @Override
127        public Display getDefaultDisplay() {
128            return mDefaultDisplay;
129        }
130
131        @Override
132        public void removeViewImmediate(View view) {
133            mWindowManager.removeViewImmediate(view);
134        }
135
136        @Override
137        public boolean isHardwareAccelerated() {
138            return mWindowManager.isHardwareAccelerated();
139        }
140
141    }
142
143    public static WindowManagerImpl getDefault() {
144        return sWindowManager;
145    }
146
147    public static WindowManager getDefault(CompatibilityInfo compatInfo) {
148        if (compatInfo == null || (!compatInfo.isScalingRequired()
149                && compatInfo.supportsScreen())) {
150            return sWindowManager;
151        }
152
153        synchronized (sLock) {
154            // NOTE: It would be cleaner to move the implementation of
155            // WindowManagerImpl into a static inner class, and have this
156            // public impl just call into that.  Then we can make multiple
157            // instances of WindowManagerImpl for compat mode rather than
158            // having to make wrappers.
159            WindowManager wm = sCompatWindowManagers.get(compatInfo);
160            if (wm == null) {
161                wm = new CompatModeWrapper(sWindowManager, compatInfo);
162                sCompatWindowManagers.put(compatInfo, wm);
163            }
164            return wm;
165        }
166    }
167
168    public boolean isHardwareAccelerated() {
169        return false;
170    }
171
172    public void addView(View view)
173    {
174        addView(view, new WindowManager.LayoutParams(
175            WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE));
176    }
177
178    public void addView(View view, ViewGroup.LayoutParams params)
179    {
180        addView(view, params, false);
181    }
182
183    public void addViewNesting(View view, ViewGroup.LayoutParams params)
184    {
185        addView(view, params, false);
186    }
187
188    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
189    {
190        if (false) Log.v("WindowManager", "addView view=" + view);
191
192        if (!(params instanceof WindowManager.LayoutParams)) {
193            throw new IllegalArgumentException(
194                    "Params must be WindowManager.LayoutParams");
195        }
196
197        final WindowManager.LayoutParams wparams
198                = (WindowManager.LayoutParams)params;
199
200        ViewAncestor root;
201        View panelParentView = null;
202
203        synchronized (this) {
204            // Here's an odd/questionable case: if someone tries to add a
205            // view multiple times, then we simply bump up a nesting count
206            // and they need to remove the view the corresponding number of
207            // times to have it actually removed from the window manager.
208            // This is useful specifically for the notification manager,
209            // which can continually add/remove the same view as a
210            // notification gets updated.
211            int index = findViewLocked(view, false);
212            if (index >= 0) {
213                if (!nest) {
214                    throw new IllegalStateException("View " + view
215                            + " has already been added to the window manager.");
216                }
217                root = mRoots[index];
218                root.mAddNesting++;
219                // Update layout parameters.
220                view.setLayoutParams(wparams);
221                root.setLayoutParams(wparams, true);
222                return;
223            }
224
225            // If this is a panel window, then find the window it is being
226            // attached to for future reference.
227            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
228                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
229                final int count = mViews != null ? mViews.length : 0;
230                for (int i=0; i<count; i++) {
231                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
232                        panelParentView = mViews[i];
233                    }
234                }
235            }
236
237            root = new ViewAncestor(view.getContext());
238            root.mAddNesting = 1;
239
240            view.setLayoutParams(wparams);
241
242            if (mViews == null) {
243                index = 1;
244                mViews = new View[1];
245                mRoots = new ViewAncestor[1];
246                mParams = new WindowManager.LayoutParams[1];
247            } else {
248                index = mViews.length + 1;
249                Object[] old = mViews;
250                mViews = new View[index];
251                System.arraycopy(old, 0, mViews, 0, index-1);
252                old = mRoots;
253                mRoots = new ViewAncestor[index];
254                System.arraycopy(old, 0, mRoots, 0, index-1);
255                old = mParams;
256                mParams = new WindowManager.LayoutParams[index];
257                System.arraycopy(old, 0, mParams, 0, index-1);
258            }
259            index--;
260
261            mViews[index] = view;
262            mRoots[index] = root;
263            mParams[index] = wparams;
264        }
265        // do this last because it fires off messages to start doing things
266        root.setView(view, wparams, panelParentView);
267    }
268
269    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
270        if (!(params instanceof WindowManager.LayoutParams)) {
271            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
272        }
273
274        final WindowManager.LayoutParams wparams
275                = (WindowManager.LayoutParams)params;
276
277        view.setLayoutParams(wparams);
278
279        synchronized (this) {
280            int index = findViewLocked(view, true);
281            ViewAncestor root = mRoots[index];
282            mParams[index] = wparams;
283            root.setLayoutParams(wparams, false);
284        }
285    }
286
287    public void removeView(View view) {
288        synchronized (this) {
289            int index = findViewLocked(view, true);
290            View curView = removeViewLocked(index);
291            if (curView == view) {
292                return;
293            }
294
295            throw new IllegalStateException("Calling with view " + view
296                    + " but the ViewAncestor is attached to " + curView);
297        }
298    }
299
300    public void removeViewImmediate(View view) {
301        synchronized (this) {
302            int index = findViewLocked(view, true);
303            ViewAncestor root = mRoots[index];
304            View curView = root.getView();
305
306            root.mAddNesting = 0;
307            root.die(true);
308            finishRemoveViewLocked(curView, index);
309            if (curView == view) {
310                return;
311            }
312
313            throw new IllegalStateException("Calling with view " + view
314                    + " but the ViewAncestor is attached to " + curView);
315        }
316    }
317
318    View removeViewLocked(int index) {
319        ViewAncestor root = mRoots[index];
320        View view = root.getView();
321
322        // Don't really remove until we have matched all calls to add().
323        root.mAddNesting--;
324        if (root.mAddNesting > 0) {
325            return view;
326        }
327
328        InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
329        if (imm != null) {
330            imm.windowDismissed(mViews[index].getWindowToken());
331        }
332        root.die(false);
333        finishRemoveViewLocked(view, index);
334        return view;
335    }
336
337    void finishRemoveViewLocked(View view, int index) {
338        final int count = mViews.length;
339
340        // remove it from the list
341        View[] tmpViews = new View[count-1];
342        removeItem(tmpViews, mViews, index);
343        mViews = tmpViews;
344
345        ViewAncestor[] tmpRoots = new ViewAncestor[count-1];
346        removeItem(tmpRoots, mRoots, index);
347        mRoots = tmpRoots;
348
349        WindowManager.LayoutParams[] tmpParams
350                = new WindowManager.LayoutParams[count-1];
351        removeItem(tmpParams, mParams, index);
352        mParams = tmpParams;
353
354        view.assignParent(null);
355        // func doesn't allow null...  does it matter if we clear them?
356        //view.setLayoutParams(null);
357    }
358
359    public void closeAll(IBinder token, String who, String what) {
360        synchronized (this) {
361            if (mViews == null)
362                return;
363
364            int count = mViews.length;
365            //Log.i("foo", "Closing all windows of " + token);
366            for (int i=0; i<count; i++) {
367                //Log.i("foo", "@ " + i + " token " + mParams[i].token
368                //        + " view " + mRoots[i].getView());
369                if (token == null || mParams[i].token == token) {
370                    ViewAncestor root = mRoots[i];
371                    root.mAddNesting = 1;
372
373                    //Log.i("foo", "Force closing " + root);
374                    if (who != null) {
375                        WindowLeaked leak = new WindowLeaked(
376                                what + " " + who + " has leaked window "
377                                + root.getView() + " that was originally added here");
378                        leak.setStackTrace(root.getLocation().getStackTrace());
379                        Log.e("WindowManager", leak.getMessage(), leak);
380                    }
381
382                    removeViewLocked(i);
383                    i--;
384                    count--;
385                }
386            }
387        }
388    }
389
390    public void setStoppedState(IBinder token, boolean stopped) {
391        synchronized (this) {
392            if (mViews == null)
393                return;
394            int count = mViews.length;
395            for (int i=0; i<count; i++) {
396                if (token == null || mParams[i].token == token) {
397                    ViewAncestor root = mRoots[i];
398                    root.setStopped(stopped);
399                }
400            }
401        }
402    }
403
404    public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
405        ViewParent vp = view.getParent();
406        while (vp != null && !(vp instanceof ViewAncestor)) {
407            vp = vp.getParent();
408        }
409
410        if (vp == null) return null;
411
412        ViewAncestor vr = (ViewAncestor)vp;
413
414        int N = mRoots.length;
415        for (int i = 0; i < N; ++i) {
416            if (mRoots[i] == vr) {
417                return mParams[i];
418            }
419        }
420
421        return null;
422    }
423
424    public void closeAll() {
425        closeAll(null, null, null);
426    }
427
428    public Display getDefaultDisplay() {
429        return new Display(Display.DEFAULT_DISPLAY, null);
430    }
431
432    private static void removeItem(Object[] dst, Object[] src, int index)
433    {
434        if (dst.length > 0) {
435            if (index > 0) {
436                System.arraycopy(src, 0, dst, 0, index);
437            }
438            if (index < dst.length) {
439                System.arraycopy(src, index+1, dst, index, src.length-index-1);
440            }
441        }
442    }
443
444    private int findViewLocked(View view, boolean required)
445    {
446        synchronized (this) {
447            final int count = mViews != null ? mViews.length : 0;
448            for (int i=0; i<count; i++) {
449                if (mViews[i] == view) {
450                    return i;
451                }
452            }
453            if (required) {
454                throw new IllegalArgumentException(
455                        "View not attached to window manager");
456            }
457            return -1;
458        }
459    }
460}
461