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