content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License
   */
  
  package com.android.server.wm;
  
  import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
  import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
  import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
  import static android.view.SurfaceControl.Transaction;
  
  import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
  import static com.android.server.wm.WindowContainerProto.ORIENTATION;
  import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
  import static com.android.server.wm.WindowContainerProto.VISIBLE;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
  import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
  import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
  
  import android.annotation.CallSuper;
  import android.annotation.IntDef;
  import android.annotation.Nullable;
  import android.app.WindowConfiguration;
  import android.content.res.Configuration;
  import android.graphics.Point;
  import android.graphics.Rect;
  import android.os.IBinder;
  import android.util.Pools;
  import android.util.Slog;
  import android.util.proto.ProtoOutputStream;
  import android.view.MagnificationSpec;
  import android.view.SurfaceControl;
  import android.view.SurfaceControl.Builder;
  import android.view.SurfaceSession;
  
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.internal.util.ToBooleanFunction;
  import com.android.server.wm.SurfaceAnimator.Animatable;
  
  import java.io.PrintWriter;
  import java.util.Comparator;
  import java.util.LinkedList;
  import java.util.function.Consumer;
  import java.util.function.Predicate;
  
  /**
   * Defines common functionality for classes that can hold windows directly or through their
   * children in a hierarchy form.
   * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
   * changes are made to this class.
   */
  class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
          implements Comparable<WindowContainer>, Animatable {
  
      private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
  
      /** Animation layer that happens above all animating {@link TaskStack}s. */
      static final int ANIMATION_LAYER_STANDARD = 0;
  
      /** Animation layer that happens above all {@link TaskStack}s. */
      static final int ANIMATION_LAYER_BOOSTED = 1;
  
      /**
       * Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
       * activities and all activities that are being controlled by the recents animation. This
       * layer is generally below all {@link TaskStack}s.
       */
      static final int ANIMATION_LAYER_HOME = 2;
  
      @IntDef(prefix = { "ANIMATION_LAYER_" }, value = {
              ANIMATION_LAYER_STANDARD,
              ANIMATION_LAYER_BOOSTED,
              ANIMATION_LAYER_HOME,
      })
      @interface AnimationLayer {}
  
      static final int POSITION_TOP = Integer.MAX_VALUE;
      static final int POSITION_BOTTOM = Integer.MIN_VALUE;
  
      /**
       * The parent of this window container.
       * For removing or setting new parent {@link #setParent} should be used, because it also
       * performs configuration updates based on new parent's settings.
       */
      private WindowContainer<WindowContainer> mParent = null;
  
      // List of children for this window container. List is in z-order as the children appear on
      // screen with the top-most window container at the tail of the list.
      protected final WindowList<E> mChildren = new WindowList<E>();
  
      // The specified orientation for this window container.
      protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
  
      private final Pools.SynchronizedPool<ForAllWindowsConsumerWrapper> mConsumerWrapperPool =
              new Pools.SynchronizedPool<>(3);
  
      // The owner/creator for this container. No controller if null.
      WindowContainerController mController;
  
      // The display this window container is on.
      protected DisplayContent mDisplayContent;
  
      protected SurfaceControl mSurfaceControl;
      private int mLastLayer = 0;
      private SurfaceControl mLastRelativeToLayer = null;
  
      // TODO(b/132320879): Remove this from WindowContainers except DisplayContent.
      private final Transaction mPendingTransaction;
  
      /**
       * Applied as part of the animation pass in "prepareSurfaces".
       */
      protected final SurfaceAnimator mSurfaceAnimator;
      protected final WindowManagerService mWmService;
  
      private final Point mTmpPos = new Point();
      protected final Point mLastSurfacePosition = new Point();
  
      /** Total number of elements in this subtree, including our own hierarchy element. */
      private int mTreeWeight = 1;
  
      /**
       * Indicates whether we are animating and have committed the transaction to reparent our
       * surface to the animation leash
       */
      private boolean mCommittedReparentToAnimationLeash;
  
      WindowContainer(WindowManagerService wms) {
          mWmService = wms;
          mPendingTransaction = wms.mTransactionFactory.make();
          mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
      }
  
      @Override
      final protected WindowContainer getParent() {
          return mParent;
      }
  
      @Override
      protected int getChildCount() {
          return mChildren.size();
      }
  
      @Override
      protected E getChildAt(int index) {
          return mChildren.get(index);
      }
  
      @Override
      public void onConfigurationChanged(Configuration newParentConfig) {
          super.onConfigurationChanged(newParentConfig);
          updateSurfacePosition();
          scheduleAnimation();
      }
  
      final protected void setParent(WindowContainer<WindowContainer> parent) {
          mParent = parent;
          onParentChanged();
      }
  
      /**
       * Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called.
       * Supposed to be overridden and contain actions that should be executed after parent was set.
       */
      @Override
      void onParentChanged() {
          super.onParentChanged();
          if (mParent == null) {
              return;
          }
  
          if (mSurfaceControl == null) {
              // If we don't yet have a surface, but we now have a parent, we should
              // build a surface.
              mSurfaceControl = makeSurface().build();
              getPendingTransaction().show(mSurfaceControl);
              updateSurfacePosition();
          } else {
              // If we have a surface but a new parent, we just need to perform a reparent. Go through
              // surface animator such that hierarchy is preserved when animating, i.e.
              // mSurfaceControl stays attached to the leash and we just reparent the leash to the
              // new parent.
              reparentSurfaceControl(getPendingTransaction(), mParent.mSurfaceControl);
          }
  
          // Either way we need to ask the parent to assign us a Z-order.
          mParent.assignChildLayers();
          scheduleAnimation();
      }
  
      // Temp. holders for a chain of containers we are currently processing.
      private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList<>();
      private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList<>();
  
      /**
       * Adds the input window container has a child of this container in order based on the input
       * comparator.
       * @param child The window container to add as a child of this window container.
       * @param comparator Comparator to use in determining the position the child should be added to.
       *                   If null, the child will be added to the top.
       */
      @CallSuper
      protected void addChild(E child, Comparator<E> comparator) {
          if (child.getParent() != null) {
              throw new IllegalArgumentException("addChild: container=" + child.getName()
                      + " is already a child of container=" + child.getParent().getName()
                      + " can't add to container=" + getName());
          }
  
          int positionToAdd = -1;
          if (comparator != null) {
              final int count = mChildren.size();
              for (int i = 0; i < count; i++) {
                  if (comparator.compare(child, mChildren.get(i)) < 0) {
                      positionToAdd = i;
                      break;
                  }
              }
          }
  
          if (positionToAdd == -1) {
              mChildren.add(child);
          } else {
              mChildren.add(positionToAdd, child);
          }
          onChildAdded(child);
  
          // Set the parent after we've actually added a child in case a subclass depends on this.
          child.setParent(this);
      }
  
      /** Adds the input window container has a child of this container at the input index. */
      @CallSuper
      void addChild(E child, int index) {
          if (child.getParent() != null) {
              throw new IllegalArgumentException("addChild: container=" + child.getName()
                      + " is already a child of container=" + child.getParent().getName()
                      + " can't add to container=" + getName());
          }
  
          if ((index < 0 && index != POSITION_BOTTOM)
                  || (index > mChildren.size() && index != POSITION_TOP)) {
              throw new IllegalArgumentException("addChild: invalid position=" + index
                      + ", children number=" + mChildren.size());
          }
  
          if (index == POSITION_TOP) {
              index = mChildren.size();
          } else if (index == POSITION_BOTTOM) {
              index = 0;
          }
  
          mChildren.add(index, child);
          onChildAdded(child);
  
          // Set the parent after we've actually added a child in case a subclass depends on this.
          child.setParent(this);
      }
  
      private void onChildAdded(WindowContainer child) {
          mTreeWeight += child.mTreeWeight;
          WindowContainer parent = getParent();
          while (parent != null) {
              parent.mTreeWeight += child.mTreeWeight;
              parent = parent.getParent();
          }
          onChildPositionChanged();
      }
  
      /**
       * Removes the input child container from this container which is its parent.
       *
       * @return True if the container did contain the input child and it was detached.
       */
      @CallSuper
      void removeChild(E child) {
          if (mChildren.remove(child)) {
              onChildRemoved(child);
              child.setParent(null);
          } else {
              throw new IllegalArgumentException("removeChild: container=" + child.getName()
                      + " is not a child of container=" + getName());
          }
      }
  
      private void onChildRemoved(WindowContainer child) {
          mTreeWeight -= child.mTreeWeight;
          WindowContainer parent = getParent();
          while (parent != null) {
              parent.mTreeWeight -= child.mTreeWeight;
              parent = parent.getParent();
          }
          onChildPositionChanged();
      }
  
      /**
       * Removes this window container and its children with no regard for what else might be going on
       * in the system. For example, the container will be removed during animation if this method is
       * called which isn't desirable. For most cases you want to call {@link #removeIfPossible()}
       * which allows the system to defer removal until a suitable time.
       */
      @CallSuper
      void removeImmediately() {
          while (!mChildren.isEmpty()) {
              final E child = mChildren.peekLast();
              child.removeImmediately();
              // Need to do this after calling remove on the child because the child might try to
              // remove/detach itself from its parent which will cause an exception if we remove
              // it before calling remove on the child.
              if (mChildren.remove(child)) {
                  onChildRemoved(child);
              }
          }
  
          if (mSurfaceControl != null) {
              getPendingTransaction().remove(mSurfaceControl);
  
              // Merge to parent transaction to ensure the transactions on this WindowContainer are
              // applied in native even if WindowContainer is removed.
              if (mParent != null) {
                  mParent.getPendingTransaction().merge(getPendingTransaction());
              }
  
              mSurfaceControl = null;
              scheduleAnimation();
          }
  
          if (mParent != null) {
              mParent.removeChild(this);
          }
  
          if (mController != null) {
              setController(null);
          }
  
      }
  
      /**
       * @return The index of this element in the hierarchy tree in prefix order.
       */
      int getPrefixOrderIndex() {
          if (mParent == null) {
              return 0;
          }
          return mParent.getPrefixOrderIndex(this);
      }
  
      private int getPrefixOrderIndex(WindowContainer child) {
          int order = 0;
          for (int i = 0; i < mChildren.size(); i++) {
              final WindowContainer childI = mChildren.get(i);
              if (child == childI) {
                  break;
              }
              order += childI.mTreeWeight;
          }
          if (mParent != null) {
              order += mParent.getPrefixOrderIndex(this);
          }
  
          // We also need to count ourselves.
          order++;
          return order;
      }
  
      /**
       * Removes this window container and its children taking care not to remove them during a
       * critical stage in the system. For example, some containers will not be removed during
       * animation if this method is called.
       */
      // TODO: figure-out implementation that works best for this.
      // E.g. when do we remove from parent list? maybe not...
      void removeIfPossible() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.removeIfPossible();
          }
      }
  
      /** Returns true if this window container has the input child. */
      boolean hasChild(E child) {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final E current = mChildren.get(i);
              if (current == child || current.hasChild(child)) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Move a child from it's current place in siblings list to the specified position,
       * with an option to move all its parents to top.
       * @param position Target position to move the child to.
       * @param child Child to move to selected position.
       * @param includingParents Flag indicating whether we need to move the entire branch of the
       *                         hierarchy when we're moving a child to {@link #POSITION_TOP} or
       *                         {@link #POSITION_BOTTOM}. When moving to other intermediate positions
       *                         this flag will do nothing.
       */
      @CallSuper
      void positionChildAt(int position, E child, boolean includingParents) {
  
          if (child.getParent() != this) {
              throw new IllegalArgumentException("removeChild: container=" + child.getName()
                      + " is not a child of container=" + getName()
                      + " current parent=" + child.getParent());
          }
  
          if ((position < 0 && position != POSITION_BOTTOM)
                  || (position > mChildren.size() && position != POSITION_TOP)) {
              throw new IllegalArgumentException("positionAt: invalid position=" + position
                      + ", children number=" + mChildren.size());
          }
  
          if (position >= mChildren.size() - 1) {
              position = POSITION_TOP;
          } else if (position == 0) {
              position = POSITION_BOTTOM;
          }
  
          switch (position) {
              case POSITION_TOP:
                  if (mChildren.peekLast() != child) {
                      mChildren.remove(child);
                      mChildren.add(child);
                      onChildPositionChanged();
                  }
                  if (includingParents && getParent() != null) {
                      getParent().positionChildAt(POSITION_TOP, this /* child */,
                              true /* includingParents */);
                  }
                  break;
              case POSITION_BOTTOM:
                  if (mChildren.peekFirst() != child) {
                      mChildren.remove(child);
                      mChildren.addFirst(child);
                      onChildPositionChanged();
                  }
                  if (includingParents && getParent() != null) {
                      getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
                              true /* includingParents */);
                  }
                  break;
              default:
                  // TODO: Removing the child before reinserting requires the caller to provide a
                  //       position that takes into account the removed child (if the index of the
                  //       child < position, then the position should be adjusted). We should consider
                  //       doing this adjustment here and remove any adjustments in the callers.
                  mChildren.remove(child);
                  mChildren.add(position, child);
                  onChildPositionChanged();
          }
      }
  
      /**
       * Notify that a child's position has changed. Possible changes are adding or removing a child.
       */
      void onChildPositionChanged() { }
  
      /**
       * Update override configuration and recalculate full config.
       * @see #mRequestedOverrideConfiguration
       * @see #mFullConfiguration
       */
      @Override
      public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
          // We must diff before the configuration is applied so that we can capture the change
          // against the existing bounds.
          final int diff = diffRequestedOverrideBounds(
                  overrideConfiguration.windowConfiguration.getBounds());
          super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
          if (mParent != null) {
              mParent.onDescendantOverrideConfigurationChanged();
          }
  
          if (diff == BOUNDS_CHANGE_NONE) {
              return;
          }
  
          if ((diff & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
              onResize();
          } else {
              onMovedByResize();
          }
      }
  
      /**
       * Notify that a descendant's overrideConfiguration has changed.
       */
      void onDescendantOverrideConfigurationChanged() {
          if (mParent != null) {
              mParent.onDescendantOverrideConfigurationChanged();
          }
      }
  
      /**
       * Notify that the display this container is on has changed. This could be either this container
       * is moved to a new display, or some configurations on the display it is on changes.
       *
       * @param dc The display this container is on after changes.
       */
      void onDisplayChanged(DisplayContent dc) {
          mDisplayContent = dc;
          if (dc != null && dc != this) {
              dc.getPendingTransaction().merge(mPendingTransaction);
          }
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer child = mChildren.get(i);
              child.onDisplayChanged(dc);
          }
      }
  
      DisplayContent getDisplayContent() {
          return mDisplayContent;
      }
  
      void setWaitingForDrawnIfResizingChanged() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.setWaitingForDrawnIfResizingChanged();
          }
      }
  
      void onResize() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.onParentResize();
          }
      }
  
      void onParentResize() {
          // In the case this container has specified its own bounds, a parent resize will not
          // affect its bounds. Any relevant changes will be propagated through changes to the
          // Configuration override.
          if (hasOverrideBounds()) {
              return;
          }
  
          // Default implementation is to treat as resize on self.
          onResize();
      }
  
      void onMovedByResize() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.onMovedByResize();
          }
      }
  
      void resetDragResizingChangeReported() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.resetDragResizingChangeReported();
          }
      }
  
      void forceWindowsScaleableInTransaction(boolean force) {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.forceWindowsScaleableInTransaction(force);
          }
      }
  
      /**
       * @return Whether our own container is running an animation or any child, no matter how deep in
       *         the hierarchy, is animating.
       */
      boolean isSelfOrChildAnimating() {
          if (isSelfAnimating()) {
              return true;
          }
          for (int j = mChildren.size() - 1; j >= 0; j--) {
              final WindowContainer wc = mChildren.get(j);
              if (wc.isSelfOrChildAnimating()) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * @return Whether our own container is running an animation or our parent is animating. This
       *         doesn't consider whether children are animating.
       */
      boolean isAnimating() {
  
          // We are animating if we ourselves are animating or if our parent is animating.
          return isSelfAnimating() || mParent != null && mParent.isAnimating();
      }
  
      /**
       * @return {@code true} if in this subtree of the hierarchy we have an {@link AppWindowToken}
       *         that is {@link #isSelfAnimating}; {@code false} otherwise.
       */
      boolean isAppAnimating() {
          for (int j = mChildren.size() - 1; j >= 0; j--) {
              final WindowContainer wc = mChildren.get(j);
              if (wc.isAppAnimating()) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * @return Whether our own container running an animation at the moment.
       */
      boolean isSelfAnimating() {
          return mSurfaceAnimator.isAnimating();
      }
  
      void sendAppVisibilityToClients() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.sendAppVisibilityToClients();
          }
      }
  
      /**
       * Returns true if the container or one of its children as some content it can display or wants
       * to display (e.g. app views or saved surface).
       *
       * NOTE: While this method will return true if the there is some content to display, it doesn't
       * mean the container is visible. Use {@link #isVisible()} to determine if the container is
       * visible.
       */
      boolean hasContentToDisplay() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              if (wc.hasContentToDisplay()) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Returns true if the container or one of its children is considered visible from the
       * WindowManager perspective which usually means valid surface and some other internal state
       * are true.
       *
       * NOTE: While this method will return true if the surface is visible, it doesn't mean the
       * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
       * the container has any content to display.
       */
      boolean isVisible() {
          // TODO: Will this be more correct if it checks the visibility of its parents?
          // It depends...For example, Tasks and Stacks are only visible if there children are visible
          // but, WindowState are not visible if there parent are not visible. Maybe have the
          // container specify which direction to traverse for visibility?
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              if (wc.isVisible()) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * @return Whether this child is on top of the window hierarchy.
       */
      boolean isOnTop() {
          return getParent().getTopChild() == this && getParent().isOnTop();
      }
  
      /** Returns the top child container. */
      E getTopChild() {
          return mChildren.peekLast();
      }
  
      /** Returns true if there is still a removal being deferred */
      boolean checkCompleteDeferredRemoval() {
          boolean stillDeferringRemoval = false;
  
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
          }
  
          return stillDeferringRemoval;
      }
  
      /** Checks if all windows in an app are all drawn and shows them if needed. */
      void checkAppWindowsReadyToShow() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.checkAppWindowsReadyToShow();
          }
      }
  
      void onAppTransitionDone() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
              wc.onAppTransitionDone();
          }
      }
  
      /**
       * Called when this container or one of its descendants changed its requested orientation, and
       * wants this container to handle it or pass it to its parent.
       *
       * @param freezeDisplayToken freeze this app window token if display needs to freeze
       * @param requestingContainer the container which orientation request has changed
       * @return {@code true} if handled; {@code false} otherwise.
       */
      boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken,
              @Nullable ConfigurationContainer requestingContainer) {
          final WindowContainer parent = getParent();
          if (parent == null) {
              return false;
          }
          return parent.onDescendantOrientationChanged(freezeDisplayToken,
                  requestingContainer);
      }
  
      /**
       * Check if this container or its parent will handle orientation changes from descendants. It's
       * different from the return value of {@link #onDescendantOrientationChanged(IBinder,
       * ConfigurationContainer)} in the sense that the return value of this method tells if this
       * container or its parent will handle the request eventually, while the return value of the
       * other method is if it handled the request synchronously.
       *
       * @return {@code true} if it handles or will handle orientation change in the future; {@code
       *         false} if it won't handle the change at anytime.
       */
      boolean handlesOrientationChangeFromDescendant() {
          final WindowContainer parent = getParent();
          return parent != null && parent.handlesOrientationChangeFromDescendant();
      }
  
      /**
       * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
       * parameters.
       *
       * @param orientation the specified orientation.
       */
      void setOrientation(int orientation) {
          setOrientation(orientation, null /* freezeDisplayToken */,
                  null /* ActivityRecord */);
      }
  
      /**
       * Sets the specified orientation of this container. It percolates this change upward along the
       * hierarchy to let each level of the hierarchy a chance to respond to it.
       *
       * @param orientation the specified orientation. Needs to be one of {@link
       *      android.content.pm.ActivityInfo.ScreenOrientation}.
       * @param freezeDisplayToken uses this token to freeze display if orientation change is not
       *                           done. Display will not be frozen if this is {@code null}, which
       *                           should only happen in tests.
       * @param requestingContainer the container which orientation request has changed. Mostly used
       *                            to ensure it gets correct configuration.
       */
      void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
              @Nullable ConfigurationContainer requestingContainer) {
          final boolean changed = mOrientation != orientation;
          mOrientation = orientation;
          if (!changed) {
              return;
          }
          final WindowContainer parent = getParent();
          if (parent != null) {
              onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
          }
      }
  
      int getOrientation() {
          return getOrientation(mOrientation);
      }
  
      /**
       * Returns the specified orientation for this window container or one of its children is there
       * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
       * specification is set.
       * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
       * specification...
       *
       * @param candidate The current orientation candidate that will be returned if we don't find a
       *                  better match.
       * @return The orientation as specified by this branch or the window hierarchy.
       */
      int getOrientation(int candidate) {
          if (!fillsParent()) {
              // Ignore containers that don't completely fill their parents.
              return SCREEN_ORIENTATION_UNSET;
          }
  
          // The container fills its parent so we can use it orientation if it has one
          // specified; otherwise we prefer to use the orientation of its topmost child that has one
          // specified and fall back on this container's unset or unspecified value as a candidate
          // if none of the children have a better candidate for the orientation.
          if (mOrientation != SCREEN_ORIENTATION_UNSET
                  && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
              return mOrientation;
          }
  
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowContainer wc = mChildren.get(i);
  
              // TODO: Maybe mOrientation should default to SCREEN_ORIENTATION_UNSET vs.
              // SCREEN_ORIENTATION_UNSPECIFIED?
              final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND
                      ? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET);
              if (orientation == SCREEN_ORIENTATION_BEHIND) {
                  // container wants us to use the orientation of the container behind it. See if we
                  // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
                  // look behind this container.
                  candidate = orientation;
                  continue;
              }
  
              if (orientation == SCREEN_ORIENTATION_UNSET) {
                  continue;
              }
  
              if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
                  // Use the orientation if the container fills its parent or requested an explicit
                  // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
                  return orientation;
              }
          }
  
          return candidate;
      }
  
      /**
       * Returns true if this container is opaque and fills all the space made available by its parent
       * container.
       *
       * NOTE: It is possible for this container to occupy more space than the parent has (or less),
       * this is just a signal from the client to window manager stating its intent, but not what it
       * actually does.
       */
      boolean fillsParent() {
          return false;
      }
  
      // TODO: Users would have their own window containers under the display container?
      void switchUser() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              mChildren.get(i).switchUser();
          }
      }
  
      /**
       * For all windows at or below this container call the callback.
       * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
       *                   stops the search if {@link ToBooleanFunction#apply} returns true.
       * @param   traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of
       *                              z-order, else from bottom-to-top.
       * @return  True if the search ended before we reached the end of the hierarchy due to
       *          {@link ToBooleanFunction#apply} returning true.
       */
      boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
          if (traverseTopToBottom) {
              for (int i = mChildren.size() - 1; i >= 0; --i) {
                  if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
                      return true;
                  }
              }
          } else {
              final int count = mChildren.size();
              for (int i = 0; i < count; i++) {
                  if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
                      return true;
                  }
              }
          }
          return false;
      }
  
      void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
          ForAllWindowsConsumerWrapper wrapper = obtainConsumerWrapper(callback);
          forAllWindows(wrapper, traverseTopToBottom);
          wrapper.release();
      }
  
      void forAllAppWindows(Consumer<AppWindowToken> callback) {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              mChildren.get(i).forAllAppWindows(callback);
          }
      }
  
      /**
       * For all tasks at or below this container call the callback.
       *
       * @param callback Callback to be called for every task.
       */
      void forAllTasks(Consumer<Task> callback) {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              mChildren.get(i).forAllTasks(callback);
          }
      }
  
      WindowState getWindow(Predicate<WindowState> callback) {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState w = mChildren.get(i).getWindow(callback);
              if (w != null) {
                  return w;
              }
          }
  
          return null;
      }
  
      /**
       * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
       * the input container in terms of z-order.
       */
      @Override
      public int compareTo(WindowContainer other) {
          if (this == other) {
              return 0;
          }
  
          if (mParent != null && mParent == other.mParent) {
              final WindowList<WindowContainer> list = mParent.mChildren;
              return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
          }
  
          final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
          final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
          try {
              getParents(thisParentChain);
              other.getParents(otherParentChain);
  
              // Find the common ancestor of both containers.
              WindowContainer commonAncestor = null;
              WindowContainer thisTop = thisParentChain.peekLast();
              WindowContainer otherTop = otherParentChain.peekLast();
              while (thisTop != null && otherTop != null && thisTop == otherTop) {
                  commonAncestor = thisParentChain.removeLast();
                  otherParentChain.removeLast();
                  thisTop = thisParentChain.peekLast();
                  otherTop = otherParentChain.peekLast();
              }
  
              // Containers don't belong to the same hierarchy???
              if (commonAncestor == null) {
                  throw new IllegalArgumentException("No in the same hierarchy this="
                          + thisParentChain + " other=" + otherParentChain);
              }
  
              // Children are always considered greater than their parents, so if one of the containers
              // we are comparing it the parent of the other then whichever is the child is greater.
              if (commonAncestor == this) {
                  return -1;
              } else if (commonAncestor == other) {
                  return 1;
              }
  
              // The position of the first non-common ancestor in the common ancestor list determines
              // which is greater the which.
              final WindowList<WindowContainer> list = commonAncestor.mChildren;
              return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
                      ? 1 : -1;
          } finally {
              mTmpChain1.clear();
              mTmpChain2.clear();
          }
      }
  
      private void getParents(LinkedList<WindowContainer> parents) {
          parents.clear();
          WindowContainer current = this;
          do {
              parents.addLast(current);
              current = current.mParent;
          } while (current != null);
      }
  
      WindowContainerController getController() {
          return mController;
      }
  
      void setController(WindowContainerController controller) {
          if (mController != null && controller != null) {
              throw new IllegalArgumentException("Can't set controller=" + mController
                      + " for container=" + this + " Already set to=" + mController);
          }
          if (controller != null) {
              controller.setContainer(this);
          } else if (mController != null) {
              mController.setContainer(null);
          }
          mController = controller;
      }
  
      SurfaceControl.Builder makeSurface() {
          final WindowContainer p = getParent();
          return p.makeChildSurface(this);
      }
  
      /**
       * @param child The WindowContainer this child surface is for, or null if the Surface
       *              is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
       */
      SurfaceControl.Builder makeChildSurface(WindowContainer child) {
          final WindowContainer p = getParent();
          // Give the parent a chance to set properties. In hierarchy v1 we rely
          // on this to set full-screen dimensions on all our Surface-less Layers.
          return p.makeChildSurface(child)
                  .setParent(mSurfaceControl);
      }
      /*
       * @return The SurfaceControl parent for this containers SurfaceControl.
       *         The SurfaceControl must be valid if non-null.
       */
      @Override
      public SurfaceControl getParentSurfaceControl() {
          final WindowContainer parent = getParent();
          if (parent == null) {
              return null;
          }
          return parent.getSurfaceControl();
      }
  
      /**
       * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
       */
      boolean shouldMagnify() {
          if (mSurfaceControl == null) {
              return false;
          }
  
          for (int i = 0; i < mChildren.size(); i++) {
              if (!mChildren.get(i).shouldMagnify()) {
                  return false;
              }
          }
          return true;
      }
  
      SurfaceSession getSession() {
          if (getParent() != null) {
              return getParent().getSession();
          }
          return null;
      }
  
      void assignLayer(Transaction t, int layer) {
          final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
          if (mSurfaceControl != null && changed) {
              setLayer(t, layer);
              mLastLayer = layer;
              mLastRelativeToLayer = null;
          }
      }
  
      void assignRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
          final boolean changed = layer != mLastLayer || mLastRelativeToLayer != relativeTo;
          if (mSurfaceControl != null && changed) {
              setRelativeLayer(t, relativeTo, layer);
              mLastLayer = layer;
              mLastRelativeToLayer = relativeTo;
          }
      }
  
      protected void setLayer(Transaction t, int layer) {
  
          // Route through surface animator to accommodate that our surface control might be
          // attached to the leash, and leash is attached to parent container.
          mSurfaceAnimator.setLayer(t, layer);
      }
  
      protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
  
          // Route through surface animator to accommodate that our surface control might be
          // attached to the leash, and leash is attached to parent container.
          mSurfaceAnimator.setRelativeLayer(t, relativeTo, layer);
      }
  
      protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
          mSurfaceAnimator.reparent(t, newParent);
      }
  
      void assignChildLayers(Transaction t) {
          int layer = 0;
  
          // We use two passes as a way to promote children which
          // need Z-boosting to the end of the list.
          for (int j = 0; j < mChildren.size(); ++j) {
              final WindowContainer wc = mChildren.get(j);
              wc.assignChildLayers(t);
              if (!wc.needsZBoost()) {
                  wc.assignLayer(t, layer++);
              }
          }
          for (int j = 0; j < mChildren.size(); ++j) {
              final WindowContainer wc = mChildren.get(j);
              if (wc.needsZBoost()) {
                  wc.assignLayer(t, layer++);
              }
          }
      }
  
      void assignChildLayers() {
          assignChildLayers(getPendingTransaction());
          scheduleAnimation();
      }
  
      boolean needsZBoost() {
          for (int i = 0; i < mChildren.size(); i++) {
              if (mChildren.get(i).needsZBoost()) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Write to a protocol buffer output stream. Protocol buffer message definition is at
       * {@link com.android.server.wm.WindowContainerProto}.
       *
       * @param proto     Stream to write the WindowContainer object to.
       * @param fieldId   Field Id of the WindowContainer as defined in the parent message.
       * @param logLevel  Determines the amount of data to be written to the Protobuf.
       * @hide
       */
      @CallSuper
      @Override
      public void writeToProto(ProtoOutputStream proto, long fieldId,
              @WindowTraceLogLevel int logLevel) {
          boolean isVisible = isVisible();
          if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible) {
              return;
          }
  
          final long token = proto.start(fieldId);
          super.writeToProto(proto, CONFIGURATION_CONTAINER, logLevel);
          proto.write(ORIENTATION, mOrientation);
          proto.write(VISIBLE, isVisible);
          if (mSurfaceAnimator.isAnimating()) {
              mSurfaceAnimator.writeToProto(proto, SURFACE_ANIMATOR);
          }
          proto.end(token);
      }
  
      private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) {
          ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire();
          if (wrapper == null) {
              wrapper = new ForAllWindowsConsumerWrapper();
          }
          wrapper.setConsumer(consumer);
          return wrapper;
      }
  
      private final class ForAllWindowsConsumerWrapper implements ToBooleanFunction<WindowState> {
  
          private Consumer<WindowState> mConsumer;
  
          void setConsumer(Consumer<WindowState> consumer) {
              mConsumer = consumer;
          }
  
          @Override
          public boolean apply(WindowState w) {
              mConsumer.accept(w);
              return false;
          }
  
          void release() {
              mConsumer = null;
              mConsumerWrapperPool.release(this);
          }
      }
  
      // TODO(b/68336570): Should this really be on WindowContainer since it
      // can only be used on the top-level nodes that aren't animated?
      // (otherwise we would be fighting other callers of setMatrix).
      void applyMagnificationSpec(Transaction t, MagnificationSpec spec) {
          if (shouldMagnify()) {
              t.setMatrix(mSurfaceControl, spec.scale, 0, 0, spec.scale)
                      .setPosition(mSurfaceControl, spec.offsetX, spec.offsetY);
          } else {
              for (int i = 0; i < mChildren.size(); i++) {
                  mChildren.get(i).applyMagnificationSpec(t, spec);
              }
          }
      }
  
      void prepareSurfaces() {
          // If a leash has been set when the transaction was committed, then the leash reparent has
          // been committed.
          mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
          for (int i = 0; i < mChildren.size(); i++) {
              mChildren.get(i).prepareSurfaces();
          }
      }
  
      /**
       * @return true if the reparent to animation leash transaction has been committed, false
       * otherwise.
       */
      boolean hasCommittedReparentToAnimationLeash() {
          return mCommittedReparentToAnimationLeash;
      }
  
      /**
       * Trigger a call to prepareSurfaces from the animation thread, such that pending transactions
       * will be applied.
       */
      void scheduleAnimation() {
          if (mParent != null) {
              mParent.scheduleAnimation();
          }
      }
  
      /**
       * @return The SurfaceControl for this container.
       *         The SurfaceControl must be valid if non-null.
       */
      @Override
      public SurfaceControl getSurfaceControl() {
          return mSurfaceControl;
      }
  
      @Override
      public Transaction getPendingTransaction() {
          final DisplayContent displayContent = getDisplayContent();
          if (displayContent != null && displayContent != this) {
              return displayContent.getPendingTransaction();
          }
          // This WindowContainer has not attached to a display yet or this is a DisplayContent, so we
          // let the caller to save the surface operations within the local mPendingTransaction.
          // If this is not a DisplayContent, we will merge it to the pending transaction of its
          // display once it attaches to it.
          return mPendingTransaction;
      }
  
      /**
       * Starts an animation on the container.
       *
       * @param anim The animation to run.
       * @param hidden Whether our container is currently hidden. TODO This should use isVisible at
       *               some point but the meaning is too weird to work for all containers.
       */
      void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
          if (DEBUG_ANIM) Slog.v(TAG, "Starting animation on " + this + ": " + anim);
  
          // TODO: This should use isVisible() but because isVisible has a really weird meaning at
          // the moment this doesn't work for all animatable window containers.
          mSurfaceAnimator.startAnimation(t, anim, hidden);
      }
  
      void transferAnimation(WindowContainer from) {
          mSurfaceAnimator.transferAnimation(from.mSurfaceAnimator);
      }
  
      void cancelAnimation() {
          mSurfaceAnimator.cancelAnimation();
      }
  
      @Override
      public Builder makeAnimationLeash() {
          return makeSurface();
      }
  
      @Override
      public SurfaceControl getAnimationLeashParent() {
          return getParentSurfaceControl();
      }
  
      /**
       * @return The layer on which all app animations are happening.
       */
      SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
          final WindowContainer parent = getParent();
          if (parent != null) {
              return parent.getAppAnimationLayer(animationLayer);
          }
          return null;
      }
  
      @Override
      public void commitPendingTransaction() {
          scheduleAnimation();
      }
  
      void reassignLayer(Transaction t) {
          final WindowContainer parent = getParent();
          if (parent != null) {
              parent.assignChildLayers(t);
          }
      }
  
      @Override
      public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
          mLastLayer = -1;
          reassignLayer(t);
      }
  
      @Override
      public void onAnimationLeashLost(Transaction t) {
          mLastLayer = -1;
          reassignLayer(t);
      }
  
      /**
       * Called when an animation has finished running.
       */
      protected void onAnimationFinished() {
          mWmService.onAnimationFinished();
      }
  
      /**
       * @return The currently running animation, if any, or {@code null} otherwise.
       */
      AnimationAdapter getAnimation() {
          return mSurfaceAnimator.getAnimation();
      }
  
      /**
       * @see SurfaceAnimator#startDelayingAnimationStart
       */
      void startDelayingAnimationStart() {
          mSurfaceAnimator.startDelayingAnimationStart();
      }
  
      /**
       * @see SurfaceAnimator#endDelayingAnimationStart
       */
      void endDelayingAnimationStart() {
          mSurfaceAnimator.endDelayingAnimationStart();
      }
  
      @Override
      public int getSurfaceWidth() {
          return mSurfaceControl.getWidth();
      }
  
      @Override
      public int getSurfaceHeight() {
          return mSurfaceControl.getHeight();
      }
  
      @CallSuper
      void dump(PrintWriter pw, String prefix, boolean dumpAll) {
          if (mSurfaceAnimator.isAnimating()) {
              pw.print(prefix); pw.println("ContainerAnimator:");
              mSurfaceAnimator.dump(pw, prefix + "  ");
          }
      }
  
      void updateSurfacePosition() {
          if (mSurfaceControl == null) {
              return;
          }
  
          getRelativeDisplayedPosition(mTmpPos);
          if (mTmpPos.equals(mLastSurfacePosition)) {
              return;
          }
  
          getPendingTransaction().setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
          mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
      }
  
      @VisibleForTesting
      Point getLastSurfacePosition() {
          return mLastSurfacePosition;
      }
  
      /**
       * Displayed bounds specify where to display this container at. It differs from bounds during
       * certain operations (like animation or interactive dragging).
       *
       * @return the bounds to display this container at.
       */
      Rect getDisplayedBounds() {
          return getBounds();
      }
  
      void getRelativeDisplayedPosition(Point outPos) {
          final Rect dispBounds = getDisplayedBounds();
          outPos.set(dispBounds.left, dispBounds.top);
          final WindowContainer parent = getParent();
          if (parent != null) {
              final Rect parentBounds = parent.getDisplayedBounds();
              outPos.offset(-parentBounds.left, -parentBounds.top);
          }
      }
  
      Dimmer getDimmer() {
          if (mParent == null) {
              return null;
          }
          return mParent.getDimmer();
      }
  }
  