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 void addView(View view)
84    {
85        addView(view, new WindowManager.LayoutParams(
86            WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE));
87    }
88
89    public void addView(View view, ViewGroup.LayoutParams params)
90    {
91        addView(view, params, false);
92    }
93
94    public void addViewNesting(View view, ViewGroup.LayoutParams params)
95    {
96        addView(view, params, false);
97    }
98
99    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
100    {
101        if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);
102
103        if (!(params instanceof WindowManager.LayoutParams)) {
104            throw new IllegalArgumentException(
105                    "Params must be WindowManager.LayoutParams");
106        }
107
108        final WindowManager.LayoutParams wparams
109                = (WindowManager.LayoutParams)params;
110
111        ViewRoot root;
112        View panelParentView = null;
113
114        synchronized (this) {
115            // Here's an odd/questionable case: if someone tries to add a
116            // view multiple times, then we simply bump up a nesting count
117            // and they need to remove the view the corresponding number of
118            // times to have it actually removed from the window manager.
119            // This is useful specifically for the notification manager,
120            // which can continually add/remove the same view as a
121            // notification gets updated.
122            int index = findViewLocked(view, false);
123            if (index >= 0) {
124                if (!nest) {
125                    throw new IllegalStateException("View " + view
126                            + " has already been added to the window manager.");
127                }
128                root = mRoots[index];
129                root.mAddNesting++;
130                // Update layout parameters.
131                view.setLayoutParams(wparams);
132                root.setLayoutParams(wparams, true);
133                return;
134            }
135
136            // If this is a panel window, then find the window it is being
137            // attached to for future reference.
138            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
139                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
140                final int count = mViews != null ? mViews.length : 0;
141                for (int i=0; i<count; i++) {
142                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
143                        panelParentView = mViews[i];
144                    }
145                }
146            }
147
148            root = new ViewRoot(view.getContext());
149            root.mAddNesting = 1;
150
151            view.setLayoutParams(wparams);
152
153            if (mViews == null) {
154                index = 1;
155                mViews = new View[1];
156                mRoots = new ViewRoot[1];
157                mParams = new WindowManager.LayoutParams[1];
158            } else {
159                index = mViews.length + 1;
160                Object[] old = mViews;
161                mViews = new View[index];
162                System.arraycopy(old, 0, mViews, 0, index-1);
163                old = mRoots;
164                mRoots = new ViewRoot[index];
165                System.arraycopy(old, 0, mRoots, 0, index-1);
166                old = mParams;
167                mParams = new WindowManager.LayoutParams[index];
168                System.arraycopy(old, 0, mParams, 0, index-1);
169            }
170            index--;
171
172            mViews[index] = view;
173            mRoots[index] = root;
174            mParams[index] = wparams;
175        }
176        // do this last because it fires off messages to start doing things
177        root.setView(view, wparams, panelParentView);
178    }
179
180    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
181        if (!(params instanceof WindowManager.LayoutParams)) {
182            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
183        }
184
185        final WindowManager.LayoutParams wparams
186                = (WindowManager.LayoutParams)params;
187
188        view.setLayoutParams(wparams);
189
190        synchronized (this) {
191            int index = findViewLocked(view, true);
192            ViewRoot root = mRoots[index];
193            mParams[index] = wparams;
194            root.setLayoutParams(wparams, false);
195        }
196    }
197
198    public void removeView(View view) {
199        synchronized (this) {
200            int index = findViewLocked(view, true);
201            View curView = removeViewLocked(index);
202            if (curView == view) {
203                return;
204            }
205
206            throw new IllegalStateException("Calling with view " + view
207                    + " but the ViewRoot is attached to " + curView);
208        }
209    }
210
211    public void removeViewImmediate(View view) {
212        synchronized (this) {
213            int index = findViewLocked(view, true);
214            ViewRoot root = mRoots[index];
215            View curView = root.getView();
216
217            root.mAddNesting = 0;
218            root.die(true);
219            finishRemoveViewLocked(curView, index);
220            if (curView == view) {
221                return;
222            }
223
224            throw new IllegalStateException("Calling with view " + view
225                    + " but the ViewRoot is attached to " + curView);
226        }
227    }
228
229    View removeViewLocked(int index) {
230        ViewRoot root = mRoots[index];
231        View view = root.getView();
232
233        // Don't really remove until we have matched all calls to add().
234        root.mAddNesting--;
235        if (root.mAddNesting > 0) {
236            return view;
237        }
238
239        InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
240        if (imm != null) {
241            imm.windowDismissed(mViews[index].getWindowToken());
242        }
243        root.die(false);
244        finishRemoveViewLocked(view, index);
245        return view;
246    }
247
248    void finishRemoveViewLocked(View view, int index) {
249        final int count = mViews.length;
250
251        // remove it from the list
252        View[] tmpViews = new View[count-1];
253        removeItem(tmpViews, mViews, index);
254        mViews = tmpViews;
255
256        ViewRoot[] tmpRoots = new ViewRoot[count-1];
257        removeItem(tmpRoots, mRoots, index);
258        mRoots = tmpRoots;
259
260        WindowManager.LayoutParams[] tmpParams
261                = new WindowManager.LayoutParams[count-1];
262        removeItem(tmpParams, mParams, index);
263        mParams = tmpParams;
264
265        view.assignParent(null);
266        // func doesn't allow null...  does it matter if we clear them?
267        //view.setLayoutParams(null);
268    }
269
270    public void closeAll(IBinder token, String who, String what) {
271        synchronized (this) {
272            if (mViews == null)
273                return;
274
275            int count = mViews.length;
276            //Log.i("foo", "Closing all windows of " + token);
277            for (int i=0; i<count; i++) {
278                //Log.i("foo", "@ " + i + " token " + mParams[i].token
279                //        + " view " + mRoots[i].getView());
280                if (token == null || mParams[i].token == token) {
281                    ViewRoot root = mRoots[i];
282                    root.mAddNesting = 1;
283
284                    //Log.i("foo", "Force closing " + root);
285                    if (who != null) {
286                        WindowLeaked leak = new WindowLeaked(
287                                what + " " + who + " has leaked window "
288                                + root.getView() + " that was originally added here");
289                        leak.setStackTrace(root.getLocation().getStackTrace());
290                        Log.e("WindowManager", leak.getMessage(), leak);
291                    }
292
293                    removeViewLocked(i);
294                    i--;
295                    count--;
296                }
297            }
298        }
299    }
300
301    public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
302        ViewParent vp = view.getParent();
303        while (vp != null && !(vp instanceof ViewRoot)) {
304            vp = vp.getParent();
305        }
306
307        if (vp == null) return null;
308
309        ViewRoot vr = (ViewRoot)vp;
310
311        int N = mRoots.length;
312        for (int i = 0; i < N; ++i) {
313            if (mRoots[i] == vr) {
314                return mParams[i];
315            }
316        }
317
318        return null;
319    }
320
321    public void closeAll() {
322        closeAll(null, null, null);
323    }
324
325    public Display getDefaultDisplay() {
326        return new Display(Display.DEFAULT_DISPLAY);
327    }
328
329    private View[] mViews;
330    private ViewRoot[] mRoots;
331    private WindowManager.LayoutParams[] mParams;
332
333    private static void removeItem(Object[] dst, Object[] src, int index)
334    {
335        if (dst.length > 0) {
336            if (index > 0) {
337                System.arraycopy(src, 0, dst, 0, index);
338            }
339            if (index < dst.length) {
340                System.arraycopy(src, index+1, dst, index, src.length-index-1);
341            }
342        }
343    }
344
345    private int findViewLocked(View view, boolean required)
346    {
347        synchronized (this) {
348            final int count = mViews != null ? mViews.length : 0;
349            for (int i=0; i<count; i++) {
350                if (mViews[i] == view) {
351                    return i;
352                }
353            }
354            if (required) {
355                throw new IllegalArgumentException(
356                        "View not attached to window manager");
357            }
358            return -1;
359        }
360    }
361
362    private static WindowManagerImpl mWindowManager = new WindowManagerImpl();
363}
364