WindowManagerImpl.java revision a8b9defade5b937d4ad64f9aff4bca792298f43c
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.app.ActivityManager;
20import android.content.ComponentCallbacks2;
21import android.content.res.Configuration;
22import android.opengl.ManagedEGLContext;
23import android.os.IBinder;
24import android.os.ServiceManager;
25import android.os.SystemProperties;
26import android.util.AndroidRuntimeException;
27import android.util.Log;
28import android.view.inputmethod.InputMethodManager;
29
30import java.io.FileDescriptor;
31import java.io.FileOutputStream;
32import java.io.PrintWriter;
33
34final class WindowLeaked extends AndroidRuntimeException {
35    public WindowLeaked(String msg) {
36        super(msg);
37    }
38}
39
40/**
41 * Low-level communication with the global system window manager.  It implements
42 * the ViewManager interface, allowing you to add any View subclass as a
43 * top-level window on the screen. Additional window manager specific layout
44 * parameters are defined for control over how windows are displayed.
45 * It also implements the WindowManager interface, allowing you to control the
46 * displays attached to the device.
47 *
48 * <p>Applications will not normally use WindowManager directly, instead relying
49 * on the higher-level facilities in {@link android.app.Activity} and
50 * {@link android.app.Dialog}.
51 *
52 * <p>Even for low-level window manager access, it is almost never correct to use
53 * this class.  For example, {@link android.app.Activity#getWindowManager}
54 * provides a ViewManager for adding windows that are associated with that
55 * activity -- the window manager will not normally allow you to add arbitrary
56 * windows that are not associated with an activity.
57 *
58 * @hide
59 */
60public class WindowManagerImpl implements WindowManager {
61    private static final String TAG = "WindowManager";
62
63    /**
64     * The user is navigating with keys (not the touch screen), so
65     * navigational focus should be shown.
66     */
67    public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1;
68
69    /**
70     * This is the first time the window is being drawn,
71     * so the client must call drawingFinished() when done
72     */
73    public static final int RELAYOUT_RES_FIRST_TIME = 0x2;
74
75    /**
76     * The window manager has changed the surface from the last call.
77     */
78    public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
79
80    /**
81     * The window manager is currently animating.  It will call
82     * IWindow.doneAnimating() when done.
83     */
84    public static final int RELAYOUT_RES_ANIMATING = 0x8;
85
86    /**
87     * Flag for relayout: the client will be later giving
88     * internal insets; as a result, the window will not impact other window
89     * layouts until the insets are given.
90     */
91    public static final int RELAYOUT_INSETS_PENDING = 0x1;
92
93    /**
94     * Flag for relayout: the client may be currently using the current surface,
95     * so if it is to be destroyed as a part of the relayout the destroy must
96     * be deferred until later.  The client will call performDeferredDestroy()
97     * when it is okay.
98     */
99    public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
100
101    public static final int ADD_FLAG_APP_VISIBLE = 0x2;
102    public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE;
103
104    public static final int ADD_OKAY = 0;
105    public static final int ADD_BAD_APP_TOKEN = -1;
106    public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
107    public static final int ADD_NOT_APP_TOKEN = -3;
108    public static final int ADD_APP_EXITING = -4;
109    public static final int ADD_DUPLICATE_ADD = -5;
110    public static final int ADD_STARTING_NOT_NEEDED = -6;
111    public static final int ADD_MULTIPLE_SINGLETON = -7;
112    public static final int ADD_PERMISSION_DENIED = -8;
113
114    private static WindowManagerImpl sDefaultWindowManager;
115    private static IWindowManager sWindowManagerService;
116
117    private final WindowManagerState mState;
118    private final Window mParentWindow;
119    private final CompatibilityInfoHolder mCompatibilityInfo;
120    private final Display mDefaultDisplay;
121
122    private WindowManagerImpl(WindowManagerState state, Window parentWindow,
123            CompatibilityInfoHolder compatibilityInfo) {
124        mState = state;
125        mParentWindow = parentWindow;
126        mCompatibilityInfo = compatibilityInfo;
127        mDefaultDisplay = mState.getDefaultDisplay(mCompatibilityInfo);
128    }
129
130    public static WindowManagerImpl getDefault() {
131        synchronized (WindowManagerImpl.class) {
132            if (sDefaultWindowManager == null) {
133                sDefaultWindowManager = new WindowManagerImpl(
134                        new WindowManagerState(), null, null);
135            }
136            return sDefaultWindowManager;
137        }
138    }
139
140    public static IWindowManager getWindowManagerService() {
141        synchronized (WindowManagerImpl.class) {
142            if (sWindowManagerService == null) {
143                sWindowManagerService = IWindowManager.Stub.asInterface(
144                        ServiceManager.getService("window"));
145            }
146            return sWindowManagerService;
147        }
148    }
149
150    public WindowManagerImpl makeLocal(Window parentWindow) {
151        return new WindowManagerImpl(mState, parentWindow, parentWindow.getCompatibilityInfo());
152    }
153
154    public WindowManagerImpl makeCompatible(CompatibilityInfoHolder compatInfo) {
155        if (compatInfo == mCompatibilityInfo) {
156            return this;
157        }
158        if (compatInfo == null && mParentWindow == null) {
159            return getDefault();
160        }
161        return new WindowManagerImpl(mState, mParentWindow, compatInfo);
162    }
163
164    @Override
165    public void addView(View view, ViewGroup.LayoutParams params) {
166        mState.addView(view, params, mParentWindow, mCompatibilityInfo);
167    }
168
169    @Override
170    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
171        mState.updateViewLayout(view, params);
172    }
173
174    @Override
175    public void removeView(View view) {
176        mState.removeView(view, false);
177    }
178
179    @Override
180    public void removeViewImmediate(View view) {
181        mState.removeView(view, true);
182    }
183
184    @Override
185    public Display getDefaultDisplay() {
186        return mDefaultDisplay;
187    }
188
189    public void closeAll(IBinder token, String who, String what) {
190        mState.closeAll(token, who, what);
191    }
192
193    public void startTrimMemory(int level) {
194        mState.startTrimMemory(level);
195    }
196
197    public void endTrimMemory() {
198        mState.endTrimMemory();
199    }
200
201    public void trimLocalMemory() {
202        mState.trimLocalMemory();
203    }
204
205    public void dumpGfxInfo(FileDescriptor fd) {
206        mState.dumpGfxInfo(fd);
207    }
208
209    public void setStoppedState(IBinder token, boolean stopped) {
210        mState.setStoppedState(token, stopped);
211    }
212
213    public void reportNewConfiguration(Configuration config) {
214        mState.reportNewConfiguration(config);
215    }
216
217    static final class WindowManagerState {
218        private final Display mDefaultDisplay;
219
220        private View[] mViews;
221        private ViewRootImpl[] mRoots;
222        private WindowManager.LayoutParams[] mParams;
223        private boolean mNeedsEglTerminate;
224
225        private Runnable mSystemPropertyUpdater;
226
227        public WindowManagerState() {
228            mDefaultDisplay = new Display(Display.DEFAULT_DISPLAY, null);
229        }
230
231        public Display getDefaultDisplay(CompatibilityInfoHolder compatInfo) {
232            if (compatInfo == null) {
233                return mDefaultDisplay;
234            }
235            return new Display(Display.DEFAULT_DISPLAY, compatInfo);
236        }
237
238        public void addView(View view, ViewGroup.LayoutParams params, Window parentWindow,
239                CompatibilityInfoHolder cih) {
240            if (!(params instanceof WindowManager.LayoutParams)) {
241                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
242            }
243
244            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
245            if (parentWindow != null) {
246                parentWindow.adjustLayoutParamsForSubWindow(wparams);
247            }
248
249            ViewRootImpl root;
250            View panelParentView = null;
251
252            synchronized (this) {
253                // Start watching for system property changes.
254                if (mSystemPropertyUpdater == null) {
255                    mSystemPropertyUpdater = new Runnable() {
256                        @Override public void run() {
257                            synchronized (this) {
258                                synchronized (this) {
259                                    for (ViewRootImpl root : mRoots) {
260                                        root.loadSystemProperties();
261                                    }
262                                }
263                            }
264                        }
265                    };
266                    SystemProperties.addChangeCallback(mSystemPropertyUpdater);
267                }
268
269                int index = findViewLocked(view, false);
270                if (index >= 0) {
271                    throw new IllegalStateException("View " + view
272                            + " has already been added to the window manager.");
273                }
274
275                // If this is a panel window, then find the window it is being
276                // attached to for future reference.
277                if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
278                        wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
279                    final int count = mViews != null ? mViews.length : 0;
280                    for (int i=0; i<count; i++) {
281                        if (mRoots[i].mWindow.asBinder() == wparams.token) {
282                            panelParentView = mViews[i];
283                        }
284                    }
285                }
286
287                root = new ViewRootImpl(view.getContext());
288                if (cih == null) {
289                    root.mCompatibilityInfo = new CompatibilityInfoHolder();
290                } else {
291                    root.mCompatibilityInfo = cih;
292                }
293
294                view.setLayoutParams(wparams);
295
296                if (mViews == null) {
297                    index = 1;
298                    mViews = new View[1];
299                    mRoots = new ViewRootImpl[1];
300                    mParams = new WindowManager.LayoutParams[1];
301                } else {
302                    index = mViews.length + 1;
303                    Object[] old = mViews;
304                    mViews = new View[index];
305                    System.arraycopy(old, 0, mViews, 0, index-1);
306                    old = mRoots;
307                    mRoots = new ViewRootImpl[index];
308                    System.arraycopy(old, 0, mRoots, 0, index-1);
309                    old = mParams;
310                    mParams = new WindowManager.LayoutParams[index];
311                    System.arraycopy(old, 0, mParams, 0, index-1);
312                }
313                index--;
314
315                mViews[index] = view;
316                mRoots[index] = root;
317                mParams[index] = wparams;
318            }
319
320            // do this last because it fires off messages to start doing things
321            root.setView(view, wparams, panelParentView);
322        }
323
324        public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
325            if (!(params instanceof WindowManager.LayoutParams)) {
326                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
327            }
328
329            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
330
331            view.setLayoutParams(wparams);
332
333            synchronized (this) {
334                int index = findViewLocked(view, true);
335                ViewRootImpl root = mRoots[index];
336                mParams[index] = wparams;
337                root.setLayoutParams(wparams, false);
338            }
339        }
340
341        public void removeView(View view, boolean immediate) {
342            synchronized (this) {
343                int index = findViewLocked(view, true);
344                View curView = removeViewLocked(index, immediate);
345                if (curView == view) {
346                    return;
347                }
348
349                throw new IllegalStateException("Calling with view " + view
350                        + " but the ViewAncestor is attached to " + curView);
351            }
352        }
353
354        public void closeAll(IBinder token, String who, String what) {
355            synchronized (this) {
356                if (mViews == null)
357                    return;
358
359                int count = mViews.length;
360                //Log.i("foo", "Closing all windows of " + token);
361                for (int i=0; i<count; i++) {
362                    //Log.i("foo", "@ " + i + " token " + mParams[i].token
363                    //        + " view " + mRoots[i].getView());
364                    if (token == null || mParams[i].token == token) {
365                        ViewRootImpl root = mRoots[i];
366
367                        //Log.i("foo", "Force closing " + root);
368                        if (who != null) {
369                            WindowLeaked leak = new WindowLeaked(
370                                    what + " " + who + " has leaked window "
371                                    + root.getView() + " that was originally added here");
372                            leak.setStackTrace(root.getLocation().getStackTrace());
373                            Log.e(TAG, leak.getMessage(), leak);
374                        }
375
376                        removeViewLocked(i, false);
377                        i--;
378                        count--;
379                    }
380                }
381            }
382        }
383
384        private View removeViewLocked(int index, boolean immediate) {
385            ViewRootImpl root = mRoots[index];
386            View view = root.getView();
387
388            if (view != null) {
389                InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
390                if (imm != null) {
391                    imm.windowDismissed(mViews[index].getWindowToken());
392                }
393            }
394            root.die(immediate);
395
396            final int count = mViews.length;
397
398            // remove it from the list
399            View[] tmpViews = new View[count-1];
400            removeItem(tmpViews, mViews, index);
401            mViews = tmpViews;
402
403            ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1];
404            removeItem(tmpRoots, mRoots, index);
405            mRoots = tmpRoots;
406
407            WindowManager.LayoutParams[] tmpParams
408                    = new WindowManager.LayoutParams[count-1];
409            removeItem(tmpParams, mParams, index);
410            mParams = tmpParams;
411
412            if (view != null) {
413                view.assignParent(null);
414                // func doesn't allow null...  does it matter if we clear them?
415                //view.setLayoutParams(null);
416            }
417            return view;
418        }
419
420        private static void removeItem(Object[] dst, Object[] src, int index) {
421            if (dst.length > 0) {
422                if (index > 0) {
423                    System.arraycopy(src, 0, dst, 0, index);
424                }
425                if (index < dst.length) {
426                    System.arraycopy(src, index+1, dst, index, src.length-index-1);
427                }
428            }
429        }
430
431        private int findViewLocked(View view, boolean required) {
432            synchronized (this) {
433                if (mViews != null) {
434                    final int count = mViews.length;
435                    for (int i = 0; i < count; i++) {
436                        if (mViews[i] == view) {
437                            return i;
438                        }
439                    }
440                }
441                if (required) {
442                    throw new IllegalArgumentException("View not attached to window manager");
443                }
444                return -1;
445            }
446        }
447
448        public void startTrimMemory(int level) {
449            if (HardwareRenderer.isAvailable()) {
450                // On low-end gfx devices we trim when memory is moderate;
451                // on high-end devices we do this when low.
452                if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
453                        || (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
454                                && !ActivityManager.isHighEndGfx(mDefaultDisplay))) {
455                    // Destroy all hardware surfaces and resources associated to
456                    // known windows
457                    synchronized (this) {
458                        if (mViews == null) return;
459                        int count = mViews.length;
460                        for (int i = 0; i < count; i++) {
461                            mRoots[i].terminateHardwareResources();
462                        }
463                    }
464                    // Force a full memory flush
465                    mNeedsEglTerminate = true;
466                    HardwareRenderer.startTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
467                    return;
468                }
469
470                HardwareRenderer.startTrimMemory(level);
471            }
472        }
473
474        public void endTrimMemory() {
475            HardwareRenderer.endTrimMemory();
476
477            if (mNeedsEglTerminate) {
478                ManagedEGLContext.doTerminate();
479                mNeedsEglTerminate = false;
480            }
481        }
482
483        public void trimLocalMemory() {
484            synchronized (this) {
485                if (mViews == null) return;
486                int count = mViews.length;
487                for (int i = 0; i < count; i++) {
488                    mRoots[i].destroyHardwareLayers();
489                }
490            }
491        }
492
493        public void dumpGfxInfo(FileDescriptor fd) {
494            FileOutputStream fout = new FileOutputStream(fd);
495            PrintWriter pw = new PrintWriter(fout);
496            try {
497                synchronized (this) {
498                    if (mViews != null) {
499                        final int count = mViews.length;
500
501                        pw.println("Profile data in ms:");
502
503                        for (int i = 0; i < count; i++) {
504                            ViewRootImpl root = mRoots[i];
505                            String name = getWindowName(root);
506                            pw.printf("\n\t%s", name);
507
508                            HardwareRenderer renderer =
509                                    root.getView().mAttachInfo.mHardwareRenderer;
510                            if (renderer != null) {
511                                renderer.dumpGfxInfo(pw);
512                            }
513                        }
514
515                        pw.println("\nView hierarchy:\n");
516
517                        int viewsCount = 0;
518                        int displayListsSize = 0;
519                        int[] info = new int[2];
520
521                        for (int i = 0; i < count; i++) {
522                            ViewRootImpl root = mRoots[i];
523                            root.dumpGfxInfo(info);
524
525                            String name = getWindowName(root);
526                            pw.printf("  %s\n  %d views, %.2f kB of display lists",
527                                    name, info[0], info[1] / 1024.0f);
528                            HardwareRenderer renderer =
529                                    root.getView().mAttachInfo.mHardwareRenderer;
530                            if (renderer != null) {
531                                pw.printf(", %d frames rendered", renderer.getFrameCount());
532                            }
533                            pw.printf("\n\n");
534
535                            viewsCount += info[0];
536                            displayListsSize += info[1];
537                        }
538
539                        pw.printf("\nTotal ViewRootImpl: %d\n", count);
540                        pw.printf("Total Views:        %d\n", viewsCount);
541                        pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
542                    }
543                }
544            } finally {
545                pw.flush();
546            }
547        }
548
549        private static String getWindowName(ViewRootImpl root) {
550            return root.mWindowAttributes.getTitle() + "/" +
551                    root.getClass().getName() + '@' + Integer.toHexString(root.hashCode());
552        }
553
554        public void setStoppedState(IBinder token, boolean stopped) {
555            synchronized (this) {
556                if (mViews != null) {
557                    int count = mViews.length;
558                    for (int i=0; i < count; i++) {
559                        if (token == null || mParams[i].token == token) {
560                            ViewRootImpl root = mRoots[i];
561                            root.setStopped(stopped);
562                        }
563                    }
564                }
565            }
566        }
567
568        public void reportNewConfiguration(Configuration config) {
569            synchronized (this) {
570                if (mViews != null) {
571                    int count = mViews.length;
572                    config = new Configuration(config);
573                    for (int i=0; i < count; i++) {
574                        ViewRootImpl root = mRoots[i];
575                        root.requestUpdateConfiguration(config);
576                    }
577                }
578            }
579        }
580    }
581}
582