WindowManagerImpl.java revision c6cc0f8c19d9eccf408a443fa2bf668af261dcd0
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.graphics.PixelFormat;
20import android.os.IBinder;
21import android.util.AndroidRuntimeException;
22import android.util.Log;
23import android.view.WindowManager;
24import android.view.inputmethod.InputMethodManager;
25
26final class WindowLeaked extends AndroidRuntimeException {
27    public WindowLeaked(String msg) {
28        super(msg);
29    }
30}
31
32/**
33 * Low-level communication with the global system window manager.  It implements
34 * the ViewManager interface, allowing you to add any View subclass as a
35 * top-level window on the screen. Additional window manager specific layout
36 * parameters are defined for control over how windows are displayed.
37 * It also implemens the WindowManager interface, allowing you to control the
38 * displays attached to the device.
39 *
40 * <p>Applications will not normally use WindowManager directly, instead relying
41 * on the higher-level facilities in {@link android.app.Activity} and
42 * {@link android.app.Dialog}.
43 *
44 * <p>Even for low-level window manager access, it is almost never correct to use
45 * this class.  For example, {@link android.app.Activity#getWindowManager}
46 * provides a ViewManager for adding windows that are associated with that
47 * activity -- the window manager will not normally allow you to add arbitrary
48 * windows that are not associated with an activity.
49 *
50 * @hide
51 */
52public class WindowManagerImpl implements WindowManager {
53    /**
54     * The user is navigating with keys (not the touch screen), so
55     * navigational focus should be shown.
56     */
57    public static final int RELAYOUT_IN_TOUCH_MODE = 0x1;
58    /**
59     * This is the first time the window is being drawn,
60     * so the client must call drawingFinished() when done
61     */
62    public static final int RELAYOUT_FIRST_TIME = 0x2;
63
64    public static final int ADD_FLAG_APP_VISIBLE = 0x2;
65    public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_IN_TOUCH_MODE;
66
67    public static final int ADD_OKAY = 0;
68    public static final int ADD_BAD_APP_TOKEN = -1;
69    public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
70    public static final int ADD_NOT_APP_TOKEN = -3;
71    public static final int ADD_APP_EXITING = -4;
72    public static final int ADD_DUPLICATE_ADD = -5;
73    public static final int ADD_STARTING_NOT_NEEDED = -6;
74    public static final int ADD_MULTIPLE_SINGLETON = -7;
75    public static final int ADD_PERMISSION_DENIED = -8;
76
77    public static WindowManagerImpl getDefault()
78    {
79        return mWindowManager;
80    }
81
82    public boolean isHardwareAccelerated() {
83        return false;
84    }
85
86    public void addView(View view)
87    {
88        addView(view, new WindowManager.LayoutParams(
89            WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE));
90    }
91
92    public void addView(View view, ViewGroup.LayoutParams params)
93    {
94        addView(view, params, false);
95    }
96
97    public void addViewNesting(View view, ViewGroup.LayoutParams params)
98    {
99        addView(view, params, false);
100    }
101
102    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
103    {
104        if (false) Log.v("WindowManager", "addView view=" + view);
105
106        if (!(params instanceof WindowManager.LayoutParams)) {
107            throw new IllegalArgumentException(
108                    "Params must be WindowManager.LayoutParams");
109        }
110
111        final WindowManager.LayoutParams wparams
112                = (WindowManager.LayoutParams)params;
113
114        ViewAncestor root;
115        View panelParentView = null;
116
117        synchronized (this) {
118            // Here's an odd/questionable case: if someone tries to add a
119            // view multiple times, then we simply bump up a nesting count
120            // and they need to remove the view the corresponding number of
121            // times to have it actually removed from the window manager.
122            // This is useful specifically for the notification manager,
123            // which can continually add/remove the same view as a
124            // notification gets updated.
125            int index = findViewLocked(view, false);
126            if (index >= 0) {
127                if (!nest) {
128                    throw new IllegalStateException("View " + view
129                            + " has already been added to the window manager.");
130                }
131                root = mRoots[index];
132                root.mAddNesting++;
133                // Update layout parameters.
134                view.setLayoutParams(wparams);
135                root.setLayoutParams(wparams, true);
136                return;
137            }
138
139            // If this is a panel window, then find the window it is being
140            // attached to for future reference.
141            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
142                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
143                final int count = mViews != null ? mViews.length : 0;
144                for (int i=0; i<count; i++) {
145                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
146                        panelParentView = mViews[i];
147                    }
148                }
149            }
150
151            root = new ViewAncestor(view.getContext());
152            root.mAddNesting = 1;
153
154            view.setLayoutParams(wparams);
155
156            if (mViews == null) {
157                index = 1;
158                mViews = new View[1];
159                mRoots = new ViewAncestor[1];
160                mParams = new WindowManager.LayoutParams[1];
161            } else {
162                index = mViews.length + 1;
163                Object[] old = mViews;
164                mViews = new View[index];
165                System.arraycopy(old, 0, mViews, 0, index-1);
166                old = mRoots;
167                mRoots = new ViewAncestor[index];
168                System.arraycopy(old, 0, mRoots, 0, index-1);
169                old = mParams;
170                mParams = new WindowManager.LayoutParams[index];
171                System.arraycopy(old, 0, mParams, 0, index-1);
172            }
173            index--;
174
175            mViews[index] = view;
176            mRoots[index] = root;
177            mParams[index] = wparams;
178        }
179        // do this last because it fires off messages to start doing things
180        root.setView(view, wparams, panelParentView);
181    }
182
183    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
184        if (!(params instanceof WindowManager.LayoutParams)) {
185            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
186        }
187
188        final WindowManager.LayoutParams wparams
189                = (WindowManager.LayoutParams)params;
190
191        view.setLayoutParams(wparams);
192
193        synchronized (this) {
194            int index = findViewLocked(view, true);
195            ViewAncestor root = mRoots[index];
196            mParams[index] = wparams;
197            root.setLayoutParams(wparams, false);
198        }
199    }
200
201    public void removeView(View view) {
202        synchronized (this) {
203            int index = findViewLocked(view, true);
204            View curView = removeViewLocked(index);
205            if (curView == view) {
206                return;
207            }
208
209            throw new IllegalStateException("Calling with view " + view
210                    + " but the ViewAncestor is attached to " + curView);
211        }
212    }
213
214    public void removeViewImmediate(View view) {
215        synchronized (this) {
216            int index = findViewLocked(view, true);
217            ViewAncestor root = mRoots[index];
218            View curView = root.getView();
219
220            root.mAddNesting = 0;
221            root.die(true);
222            finishRemoveViewLocked(curView, index);
223            if (curView == view) {
224                return;
225            }
226
227            throw new IllegalStateException("Calling with view " + view
228                    + " but the ViewAncestor is attached to " + curView);
229        }
230    }
231
232    View removeViewLocked(int index) {
233        ViewAncestor root = mRoots[index];
234        View view = root.getView();
235
236        // Don't really remove until we have matched all calls to add().
237        root.mAddNesting--;
238        if (root.mAddNesting > 0) {
239            return view;
240        }
241
242        InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
243        if (imm != null) {
244            imm.windowDismissed(mViews[index].getWindowToken());
245        }
246        root.die(false);
247        finishRemoveViewLocked(view, index);
248        return view;
249    }
250
251    void finishRemoveViewLocked(View view, int index) {
252        final int count = mViews.length;
253
254        // remove it from the list
255        View[] tmpViews = new View[count-1];
256        removeItem(tmpViews, mViews, index);
257        mViews = tmpViews;
258
259        ViewAncestor[] tmpRoots = new ViewAncestor[count-1];
260        removeItem(tmpRoots, mRoots, index);
261        mRoots = tmpRoots;
262
263        WindowManager.LayoutParams[] tmpParams
264                = new WindowManager.LayoutParams[count-1];
265        removeItem(tmpParams, mParams, index);
266        mParams = tmpParams;
267
268        view.assignParent(null);
269        // func doesn't allow null...  does it matter if we clear them?
270        //view.setLayoutParams(null);
271    }
272
273    public void closeAll(IBinder token, String who, String what) {
274        synchronized (this) {
275            if (mViews == null)
276                return;
277
278            int count = mViews.length;
279            //Log.i("foo", "Closing all windows of " + token);
280            for (int i=0; i<count; i++) {
281                //Log.i("foo", "@ " + i + " token " + mParams[i].token
282                //        + " view " + mRoots[i].getView());
283                if (token == null || mParams[i].token == token) {
284                    ViewAncestor root = mRoots[i];
285                    root.mAddNesting = 1;
286
287                    //Log.i("foo", "Force closing " + root);
288                    if (who != null) {
289                        WindowLeaked leak = new WindowLeaked(
290                                what + " " + who + " has leaked window "
291                                + root.getView() + " that was originally added here");
292                        leak.setStackTrace(root.getLocation().getStackTrace());
293                        Log.e("WindowManager", leak.getMessage(), leak);
294                    }
295
296                    removeViewLocked(i);
297                    i--;
298                    count--;
299                }
300            }
301        }
302    }
303
304    public void setStoppedState(IBinder token, boolean stopped) {
305        synchronized (this) {
306            if (mViews == null)
307                return;
308            int count = mViews.length;
309            for (int i=0; i<count; i++) {
310                if (token == null || mParams[i].token == token) {
311                    ViewAncestor root = mRoots[i];
312                    root.setStopped(stopped);
313                }
314            }
315        }
316    }
317
318    public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
319        ViewParent vp = view.getParent();
320        while (vp != null && !(vp instanceof ViewAncestor)) {
321            vp = vp.getParent();
322        }
323
324        if (vp == null) return null;
325
326        ViewAncestor vr = (ViewAncestor)vp;
327
328        int N = mRoots.length;
329        for (int i = 0; i < N; ++i) {
330            if (mRoots[i] == vr) {
331                return mParams[i];
332            }
333        }
334
335        return null;
336    }
337
338    public void closeAll() {
339        closeAll(null, null, null);
340    }
341
342    public Display getDefaultDisplay() {
343        return new Display(Display.DEFAULT_DISPLAY);
344    }
345
346    private View[] mViews;
347    private ViewAncestor[] mRoots;
348    private WindowManager.LayoutParams[] mParams;
349
350    private static void removeItem(Object[] dst, Object[] src, int index)
351    {
352        if (dst.length > 0) {
353            if (index > 0) {
354                System.arraycopy(src, 0, dst, 0, index);
355            }
356            if (index < dst.length) {
357                System.arraycopy(src, index+1, dst, index, src.length-index-1);
358            }
359        }
360    }
361
362    private int findViewLocked(View view, boolean required)
363    {
364        synchronized (this) {
365            final int count = mViews != null ? mViews.length : 0;
366            for (int i=0; i<count; i++) {
367                if (mViews[i] == view) {
368                    return i;
369                }
370            }
371            if (required) {
372                throw new IllegalArgumentException(
373                        "View not attached to window manager");
374            }
375            return -1;
376        }
377    }
378
379    private static WindowManagerImpl mWindowManager = new WindowManagerImpl();
380}
381