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