1/*
2 * Copyright (C) 2012 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.animation.ValueAnimator;
20import android.app.ActivityManager;
21import android.content.ComponentCallbacks2;
22import android.content.res.Configuration;
23import android.opengl.ManagedEGLContext;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.os.ServiceManager;
27import android.os.SystemProperties;
28import android.util.AndroidRuntimeException;
29import android.util.ArraySet;
30import android.util.Log;
31import android.view.inputmethod.InputMethodManager;
32import com.android.internal.util.FastPrintWriter;
33
34import java.io.FileDescriptor;
35import java.io.FileOutputStream;
36import java.io.PrintWriter;
37import java.util.ArrayList;
38
39/**
40 * Provides low-level communication with the system window manager for
41 * operations that are not associated with any particular context.
42 *
43 * This class is only used internally to implement global functions where
44 * the caller already knows the display and relevant compatibility information
45 * for the operation.  For most purposes, you should use {@link WindowManager} instead
46 * since it is bound to a context.
47 *
48 * @see WindowManagerImpl
49 * @hide
50 */
51public final class WindowManagerGlobal {
52    private static final String TAG = "WindowManager";
53
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_RES_IN_TOUCH_MODE = 0x1;
59
60    /**
61     * This is the first time the window is being drawn,
62     * so the client must call drawingFinished() when done
63     */
64    public static final int RELAYOUT_RES_FIRST_TIME = 0x2;
65
66    /**
67     * The window manager has changed the surface from the last call.
68     */
69    public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
70
71    /**
72     * The window manager is currently animating.  It will call
73     * IWindow.doneAnimating() when done.
74     */
75    public static final int RELAYOUT_RES_ANIMATING = 0x8;
76
77    /**
78     * Flag for relayout: the client will be later giving
79     * internal insets; as a result, the window will not impact other window
80     * layouts until the insets are given.
81     */
82    public static final int RELAYOUT_INSETS_PENDING = 0x1;
83
84    /**
85     * Flag for relayout: the client may be currently using the current surface,
86     * so if it is to be destroyed as a part of the relayout the destroy must
87     * be deferred until later.  The client will call performDeferredDestroy()
88     * when it is okay.
89     */
90    public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
91
92    public static final int ADD_FLAG_APP_VISIBLE = 0x2;
93    public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE;
94
95    public static final int ADD_OKAY = 0;
96    public static final int ADD_BAD_APP_TOKEN = -1;
97    public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
98    public static final int ADD_NOT_APP_TOKEN = -3;
99    public static final int ADD_APP_EXITING = -4;
100    public static final int ADD_DUPLICATE_ADD = -5;
101    public static final int ADD_STARTING_NOT_NEEDED = -6;
102    public static final int ADD_MULTIPLE_SINGLETON = -7;
103    public static final int ADD_PERMISSION_DENIED = -8;
104    public static final int ADD_INVALID_DISPLAY = -9;
105
106    private static WindowManagerGlobal sDefaultWindowManager;
107    private static IWindowManager sWindowManagerService;
108    private static IWindowSession sWindowSession;
109
110    private final Object mLock = new Object();
111
112    private final ArrayList<View> mViews = new ArrayList<View>();
113    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
114    private final ArrayList<WindowManager.LayoutParams> mParams =
115            new ArrayList<WindowManager.LayoutParams>();
116    private final ArraySet<View> mDyingViews = new ArraySet<View>();
117    private boolean mNeedsEglTerminate;
118
119    private Runnable mSystemPropertyUpdater;
120
121    private WindowManagerGlobal() {
122    }
123
124    public static WindowManagerGlobal getInstance() {
125        synchronized (WindowManagerGlobal.class) {
126            if (sDefaultWindowManager == null) {
127                sDefaultWindowManager = new WindowManagerGlobal();
128            }
129            return sDefaultWindowManager;
130        }
131    }
132
133    public static IWindowManager getWindowManagerService() {
134        synchronized (WindowManagerGlobal.class) {
135            if (sWindowManagerService == null) {
136                sWindowManagerService = IWindowManager.Stub.asInterface(
137                        ServiceManager.getService("window"));
138            }
139            return sWindowManagerService;
140        }
141    }
142
143    public static IWindowSession getWindowSession() {
144        synchronized (WindowManagerGlobal.class) {
145            if (sWindowSession == null) {
146                try {
147                    InputMethodManager imm = InputMethodManager.getInstance();
148                    IWindowManager windowManager = getWindowManagerService();
149                    sWindowSession = windowManager.openSession(
150                            imm.getClient(), imm.getInputContext());
151                    float animatorScale = windowManager.getAnimationScale(2);
152                    ValueAnimator.setDurationScale(animatorScale);
153                } catch (RemoteException e) {
154                    Log.e(TAG, "Failed to open window session", e);
155                }
156            }
157            return sWindowSession;
158        }
159    }
160
161    public static IWindowSession peekWindowSession() {
162        synchronized (WindowManagerGlobal.class) {
163            return sWindowSession;
164        }
165    }
166
167    public String[] getViewRootNames() {
168        synchronized (mLock) {
169            final int numRoots = mRoots.size();
170            String[] mViewRoots = new String[numRoots];
171            for (int i = 0; i < numRoots; ++i) {
172                mViewRoots[i] = getWindowName(mRoots.get(i));
173            }
174            return mViewRoots;
175        }
176    }
177
178    public View getRootView(String name) {
179        synchronized (mLock) {
180            for (int i = mRoots.size() - 1; i >= 0; --i) {
181                final ViewRootImpl root = mRoots.get(i);
182                if (name.equals(getWindowName(root))) return root.getView();
183            }
184        }
185
186        return null;
187    }
188
189    public void addView(View view, ViewGroup.LayoutParams params,
190            Display display, Window parentWindow) {
191        if (view == null) {
192            throw new IllegalArgumentException("view must not be null");
193        }
194        if (display == null) {
195            throw new IllegalArgumentException("display must not be null");
196        }
197        if (!(params instanceof WindowManager.LayoutParams)) {
198            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
199        }
200
201        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
202        if (parentWindow != null) {
203            parentWindow.adjustLayoutParamsForSubWindow(wparams);
204        }
205
206        ViewRootImpl root;
207        View panelParentView = null;
208
209        synchronized (mLock) {
210            // Start watching for system property changes.
211            if (mSystemPropertyUpdater == null) {
212                mSystemPropertyUpdater = new Runnable() {
213                    @Override public void run() {
214                        synchronized (mLock) {
215                            for (int i = mRoots.size() - 1; i >= 0; --i) {
216                                mRoots.get(i).loadSystemProperties();
217                            }
218                        }
219                    }
220                };
221                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
222            }
223
224            int index = findViewLocked(view, false);
225            if (index >= 0) {
226                if (mDyingViews.contains(view)) {
227                    // Don't wait for MSG_DIE to make it's way through root's queue.
228                    mRoots.get(index).doDie();
229                } else {
230                    throw new IllegalStateException("View " + view
231                            + " has already been added to the window manager.");
232                }
233                // The previous removeView() had not completed executing. Now it has.
234            }
235
236            // If this is a panel window, then find the window it is being
237            // attached to for future reference.
238            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
239                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
240                final int count = mViews.size();
241                for (int i = 0; i < count; i++) {
242                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
243                        panelParentView = mViews.get(i);
244                    }
245                }
246            }
247
248            root = new ViewRootImpl(view.getContext(), display);
249
250            view.setLayoutParams(wparams);
251
252            mViews.add(view);
253            mRoots.add(root);
254            mParams.add(wparams);
255        }
256
257        // do this last because it fires off messages to start doing things
258        try {
259            root.setView(view, wparams, panelParentView);
260        } catch (RuntimeException e) {
261            // BadTokenException or InvalidDisplayException, clean up.
262            synchronized (mLock) {
263                final int index = findViewLocked(view, false);
264                if (index >= 0) {
265                    removeViewLocked(index, true);
266                }
267            }
268            throw e;
269        }
270    }
271
272    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
273        if (view == null) {
274            throw new IllegalArgumentException("view must not be null");
275        }
276        if (!(params instanceof WindowManager.LayoutParams)) {
277            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
278        }
279
280        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
281
282        view.setLayoutParams(wparams);
283
284        synchronized (mLock) {
285            int index = findViewLocked(view, true);
286            ViewRootImpl root = mRoots.get(index);
287            mParams.remove(index);
288            mParams.add(index, wparams);
289            root.setLayoutParams(wparams, false);
290        }
291    }
292
293    public void removeView(View view, boolean immediate) {
294        if (view == null) {
295            throw new IllegalArgumentException("view must not be null");
296        }
297
298        synchronized (mLock) {
299            int index = findViewLocked(view, true);
300            View curView = mRoots.get(index).getView();
301            removeViewLocked(index, immediate);
302            if (curView == view) {
303                return;
304            }
305
306            throw new IllegalStateException("Calling with view " + view
307                    + " but the ViewAncestor is attached to " + curView);
308        }
309    }
310
311    public void closeAll(IBinder token, String who, String what) {
312        synchronized (mLock) {
313            int count = mViews.size();
314            //Log.i("foo", "Closing all windows of " + token);
315            for (int i = 0; i < count; i++) {
316                //Log.i("foo", "@ " + i + " token " + mParams[i].token
317                //        + " view " + mRoots[i].getView());
318                if (token == null || mParams.get(i).token == token) {
319                    ViewRootImpl root = mRoots.get(i);
320
321                    //Log.i("foo", "Force closing " + root);
322                    if (who != null) {
323                        WindowLeaked leak = new WindowLeaked(
324                                what + " " + who + " has leaked window "
325                                + root.getView() + " that was originally added here");
326                        leak.setStackTrace(root.getLocation().getStackTrace());
327                        Log.e(TAG, "", leak);
328                    }
329
330                    removeViewLocked(i, false);
331                }
332            }
333        }
334    }
335
336    private void removeViewLocked(int index, boolean immediate) {
337        ViewRootImpl root = mRoots.get(index);
338        View view = root.getView();
339
340        if (view != null) {
341            InputMethodManager imm = InputMethodManager.getInstance();
342            if (imm != null) {
343                imm.windowDismissed(mViews.get(index).getWindowToken());
344            }
345        }
346        boolean deferred = root.die(immediate);
347        if (view != null) {
348            view.assignParent(null);
349            if (deferred) {
350                mDyingViews.add(view);
351            }
352        }
353    }
354
355    void doRemoveView(ViewRootImpl root) {
356        synchronized (mLock) {
357            final int index = mRoots.indexOf(root);
358            if (index >= 0) {
359                mRoots.remove(index);
360                mParams.remove(index);
361                final View view = mViews.remove(index);
362                mDyingViews.remove(view);
363            }
364        }
365    }
366
367    private int findViewLocked(View view, boolean required) {
368        final int index = mViews.indexOf(view);
369        if (required && index < 0) {
370            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
371        }
372        return index;
373    }
374
375    public void startTrimMemory(int level) {
376        if (HardwareRenderer.isAvailable()) {
377            // On low-end gfx devices we trim when memory is moderate;
378            // on high-end devices we do this when low.
379            if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
380                    || (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
381                            && !ActivityManager.isHighEndGfx())) {
382                // Destroy all hardware surfaces and resources associated to
383                // known windows
384                synchronized (mLock) {
385                    for (int i = mRoots.size() - 1; i >= 0; --i) {
386                        mRoots.get(i).destroyHardwareResources();
387                    }
388                }
389                // Force a full memory flush
390                mNeedsEglTerminate = true;
391                HardwareRenderer.startTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
392                return;
393            }
394
395            HardwareRenderer.startTrimMemory(level);
396        }
397    }
398
399    public void endTrimMemory() {
400        HardwareRenderer.endTrimMemory();
401
402        if (mNeedsEglTerminate) {
403            ManagedEGLContext.doTerminate();
404            mNeedsEglTerminate = false;
405        }
406    }
407
408    public void trimLocalMemory() {
409        synchronized (mLock) {
410            for (int i = mRoots.size() - 1; i >= 0; --i) {
411                mRoots.get(i).destroyHardwareLayers();
412            }
413        }
414    }
415
416    public void dumpGfxInfo(FileDescriptor fd) {
417        FileOutputStream fout = new FileOutputStream(fd);
418        PrintWriter pw = new FastPrintWriter(fout);
419        try {
420            synchronized (mLock) {
421                final int count = mViews.size();
422
423                pw.println("Profile data in ms:");
424
425                for (int i = 0; i < count; i++) {
426                    ViewRootImpl root = mRoots.get(i);
427                    String name = getWindowName(root);
428                    pw.printf("\n\t%s", name);
429
430                    HardwareRenderer renderer =
431                            root.getView().mAttachInfo.mHardwareRenderer;
432                    if (renderer != null) {
433                        renderer.dumpGfxInfo(pw);
434                    }
435                }
436
437                pw.println("\nView hierarchy:\n");
438
439                int viewsCount = 0;
440                int displayListsSize = 0;
441                int[] info = new int[2];
442
443                for (int i = 0; i < count; i++) {
444                    ViewRootImpl root = mRoots.get(i);
445                    root.dumpGfxInfo(info);
446
447                    String name = getWindowName(root);
448                    pw.printf("  %s\n  %d views, %.2f kB of display lists",
449                            name, info[0], info[1] / 1024.0f);
450                    HardwareRenderer renderer =
451                            root.getView().mAttachInfo.mHardwareRenderer;
452                    if (renderer != null) {
453                        pw.printf(", %d frames rendered", renderer.getFrameCount());
454                    }
455                    pw.printf("\n\n");
456
457                    viewsCount += info[0];
458                    displayListsSize += info[1];
459                }
460
461                pw.printf("\nTotal ViewRootImpl: %d\n", count);
462                pw.printf("Total Views:        %d\n", viewsCount);
463                pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
464            }
465        } finally {
466            pw.flush();
467        }
468    }
469
470    private static String getWindowName(ViewRootImpl root) {
471        return root.mWindowAttributes.getTitle() + "/" +
472                root.getClass().getName() + '@' + Integer.toHexString(root.hashCode());
473    }
474
475    public void setStoppedState(IBinder token, boolean stopped) {
476        synchronized (mLock) {
477            int count = mViews.size();
478            for (int i = 0; i < count; i++) {
479                if (token == null || mParams.get(i).token == token) {
480                    ViewRootImpl root = mRoots.get(i);
481                    root.setStopped(stopped);
482                }
483            }
484        }
485    }
486
487    public void reportNewConfiguration(Configuration config) {
488        synchronized (mLock) {
489            int count = mViews.size();
490            config = new Configuration(config);
491            for (int i=0; i < count; i++) {
492                ViewRootImpl root = mRoots.get(i);
493                root.requestUpdateConfiguration(config);
494            }
495        }
496    }
497
498    /** @hide */
499    public void changeCanvasOpacity(IBinder token, boolean opaque) {
500        if (token == null) {
501            return;
502        }
503        synchronized (mLock) {
504            for (int i = mParams.size() - 1; i >= 0; --i) {
505                if (mParams.get(i).token == token) {
506                    mRoots.get(i).changeCanvasOpacity(opaque);
507                    return;
508                }
509            }
510        }
511    }
512}
513
514final class WindowLeaked extends AndroidRuntimeException {
515    public WindowLeaked(String msg) {
516        super(msg);
517    }
518}
519