content: /*
   * Copyright (C) 2017 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.app.ActivityTaskManager.INVALID_STACK_ID;
  import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
  import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
  import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
  import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
  import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
  import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
  import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
  import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
  import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
  import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
  import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
  import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
  import static android.view.Display.DEFAULT_DISPLAY;
  import static android.view.Display.FLAG_PRIVATE;
  import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
  
  import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
  import static com.android.server.am.ActivityDisplayProto.FOCUSED_STACK_ID;
  import static com.android.server.am.ActivityDisplayProto.ID;
  import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
  import static com.android.server.am.ActivityDisplayProto.SINGLE_TASK_INSTANCE;
  import static com.android.server.am.ActivityDisplayProto.STACKS;
  import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
  import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
  import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
  import static com.android.server.wm.RootActivityContainer.FindTaskResult;
  import static com.android.server.wm.RootActivityContainer.TAG_STATES;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
  import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
  import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
  
  import android.annotation.Nullable;
  import android.app.ActivityOptions;
  import android.app.WindowConfiguration;
  import android.content.res.Configuration;
  import android.graphics.Point;
  import android.os.IBinder;
  import android.os.UserHandle;
  import android.util.IntArray;
  import android.util.Slog;
  import android.util.proto.ProtoOutputStream;
  import android.view.Display;
  
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.server.am.EventLogTags;
  
  import java.io.PrintWriter;
  import java.util.ArrayList;
  
  /**
   * Exactly one of these classes per Display in the system. Capable of holding zero or more
   * attached {@link ActivityStack}s.
   */
  class ActivityDisplay extends ConfigurationContainer<ActivityStack>
          implements WindowContainerListener {
      private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_ATM;
      private static final String TAG_STACK = TAG + POSTFIX_STACK;
  
      static final int POSITION_TOP = Integer.MAX_VALUE;
      static final int POSITION_BOTTOM = Integer.MIN_VALUE;
  
  
      /**
       * Counter for next free stack ID to use for dynamic activity stacks. Unique across displays.
       */
      private static int sNextFreeStackId = 0;
  
      private ActivityTaskManagerService mService;
      private RootActivityContainer mRootActivityContainer;
      // TODO: Remove once unification is complete.
      DisplayContent mDisplayContent;
      /** Actual Display this object tracks. */
      int mDisplayId;
      Display mDisplay;
  
      /**
       * All of the stacks on this display. Order matters, topmost stack is in front of all other
       * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
       * changing the list should also call {@link #onStackOrderChanged()}.
       */
      private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
      private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
  
      /** Array of all UIDs that are present on the display. */
      private IntArray mDisplayAccessUIDs = new IntArray();
  
      /** All tokens used to put activities on this stack to sleep (including mOffToken) */
      final ArrayList<ActivityTaskManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
      /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
      ActivityTaskManagerInternal.SleepToken mOffToken;
  
      private boolean mSleeping;
  
      /**
       * The display is removed from the system and we are just waiting for all activities on it to be
       * finished before removing this object.
       */
      private boolean mRemoved;
  
      /** The display can only contain one task. */
      private boolean mSingleTaskInstance;
  
      /**
       * Non-null if the last size compatibility mode activity is using non-native screen
       * configuration. The activity is not able to put in multi-window mode, so it exists only one
       * per display.
       */
      private ActivityRecord mLastCompatModeActivity;
  
      /**
       * A focusable stack that is purposely to be positioned at the top. Although the stack may not
       * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
       * target stack properly when there are other focusable always-on-top stacks.
       */
      private ActivityStack mPreferredTopFocusableStack;
  
      /**
       * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
       * stack has been resumed. If stacks are changing position this will hold the old stack until
       * the new stack becomes resumed after which it will be set to current focused stack.
       */
      private ActivityStack mLastFocusedStack;
  
      // Cached reference to some special stacks we tend to get a lot so we don't need to loop
      // through the list to find them.
      private ActivityStack mHomeStack = null;
      private ActivityStack mRecentsStack = null;
      private ActivityStack mPinnedStack = null;
      private ActivityStack mSplitScreenPrimaryStack = null;
  
      // Used in updating the display size
      private Point mTmpDisplaySize = new Point();
  
      private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
  
      ActivityDisplay(RootActivityContainer root, Display display) {
          mRootActivityContainer = root;
          mService = root.mService;
          mDisplayId = display.getDisplayId();
          mDisplay = display;
          mDisplayContent = createDisplayContent();
          updateBounds();
      }
  
      protected DisplayContent createDisplayContent() {
          return mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
      }
  
      private void updateBounds() {
          mDisplay.getRealSize(mTmpDisplaySize);
          setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
      }
  
      void onDisplayChanged() {
          // The window policy is responsible for stopping activities on the default display.
          final int displayId = mDisplay.getDisplayId();
          if (displayId != DEFAULT_DISPLAY) {
              final int displayState = mDisplay.getState();
              if (displayState == Display.STATE_OFF && mOffToken == null) {
                  mOffToken = mService.acquireSleepToken("Display-off", displayId);
              } else if (displayState == Display.STATE_ON && mOffToken != null) {
                  mOffToken.release();
                  mOffToken = null;
              }
          }
  
          updateBounds();
          if (mDisplayContent != null) {
              mDisplayContent.updateDisplayInfo();
              mService.mWindowManager.requestTraversal();
          }
      }
  
      @Override
      public void onInitializeOverrideConfiguration(Configuration config) {
          getRequestedOverrideConfiguration().updateFrom(config);
      }
  
      void addChild(ActivityStack stack, int position) {
          if (position == POSITION_BOTTOM) {
              position = 0;
          } else if (position == POSITION_TOP) {
              position = mStacks.size();
          }
          if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
                  + " to displayId=" + mDisplayId + " position=" + position);
          addStackReferenceIfNeeded(stack);
          positionChildAt(stack, position);
          mService.updateSleepIfNeededLocked();
      }
  
      void removeChild(ActivityStack stack) {
          if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
                  + " from displayId=" + mDisplayId);
          mStacks.remove(stack);
          if (mPreferredTopFocusableStack == stack) {
              mPreferredTopFocusableStack = null;
          }
          removeStackReferenceIfNeeded(stack);
          releaseSelfIfNeeded();
          mService.updateSleepIfNeededLocked();
          onStackOrderChanged(stack);
      }
  
      void positionChildAtTop(ActivityStack stack, boolean includingParents) {
          positionChildAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
      }
  
      void positionChildAtTop(ActivityStack stack, boolean includingParents,
              String updateLastFocusedStackReason) {
          positionChildAt(stack, mStacks.size(), includingParents, updateLastFocusedStackReason);
      }
  
      void positionChildAtBottom(ActivityStack stack) {
          positionChildAtBottom(stack, null /* updateLastFocusedStackReason */);
      }
  
      void positionChildAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
          positionChildAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
      }
  
      private void positionChildAt(ActivityStack stack, int position) {
          positionChildAt(stack, position, false /* includingParents */,
                  null /* updateLastFocusedStackReason */);
      }
  
      private void positionChildAt(ActivityStack stack, int position, boolean includingParents,
              String updateLastFocusedStackReason) {
          // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
          //       the position internally, also update the logic here
          final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
                  ? getFocusedStack() : null;
          final boolean wasContained = mStacks.remove(stack);
          if (mSingleTaskInstance && getChildCount() > 0) {
              throw new IllegalStateException(
                      "positionChildAt: Can only have one child on display=" + this);
          }
          final int insertPosition = getTopInsertPosition(stack, position);
          mStacks.add(insertPosition, stack);
  
          // The insert position may be adjusted to non-top when there is always-on-top stack. Since
          // the original position is preferred to be top, the stack should have higher priority when
          // we are looking for top focusable stack. The condition {@code wasContained} restricts the
          // preferred stack is set only when moving an existing stack to top instead of adding a new
          // stack that may be too early (e.g. in the middle of launching or reparenting).
          if (wasContained && position >= mStacks.size() - 1 && stack.isFocusableAndVisible()) {
              mPreferredTopFocusableStack = stack;
          } else if (mPreferredTopFocusableStack == stack) {
              mPreferredTopFocusableStack = null;
          }
  
          if (updateLastFocusedStackReason != null) {
              final ActivityStack currentFocusedStack = getFocusedStack();
              if (currentFocusedStack != prevFocusedStack) {
                  mLastFocusedStack = prevFocusedStack;
                  EventLogTags.writeAmFocusedStack(mRootActivityContainer.mCurrentUser, mDisplayId,
                          currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
                          mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
                          updateLastFocusedStackReason);
              }
          }
  
          // Since positionChildAt() is called during the creation process of pinned stacks,
          // ActivityStack#getStack() can be null. In this special case,
          // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
          // we don't have to call WindowContainerController#positionChildAt() here.
          if (stack.getTaskStack() != null && mDisplayContent != null) {
              mDisplayContent.positionStackAt(insertPosition,
                      stack.getTaskStack(), includingParents);
          }
          if (!wasContained) {
              stack.setParent(this);
          }
          onStackOrderChanged(stack);
      }
  
      private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
          int position = mStacks.size();
          if (stack.inPinnedWindowingMode()) {
              // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
              // just return the candidate position.
              return Math.min(position, candidatePosition);
          }
          while (position > 0) {
              final ActivityStack targetStack = mStacks.get(position - 1);
              if (!targetStack.isAlwaysOnTop()) {
                  // We reached a stack that isn't always-on-top.
                  break;
              }
              if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
                  // Always on-top non-pinned windowing mode stacks can go anywhere below pinned stack.
                  break;
              }
              position--;
          }
          return Math.min(position, candidatePosition);
      }
  
      <T extends ActivityStack> T getStack(int stackId) {
          for (int i = mStacks.size() - 1; i >= 0; --i) {
              final ActivityStack stack = mStacks.get(i);
              if (stack.mStackId == stackId) {
                  return (T) stack;
              }
          }
          return null;
      }
  
      /**
       * @return the topmost stack on the display that is compatible with the input windowing mode and
       * activity type. {@code null} means no compatible stack on the display.
       * @see ConfigurationContainer#isCompatible(int, int)
       */
      <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
          if (activityType == ACTIVITY_TYPE_HOME) {
              return (T) mHomeStack;
          } else if (activityType == ACTIVITY_TYPE_RECENTS) {
              return (T) mRecentsStack;
          }
          if (windowingMode == WINDOWING_MODE_PINNED) {
              return (T) mPinnedStack;
          } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
              return (T) mSplitScreenPrimaryStack;
          }
  
          for (int i = mStacks.size() - 1; i >= 0; --i) {
              final ActivityStack stack = mStacks.get(i);
              if (stack.isCompatible(windowingMode, activityType)) {
                  return (T) stack;
              }
          }
          return null;
      }
  
      private boolean alwaysCreateStack(int windowingMode, int activityType) {
          // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
          // modes so that we can manage visual ordering and return types correctly.
          return activityType == ACTIVITY_TYPE_STANDARD
                  && (windowingMode == WINDOWING_MODE_FULLSCREEN
                  || windowingMode == WINDOWING_MODE_FREEFORM
                  || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
      }
  
      /**
       * Returns an existing stack compatible with the windowing mode and activity type or creates one
       * if a compatible stack doesn't exist.
       * @see #getStack(int, int)
       * @see #createStack(int, int, boolean)
       */
      <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
              boolean onTop) {
          if (!alwaysCreateStack(windowingMode, activityType)) {
              T stack = getStack(windowingMode, activityType);
              if (stack != null) {
                  return stack;
              }
          }
          return createStack(windowingMode, activityType, onTop);
      }
  
      /**
       * Returns an existing stack compatible with the input params or creates one
       * if a compatible stack doesn't exist.
       * @see #getOrCreateStack(int, int, boolean)
       */
      <T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
              @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, int activityType,
              boolean onTop) {
          // First preference is the windowing mode in the activity options if set.
          int windowingMode = (options != null)
                  ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
          // Validate that our desired windowingMode will work under the current conditions.
          // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
          // it's display's windowing mode.
          windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
          return getOrCreateStack(windowingMode, activityType, onTop);
      }
  
      @VisibleForTesting
      int getNextStackId() {
          return sNextFreeStackId++;
      }
  
      /**
       * Creates a stack matching the input windowing mode and activity type on this display.
       * @param windowingMode The windowing mode the stack should be created in. If
       *                      {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
       *                      inherit it's parent's windowing mode.
       * @param activityType The activityType the stack should be created in. If
       *                     {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
       *                     be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
       * @param onTop If true the stack will be created at the top of the display, else at the bottom.
       * @return The newly created stack.
       */
      <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
  
          if (mSingleTaskInstance && getChildCount() > 0) {
              // Create stack on default display instead since this display can only contain 1 stack.
              // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
              // this goes away once ActivityView is no longer using virtual displays.
              return mRootActivityContainer.getDefaultDisplay().createStack(
                      windowingMode, activityType, onTop);
          }
  
          if (activityType == ACTIVITY_TYPE_UNDEFINED) {
              // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
              // anything else should be passing it in anyways...
              activityType = ACTIVITY_TYPE_STANDARD;
          }
  
          if (activityType != ACTIVITY_TYPE_STANDARD) {
              // For now there can be only one stack of a particular non-standard activity type on a
              // display. So, get that ignoring whatever windowing mode it is currently in.
              T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
              if (stack != null) {
                  throw new IllegalArgumentException("Stack=" + stack + " of activityType="
                          + activityType + " already on display=" + this + ". Can't have multiple.");
              }
          }
  
          if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow,
                  mService.mSupportsSplitScreenMultiWindow,
                  mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture,
                  activityType)) {
              throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
                      + windowingMode);
          }
  
          final int stackId = getNextStackId();
          return createStackUnchecked(windowingMode, activityType, stackId, onTop);
      }
  
      @VisibleForTesting
      <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
              int stackId, boolean onTop) {
          if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
              throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
                      + "activity type.");
          }
          return (T) new ActivityStack(this, stackId,
                  mRootActivityContainer.mStackSupervisor, windowingMode, activityType, onTop);
      }
  
      /**
       * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
       * focusable and visible stack from the top of stacks in this display.
       */
      ActivityStack getFocusedStack() {
          if (mPreferredTopFocusableStack != null) {
              return mPreferredTopFocusableStack;
          }
  
          for (int i = mStacks.size() - 1; i >= 0; --i) {
              final ActivityStack stack = mStacks.get(i);
              if (stack.isFocusableAndVisible()) {
                  return stack;
              }
          }
  
          return null;
      }
  
      ActivityStack getNextFocusableStack() {
          return getNextFocusableStack(null /* currentFocus */, false /* ignoreCurrent */);
      }
  
      ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
          final int currentWindowingMode = currentFocus != null
                  ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
  
          ActivityStack candidate = null;
          for (int i = mStacks.size() - 1; i >= 0; --i) {
              final ActivityStack stack = mStacks.get(i);
              if (ignoreCurrent && stack == currentFocus) {
                  continue;
              }
              if (!stack.isFocusableAndVisible()) {
                  continue;
              }
  
              if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                      && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
                  // If the currently focused stack is in split-screen secondary we save off the
                  // top primary split-screen stack as a candidate for focus because we might
                  // prefer focus to move to an other stack to avoid primary split-screen stack
                  // overlapping with a fullscreen stack when a fullscreen stack is higher in z
                  // than the next split-screen stack. Assistant stack, I am looking at you...
                  // We only move the focus to the primary-split screen stack if there isn't a
                  // better alternative.
                  candidate = stack;
                  continue;
              }
              if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
                  // Use the candidate stack since we are now at the secondary split-screen.
                  return candidate;
              }
              return stack;
          }
          return candidate;
      }
  
      ActivityRecord getResumedActivity() {
          final ActivityStack focusedStack = getFocusedStack();
          if (focusedStack == null) {
              return null;
          }
          // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
          // Check if the focused stack has the resumed activity
          ActivityRecord resumedActivity = focusedStack.getResumedActivity();
          if (resumedActivity == null || resumedActivity.app == null) {
              // If there is no registered resumed activity in the stack or it is not running -
              // try to use previously resumed one.
              resumedActivity = focusedStack.mPausingActivity;
              if (resumedActivity == null || resumedActivity.app == null) {
                  // If previously resumed activity doesn't work either - find the topmost running
                  // activity that can be focused.
                  resumedActivity = focusedStack.topRunningActivityLocked(true /* focusableOnly */);
              }
          }
          return resumedActivity;
      }
  
      ActivityStack getLastFocusedStack() {
          return mLastFocusedStack;
      }
  
      boolean allResumedActivitiesComplete() {
          for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
              final ActivityRecord r = mStacks.get(stackNdx).getResumedActivity();
              if (r != null && !r.isState(RESUMED)) {
                  return false;
              }
          }
          final ActivityStack currentFocusedStack = getFocusedStack();
          if (DEBUG_STACK) {
              Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
                      + mLastFocusedStack + " to=" + currentFocusedStack);
          }
          mLastFocusedStack = currentFocusedStack;
          return true;
      }
  
      /**
       * Pause all activities in either all of the stacks or just the back stacks. This is done before
       * resuming a new activity and to make sure that previously active activities are
       * paused in stacks that are no longer visible or in pinned windowing mode. This does not
       * pause activities in visible stacks, so if an activity is launched within the same stack/task,
       * then we should explicitly pause that stack's top activity.
       * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
       * @param resuming The resuming activity.
       * @param dontWait The resuming activity isn't going to wait for all activities to be paused
       *                 before resuming.
       * @return {@code true} if any activity was paused as a result of this call.
       */
      boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
          boolean someActivityPaused = false;
          for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
              final ActivityStack stack = mStacks.get(stackNdx);
              final ActivityRecord resumedActivity = stack.getResumedActivity();
              if (resumedActivity != null
                      && (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE
                          || !stack.isFocusable())) {
                  if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
                          " mResumedActivity=" + resumedActivity);
                  someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
                          dontWait);
              }
          }
          return someActivityPaused;
      }
  
      /**
       * Find task for putting the Activity in.
       */
      void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
              FindTaskResult result) {
          mTmpFindTaskResult.clear();
          for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
              final ActivityStack stack = getChildAt(stackNdx);
              if (!r.hasCompatibleActivityType(stack)) {
                  if (DEBUG_TASKS) {
                      Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
                  }
                  continue;
              }
  
              stack.findTaskLocked(r, mTmpFindTaskResult);
              // It is possible to have tasks in multiple stacks with the same root affinity, so
              // we should keep looking after finding an affinity match to see if there is a
              // better match in another stack. Also, task affinity isn't a good enough reason
              // to target a display which isn't the source of the intent, so skip any affinity
              // matches not on the specified display.
              if (mTmpFindTaskResult.mRecord != null) {
                  if (mTmpFindTaskResult.mIdealMatch) {
                      result.setTo(mTmpFindTaskResult);
                      return;
                  } else if (isPreferredDisplay) {
                      // Note: since the traversing through the stacks is top down, the floating
                      // tasks should always have lower priority than any affinity-matching tasks
                      // in the fullscreen stacks
                      result.setTo(mTmpFindTaskResult);
                  }
              }
          }
      }
  
      /**
       * Removes stacks in the input windowing modes from the system if they are of activity type
       * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
       */
      void removeStacksInWindowingModes(int... windowingModes) {
          if (windowingModes == null || windowingModes.length == 0) {
              return;
          }
  
          // Collect the stacks that are necessary to be removed instead of performing the removal
          // by looping mStacks, so that we don't miss any stacks after the stack size changed or
          // stacks reordered.
          final ArrayList<ActivityStack> stacks = new ArrayList<>();
          for (int j = windowingModes.length - 1 ; j >= 0; --j) {
              final int windowingMode = windowingModes[j];
              for (int i = mStacks.size() - 1; i >= 0; --i) {
                  final ActivityStack stack = mStacks.get(i);
                  if (!stack.isActivityTypeStandardOrUndefined()) {
                      continue;
                  }
                  if (stack.getWindowingMode() != windowingMode) {
                      continue;
                  }
                  stacks.add(stack);
              }
          }
  
          for (int i = stacks.size() - 1; i >= 0; --i) {
              mRootActivityContainer.mStackSupervisor.removeStack(stacks.get(i));
          }
      }
  
      void removeStacksWithActivityTypes(int... activityTypes) {
          if (activityTypes == null || activityTypes.length == 0) {
              return;
          }
  
          // Collect the stacks that are necessary to be removed instead of performing the removal
          // by looping mStacks, so that we don't miss any stacks after the stack size changed or
          // stacks reordered.
          final ArrayList<ActivityStack> stacks = new ArrayList<>();
          for (int j = activityTypes.length - 1 ; j >= 0; --j) {
              final int activityType = activityTypes[j];
              for (int i = mStacks.size() - 1; i >= 0; --i) {
                  final ActivityStack stack = mStacks.get(i);
                  if (stack.getActivityType() == activityType) {
                      stacks.add(stack);
                  }
              }
          }
  
          for (int i = stacks.size() - 1; i >= 0; --i) {
              mRootActivityContainer.mStackSupervisor.removeStack(stacks.get(i));
          }
      }
  
      void onStackWindowingModeChanged(ActivityStack stack) {
          removeStackReferenceIfNeeded(stack);
          addStackReferenceIfNeeded(stack);
      }
  
      private void addStackReferenceIfNeeded(ActivityStack stack) {
          final int activityType = stack.getActivityType();
          final int windowingMode = stack.getWindowingMode();
  
          if (activityType == ACTIVITY_TYPE_HOME) {
              if (mHomeStack != null && mHomeStack != stack) {
                  throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
                          + mHomeStack + " already exist on display=" + this + " stack=" + stack);
              }
              mHomeStack = stack;
          } else if (activityType == ACTIVITY_TYPE_RECENTS) {
              if (mRecentsStack != null && mRecentsStack != stack) {
                  throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
                          + mRecentsStack + " already exist on display=" + this + " stack=" + stack);
              }
              mRecentsStack = stack;
          }
          if (windowingMode == WINDOWING_MODE_PINNED) {
              if (mPinnedStack != null && mPinnedStack != stack) {
                  throw new IllegalArgumentException("addStackReferenceIfNeeded: pinned stack="
                          + mPinnedStack + " already exist on display=" + this
                          + " stack=" + stack);
              }
              mPinnedStack = stack;
          } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
              if (mSplitScreenPrimaryStack != null && mSplitScreenPrimaryStack != stack) {
                  throw new IllegalArgumentException("addStackReferenceIfNeeded:"
                          + " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
                          + " already exist on display=" + this + " stack=" + stack);
              }
              mSplitScreenPrimaryStack = stack;
              onSplitScreenModeActivated();
          }
      }
  
      private void removeStackReferenceIfNeeded(ActivityStack stack) {
          if (stack == mHomeStack) {
              mHomeStack = null;
          } else if (stack == mRecentsStack) {
              mRecentsStack = null;
          } else if (stack == mPinnedStack) {
              mPinnedStack = null;
          } else if (stack == mSplitScreenPrimaryStack) {
              mSplitScreenPrimaryStack = null;
              // Inform the reset of the system that split-screen mode was dismissed so things like
              // resizing all the other stacks can take place.
              onSplitScreenModeDismissed();
          }
      }
  
      private void onSplitScreenModeDismissed() {
          mRootActivityContainer.mWindowManager.deferSurfaceLayout();
          try {
              // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
              for (int i = mStacks.size() - 1; i >= 0; --i) {
                  final ActivityStack otherStack = mStacks.get(i);
                  if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
                      continue;
                  }
                  otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED, false /* animate */,
                          false /* showRecents */, false /* enteringSplitScreenMode */,
                          true /* deferEnsuringVisibility */, false /* creating */);
              }
          } finally {
              final ActivityStack topFullscreenStack =
                      getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
              if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
                  // Whenever split-screen is dismissed we want the home stack directly behind the
                  // current top fullscreen stack so it shows up when the top stack is finished.
                  // TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
                  // ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
                  // once we have that.
                  mHomeStack.moveToFront("onSplitScreenModeDismissed");
                  topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
              }
              mRootActivityContainer.mWindowManager.continueSurfaceLayout();
          }
      }
  
      private void onSplitScreenModeActivated() {
          mRootActivityContainer.mWindowManager.deferSurfaceLayout();
          try {
              // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
              for (int i = mStacks.size() - 1; i >= 0; --i) {
                  final ActivityStack otherStack = mStacks.get(i);
                  if (otherStack == mSplitScreenPrimaryStack
                          || !otherStack.affectedBySplitScreenResize()) {
                      continue;
                  }
                  otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
                          false /* animate */, false /* showRecents */,
                          true /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
                          false /* creating */);
              }
          } finally {
              mRootActivityContainer.mWindowManager.continueSurfaceLayout();
          }
      }
  
      /**
       * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
       * @param windowingMode The windowing mode we are checking support for.
       * @param supportsMultiWindow If we should consider support for multi-window mode in general.
       * @param supportsSplitScreen If we should consider support for split-screen multi-window.
       * @param supportsFreeform If we should consider support for freeform multi-window.
       * @param supportsPip If we should consider support for picture-in-picture mutli-window.
       * @param activityType The activity type under consideration.
       * @return true if the windowing mode is supported.
       */
      private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
              boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
              int activityType) {
  
          if (windowingMode == WINDOWING_MODE_UNDEFINED
                  || windowingMode == WINDOWING_MODE_FULLSCREEN) {
              return true;
          }
          if (!supportsMultiWindow) {
              return false;
          }
  
          final int displayWindowingMode = getWindowingMode();
          if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                  || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
              return supportsSplitScreen
                      && WindowConfiguration.supportSplitScreenWindowingMode(activityType)
                      // Freeform windows and split-screen windows don't mix well, so prevent
                      // split windowing modes on freeform displays.
                      && displayWindowingMode != WINDOWING_MODE_FREEFORM;
          }
  
          if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
              return false;
          }
  
          if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
              return false;
          }
          return true;
      }
  
      /**
       * Resolves the windowing mode that an {@link ActivityRecord} would be in if started on this
       * display with the provided parameters.
       *
       * @param r The ActivityRecord in question.
       * @param options Options to start with.
       * @param task The task within-which the activity would start.
       * @param activityType The type of activity to start.
       * @return The resolved (not UNDEFINED) windowing-mode that the activity would be in.
       */
      int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
              @Nullable TaskRecord task, int activityType) {
  
          // First preference if the windowing mode in the activity options if set.
          int windowingMode = (options != null)
                  ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
  
          // If windowing mode is unset, then next preference is the candidate task, then the
          // activity record.
          if (windowingMode == WINDOWING_MODE_UNDEFINED) {
              if (task != null) {
                  windowingMode = task.getWindowingMode();
              }
              if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
                  windowingMode = r.getWindowingMode();
              }
              if (windowingMode == WINDOWING_MODE_UNDEFINED) {
                  // Use the display's windowing mode.
                  windowingMode = getWindowingMode();
              }
          }
          windowingMode = validateWindowingMode(windowingMode, r, task, activityType);
          return windowingMode != WINDOWING_MODE_UNDEFINED
                  ? windowingMode : WINDOWING_MODE_FULLSCREEN;
      }
  
      /**
       * Check that the requested windowing-mode is appropriate for the specified task and/or activity
       * on this display.
       *
       * @param windowingMode The windowing-mode to validate.
       * @param r The {@link ActivityRecord} to check against.
       * @param task The {@link TaskRecord} to check against.
       * @param activityType An activity type.
       * @return The provided windowingMode or the closest valid mode which is appropriate.
       */
      int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
          @Nullable TaskRecord task, int activityType) {
          // Make sure the windowing mode we are trying to use makes sense for what is supported.
          boolean supportsMultiWindow = mService.mSupportsMultiWindow;
          boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
          boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
          boolean supportsPip = mService.mSupportsPictureInPicture;
          if (supportsMultiWindow) {
              if (task != null) {
                  supportsMultiWindow = task.isResizeable();
                  supportsSplitScreen = task.supportsSplitScreenWindowingMode();
                  // TODO: Do we need to check for freeform and Pip support here?
              } else if (r != null) {
                  supportsMultiWindow = r.isResizeable();
                  supportsSplitScreen = r.supportsSplitScreenWindowingMode();
                  supportsFreeform = r.supportsFreeform();
                  supportsPip = r.supportsPictureInPicture();
              }
          }
  
          final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
          if (!inSplitScreenMode
                  && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
              // Switch to the display's windowing mode if we are not in split-screen mode and we are
              // trying to launch in split-screen secondary.
              windowingMode = WINDOWING_MODE_UNDEFINED;
          } else if (inSplitScreenMode && (windowingMode == WINDOWING_MODE_FULLSCREEN
                          || windowingMode == WINDOWING_MODE_UNDEFINED)
                  && supportsSplitScreen) {
              windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
          }
  
          if (windowingMode != WINDOWING_MODE_UNDEFINED
                  && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
                          supportsFreeform, supportsPip, activityType)) {
              return windowingMode;
          }
          return WINDOWING_MODE_UNDEFINED;
      }
  
      /**
       * Get the topmost stack on the display. It may be different from focused stack, because
       * some stacks are not focusable (e.g. PiP).
       */
      ActivityStack getTopStack() {
          return mStacks.isEmpty() ? null : mStacks.get(mStacks.size() - 1);
      }
  
      boolean isTopStack(ActivityStack stack) {
          return stack == getTopStack();
      }
  
      boolean isTopNotPinnedStack(ActivityStack stack) {
          for (int i = mStacks.size() - 1; i >= 0; --i) {
              final ActivityStack current = mStacks.get(i);
              if (!current.inPinnedWindowingMode()) {
                  return current == stack;
              }
          }
          return false;
      }
  
      ActivityStack getTopStackInWindowingMode(int windowingMode) {
          for (int i = mStacks.size() - 1; i >= 0; --i) {
              final ActivityStack current = mStacks.get(i);
              if (windowingMode == current.getWindowingMode()) {
                  return current;
              }
          }
          return null;
      }
  
      ActivityRecord topRunningActivity() {
          return topRunningActivity(false /* considerKeyguardState */);
      }
  
      /**
       * Returns the top running activity in the focused stack. In the case the focused stack has no
       * such activity, the next focusable stack on this display is returned.
       *
       * @param considerKeyguardState Indicates whether the locked state should be considered. if
       *                              {@code true} and the keyguard is locked, only activities that
       *                              can be shown on top of the keyguard will be considered.
       * @return The top running activity. {@code null} if none is available.
       */
      ActivityRecord topRunningActivity(boolean considerKeyguardState) {
          ActivityRecord topRunning = null;
          final ActivityStack focusedStack = getFocusedStack();
          if (focusedStack != null) {
              topRunning = focusedStack.topRunningActivityLocked();
          }
  
          // Look in other focusable stacks.
          if (topRunning == null) {
              for (int i = mStacks.size() - 1; i >= 0; --i) {
                  final ActivityStack stack = mStacks.get(i);
                  // Only consider focusable stacks other than the current focused one.
                  if (stack == focusedStack || !stack.isFocusable()) {
                      continue;
                  }
                  topRunning = stack.topRunningActivityLocked();
                  if (topRunning != null) {
                      break;
                  }
              }
          }
  
          // This activity can be considered the top running activity if we are not considering
          // the locked state, the keyguard isn't locked, or we can show when locked.
          if (topRunning != null && considerKeyguardState
                  && mRootActivityContainer.mStackSupervisor.getKeyguardController().isKeyguardLocked()
                  && !topRunning.canShowWhenLocked()) {
              return null;
          }
  
          return topRunning;
      }
  
      int getIndexOf(ActivityStack stack) {
          return mStacks.indexOf(stack);
      }
  
      @Override
      public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
          final int currRotation =
                  getRequestedOverrideConfiguration().windowConfiguration.getRotation();
          if (currRotation != ROTATION_UNDEFINED
                  && currRotation != overrideConfiguration.windowConfiguration.getRotation()
                  && mDisplayContent != null) {
              mDisplayContent.applyRotationLocked(currRotation,
                      overrideConfiguration.windowConfiguration.getRotation());
          }
          super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
          if (mDisplayContent != null) {
              mService.mWindowManager.setNewDisplayOverrideConfiguration(
                      overrideConfiguration, mDisplayContent);
          }
      }
  
      @Override
      public void onConfigurationChanged(Configuration newParentConfig) {
          // update resources before cascade so that docked/pinned stacks use the correct info
          if (mDisplayContent != null) {
              mDisplayContent.preOnConfigurationChanged();
          }
          super.onConfigurationChanged(newParentConfig);
      }
  
      void onLockTaskPackagesUpdated() {
          for (int i = mStacks.size() - 1; i >= 0; --i) {
              mStacks.get(i).onLockTaskPackagesUpdated();
          }
      }
  
      /** We are in the process of exiting split-screen mode. */
      void onExitingSplitScreenMode() {
          // Remove reference to the primary-split-screen stack so it no longer has any effect on the
          // display. For example, we want to be able to create fullscreen stack for standard activity
          // types when exiting split-screen mode.
          mSplitScreenPrimaryStack = null;
      }
  
      /** Checks whether the given activity is in size compatibility mode and notifies the change. */
      void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
          if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
              // The callback is only interested in the foreground changes of fullscreen activity.
              return;
          }
          if (!r.inSizeCompatMode()) {
              if (mLastCompatModeActivity != null) {
                  mService.getTaskChangeNotificationController()
                          .notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
              }
              mLastCompatModeActivity = null;
              return;
          }
          if (mLastCompatModeActivity == r) {
              return;
          }
          mLastCompatModeActivity = r;
          mService.getTaskChangeNotificationController()
                  .notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
      }
  
      ActivityStack getSplitScreenPrimaryStack() {
          return mSplitScreenPrimaryStack;
      }
  
      boolean hasSplitScreenPrimaryStack() {
          return mSplitScreenPrimaryStack != null;
      }
  
      ActivityStack getPinnedStack() {
          return mPinnedStack;
      }
  
      boolean hasPinnedStack() {
          return mPinnedStack != null;
      }
  
      @Override
      public String toString() {
          return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
      }
  
      @Override
      protected int getChildCount() {
          return mStacks.size();
      }
  
      @Override
      protected ActivityStack getChildAt(int index) {
          return mStacks.get(index);
      }
  
      @Override
      protected ConfigurationContainer getParent() {
          return mRootActivityContainer;
      }
  
      boolean isPrivate() {
          return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
      }
  
      boolean isUidPresent(int uid) {
          for (ActivityStack stack : mStacks) {
              if (stack.isUidPresent(uid)) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * @see #mRemoved
       */
      boolean isRemoved() {
          return mRemoved;
      }
  
      void remove() {
          final boolean destroyContentOnRemoval = shouldDestroyContentOnRemove();
          ActivityStack lastReparentedStack = null;
          mPreferredTopFocusableStack = null;
  
          // Stacks could be reparented from the removed display to other display. While
          // reparenting the last stack of the removed display, the remove display is ready to be
          // released (no more ActivityStack). But, we cannot release it at that moment or the
          // related WindowContainer and WindowContainerController will also be removed. So, we
          // set display as removed after reparenting stack finished.
          final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay();
          mRootActivityContainer.mStackSupervisor.beginDeferResume();
          try {
              int numStacks = mStacks.size();
              // Keep the order from bottom to top.
              for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
                  final ActivityStack stack = mStacks.get(stackNdx);
                  // Always finish non-standard type stacks.
                  if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
                      stack.finishAllActivitiesLocked(true /* immediately */);
                  } else {
                      // If default display is in split-window mode, set windowing mode of the stack
                      // to split-screen secondary. Otherwise, set the windowing mode to undefined by
                      // default to let stack inherited the windowing mode from the new display.
                      final int windowingMode = toDisplay.hasSplitScreenPrimaryStack()
                              ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                              : WINDOWING_MODE_UNDEFINED;
                      stack.reparent(toDisplay, true /* onTop */, true /* displayRemoved */);
                      stack.setWindowingMode(windowingMode);
                      lastReparentedStack = stack;
                  }
                  // Stacks may be removed from this display. Ensure each stack will be processed and
                  // the loop will end.
                  stackNdx -= numStacks - mStacks.size();
                  numStacks = mStacks.size();
              }
          } finally {
              mRootActivityContainer.mStackSupervisor.endDeferResume();
          }
          mRemoved = true;
  
          // Only update focus/visibility for the last one because there may be many stacks are
          // reparented and the intermediate states are unnecessary.
          if (lastReparentedStack != null) {
              lastReparentedStack.postReparent();
          }
          releaseSelfIfNeeded();
  
          if (!mAllSleepTokens.isEmpty()) {
              mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens);
              mAllSleepTokens.clear();
              mService.updateSleepIfNeededLocked();
          }
      }
  
      private void releaseSelfIfNeeded() {
          if (!mRemoved || mDisplayContent == null) {
              return;
          }
  
          final ActivityStack stack = mStacks.size() == 1 ? mStacks.get(0) : null;
          if (stack != null && stack.isActivityTypeHome() && stack.getAllTasks().isEmpty()) {
              // Release this display if an empty home stack is the only thing left.
              // Since it is the last stack, this display will be released along with the stack
              // removal.
              stack.remove();
          } else if (mStacks.isEmpty()) {
              mDisplayContent.removeIfPossible();
              mDisplayContent = null;
              mRootActivityContainer.removeChild(this);
              mRootActivityContainer.mStackSupervisor
                      .getKeyguardController().onDisplayRemoved(mDisplayId);
          }
      }
  
      /** Update and get all UIDs that are present on the display and have access to it. */
      IntArray getPresentUIDs() {
          mDisplayAccessUIDs.clear();
          for (ActivityStack stack : mStacks) {
              stack.getPresentUIDs(mDisplayAccessUIDs);
          }
          return mDisplayAccessUIDs;
      }
  
      /**
       * Checks if system decorations should be shown on this display.
       *
       * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
       */
      boolean supportsSystemDecorations() {
          return mDisplayContent.supportsSystemDecorations();
      }
  
      @VisibleForTesting
      boolean shouldDestroyContentOnRemove() {
          return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
      }
  
      boolean shouldSleep() {
          return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
                  && (mService.mRunningVoice == null);
      }
  
      void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
          if (mDisplayContent == null) {
              return;
          }
          final AppWindowToken newFocus;
          final IBinder token = r.appToken;
          if (token == null) {
              if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
                      + mDisplayId);
              newFocus = null;
          } else {
              newFocus = mService.mWindowManager.mRoot.getAppWindowToken(token);
              if (newFocus == null) {
                  Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
                          + ", displayId=" + mDisplayId);
              }
              if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
                      + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
          }
  
          final boolean changed = mDisplayContent.setFocusedApp(newFocus);
          if (moveFocusNow && changed) {
              mService.mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
                      true /*updateInputWindows*/);
          }
      }
  
      /**
       * @return the stack currently above the {@param stack}.  Can be null if the {@param stack} is
       *         already top-most.
       */
      ActivityStack getStackAbove(ActivityStack stack) {
          final int stackIndex = mStacks.indexOf(stack) + 1;
          return (stackIndex < mStacks.size()) ? mStacks.get(stackIndex) : null;
      }
  
      /**
       * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
       * Generally used in conjunction with {@link #moveStackBehindStack}.
       */
      void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
          if (stack.shouldBeVisible(null)) {
              // Skip if the stack is already visible
              return;
          }
  
          // Move the stack to the bottom to not affect the following visibility checks
          positionChildAtBottom(stack);
  
          // Find the next position where the stack should be placed
          final int numStacks = mStacks.size();
          for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
              final ActivityStack s = mStacks.get(stackNdx);
              if (s == stack) {
                  continue;
              }
              final int winMode = s.getWindowingMode();
              final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN ||
                      winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
              if (s.shouldBeVisible(null) && isValidWindowingMode) {
                  // Move the provided stack to behind this stack
                  positionChildAt(stack, Math.max(0, stackNdx - 1));
                  break;
              }
          }
      }
  
      /**
       * Moves the {@param stack} behind the given {@param behindStack} if possible. If
       * {@param behindStack} is not currently in the display, then then the stack is moved to the
       * back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}.
       */
      void moveStackBehindStack(ActivityStack stack, ActivityStack behindStack) {
          if (behindStack == null || behindStack == stack) {
              return;
          }
  
          // Note that positionChildAt will first remove the given stack before inserting into the
          // list, so we need to adjust the insertion index to account for the removed index
          // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
          //       position internally
          final int stackIndex = mStacks.indexOf(stack);
          final int behindStackIndex = mStacks.indexOf(behindStack);
          final int insertIndex = stackIndex <= behindStackIndex
                  ? behindStackIndex - 1 : behindStackIndex;
          positionChildAt(stack, Math.max(0, insertIndex));
      }
  
      void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
              boolean preserveWindows, boolean notifyClients) {
          for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
              final ActivityStack stack = getChildAt(stackNdx);
              stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
                      notifyClients);
          }
      }
  
      void moveHomeStackToFront(String reason) {
          if (mHomeStack != null) {
              mHomeStack.moveToFront(reason);
          }
      }
  
      /**
       * Moves the focusable home activity to top. If there is no such activity, the home stack will
       * still move to top.
       */
      void moveHomeActivityToTop(String reason) {
          final ActivityRecord top = getHomeActivity();
          if (top == null) {
              moveHomeStackToFront(reason);
              return;
          }
          top.moveFocusableActivityToTop(reason);
      }
  
      @Nullable
      ActivityStack getHomeStack() {
          return mHomeStack;
      }
  
      @Nullable
      ActivityRecord getHomeActivity() {
          return getHomeActivityForUser(mRootActivityContainer.mCurrentUser);
      }
  
      @Nullable
      ActivityRecord getHomeActivityForUser(int userId) {
          if (mHomeStack == null) {
              return null;
          }
  
          final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
          for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
              final TaskRecord task = tasks.get(taskNdx);
              if (!task.isActivityTypeHome()) {
                  continue;
              }
  
              final ArrayList<ActivityRecord> activities = task.mActivities;
              for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                  final ActivityRecord r = activities.get(activityNdx);
                  if (r.isActivityTypeHome()
                          && ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
                      return r;
                  }
              }
          }
          return null;
      }
  
      boolean isSleeping() {
          return mSleeping;
      }
  
      void setIsSleeping(boolean asleep) {
          mSleeping = asleep;
      }
  
      /**
       * Adds a listener to be notified whenever the stack order in the display changes. Currently
       * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the
       * current animation when the system state changes.
       */
      void registerStackOrderChangedListener(OnStackOrderChangedListener listener) {
          if (!mStackOrderChangedCallbacks.contains(listener)) {
              mStackOrderChangedCallbacks.add(listener);
          }
      }
  
      /**
       * Removes a previously registered stack order change listener.
       */
      void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) {
          mStackOrderChangedCallbacks.remove(listener);
      }
  
      /**
       * Notifies of a stack order change
       * @param stack The stack which triggered the order change
       */
      private void onStackOrderChanged(ActivityStack stack) {
          for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
              mStackOrderChangedCallbacks.get(i).onStackOrderChanged(stack);
          }
      }
  
      /**
       * See {@link DisplayContent#deferUpdateImeTarget()}
       */
      public void deferUpdateImeTarget() {
          if (mDisplayContent != null) {
              mDisplayContent.deferUpdateImeTarget();
          }
      }
  
      /**
       * See {@link DisplayContent#deferUpdateImeTarget()}
       */
      public void continueUpdateImeTarget() {
          if (mDisplayContent != null) {
              mDisplayContent.continueUpdateImeTarget();
          }
      }
  
      void setDisplayToSingleTaskInstance() {
          final int childCount = getChildCount();
          if (childCount > 1) {
              throw new IllegalArgumentException("Display already has multiple stacks. display="
                      + this);
          }
          if (childCount > 0) {
              final ActivityStack stack = getChildAt(0);
              if (stack.getChildCount() > 1) {
                  throw new IllegalArgumentException("Display stack already has multiple tasks."
                          + " display=" + this + " stack=" + stack);
              }
          }
  
          mSingleTaskInstance = true;
      }
  
      /** Returns true if the display can only contain one task */
      boolean isSingleTaskInstance() {
          return mSingleTaskInstance;
      }
  
      public void dump(PrintWriter pw, String prefix) {
          pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size()
                  + (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
          final String myPrefix = prefix + " ";
          if (mHomeStack != null) {
              pw.println(myPrefix + "mHomeStack=" + mHomeStack);
          }
          if (mRecentsStack != null) {
              pw.println(myPrefix + "mRecentsStack=" + mRecentsStack);
          }
          if (mPinnedStack != null) {
              pw.println(myPrefix + "mPinnedStack=" + mPinnedStack);
          }
          if (mSplitScreenPrimaryStack != null) {
              pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
          }
          if (mPreferredTopFocusableStack != null) {
              pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
          }
          if (mLastFocusedStack != null) {
              pw.println(myPrefix + "mLastFocusedStack=" + mLastFocusedStack);
          }
      }
  
      public void dumpStacks(PrintWriter pw) {
          for (int i = mStacks.size() - 1; i >= 0; --i) {
              pw.print(mStacks.get(i).mStackId);
              if (i > 0) {
                  pw.print(",");
              }
          }
      }
  
      public void writeToProto(ProtoOutputStream proto, long fieldId,
              @WindowTraceLogLevel int logLevel) {
          final long token = proto.start(fieldId);
          super.writeToProto(proto, CONFIGURATION_CONTAINER, logLevel);
          proto.write(ID, mDisplayId);
          proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
          final ActivityStack focusedStack = getFocusedStack();
          if (focusedStack != null) {
              proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
              final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
              if (focusedActivity != null) {
                  focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
              }
          } else {
              proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
          }
          for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
              final ActivityStack stack = mStacks.get(stackNdx);
              stack.writeToProto(proto, STACKS, logLevel);
          }
          proto.end(token);
      }
  
      /**
       * Callback for when the order of the stacks in the display changes.
       */
      interface OnStackOrderChangedListener {
          void onStackOrderChanged(ActivityStack stack);
      }
  }
  