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