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