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