WindowContainer.java revision 4551c8bbee4114fa884938dbe90ac0d06ca78fc5
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.content.res.Configuration;
21import com.android.internal.util.ToBooleanFunction;
22
23import java.util.Comparator;
24import java.util.LinkedList;
25import java.util.function.Consumer;
26import java.util.function.Function;
27import java.util.function.Predicate;
28
29import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
30import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
31import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
32
33/**
34 * Defines common functionality for classes that can hold windows directly or through their
35 * children in a hierarchy form.
36 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
37 * changes are made to this class.
38 */
39class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> {
40
41    /**
42     * The parent of this window container.
43     * For removing or setting new parent {@link #setParent} should be used, because it also
44     * performs configuration updates based on new parent's settings.
45     */
46    private WindowContainer mParent = null;
47
48    // List of children for this window container. List is in z-order as the children appear on
49    // screen with the top-most window container at the tail of the list.
50    protected final LinkedList<E> mChildren = new LinkedList();
51
52    /** Contains override configuration settings applied to this window container. */
53    private Configuration mOverrideConfiguration = new Configuration();
54
55    /**
56     * Contains full configuration applied to this window container. Corresponds to full parent's
57     * config with applied {@link #mOverrideConfiguration}.
58     */
59    private Configuration mFullConfiguration = new Configuration();
60
61    /**
62     * Contains merged override configuration settings from the top of the hierarchy down to this
63     * particular instance. It is different from {@link #mFullConfiguration} because it starts from
64     * topmost container's override config instead of global config.
65     */
66    private Configuration mMergedOverrideConfiguration = new Configuration();
67
68    // The specified orientation for this window container.
69    protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
70
71    final protected WindowContainer getParent() {
72        return mParent;
73    }
74
75    final protected void setParent(WindowContainer parent) {
76        mParent = parent;
77        // Removing parent usually means that we've detached this entity to destroy it or to attach
78        // to another parent. In both cases we don't need to update the configuration now.
79        if (mParent != null) {
80            // Update full configuration of this container and all its children.
81            onConfigurationChanged(mParent.mFullConfiguration);
82            // Update merged override configuration of this container and all its children.
83            onMergedOverrideConfigurationChanged();
84        }
85    }
86
87    // Temp. holders for a chain of containers we are currently processing.
88    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
89    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
90
91    /**
92     * Adds the input window container has a child of this container in order based on the input
93     * comparator.
94     * @param child The window container to add as a child of this window container.
95     * @param comparator Comparator to use in determining the position the child should be added to.
96     *                   If null, the child will be added to the top.
97     */
98    @CallSuper
99    protected void addChild(E child, Comparator<E> comparator) {
100        if (child.getParent() != null) {
101            throw new IllegalArgumentException("addChild: container=" + child.getName()
102                    + " is already a child of container=" + child.getParent().getName()
103                    + " can't add to container=" + getName());
104        }
105
106        int positionToAdd = -1;
107        if (comparator != null) {
108            final int count = mChildren.size();
109            for (int i = 0; i < count; i++) {
110                if (comparator.compare(child, mChildren.get(i)) < 0) {
111                    positionToAdd = i;
112                    break;
113                }
114            }
115        }
116
117        if (positionToAdd == -1) {
118            mChildren.add(child);
119        } else {
120            mChildren.add(positionToAdd, child);
121        }
122        // Set the parent after we've actually added a child in case a subclass depends on this.
123        child.setParent(this);
124    }
125
126    /** Adds the input window container has a child of this container at the input index. */
127    @CallSuper
128    protected void addChild(E child, int index) {
129        if (child.getParent() != null) {
130            throw new IllegalArgumentException("addChild: container=" + child.getName()
131                    + " is already a child of container=" + child.getParent().getName()
132                    + " can't add to container=" + getName());
133        }
134        mChildren.add(index, child);
135        // Set the parent after we've actually added a child in case a subclass depends on this.
136        child.setParent(this);
137    }
138
139    /**
140     * Removes the input child container from this container which is its parent.
141     *
142     * @return True if the container did contain the input child and it was detached.
143     */
144    @CallSuper
145    void removeChild(E child) {
146        if (mChildren.remove(child)) {
147            child.setParent(null);
148        } else {
149            throw new IllegalArgumentException("removeChild: container=" + child.getName()
150                    + " is not a child of container=" + getName());
151        }
152    }
153
154    /**
155     * Removes this window container and its children with no regard for what else might be going on
156     * in the system. For example, the container will be removed during animation if this method is
157     * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()}
158     * which allows the system to defer removal until a suitable time.
159     */
160    @CallSuper
161    void removeImmediately() {
162        while (!mChildren.isEmpty()) {
163            final WindowContainer child = mChildren.peekLast();
164            child.removeImmediately();
165            // Need to do this after calling remove on the child because the child might try to
166            // remove/detach itself from its parent which will cause an exception if we remove
167            // it before calling remove on the child.
168            mChildren.remove(child);
169        }
170
171        if (mParent != null) {
172            mParent.removeChild(this);
173        }
174    }
175
176    /**
177     * Removes this window container and its children taking care not to remove them during a
178     * critical stage in the system. For example, some containers will not be removed during
179     * animation if this method is called.
180     */
181    // TODO: figure-out implementation that works best for this.
182    // E.g. when do we remove from parent list? maybe not...
183    void removeIfPossible() {
184        for (int i = mChildren.size() - 1; i >= 0; --i) {
185            final WindowContainer wc = mChildren.get(i);
186            wc.removeIfPossible();
187        }
188    }
189
190    /** Returns true if this window container has the input child. */
191    boolean hasChild(WindowContainer child) {
192        for (int i = mChildren.size() - 1; i >= 0; --i) {
193            final WindowContainer current = mChildren.get(i);
194            if (current == child || current.hasChild(child)) {
195                return true;
196            }
197        }
198        return false;
199    }
200
201    /**
202     * Returns full configuration applied to this window container.
203     * This method should be used for getting settings applied in each particular level of the
204     * hierarchy.
205     */
206    Configuration getConfiguration() {
207        return mFullConfiguration;
208    }
209
210    /**
211     * Notify that parent config changed and we need to update full configuration.
212     * @see #mFullConfiguration
213     */
214    void onConfigurationChanged(Configuration newParentConfig) {
215        mFullConfiguration.setTo(newParentConfig);
216        mFullConfiguration.updateFrom(mOverrideConfiguration);
217        for (int i = mChildren.size() - 1; i >= 0; --i) {
218            final WindowContainer child = mChildren.get(i);
219            child.onConfigurationChanged(mFullConfiguration);
220        }
221    }
222
223    /** Returns override configuration applied to this window container. */
224    Configuration getOverrideConfiguration() {
225        return mOverrideConfiguration;
226    }
227
228    /**
229     * Update override configuration and recalculate full config.
230     * @see #mOverrideConfiguration
231     * @see #mFullConfiguration
232     */
233    void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
234        mOverrideConfiguration.setTo(overrideConfiguration);
235        // Update full configuration of this container and all its children.
236        onConfigurationChanged(mParent != null ? mParent.getConfiguration() : Configuration.EMPTY);
237        // Update merged override config of this container and all its children.
238        onMergedOverrideConfigurationChanged();
239    }
240
241    /**
242     * Get merged override configuration from the top of the hierarchy down to this
243     * particular instance. This should be reported to client as override config.
244     */
245    Configuration getMergedOverrideConfiguration() {
246        return mMergedOverrideConfiguration;
247    }
248
249    /**
250     * Update merged override configuration based on corresponding parent's config and notify all
251     * its children. If there is no parent, merged override configuration will set equal to current
252     * override config.
253     * @see #mMergedOverrideConfiguration
254     */
255    private void onMergedOverrideConfigurationChanged() {
256        if (mParent != null) {
257            mMergedOverrideConfiguration.setTo(mParent.getMergedOverrideConfiguration());
258            mMergedOverrideConfiguration.updateFrom(mOverrideConfiguration);
259        } else {
260            mMergedOverrideConfiguration.setTo(mOverrideConfiguration);
261        }
262        for (int i = mChildren.size() - 1; i >= 0; --i) {
263            final WindowContainer child = mChildren.get(i);
264            child.onMergedOverrideConfigurationChanged();
265        }
266    }
267
268    /**
269     * Notify that the display this container is on has changed.
270     * @param dc The new display this container is on.
271     */
272    void onDisplayChanged(DisplayContent dc) {
273        for (int i = mChildren.size() - 1; i >= 0; --i) {
274            final WindowContainer child = mChildren.get(i);
275            child.onDisplayChanged(dc);
276        }
277    }
278
279    void setWaitingForDrawnIfResizingChanged() {
280        for (int i = mChildren.size() - 1; i >= 0; --i) {
281            final WindowContainer wc = mChildren.get(i);
282            wc.setWaitingForDrawnIfResizingChanged();
283        }
284    }
285
286    void onResize() {
287        for (int i = mChildren.size() - 1; i >= 0; --i) {
288            final WindowContainer wc = mChildren.get(i);
289            wc.onResize();
290        }
291    }
292
293    void onMovedByResize() {
294        for (int i = mChildren.size() - 1; i >= 0; --i) {
295            final WindowContainer wc = mChildren.get(i);
296            wc.onMovedByResize();
297        }
298    }
299
300    void resetDragResizingChangeReported() {
301        for (int i = mChildren.size() - 1; i >= 0; --i) {
302            final WindowContainer wc = mChildren.get(i);
303            wc.resetDragResizingChangeReported();
304        }
305    }
306
307    void forceWindowsScaleableInTransaction(boolean force) {
308        for (int i = mChildren.size() - 1; i >= 0; --i) {
309            final WindowContainer wc = mChildren.get(i);
310            wc.forceWindowsScaleableInTransaction(force);
311        }
312    }
313
314    boolean isAnimating() {
315        for (int j = mChildren.size() - 1; j >= 0; j--) {
316            final WindowContainer wc = mChildren.get(j);
317            if (wc.isAnimating()) {
318                return true;
319            }
320        }
321        return false;
322    }
323
324    void sendAppVisibilityToClients() {
325        for (int i = mChildren.size() - 1; i >= 0; --i) {
326            final WindowContainer wc = mChildren.get(i);
327            wc.sendAppVisibilityToClients();
328        }
329    }
330
331    void setVisibleBeforeClientHidden() {
332        for (int i = mChildren.size() - 1; i >= 0; --i) {
333            final WindowContainer wc = mChildren.get(i);
334            wc.setVisibleBeforeClientHidden();
335        }
336    }
337
338    /**
339     * Returns true if the container or one of its children as some content it can display or wants
340     * to display (e.g. app views or saved surface).
341     *
342     * NOTE: While this method will return true if the there is some content to display, it doesn't
343     * mean the container is visible. Use {@link #isVisible()} to determine if the container is
344     * visible.
345     */
346    boolean hasContentToDisplay() {
347        for (int i = mChildren.size() - 1; i >= 0; --i) {
348            final WindowContainer wc = mChildren.get(i);
349            if (wc.hasContentToDisplay()) {
350                return true;
351            }
352        }
353        return false;
354    }
355
356    /**
357     * Returns true if the container or one of its children is considered visible from the
358     * WindowManager perspective which usually means valid surface and some other internal state
359     * are true.
360     *
361     * NOTE: While this method will return true if the surface is visible, it doesn't mean the
362     * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
363     * the container has any content to display.
364     */
365    boolean isVisible() {
366        // TODO: Will this be more correct if it checks the visibility of its parents?
367        // It depends...For example, Tasks and Stacks are only visible if there children are visible
368        // but, WindowState are not visible if there parent are not visible. Maybe have the
369        // container specify which direction to treverse for for visibility?
370        for (int i = mChildren.size() - 1; i >= 0; --i) {
371            final WindowContainer wc = mChildren.get(i);
372            if (wc.isVisible()) {
373                return true;
374            }
375        }
376        return false;
377    }
378
379    /** Returns the top child container or this container if there are no children. */
380    WindowContainer getTop() {
381        return mChildren.isEmpty() ? this : mChildren.peekLast();
382    }
383
384    /** Returns true if there is still a removal being deferred */
385    boolean checkCompleteDeferredRemoval() {
386        boolean stillDeferringRemoval = false;
387
388        for (int i = mChildren.size() - 1; i >= 0; --i) {
389            final WindowContainer wc = mChildren.get(i);
390            stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
391        }
392
393        return stillDeferringRemoval;
394    }
395
396    /** Checks if all windows in an app are all drawn and shows them if needed. */
397    void checkAppWindowsReadyToShow() {
398        for (int i = mChildren.size() - 1; i >= 0; --i) {
399            final WindowContainer wc = mChildren.get(i);
400            wc.checkAppWindowsReadyToShow();
401        }
402    }
403
404    /** Step currently ongoing animation for App window containers. */
405    void stepAppWindowsAnimation(long currentTime) {
406        for (int i = mChildren.size() - 1; i >= 0; --i) {
407            final WindowContainer wc = mChildren.get(i);
408            wc.stepAppWindowsAnimation(currentTime);
409        }
410    }
411
412    void onAppTransitionDone() {
413        for (int i = mChildren.size() - 1; i >= 0; --i) {
414            final WindowContainer wc = mChildren.get(i);
415            wc.onAppTransitionDone();
416        }
417    }
418
419    void setOrientation(int orientation) {
420        mOrientation = orientation;
421    }
422
423    /**
424     * Returns the specified orientation for this window container or one of its children is there
425     * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
426     * specification is set.
427     * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
428     * specification...
429     */
430    int getOrientation() {
431
432        if (!fillsParent() || !isVisible()) {
433            // Ignore invisible containers or containers that don't completely fills their parents.
434            return SCREEN_ORIENTATION_UNSET;
435        }
436
437        // The container fills its parent so we can use it orientation if it has one specified,
438        // otherwise we prefer to use the orientation of its topmost child that has one
439        // specified and fall back on this container's unset or unspecified value as a candidate
440        // if none of the children have a better candidate for the orientation.
441        if (mOrientation != SCREEN_ORIENTATION_UNSET
442                && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
443            return mOrientation;
444        }
445        int candidate = mOrientation;
446
447        for (int i = mChildren.size() - 1; i >= 0; --i) {
448            final WindowContainer wc = mChildren.get(i);
449
450            final int orientation = wc.getOrientation();
451            if (orientation == SCREEN_ORIENTATION_BEHIND) {
452                // container wants us to use the orientation of the container behind it. See if we
453                // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
454                // look behind this container.
455                candidate = orientation;
456                continue;
457            }
458
459            if (orientation == SCREEN_ORIENTATION_UNSET) {
460                continue;
461            }
462
463            if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
464                // Use the orientation if the container fills its parent or requested an explicit
465                // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
466                return orientation;
467            }
468        }
469
470        return candidate;
471    }
472
473    /**
474     * Returns true if this container is opaque and fills all the space made available by its parent
475     * container.
476     *
477     * NOTE: It is possible for this container to occupy more space than the parent has (or less),
478     * this is just a signal from the client to window manager stating its intent, but not what it
479     * actually does.
480     */
481    boolean fillsParent() {
482        return false;
483    }
484
485    // TODO: Users would have their own window containers under the display container?
486    void switchUser() {
487        for (int i = mChildren.size() - 1; i >= 0; --i) {
488            mChildren.get(i).switchUser();
489        }
490    }
491
492    /**
493     * For all windows at or below this container call the callback.
494     * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
495     *                   stops the search if {@link ToBooleanFunction#apply} returns true.
496     * @param   traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of
497     *                              z-order, else from bottom-to-top.
498     * @return  True if the search ended before we reached the end of the hierarchy due to
499     *          {@link Function#apply} returning true.
500     */
501    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
502        if (traverseTopToBottom) {
503            for (int i = mChildren.size() - 1; i >= 0; --i) {
504                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
505                    return true;
506                }
507            }
508        } else {
509            final int count = mChildren.size();
510            for (int i = 0; i < count; i++) {
511                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
512                    return true;
513                }
514            }
515        }
516        return false;
517    }
518
519    void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
520        forAllWindows(w -> {
521            callback.accept(w);
522            return false;
523        }, traverseTopToBottom);
524    }
525
526    WindowState getWindow(Predicate<WindowState> callback) {
527        for (int i = mChildren.size() - 1; i >= 0; --i) {
528            final WindowState w = mChildren.get(i).getWindow(callback);
529            if (w != null) {
530                return w;
531            }
532        }
533
534        return null;
535    }
536
537    /**
538     * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
539     * the input container in terms of z-order.
540     */
541    @Override
542    public int compareTo(WindowContainer other) {
543        if (this == other) {
544            return 0;
545        }
546
547        if (mParent != null && mParent == other.mParent) {
548            final LinkedList<WindowContainer> list = mParent.mChildren;
549            return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
550        }
551
552        final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
553        final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
554        getParents(thisParentChain);
555        other.getParents(otherParentChain);
556
557        // Find the common ancestor of both containers.
558        WindowContainer commonAncestor = null;
559        WindowContainer thisTop = thisParentChain.peekLast();
560        WindowContainer otherTop = otherParentChain.peekLast();
561        while (thisTop != null && otherTop != null && thisTop == otherTop) {
562            commonAncestor = thisParentChain.removeLast();
563            otherParentChain.removeLast();
564            thisTop = thisParentChain.peekLast();
565            otherTop = otherParentChain.peekLast();
566        }
567
568        // Containers don't belong to the same hierarchy???
569        if (commonAncestor == null) {
570            throw new IllegalArgumentException("No in the same hierarchy this="
571                    + thisParentChain + " other=" + otherParentChain);
572        }
573
574        // Children are always considered greater than their parents, so if one of the containers
575        // we are comparing it the parent of the other then whichever is the child is greater.
576        if (commonAncestor == this) {
577            return -1;
578        } else if (commonAncestor == other) {
579            return 1;
580        }
581
582        // The position of the first non-common ancestor in the common ancestor list determines
583        // which is greater the which.
584        final LinkedList<WindowContainer> list = commonAncestor.mChildren;
585        return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
586                ? 1 : -1;
587    }
588
589    private void getParents(LinkedList<WindowContainer> parents) {
590        parents.clear();
591        WindowContainer current = this;
592        do {
593            parents.addLast(current);
594            current = current.mParent;
595        } while (current != null);
596    }
597
598    /**
599     * Dumps the names of this container children in the input print writer indenting each
600     * level with the input prefix.
601     */
602    void dumpChildrenNames(StringBuilder out, String prefix) {
603        final String childPrefix = prefix + " ";
604        out.append(getName() + "\n");
605        for (int i = mChildren.size() - 1; i >= 0; --i) {
606            final WindowContainer wc = mChildren.get(i);
607            out.append(childPrefix + "#" + i + " ");
608            wc.dumpChildrenNames(out, childPrefix);
609        }
610    }
611
612    String getName() {
613        return toString();
614    }
615
616}
617