WindowContainer.java revision 1012458343643e899ed8ca2684380b92d73fb47b
1/*
2 * Copyright (C) 2016 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 com.android.server.wm;
18
19import android.annotation.CallSuper;
20import android.view.animation.Animation;
21
22import java.io.PrintWriter;
23import java.util.Comparator;
24import java.util.LinkedList;
25
26import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
27import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
28import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
29
30/**
31 * Defines common functionality for classes that can hold windows directly or through their
32 * children in a hierarchy form.
33 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
34 * changes are made to this class.
35 */
36class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> {
37
38    // The parent of this window container.
39    protected WindowContainer mParent = null;
40
41    // List of children for this window container. List is in z-order as the children appear on
42    // screen with the top-most window container at the tail of the list.
43    protected final LinkedList<E> mChildren = new LinkedList();
44
45    // The specified orientation for this window container.
46    protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
47
48    final protected WindowContainer getParent() {
49        return mParent;
50    }
51
52    // Temp. holders for a chain of containers we are currently processing.
53    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
54    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
55
56    /**
57     * Adds the input window container has a child of this container in order based on the input
58     * comparator.
59     * @param child The window container to add as a child of this window container.
60     * @param comparator Comparator to use in determining the position the child should be added to.
61     *                   If null, the child will be added to the top.
62     */
63    @CallSuper
64    protected void addChild(E child, Comparator<E> comparator) {
65        if (child.mParent != null) {
66            throw new IllegalArgumentException("addChild: container=" + child
67                    + " is already a child of container=" + child.mParent
68                    + " can't add to container=" + this);
69        }
70        child.mParent = this;
71
72        if (mChildren.isEmpty() || comparator == null) {
73            mChildren.add(child);
74            return;
75        }
76
77        final int count = mChildren.size();
78        for (int i = 0; i < count; i++) {
79            if (comparator.compare(child, mChildren.get(i)) < 0) {
80                mChildren.add(i, child);
81                return;
82            }
83        }
84
85        mChildren.add(child);
86    }
87
88    /** Adds the input window container has a child of this container at the input index. */
89    @CallSuper
90    protected void addChild(E child, int index) {
91        if (child.mParent != null) {
92            throw new IllegalArgumentException("addChild: container=" + child
93                    + " is already a child of container=" + child.mParent
94                    + " can't add to container=" + this);
95        }
96        child.mParent = this;
97        mChildren.add(index, child);
98    }
99
100    /**
101     * Removes the input child container from this container which is its parent.
102     *
103     * @return True if the container did contain the input child and it was detached.
104     */
105    @CallSuper
106    void removeChild(E child) {
107        if (mChildren.remove(child)) {
108            child.mParent = null;
109        } else {
110            throw new IllegalArgumentException("removeChild: container=" + child
111                    + " is not a child of container=" + this);
112        }
113    }
114
115    /**
116     * Removes this window container and its children with no regard for what else might be going on
117     * in the system. For example, the container will be removed during animation if this method is
118     * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()}
119     * which allows the system to defer removal until a suitable time.
120     */
121    @CallSuper
122    void removeImmediately() {
123        while (!mChildren.isEmpty()) {
124            final WindowContainer child = mChildren.peekLast();
125            child.removeImmediately();
126            // Need to do this after calling remove on the child because the child might try to
127            // remove/detach itself from its parent which will cause an exception if we remove
128            // it before calling remove on the child.
129            mChildren.remove(child);
130        }
131
132        if (mParent != null) {
133            mParent.removeChild(this);
134        }
135    }
136
137    /**
138     * Removes this window container and its children taking care not to remove them during a
139     * critical stage in the system. For example, some containers will not be removed during
140     * animation if this method is called.
141     */
142    // TODO: figure-out implementation that works best for this.
143    // E.g. when do we remove from parent list? maybe not...
144    void removeIfPossible() {
145        for (int i = mChildren.size() - 1; i >= 0; --i) {
146            final WindowContainer wc = mChildren.get(i);
147            wc.removeIfPossible();
148        }
149    }
150
151    /** Returns true if this window container has the input child. */
152    boolean hasChild(WindowContainer child) {
153        for (int i = mChildren.size() - 1; i >= 0; --i) {
154            final WindowContainer current = mChildren.get(i);
155            if (current == child || current.hasChild(child)) {
156                return true;
157            }
158        }
159        return false;
160    }
161
162    void setWaitingForDrawnIfResizingChanged() {
163        for (int i = mChildren.size() - 1; i >= 0; --i) {
164            final WindowContainer wc = mChildren.get(i);
165            wc.setWaitingForDrawnIfResizingChanged();
166        }
167    }
168
169    void onResize() {
170        for (int i = mChildren.size() - 1; i >= 0; --i) {
171            final WindowContainer wc = mChildren.get(i);
172            wc.onResize();
173        }
174    }
175
176    void onMovedByResize() {
177        for (int i = mChildren.size() - 1; i >= 0; --i) {
178            final WindowContainer wc = mChildren.get(i);
179            wc.onMovedByResize();
180        }
181    }
182
183    void resetDragResizingChangeReported() {
184        for (int i = mChildren.size() - 1; i >= 0; --i) {
185            final WindowContainer wc = mChildren.get(i);
186            wc.resetDragResizingChangeReported();
187        }
188    }
189
190    void forceWindowsScaleableInTransaction(boolean force) {
191        for (int i = mChildren.size() - 1; i >= 0; --i) {
192            final WindowContainer wc = mChildren.get(i);
193            wc.forceWindowsScaleableInTransaction(force);
194        }
195    }
196
197    boolean isAnimating() {
198        for (int j = mChildren.size() - 1; j >= 0; j--) {
199            final WindowContainer wc = mChildren.get(j);
200            if (wc.isAnimating()) {
201                return true;
202            }
203        }
204        return false;
205    }
206
207    void sendAppVisibilityToClients() {
208        for (int i = mChildren.size() - 1; i >= 0; --i) {
209            final WindowContainer wc = mChildren.get(i);
210            wc.sendAppVisibilityToClients();
211        }
212    }
213
214    void setVisibleBeforeClientHidden() {
215        for (int i = mChildren.size() - 1; i >= 0; --i) {
216            final WindowContainer wc = mChildren.get(i);
217            wc.setVisibleBeforeClientHidden();
218        }
219    }
220
221    /**
222     * Returns true if the container or one of its children as some content it can display or wants
223     * to display (e.g. app views or saved surface).
224     *
225     * NOTE: While this method will return true if the there is some content to display, it doesn't
226     * mean the container is visible. Use {@link #isVisible()} to determine if the container is
227     * visible.
228     */
229    boolean hasContentToDisplay() {
230        for (int i = mChildren.size() - 1; i >= 0; --i) {
231            final WindowContainer wc = mChildren.get(i);
232            if (wc.hasContentToDisplay()) {
233                return true;
234            }
235        }
236        return false;
237    }
238
239    /**
240     * Returns true if the container or one of its children is considered visible from the
241     * WindowManager perspective which usually means valid surface and some other internal state
242     * are true.
243     *
244     * NOTE: While this method will return true if the surface is visible, it doesn't mean the
245     * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
246     * the container has any content to display.
247     */
248    boolean isVisible() {
249        // TODO: Will this be more correct if it checks the visibility of its parents?
250        // It depends...For example, Tasks and Stacks are only visible if there children are visible
251        // but, WindowState are not visible if there parent are not visible. Maybe have the
252        // container specify which direction to treverse for for visibility?
253        for (int i = mChildren.size() - 1; i >= 0; --i) {
254            final WindowContainer wc = mChildren.get(i);
255            if (wc.isVisible()) {
256                return true;
257            }
258        }
259        return false;
260    }
261
262    /** Returns the top child container or this container if there are no children. */
263    WindowContainer getTop() {
264        return mChildren.isEmpty() ? this : mChildren.peekLast();
265    }
266
267    /** Returns true if there is still a removal being deferred */
268    boolean checkCompleteDeferredRemoval() {
269        boolean stillDeferringRemoval = false;
270
271        for (int i = mChildren.size() - 1; i >= 0; --i) {
272            final WindowContainer wc = mChildren.get(i);
273            stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
274        }
275
276        return stillDeferringRemoval;
277    }
278
279    /** Checks if all windows in an app are all drawn and shows them if needed. */
280    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
281    // display. Remove once we migrate DisplayContent to use WindowContainer.
282    void checkAppWindowsReadyToShow(int displayId) {
283        for (int i = mChildren.size() - 1; i >= 0; --i) {
284            final WindowContainer wc = mChildren.get(i);
285            wc.checkAppWindowsReadyToShow(displayId);
286        }
287    }
288
289    /**
290     * Updates the current all drawn status for this container. That is all its children
291     * that should draw something have done so.
292     */
293    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
294    // display. Remove once we migrate DisplayContent to use WindowContainer.
295    void updateAllDrawn(int displayId) {
296        for (int i = mChildren.size() - 1; i >= 0; --i) {
297            final WindowContainer wc = mChildren.get(i);
298            wc.updateAllDrawn(displayId);
299        }
300    }
301
302    /** Step currently ongoing animation for App window containers. */
303    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
304    // display. Remove once we migrate DisplayContent to use WindowContainer.
305    void stepAppWindowsAnimation(long currentTime, int displayId) {
306        for (int i = mChildren.size() - 1; i >= 0; --i) {
307            final WindowContainer wc = mChildren.get(i);
308            wc.stepAppWindowsAnimation(currentTime, displayId);
309        }
310    }
311
312    void onAppTransitionDone() {
313        for (int i = mChildren.size() - 1; i >= 0; --i) {
314            final WindowContainer wc = mChildren.get(i);
315            wc.onAppTransitionDone();
316        }
317    }
318
319    void overridePlayingAppAnimations(Animation a) {
320        for (int i = mChildren.size() - 1; i >= 0; i--) {
321            mChildren.get(i).overridePlayingAppAnimations(a);
322        }
323    }
324
325    void setOrientation(int orientation) {
326        mOrientation = orientation;
327    }
328
329    /**
330     * Returns the specified orientation for this window container or one of its children is there
331     * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
332     * specification is set.
333     * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
334     * specification...
335     */
336    int getOrientation() {
337
338        if (!fillsParent() || !isVisible()) {
339            // Ignore invisible containers or containers that don't completely fills their parents.
340            return SCREEN_ORIENTATION_UNSET;
341        }
342
343        // The container fills its parent so we can use it orientation if it has one specified,
344        // otherwise we prefer to use the orientation of its topmost child that has one
345        // specified and fall back on this container's unset or unspecified value as a candidate
346        // if none of the children have a better candidate for the orientation.
347        if (mOrientation != SCREEN_ORIENTATION_UNSET
348                && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
349            return mOrientation;
350        }
351        int candidate = mOrientation;
352
353        for (int i = mChildren.size() - 1; i >= 0; --i) {
354            final WindowContainer wc = mChildren.get(i);
355
356            final int orientation = wc.getOrientation();
357            if (orientation == SCREEN_ORIENTATION_BEHIND) {
358                // container wants us to use the orientation of the container behind it. See if we
359                // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
360                // look behind this container.
361                candidate = orientation;
362                continue;
363            }
364
365            if (orientation == SCREEN_ORIENTATION_UNSET) {
366                continue;
367            }
368
369            if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
370                // Use the orientation if the container fills its parent or requested an explicit
371                // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
372                return orientation;
373            }
374        }
375
376        return candidate;
377    }
378
379    /**
380     * Returns true if this container is opaque and fills all the space made available by its parent
381     * container.
382     *
383     * NOTE: It is possible for this container to occupy more space than the parent has (or less),
384     * this is just a signal from the client to window manager stating its intent, but not what it
385     * actually does.
386     */
387    boolean fillsParent() {
388        return false;
389    }
390
391    /**
392     * Rebuilds the WindowList for the input display content.
393     * @param dc The display content to rebuild the window list for.
394     * @param addIndex The index in the window list to add the next entry to.
395     * @return The next index in the window list to.
396     */
397    // TODO: Hoping we can get rid of WindowList so this method wouldn't be needed.
398    int rebuildWindowList(DisplayContent dc, int addIndex) {
399        final int count = mChildren.size();
400        for (int i = 0; i < count; i++) {
401            final WindowContainer wc = mChildren.get(i);
402            addIndex = wc.rebuildWindowList(dc, addIndex);
403        }
404        return addIndex;
405    }
406
407    /**
408     * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
409     * the input container in terms of z-order.
410     */
411    @Override
412    public int compareTo(WindowContainer other) {
413        if (this == other) {
414            return 0;
415        }
416
417        if (mParent != null && mParent == other.mParent) {
418            final LinkedList<WindowContainer> list = mParent.mChildren;
419            return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
420        }
421
422        final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
423        final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
424        getParents(thisParentChain);
425        other.getParents(otherParentChain);
426
427        // Find the common ancestor of both containers.
428        WindowContainer commonAncestor = null;
429        WindowContainer thisTop = thisParentChain.peekLast();
430        WindowContainer otherTop = otherParentChain.peekLast();
431        while (thisTop != null && otherTop != null && thisTop == otherTop) {
432            commonAncestor = thisParentChain.removeLast();
433            otherParentChain.removeLast();
434            thisTop = thisParentChain.peekLast();
435            otherTop = otherParentChain.peekLast();
436        }
437
438        // Containers don't belong to the same hierarchy???
439        if (commonAncestor == null) {
440            throw new IllegalArgumentException("No in the same hierarchy this="
441                    + thisParentChain + " other=" + otherParentChain);
442        }
443
444        // Children are always considered greater than their parents, so if one of the containers
445        // we are comparing it the parent of the other then whichever is the child is greater.
446        if (commonAncestor == this) {
447            return -1;
448        } else if (commonAncestor == other) {
449            return 1;
450        }
451
452        // The position of the first non-common ancestor in the common ancestor list determines
453        // which is greater the which.
454        final LinkedList<WindowContainer> list = commonAncestor.mChildren;
455        return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
456                ? 1 : -1;
457    }
458
459    private void getParents(LinkedList<WindowContainer> parents) {
460        parents.clear();
461        WindowContainer current = this;
462        do {
463            parents.addLast(current);
464            current = current.mParent;
465        } while (current != null);
466    }
467
468    /**
469     * Dumps the names of this container children in the input print writer indenting each
470     * level with the input prefix.
471     */
472    void dumpChildrenNames(PrintWriter pw, String prefix) {
473        final String childPrefix = prefix + prefix;
474        for (int i = mChildren.size() - 1; i >= 0; --i) {
475            final WindowContainer wc = mChildren.get(i);
476            pw.println("#" + i + " " + getName());
477            wc.dumpChildrenNames(pw, childPrefix);
478        }
479    }
480
481    String getName() {
482        return toString();
483    }
484
485}
486