content: /*
   * Copyright (C) 2013 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.Manifest.permission.ACTIVITY_EMBEDDING;
  import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
  import static android.Manifest.permission.START_ANY_ACTIVITY;
  import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
  import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
  import static android.app.ActivityManager.START_FLAG_DEBUG;
  import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
  import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
  import static android.app.ActivityManager.START_TASK_TO_FRONT;
  import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
  import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
  import static android.app.WaitResult.INVALID_DELAY;
  import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
  import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
  import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
  import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
  import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
  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.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
  import static android.content.pm.PackageManager.PERMISSION_DENIED;
  import static android.content.pm.PackageManager.PERMISSION_GRANTED;
  import static android.graphics.Rect.copyOrNull;
  import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
  import static android.os.Process.SYSTEM_UID;
  import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
  import static android.view.Display.DEFAULT_DISPLAY;
  import static android.view.Display.INVALID_DISPLAY;
  import static android.view.Display.TYPE_VIRTUAL;
  import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
  
  import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
  import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
  import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
  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_SWITCH;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
  import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
  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.ActivityTaskManagerService.ANIMATE;
  import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
  import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
  import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
  import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
  import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
  import static com.android.server.wm.RootActivityContainer.TAG_STATES;
  import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
  import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
  import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
  import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
  import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
  import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
  
  import android.Manifest;
  import android.app.Activity;
  import android.app.ActivityManager;
  import android.app.ActivityManagerInternal;
  import android.app.ActivityOptions;
  import android.app.AppOpsManager;
  import android.app.ProfilerInfo;
  import android.app.ResultInfo;
  import android.app.WaitResult;
  import android.app.servertransaction.ActivityLifecycleItem;
  import android.app.servertransaction.ClientTransaction;
  import android.app.servertransaction.LaunchActivityItem;
  import android.app.servertransaction.PauseActivityItem;
  import android.app.servertransaction.ResumeActivityItem;
  import android.content.ComponentName;
  import android.content.Intent;
  import android.content.pm.ActivityInfo;
  import android.content.pm.ApplicationInfo;
  import android.content.pm.PackageInfo;
  import android.content.pm.PackageManager;
  import android.content.pm.ResolveInfo;
  import android.content.pm.UserInfo;
  import android.content.res.Configuration;
  import android.graphics.Rect;
  import android.os.Binder;
  import android.os.Bundle;
  import android.os.Debug;
  import android.os.Handler;
  import android.os.IBinder;
  import android.os.Looper;
  import android.os.Message;
  import android.os.PowerManager;
  import android.os.Process;
  import android.os.RemoteException;
  import android.os.SystemClock;
  import android.os.Trace;
  import android.os.UserHandle;
  import android.os.UserManager;
  import android.os.WorkSource;
  import android.provider.MediaStore;
  import android.util.ArrayMap;
  import android.util.ArraySet;
  import android.util.EventLog;
  import android.util.MergedConfiguration;
  import android.util.Slog;
  import android.util.SparseArray;
  import android.util.SparseIntArray;
  
  import com.android.internal.R;
  import com.android.internal.annotations.GuardedBy;
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.internal.content.ReferrerIntent;
  import com.android.internal.os.TransferPipe;
  import com.android.internal.os.logging.MetricsLoggerWrapper;
  import com.android.internal.util.ArrayUtils;
  import com.android.internal.util.function.pooled.PooledLambda;
  import com.android.server.am.ActivityManagerService;
  import com.android.server.am.EventLogTags;
  import com.android.server.am.UserState;
  
  import java.io.FileDescriptor;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.util.ArrayList;
  import java.util.List;
  
  // TODO: This class has become a dumping ground. Let's
  // - Move things relating to the hierarchy to RootWindowContainer
  // - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler
  // - Move interface things to ActivityTaskManagerService.
  // - All other little things to other files.
  public class ActivityStackSupervisor implements RecentTasks.Callbacks {
      private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
      private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
      private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
      private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
      private static final String TAG_STACK = TAG + POSTFIX_STACK;
      private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
      static final String TAG_TASKS = TAG + POSTFIX_TASKS;
  
      /** How long we wait until giving up on the last activity telling us it is idle. */
      static final int IDLE_TIMEOUT = 10 * 1000;
  
      /** How long we can hold the sleep wake lock before giving up. */
      static final int SLEEP_TIMEOUT = 5 * 1000;
  
      // How long we can hold the launch wake lock before giving up.
      static final int LAUNCH_TIMEOUT = 10 * 1000;
  
      /** How long we wait until giving up on the activity telling us it released the top state. */
      static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500;
  
      static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
      static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
      static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
      static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
      static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
      static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
      static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
      static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
      static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
      static final int REPORT_HOME_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 16;
      static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 17;
  
      // Used to indicate that windows of activities should be preserved during the resize.
      static final boolean PRESERVE_WINDOWS = true;
  
      // Used to indicate if an object (e.g. task) should be moved/created
      // at the top of its container (e.g. stack).
      static final boolean ON_TOP = true;
  
      // Don't execute any calls to resume.
      static final boolean DEFER_RESUME = true;
  
      // Used to indicate that a task is removed it should also be removed from recents.
      static final boolean REMOVE_FROM_RECENTS = true;
  
      // Used to indicate that pausing an activity should occur immediately without waiting for
      // the activity callback indicating that it has completed pausing
      static final boolean PAUSE_IMMEDIATELY = true;
  
      /** True if the docked stack is currently being resized. */
      private boolean mDockedStackResizing;
  
      /**
       * True if there are pending docked bounds that need to be applied after
       * {@link #mDockedStackResizing} is reset to false.
       */
      private boolean mHasPendingDockedBounds;
      private Rect mPendingDockedBounds;
      private Rect mPendingTempDockedTaskBounds;
      private Rect mPendingTempDockedTaskInsetBounds;
      private Rect mPendingTempOtherTaskBounds;
      private Rect mPendingTempOtherTaskInsetBounds;
  
      // Activity actions an app cannot start if it uses a permission which is not granted.
      private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
              new ArrayMap<>();
  
      static {
          ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
                  Manifest.permission.CAMERA);
          ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
                  Manifest.permission.CAMERA);
          ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
                  Manifest.permission.CALL_PHONE);
      }
  
      /** Action restriction: launching the activity is not restricted. */
      private static final int ACTIVITY_RESTRICTION_NONE = 0;
      /** Action restriction: launching the activity is restricted by a permission. */
      private static final int ACTIVITY_RESTRICTION_PERMISSION = 1;
      /** Action restriction: launching the activity is restricted by an app op. */
      private static final int ACTIVITY_RESTRICTION_APPOP = 2;
  
      // For debugging to make sure the caller when acquiring/releasing our
      // wake lock is the system process.
      static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
      /** The number of distinct task ids that can be assigned to the tasks of a single user */
      private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
  
      final ActivityTaskManagerService mService;
      RootActivityContainer mRootActivityContainer;
  
      /** The historial list of recent tasks including inactive tasks */
      RecentTasks mRecentTasks;
  
      /** Helper class to abstract out logic for fetching the set of currently running tasks */
      RunningTasks mRunningTasks;
  
      final ActivityStackSupervisorHandler mHandler;
      final Looper mLooper;
  
      /** Short cut */
      WindowManagerService mWindowManager;
  
       /** Common synchronization logic used to save things to disks. */
      PersisterQueue mPersisterQueue;
      LaunchParamsPersister mLaunchParamsPersister;
      private LaunchParamsController mLaunchParamsController;
  
      /**
       * Maps the task identifier that activities are currently being started in to the userId of the
       * task. Each time a new task is created, the entry for the userId of the task is incremented
       */
      private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
  
      /** List of processes waiting to find out when a specific activity becomes visible. */
      private final ArrayList<WaitInfo> mWaitingForActivityVisible = new ArrayList<>();
  
      /** List of processes waiting to find out about the next launched activity. */
      final ArrayList<WaitResult> mWaitingActivityLaunched = new ArrayList<>();
  
      /** List of activities that are ready to be stopped, but waiting for the next activity to
       * settle down before doing so. */
      final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<>();
  
      /** List of activities that are ready to be finished, but waiting for the previous activity to
       * settle down before doing so.  It contains ActivityRecord objects. */
      final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<>();
  
      /** List of activities that are in the process of going to sleep. */
      final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<>();
  
      /** List of activities whose multi-window mode changed that we need to report to the
       * application */
      final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
  
      /** List of activities whose picture-in-picture mode changed that we need to report to the
       * application */
      final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
  
      /**
       * Animations that for the current transition have requested not to
       * be considered for the transition animation.
       */
      final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<>();
  
      /**
       * Cached value of the topmost resumed activity in the system. Updated when new activity is
       * resumed.
       */
      private ActivityRecord mTopResumedActivity;
  
      /**
       * Flag indicating whether we're currently waiting for the previous top activity to handle the
       * loss of the state and report back before making new activity top resumed.
       */
      private boolean mTopResumedActivityWaitingForPrev;
  
      /** The target stack bounds for the picture-in-picture mode changed that we need to report to
       * the application */
      Rect mPipModeChangedTargetStackBounds;
  
      /** Used on user changes */
      final ArrayList<UserState> mStartingUsers = new ArrayList<>();
  
      /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
       * is being brought in front of us. */
      boolean mUserLeaving = false;
  
      /**
       * The system chooser activity which worked as a delegate of
       * {@link com.android.internal.app.ResolverActivity}.
       */
      private ComponentName mSystemChooserActivity;
  
      /**
       * We don't want to allow the device to go to sleep while in the process
       * of launching an activity.  This is primarily to allow alarm intent
       * receivers to launch an activity and get that to run before the device
       * goes back to sleep.
       */
      PowerManager.WakeLock mLaunchingActivityWakeLock;
  
      /**
       * Set when the system is going to sleep, until we have
       * successfully paused the current activity and released our wake lock.
       * At that point the system is allowed to actually sleep.
       */
      PowerManager.WakeLock mGoingToSleepWakeLock;
  
      /**
       * Temporary rect used during docked stack resize calculation so we don't need to create a new
       * object each time.
       */
      private final Rect tempRect = new Rect();
      private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
  
      private ActivityMetricsLogger mActivityMetricsLogger;
  
      /** Check if placing task or activity on specified display is allowed. */
      boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
              ActivityInfo activityInfo) {
          if (displayId == DEFAULT_DISPLAY) {
              // No restrictions for the default display.
              return true;
          }
          if (!mService.mSupportsMultiDisplay) {
              // Can't launch on secondary displays if feature is not supported.
              return false;
          }
          if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
              // Can't place activities to a display that has restricted launch rules.
              // In this case the request should be made by explicitly adding target display id and
              // by caller with corresponding permissions. See #isCallerAllowedToLaunchOnDisplay().
              return false;
          }
          return true;
      }
  
      /**
       * Used to keep track whether app visibilities got changed since the last pause. Useful to
       * determine whether to invoke the task stack change listener after pausing.
       */
      boolean mAppVisibilitiesChangedSinceLastPause;
  
      /**
       * Set of tasks that are in resizing mode during an app transition to fill the "void".
       */
      private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
  
  
      /**
       * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
       * will be ignored. Useful for the case where the caller is handling resizing of other stack and
       * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
       * like the docked stack going empty.
       */
      private boolean mAllowDockedStackResize = true;
  
      private KeyguardController mKeyguardController;
  
      private PowerManager mPowerManager;
      private int mDeferResumeCount;
  
      private boolean mInitialized;
  
      /**
       * Description of a request to start a new activity, which has been held
       * due to app switches being disabled.
       */
      static class PendingActivityLaunch {
          final ActivityRecord r;
          final ActivityRecord sourceRecord;
          final int startFlags;
          final ActivityStack stack;
          final WindowProcessController callerApp;
  
          PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
                  int _startFlags, ActivityStack _stack, WindowProcessController app) {
              r = _r;
              sourceRecord = _sourceRecord;
              startFlags = _startFlags;
              stack = _stack;
              callerApp = app;
          }
  
          void sendErrorResult(String message) {
              try {
                  if (callerApp != null && callerApp.hasThread()) {
                      callerApp.getThread().scheduleCrash(message);
                  }
              } catch (RemoteException e) {
                  Slog.e(TAG, "Exception scheduling crash of failed "
                          + "activity launcher sourceRecord=" + sourceRecord, e);
              }
          }
      }
  
      public ActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
          mService = service;
          mLooper = looper;
          mHandler = new ActivityStackSupervisorHandler(looper);
      }
  
      public void initialize() {
          if (mInitialized) {
              return;
          }
  
          mInitialized = true;
          mRunningTasks = createRunningTasks();
  
          mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
                  mHandler.getLooper());
          mKeyguardController = new KeyguardController(mService, this);
  
          mPersisterQueue = new PersisterQueue();
          mLaunchParamsPersister = new LaunchParamsPersister(mPersisterQueue, this);
          mLaunchParamsController = new LaunchParamsController(mService, mLaunchParamsPersister);
          mLaunchParamsController.registerDefaultModifiers(this);
      }
  
      void onSystemReady() {
          mLaunchParamsPersister.onSystemReady();
      }
  
      void onUserUnlocked(int userId) {
          // Only start persisting when the first user is unlocked. The method call is
          // idempotent so there is no side effect to call it again when the second user is
          // unlocked.
          mPersisterQueue.startPersisting();
          mLaunchParamsPersister.onUnlockUser(userId);
      }
  
      public ActivityMetricsLogger getActivityMetricsLogger() {
          return mActivityMetricsLogger;
      }
  
      public KeyguardController getKeyguardController() {
          return mKeyguardController;
      }
  
      ComponentName getSystemChooserActivity() {
          if (mSystemChooserActivity == null) {
              mSystemChooserActivity = ComponentName.unflattenFromString(
                      mService.mContext.getResources().getString(R.string.config_chooserActivity));
          }
          return mSystemChooserActivity;
      }
  
      void setRecentTasks(RecentTasks recentTasks) {
          mRecentTasks = recentTasks;
          mRecentTasks.registerCallback(this);
      }
  
      @VisibleForTesting
      RunningTasks createRunningTasks() {
          return new RunningTasks();
      }
  
      /**
       * At the time when the constructor runs, the power manager has not yet been
       * initialized.  So we initialize our wakelocks afterwards.
       */
      void initPowerManagement() {
          mPowerManager = mService.mContext.getSystemService(PowerManager.class);
          mGoingToSleepWakeLock = mPowerManager
                  .newWakeLock(PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
          mLaunchingActivityWakeLock = mPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "*launch*");
          mLaunchingActivityWakeLock.setReferenceCounted(false);
      }
  
      void setWindowManager(WindowManagerService wm) {
          mWindowManager = wm;
          getKeyguardController().setWindowManager(wm);
      }
  
      void moveRecentsStackToFront(String reason) {
          final ActivityStack recentsStack = mRootActivityContainer.getDefaultDisplay().getStack(
                  WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
          if (recentsStack != null) {
              recentsStack.moveToFront(reason);
          }
      }
  
      void setNextTaskIdForUserLocked(int taskId, int userId) {
          final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
          if (taskId > currentTaskId) {
              mCurTaskIdForUser.put(userId, taskId);
          }
      }
  
      static int nextTaskIdForUser(int taskId, int userId) {
          int nextTaskId = taskId + 1;
          if (nextTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
              // Wrap around as there will be smaller task ids that are available now.
              nextTaskId -= MAX_TASK_IDS_PER_USER;
          }
          return nextTaskId;
      }
  
      int getNextTaskIdForUserLocked(int userId) {
          final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER);
          // for a userId u, a taskId can only be in the range
          // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
          // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
          int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
          while (mRecentTasks.containsTaskId(candidateTaskId, userId)
                  || mRootActivityContainer.anyTaskForId(
                          candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
              candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
              if (candidateTaskId == currentTaskId) {
                  // Something wrong!
                  // All MAX_TASK_IDS_PER_USER task ids are taken up by running tasks for this user
                  throw new IllegalStateException("Cannot get an available task id."
                          + " Reached limit of " + MAX_TASK_IDS_PER_USER
                          + " running tasks per user.");
              }
          }
          mCurTaskIdForUser.put(userId, candidateTaskId);
          return candidateTaskId;
      }
  
      void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) {
          final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs);
          mWaitingForActivityVisible.add(waitInfo);
      }
  
      void cleanupActivity(ActivityRecord r) {
          // Make sure this record is no longer in the pending finishes list.
          // This could happen, for example, if we are trimming activities
          // down to the max limit while they are still waiting to finish.
          mFinishingActivities.remove(r);
  
          stopWaitingForActivityVisible(r);
      }
  
      void stopWaitingForActivityVisible(ActivityRecord r) {
          boolean changed = false;
          for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
              final WaitInfo w = mWaitingForActivityVisible.get(i);
              if (w.matches(r.mActivityComponent)) {
                  final WaitResult result = w.getResult();
                  changed = true;
                  result.timeout = false;
                  result.who = w.getComponent();
                  result.totalTime = SystemClock.uptimeMillis() - w.getStartTime();
                  mWaitingForActivityVisible.remove(w);
              }
          }
          if (changed) {
              mService.mGlobalLock.notifyAll();
          }
      }
  
      void reportWaitingActivityLaunchedIfNeeded(ActivityRecord r, int result) {
          if (mWaitingActivityLaunched.isEmpty()) {
              return;
          }
  
          if (result != START_DELIVERED_TO_TOP && result != START_TASK_TO_FRONT) {
              return;
          }
  
          boolean changed = false;
  
          for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
              WaitResult w = mWaitingActivityLaunched.remove(i);
              if (w.who == null) {
                  changed = true;
                  w.result = result;
  
                  // Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there
                  // will be no followup launch signals. Assign the result and launched component.
                  if (result == START_DELIVERED_TO_TOP) {
                      w.who = r.mActivityComponent;
                  }
              }
          }
  
          if (changed) {
              mService.mGlobalLock.notifyAll();
          }
      }
  
      void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime,
              @WaitResult.LaunchState int launchState) {
          boolean changed = false;
          for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
              WaitResult w = mWaitingActivityLaunched.remove(i);
              if (w.who == null) {
                  changed = true;
                  w.timeout = timeout;
                  if (r != null) {
                      w.who = new ComponentName(r.info.packageName, r.info.name);
                  }
                  w.totalTime = totalTime;
                  w.launchState = launchState;
                  // Do not modify w.result.
              }
          }
          if (changed) {
              mService.mGlobalLock.notifyAll();
          }
      }
  
      ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
              ProfilerInfo profilerInfo) {
          final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
          if (aInfo != null) {
              // Store the found target back into the intent, because now that
              // we have it we never want to do this again.  For example, if the
              // user navigates back to this point in the history, we should
              // always restart the exact same activity.
              intent.setComponent(new ComponentName(
                      aInfo.applicationInfo.packageName, aInfo.name));
  
              // Don't debug things in the system process
              if (!aInfo.processName.equals("system")) {
                  if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
                          | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
  
                       // Mimic an AMS synchronous call by passing a message to AMS and wait for AMS
                       // to notify us that the task has completed.
                       // TODO(b/80414790) look into further untangling for the situation where the
                       // caller is on the same thread as the handler we are posting to.
                      synchronized (mService.mGlobalLock) {
                          // Post message to AMS.
                          final Message msg = PooledLambda.obtainMessage(
                                  ActivityManagerInternal::setDebugFlagsForStartingActivity,
                                  mService.mAmInternal, aInfo, startFlags, profilerInfo,
                                  mService.mGlobalLock);
                          mService.mH.sendMessage(msg);
                          try {
                              mService.mGlobalLock.wait();
                          } catch (InterruptedException ignore) {
  
                          }
                      }
                  }
              }
              final String intentLaunchToken = intent.getLaunchToken();
              if (aInfo.launchToken == null && intentLaunchToken != null) {
                  aInfo.launchToken = intentLaunchToken;
              }
          }
          return aInfo;
      }
  
      ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
              int filterCallingUid) {
          try {
              Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
              int modifiedFlags = flags
                      | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
              if (intent.isWebIntent()
                          || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
                  modifiedFlags |= PackageManager.MATCH_INSTANT;
              }
  
              // In order to allow cross-profile lookup, we clear the calling identity here.
              // Note the binder identity won't affect the result, but filterCallingUid will.
  
              // Cross-user/profile call check are done at the entry points
              // (e.g. AMS.startActivityAsUser).
              final long token = Binder.clearCallingIdentity();
              try {
                  return mService.getPackageManagerInternalLocked().resolveIntent(
                          intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
              } finally {
                  Binder.restoreCallingIdentity(token);
              }
          } finally {
              Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
          }
      }
  
      ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
              ProfilerInfo profilerInfo, int userId, int filterCallingUid) {
          final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId, 0, filterCallingUid);
          return resolveActivity(intent, rInfo, startFlags, profilerInfo);
      }
  
      boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
              boolean andResume, boolean checkConfig) throws RemoteException {
  
          if (!mRootActivityContainer.allPausedActivitiesComplete()) {
              // While there are activities pausing we skipping starting any new activities until
              // pauses are complete. NOTE: that we also do this for activities that are starting in
              // the paused state because they will first be resumed then paused on the client side.
              if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                      "realStartActivityLocked: Skipping start of r=" + r
                      + " some activities pausing...");
              return false;
          }
  
          final TaskRecord task = r.getTaskRecord();
          final ActivityStack stack = task.getStack();
  
          beginDeferResume();
  
          try {
              r.startFreezingScreenLocked(proc, 0);
  
              // schedule launch ticks to collect information about slow apps.
              r.startLaunchTickingLocked();
  
              r.setProcess(proc);
  
              // Ensure activity is allowed to be resumed after process has set.
              if (andResume && !r.canResumeByCompat()) {
                  andResume = false;
              }
  
              if (getKeyguardController().isKeyguardLocked()) {
                  r.notifyUnknownVisibilityLaunched();
              }
  
              // Have the window manager re-evaluate the orientation of the screen based on the new
              // activity order.  Note that as a result of this, it can call back into the activity
              // manager with a new orientation.  We don't care about that, because the activity is
              // not currently running so we are just restarting it anyway.
              if (checkConfig) {
                  // Deferring resume here because we're going to launch new activity shortly.
                  // We don't want to perform a redundant launch of the same record while ensuring
                  // configurations and trying to resume top activity of focused stack.
                  mRootActivityContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),
                          false /* markFrozenIfConfigChanged */, true /* deferResume */);
              }
  
              if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
                      true /* isTop */)) {
                  // We only set the visibility to true if the activity is allowed to be visible
                  // based on
                  // keyguard state. This avoids setting this into motion in window manager that is
                  // later cancelled due to later calls to ensure visible activities that set
                  // visibility back to false.
                  r.setVisibility(true);
              }
  
              final int applicationInfoUid =
                      (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
              if ((r.mUserId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
                  Slog.wtf(TAG,
                          "User ID for activity changing for " + r
                                  + " appInfo.uid=" + r.appInfo.uid
                                  + " info.ai.uid=" + applicationInfoUid
                                  + " old=" + r.app + " new=" + proc);
              }
  
              r.launchCount++;
              r.lastLaunchTime = SystemClock.uptimeMillis();
  
              if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
  
              proc.addActivityIfNeeded(r);
  
              final LockTaskController lockTaskController = mService.getLockTaskController();
              if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
                      || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
                      || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
                              && lockTaskController.getLockTaskModeState()
                                      == LOCK_TASK_MODE_LOCKED)) {
                  lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
              }
  
              try {
                  if (!proc.hasThread()) {
                      throw new RemoteException();
                  }
                  List<ResultInfo> results = null;
                  List<ReferrerIntent> newIntents = null;
                  if (andResume) {
                      // We don't need to deliver new intents and/or set results if activity is going
                      // to pause immediately after launch.
                      results = r.results;
                      newIntents = r.newIntents;
                  }
                  if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
                          "Launching: " + r + " icicle=" + r.icicle + " with results=" + results
                                  + " newIntents=" + newIntents + " andResume=" + andResume);
                  EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.mUserId,
                          System.identityHashCode(r), task.taskId, r.shortComponentName);
                  if (r.isActivityTypeHome()) {
                      // Home process is the root process of the task.
                      updateHomeProcess(task.mActivities.get(0).app);
                  }
                  mService.getPackageManagerInternalLocked().notifyPackageUse(
                          r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
                  r.sleeping = false;
                  r.forceNewConfig = false;
                  mService.getAppWarningsLocked().onStartActivity(r);
                  r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
  
                  // Because we could be starting an Activity in the system process this may not go
                  // across a Binder interface which would create a new Configuration. Consequently
                  // we have to always create a new Configuration here.
  
                  final MergedConfiguration mergedConfiguration = new MergedConfiguration(
                          proc.getConfiguration(), r.getMergedOverrideConfiguration());
                  r.setLastReportedConfiguration(mergedConfiguration);
  
                  logIfTransactionTooLarge(r.intent, r.icicle);
  
  
                  // Create activity launch transaction.
                  final ClientTransaction clientTransaction = ClientTransaction.obtain(
                          proc.getThread(), r.appToken);
  
                  final DisplayContent dc = r.getDisplay().mDisplayContent;
                  clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                          System.identityHashCode(r), r.info,
                          // TODO: Have this take the merged configuration instead of separate global
                          // and override configs.
                          mergedConfiguration.getGlobalConfiguration(),
                          mergedConfiguration.getOverrideConfiguration(), r.compat,
                          r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                          r.icicle, r.persistentState, results, newIntents,
                          dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                                  r.assistToken));
  
                  // Set desired final state.
                  final ActivityLifecycleItem lifecycleItem;
                  if (andResume) {
                      lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                  } else {
                      lifecycleItem = PauseActivityItem.obtain();
                  }
                  clientTransaction.setLifecycleStateRequest(lifecycleItem);
  
                  // Schedule transaction.
                  mService.getLifecycleManager().scheduleTransaction(clientTransaction);
  
                  if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
                          && mService.mHasHeavyWeightFeature) {
                      // This may be a heavy-weight process! Note that the package manager will ensure
                      // that only activity can run in the main process of the .apk, which is the only
                      // thing that will be considered heavy-weight.
                      if (proc.mName.equals(proc.mInfo.packageName)) {
                          if (mService.mHeavyWeightProcess != null
                                  && mService.mHeavyWeightProcess != proc) {
                              Slog.w(TAG, "Starting new heavy weight process " + proc
                                      + " when already running "
                                      + mService.mHeavyWeightProcess);
                          }
                          mService.setHeavyWeightProcess(r);
                      }
                  }
  
              } catch (RemoteException e) {
                  if (r.launchFailed) {
                      // This is the second time we failed -- finish activity and give up.
                      Slog.e(TAG, "Second failure launching "
                              + r.intent.getComponent().flattenToShortString() + ", giving up", e);
                      proc.appDied();
                      stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                              "2nd-crash", false);
                      return false;
                  }
  
                  // This is the first time we failed -- restart process and
                  // retry.
                  r.launchFailed = true;
                  proc.removeActivity(r);
                  throw e;
              }
          } finally {
              endDeferResume();
          }
  
          r.launchFailed = false;
          if (stack.updateLRUListLocked(r)) {
              Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
          }
  
          // TODO(lifecycler): Resume or pause requests are done as part of launch transaction,
          // so updating the state should be done accordingly.
          if (andResume && readyToResume()) {
              // As part of the process of launching, ActivityThread also performs
              // a resume.
              stack.minimalResumeActivityLocked(r);
          } else {
              // This activity is not starting in the resumed state... which should look like we asked
              // it to pause+stop (but remain visible), and it has done so and reported back the
              // current icicle and other state.
              if (DEBUG_STATES) Slog.v(TAG_STATES,
                      "Moving to PAUSED: " + r + " (starting in paused state)");
              r.setState(PAUSED, "realStartActivityLocked");
          }
          // Perform OOM scoring after the activity state is set, so the process can be updated with
          // the latest state.
          proc.onStartActivity(mService.mTopProcessState, r.info);
  
          // Launch the new version setup screen if needed.  We do this -after-
          // launching the initial activity (that is, home), so that it can have
          // a chance to initialize itself while in the background, making the
          // switch back to it faster and look better.
          if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
              mService.getActivityStartController().startSetupActivity();
          }
  
          // Update any services we are bound to that might care about whether
          // their client may have activities.
          if (r.app != null) {
              r.app.updateServiceConnectionActivities();
          }
  
          return true;
      }
  
      void updateHomeProcess(WindowProcessController app) {
          if (app != null && mService.mHomeProcess != app) {
              if (!mHandler.hasMessages(REPORT_HOME_CHANGED_MSG)) {
                  mHandler.sendEmptyMessage(REPORT_HOME_CHANGED_MSG);
              }
              mService.mHomeProcess = app;
          }
      }
  
      private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
          int extrasSize = 0;
          if (intent != null) {
              final Bundle extras = intent.getExtras();
              if (extras != null) {
                  extrasSize = extras.getSize();
              }
          }
          int icicleSize = (icicle == null ? 0 : icicle.getSize());
          if (extrasSize + icicleSize > 200000) {
              Slog.e(TAG, "Transaction too large, intent: " + intent + ", extras size: " + extrasSize
                      + ", icicle size: " + icicleSize);
          }
      }
  
      void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
          // Is this activity's application already running?
          final WindowProcessController wpc =
                  mService.getProcessController(r.processName, r.info.applicationInfo.uid);
  
          boolean knownToBeDead = false;
          if (wpc != null && wpc.hasThread()) {
              try {
                  realStartActivityLocked(r, wpc, andResume, checkConfig);
                  return;
              } catch (RemoteException e) {
                  Slog.w(TAG, "Exception when starting activity "
                          + r.intent.getComponent().flattenToShortString(), e);
              }
  
              // If a dead object exception was thrown -- fall through to
              // restart the application.
              knownToBeDead = true;
          }
  
          // Suppress transition until the new activity becomes ready, otherwise the keyguard can
          // appear for a short amount of time before the new process with the new activity had the
          // ability to set its showWhenLocked flags.
          if (getKeyguardController().isKeyguardLocked()) {
              r.notifyUnknownVisibilityLaunched();
          }
  
          try {
              if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
                  Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
                          + r.processName);
              }
              // Post message to start process to avoid possible deadlock of calling into AMS with the
              // ATMS lock held.
              final Message msg = PooledLambda.obtainMessage(
                      ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
                      r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
              mService.mH.sendMessage(msg);
          } finally {
              Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
          }
      }
  
      boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
              int requestCode, int callingPid, int callingUid, String callingPackage,
              boolean ignoreTargetSecurity, boolean launchingInTask,
              WindowProcessController callerApp, ActivityRecord resultRecord,
              ActivityStack resultStack) {
          final boolean isCallerRecents = mService.getRecentTasks() != null
                  && mService.getRecentTasks().isCallerRecents(callingUid);
          final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
                  callingUid);
          if (startAnyPerm == PERMISSION_GRANTED || (isCallerRecents && launchingInTask)) {
              // If the caller has START_ANY_ACTIVITY, ignore all checks below. In addition, if the
              // caller is the recents component and we are specifically starting an activity in an
              // existing task, then also allow the activity to be fully relaunched.
              return true;
          }
          final int componentRestriction = getComponentRestrictionForCallingPackage(
                  aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
          final int actionRestriction = getActionRestrictionForCallingPackage(
                  intent.getAction(), callingPackage, callingPid, callingUid);
          if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
                  || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
              if (resultRecord != null) {
                  resultStack.sendActivityResultLocked(-1,
                          resultRecord, resultWho, requestCode,
                          Activity.RESULT_CANCELED, null);
              }
              final String msg;
              if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
                  msg = "Permission Denial: starting " + intent.toString()
                          + " from " + callerApp + " (pid=" + callingPid
                          + ", uid=" + callingUid + ")" + " with revoked permission "
                          + ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
              } else if (!aInfo.exported) {
                  msg = "Permission Denial: starting " + intent.toString()
                          + " from " + callerApp + " (pid=" + callingPid
                          + ", uid=" + callingUid + ")"
                          + " not exported from uid " + aInfo.applicationInfo.uid;
              } else {
                  msg = "Permission Denial: starting " + intent.toString()
                          + " from " + callerApp + " (pid=" + callingPid
                          + ", uid=" + callingUid + ")"
                          + " requires " + aInfo.permission;
              }
              Slog.w(TAG, msg);
              throw new SecurityException(msg);
          }
  
          if (actionRestriction == ACTIVITY_RESTRICTION_APPOP) {
              final String message = "Appop Denial: starting " + intent.toString()
                      + " from " + callerApp + " (pid=" + callingPid
                      + ", uid=" + callingUid + ")"
                      + " requires " + AppOpsManager.permissionToOp(
                              ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction()));
              Slog.w(TAG, message);
              return false;
          } else if (componentRestriction == ACTIVITY_RESTRICTION_APPOP) {
              final String message = "Appop Denial: starting " + intent.toString()
                      + " from " + callerApp + " (pid=" + callingPid
                      + ", uid=" + callingUid + ")"
                      + " requires appop " + AppOpsManager.permissionToOp(aInfo.permission);
              Slog.w(TAG, message);
              return false;
          }
  
          return true;
      }
  
      /** Check if caller is allowed to launch activities on specified display. */
      boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId,
              ActivityInfo aInfo) {
          if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: displayId=" + launchDisplayId
                  + " callingPid=" + callingPid + " callingUid=" + callingUid);
  
          if (callingPid == -1 && callingUid == -1) {
              if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: no caller info, skip check");
              return true;
          }
  
          final ActivityDisplay activityDisplay =
                  mRootActivityContainer.getActivityDisplayOrCreate(launchDisplayId);
          if (activityDisplay == null || activityDisplay.isRemoved()) {
              Slog.w(TAG, "Launch on display check: display not found");
              return false;
          }
  
          // Check if the caller has enough privileges to embed activities and launch to private
          // displays.
          final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
                  callingUid);
          if (startAnyPerm == PERMISSION_GRANTED) {
              if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                      + " allow launch any on display");
              return true;
          }
  
          // Check if caller is already present on display
          final boolean uidPresentOnDisplay = activityDisplay.isUidPresent(callingUid);
  
          final int displayOwnerUid = activityDisplay.mDisplay.getOwnerUid();
          if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID
                  && displayOwnerUid != aInfo.applicationInfo.uid) {
              // Limit launching on virtual displays, because their contents can be read from Surface
              // by apps that created them.
              if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
                  if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                          + " disallow launch on virtual display for not-embedded activity.");
                  return false;
              }
              // Check if the caller is allowed to embed activities from other apps.
              if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
                      == PERMISSION_DENIED && !uidPresentOnDisplay) {
                  if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                          + " disallow activity embedding without permission.");
                  return false;
              }
          }
  
          if (!activityDisplay.isPrivate()) {
              // Anyone can launch on a public display.
              if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                      + " allow launch on public display");
              return true;
          }
  
          // Check if the caller is the owner of the display.
          if (displayOwnerUid == callingUid) {
              if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                      + " allow launch for owner of the display");
              return true;
          }
  
          if (uidPresentOnDisplay) {
              if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                      + " allow launch for caller present on the display");
              return true;
          }
  
          Slog.w(TAG, "Launch on display check: denied");
          return false;
      }
  
      UserInfo getUserInfo(int userId) {
          final long identity = Binder.clearCallingIdentity();
          try {
              return UserManager.get(mService.mContext).getUserInfo(userId);
          } finally {
              Binder.restoreCallingIdentity(identity);
          }
      }
  
      private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
              String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
          if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
                  callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
                  == PERMISSION_DENIED) {
              return ACTIVITY_RESTRICTION_PERMISSION;
          }
  
          if (activityInfo.permission == null) {
              return ACTIVITY_RESTRICTION_NONE;
          }
  
          final int opCode = AppOpsManager.permissionToOpCode(activityInfo.permission);
          if (opCode == AppOpsManager.OP_NONE) {
              return ACTIVITY_RESTRICTION_NONE;
          }
  
          if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
                  != AppOpsManager.MODE_ALLOWED) {
              if (!ignoreTargetSecurity) {
                  return ACTIVITY_RESTRICTION_APPOP;
              }
          }
  
          return ACTIVITY_RESTRICTION_NONE;
      }
  
      private int getActionRestrictionForCallingPackage(String action,
              String callingPackage, int callingPid, int callingUid) {
          if (action == null) {
              return ACTIVITY_RESTRICTION_NONE;
          }
  
          String permission = ACTION_TO_RUNTIME_PERMISSION.get(action);
          if (permission == null) {
              return ACTIVITY_RESTRICTION_NONE;
          }
  
          final PackageInfo packageInfo;
          try {
              packageInfo = mService.mContext.getPackageManager()
                      .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS);
          } catch (PackageManager.NameNotFoundException e) {
              Slog.i(TAG, "Cannot find package info for " + callingPackage);
              return ACTIVITY_RESTRICTION_NONE;
          }
  
          if (!ArrayUtils.contains(packageInfo.requestedPermissions, permission)) {
              return ACTIVITY_RESTRICTION_NONE;
          }
  
          if (mService.checkPermission(permission, callingPid, callingUid) == PERMISSION_DENIED) {
              return ACTIVITY_RESTRICTION_PERMISSION;
          }
  
          final int opCode = AppOpsManager.permissionToOpCode(permission);
          if (opCode == AppOpsManager.OP_NONE) {
              return ACTIVITY_RESTRICTION_NONE;
          }
  
          if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
                  != AppOpsManager.MODE_ALLOWED) {
              return ACTIVITY_RESTRICTION_APPOP;
          }
  
          return ACTIVITY_RESTRICTION_NONE;
      }
  
      void setLaunchSource(int uid) {
          mLaunchingActivityWakeLock.setWorkSource(new WorkSource(uid));
      }
  
      void acquireLaunchWakelock() {
          if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
              throw new IllegalStateException("Calling must be system uid");
          }
          mLaunchingActivityWakeLock.acquire();
          if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
              // To be safe, don't allow the wake lock to be held for too long.
              mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
          }
      }
  
      /**
       * Called when all resumed tasks/stacks are idle.
       * @return the state of mService.mAm.mBooting before this was called.
       */
      @GuardedBy("mService")
      private boolean checkFinishBootingLocked() {
          final boolean booting = mService.isBooting();
          boolean enableScreen = false;
          mService.setBooting(false);
          if (!mService.isBooted()) {
              mService.setBooted(true);
              enableScreen = true;
          }
          if (booting || enableScreen) {
              mService.postFinishBooting(booting, enableScreen);
          }
          return booting;
      }
  
      // Checked.
      @GuardedBy("mService")
      final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
              boolean processPausingActivities, Configuration config) {
          if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
  
          ArrayList<ActivityRecord> finishes = null;
          ArrayList<UserState> startingUsers = null;
          int NS = 0;
          int NF = 0;
          boolean booting = false;
          boolean activityRemoved = false;
  
          ActivityRecord r = ActivityRecord.forTokenLocked(token);
          if (r != null) {
              if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
                      + Debug.getCallers(4));
              mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
              r.finishLaunchTickingLocked();
              if (fromTimeout) {
                  reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY,
                          -1 /* launchState */);
              }
  
              // This is a hack to semi-deal with a race condition
              // in the client where it can be constructed with a
              // newer configuration from when we asked it to launch.
              // We'll update with whatever configuration it now says
              // it used to launch.
              if (config != null) {
                  r.setLastReportedGlobalConfiguration(config);
              }
  
              // We are now idle.  If someone is waiting for a thumbnail from
              // us, we can now deliver.
              r.idle = true;
  
              //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
  
              // Check if able to finish booting when device is booting and all resumed activities
              // are idle.
              if ((mService.isBooting() && mRootActivityContainer.allResumedActivitiesIdle())
                      || fromTimeout) {
                  booting = checkFinishBootingLocked();
              }
  
              // When activity is idle, we consider the relaunch must be successful, so let's clear
              // the flag.
              r.mRelaunchReason = RELAUNCH_REASON_NONE;
          }
  
          if (mRootActivityContainer.allResumedActivitiesIdle()) {
              if (r != null) {
                  mService.scheduleAppGcsLocked();
              }
  
              if (mLaunchingActivityWakeLock.isHeld()) {
                  mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
                  if (VALIDATE_WAKE_LOCK_CALLER &&
                          Binder.getCallingUid() != Process.myUid()) {
                      throw new IllegalStateException("Calling must be system uid");
                  }
                  mLaunchingActivityWakeLock.release();
              }
              mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
          }
  
          // Atomically retrieve all of the other things to do.
          final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
                  true /* remove */, processPausingActivities);
          NS = stops != null ? stops.size() : 0;
          if ((NF = mFinishingActivities.size()) > 0) {
              finishes = new ArrayList<>(mFinishingActivities);
              mFinishingActivities.clear();
          }
  
          if (mStartingUsers.size() > 0) {
              startingUsers = new ArrayList<>(mStartingUsers);
              mStartingUsers.clear();
          }
  
          // Stop any activities that are scheduled to do so but have been
          // waiting for the next one to start.
          for (int i = 0; i < NS; i++) {
              r = stops.get(i);
              final ActivityStack stack = r.getActivityStack();
              if (stack != null) {
                  if (r.finishing) {
                      stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
                              "activityIdleInternalLocked");
                  } else {
                      stack.stopActivityLocked(r);
                  }
              }
          }
  
          // Finish any activities that are scheduled to do so but have been
          // waiting for the next one to start.
          for (int i = 0; i < NF; i++) {
              r = finishes.get(i);
              final ActivityStack stack = r.getActivityStack();
              if (stack != null) {
                  activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
              }
          }
  
          if (!booting) {
              // Complete user switch
              if (startingUsers != null) {
                  for (int i = 0; i < startingUsers.size(); i++) {
                      mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
                  }
              }
          }
  
          mService.mH.post(() -> mService.mAmInternal.trimApplications());
          //dump();
          //mWindowManager.dump();
  
          if (activityRemoved) {
              mRootActivityContainer.resumeFocusedStacksTopActivities();
          }
  
          return r;
      }
  
      /** This doesn't just find a task, it also moves the task to front. */
      void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
              boolean forceNonResizeable) {
          ActivityStack currentStack = task.getStack();
          if (currentStack == null) {
              Slog.e(TAG, "findTaskToMoveToFront: can't move task="
                      + task + " to front. Stack is null");
              return;
          }
  
          if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
              mUserLeaving = true;
          }
  
          reason = reason + " findTaskToMoveToFront";
          boolean reparented = false;
          if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
              final Rect bounds = options.getLaunchBounds();
              task.updateOverrideConfiguration(bounds);
  
              ActivityStack stack =
                      mRootActivityContainer.getLaunchStack(null, options, task, ON_TOP);
  
              if (stack != currentStack) {
                  moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
                  task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
                          reason);
                  currentStack = stack;
                  reparented = true;
                  // task.reparent() should already placed the task on top,
                  // still need moveTaskToFrontLocked() below for any transition settings.
              }
              if (stack.resizeStackWithLaunchBounds()) {
                  mRootActivityContainer.resizeStack(stack, bounds, null /* tempTaskBounds */,
                          null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
                          true /* allowResizeInDockedMode */, !DEFER_RESUME);
              } else {
                  // WM resizeTask must be done after the task is moved to the correct stack,
                  // because Task's setBounds() also updates dim layer's bounds, but that has
                  // dependency on the stack.
                  task.resizeWindowContainer();
              }
          }
  
          if (!reparented) {
              moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplay(), reason);
          }
  
          final ActivityRecord r = task.getTopActivity();
          currentStack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
                  r == null ? null : r.appTimeTracker, reason);
  
          if (DEBUG_STACK) Slog.d(TAG_STACK,
                  "findTaskToMoveToFront: moved to front of stack=" + currentStack);
  
          handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY,
                  currentStack, forceNonResizeable);
      }
  
      private void moveHomeStackToFrontIfNeeded(int flags, ActivityDisplay display, String reason) {
          final ActivityStack focusedStack = display.getFocusedStack();
  
          if ((display.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                  && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
                  || (focusedStack != null && focusedStack.isActivityTypeRecents())) {
              // We move home stack to front when we are on a fullscreen display and caller has
              // requested the home activity to move with it. Or the previous stack is recents.
              display.moveHomeStackToFront(reason);
          }
      }
  
      boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
          // We use the launch bounds in the activity options is the device supports freeform
          // window management or is launching into the pinned stack.
          if (options == null || options.getLaunchBounds() == null) {
              return false;
          }
          return (mService.mSupportsPictureInPicture
                  && options.getLaunchWindowingMode() == WINDOWING_MODE_PINNED)
                  || mService.mSupportsFreeformWindowManagement;
      }
  
      LaunchParamsController getLaunchParamsController() {
          return mLaunchParamsController;
      }
  
      private void deferUpdateRecentsHomeStackBounds() {
          mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
          mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME);
      }
  
      private void continueUpdateRecentsHomeStackBounds() {
          mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
          mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME);
      }
  
      void notifyAppTransitionDone() {
          continueUpdateRecentsHomeStackBounds();
          for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
              final int taskId = mResizingTasksDuringAnimation.valueAt(i);
              final TaskRecord task =
                      mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY);
              if (task != null) {
                  task.setTaskDockedResizing(false);
              }
          }
          mResizingTasksDuringAnimation.clear();
      }
  
      /**
       * TODO: This should just change the windowing mode and resize vs. actually moving task around.
       * Can do that once we are no longer using static stack ids.
       */
      private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
              int toDisplayId, boolean onTop) {
  
          mWindowManager.deferSurfaceLayout();
          try {
              final int windowingMode = fromStack.getWindowingMode();
              final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
              final ActivityDisplay toDisplay =
                      mRootActivityContainer.getActivityDisplay(toDisplayId);
  
              if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                  // Tell the display we are exiting split-screen mode.
                  toDisplay.onExitingSplitScreenMode();
                  // We are moving all tasks from the docked stack to the fullscreen stack,
                  // which is dismissing the docked stack, so resize all other stacks to
                  // fullscreen here already so we don't end up with resize trashing.
                  for (int i = toDisplay.getChildCount() - 1; i >= 0; --i) {
                      final ActivityStack otherStack = toDisplay.getChildAt(i);
                      if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
                          continue;
                      }
                      otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
                  }
  
                  // Also disable docked stack resizing since we have manually adjusted the
                  // size of other stacks above and we don't want to trigger a docked stack
                  // resize when we remove task from it below and it is detached from the
                  // display because it no longer contains any tasks.
                  mAllowDockedStackResize = false;
              }
  
              // If we are moving from the pinned stack, then the animation takes care of updating
              // the picture-in-picture mode.
              final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
              final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
  
              if (!tasks.isEmpty()) {
                  mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
                  final int size = tasks.size();
                  for (int i = 0; i < size; ++i) {
                      final TaskRecord task = tasks.get(i);
                      final ActivityStack toStack = toDisplay.getOrCreateStack(
                                  null, mTmpOptions, task, task.getActivityType(), onTop);
  
                      if (onTop) {
                          final boolean isTopTask = i == (size - 1);
                          // Defer resume until all the tasks have been moved to the fullscreen stack
                          task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
                                  isTopTask /* animate */, DEFER_RESUME,
                                  schedulePictureInPictureModeChange,
                                  "moveTasksToFullscreenStack - onTop");
                          MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
                                  task.effectiveUid, task.realActivity.flattenToString());
                      } else {
                          // Position the tasks in the fullscreen stack in order at the bottom of the
                          // stack. Also defer resume until all the tasks have been moved to the
                          // fullscreen stack.
                          task.reparent(toStack, ON_TOP,
                                  REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
                                  schedulePictureInPictureModeChange,
                                  "moveTasksToFullscreenStack - NOT_onTop");
                      }
                  }
              }
  
              mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
              mRootActivityContainer.resumeFocusedStacksTopActivities();
          } finally {
              mAllowDockedStackResize = true;
              mWindowManager.continueSurfaceLayout();
          }
      }
  
      void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
          moveTasksToFullscreenStackLocked(fromStack, DEFAULT_DISPLAY, onTop);
      }
  
      void moveTasksToFullscreenStackLocked(ActivityStack fromStack, int toDisplayId, boolean onTop) {
          mWindowManager.inSurfaceTransaction(() ->
                  moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
      }
  
      void setSplitScreenResizing(boolean resizing) {
          if (resizing == mDockedStackResizing) {
              return;
          }
  
          mDockedStackResizing = resizing;
          mWindowManager.setDockedStackResizing(resizing);
  
          if (!resizing && mHasPendingDockedBounds) {
              resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds,
                      mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds,
                      mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS);
  
              mHasPendingDockedBounds = false;
              mPendingDockedBounds = null;
              mPendingTempDockedTaskBounds = null;
              mPendingTempDockedTaskInsetBounds = null;
              mPendingTempOtherTaskBounds = null;
              mPendingTempOtherTaskInsetBounds = null;
          }
      }
  
      void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
              Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
              boolean preserveWindows) {
          resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
                  tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows,
                  false /* deferResume */);
      }
  
      void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
              Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
              boolean preserveWindows, boolean deferResume) {
  
          if (!mAllowDockedStackResize) {
              // Docked stack resize currently disabled.
              return;
          }
  
          final ActivityStack stack =
                  mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
          if (stack == null) {
              Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
              return;
          }
  
          if (mDockedStackResizing) {
              mHasPendingDockedBounds = true;
              mPendingDockedBounds = copyOrNull(dockedBounds);
              mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
              mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
              mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
              mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
          }
  
          Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
          mWindowManager.deferSurfaceLayout();
          try {
              // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
              mAllowDockedStackResize = false;
              ActivityRecord r = stack.topRunningActivityLocked();
              stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds);
  
              // TODO: Checking for isAttached might not be needed as if the user passes in null
              // dockedBounds then they want the docked stack to be dismissed.
              if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                      || (dockedBounds == null && !stack.isAttached())) {
                  // The dock stack either was dismissed or went fullscreen, which is kinda the same.
                  // In this case we make all other static stacks fullscreen and move all
                  // docked stack tasks to the fullscreen stack.
                  moveTasksToFullscreenStackLocked(stack, ON_TOP);
  
                  // stack shouldn't contain anymore activities, so nothing to resume.
                  r = null;
              } else {
                  // Docked stacks occupy a dedicated region on screen so the size of all other
                  // static stacks need to be adjusted so they don't overlap with the docked stack.
                  // We get the bounds to use from window manager which has been adjusted for any
                  // screen controls and is also the same for all stacks.
                  final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
                  final Rect otherTaskRect = new Rect();
                  for (int i = display.getChildCount() - 1; i >= 0; --i) {
                      final ActivityStack current = display.getChildAt(i);
                      if (!current.inSplitScreenSecondaryWindowingMode()) {
                          continue;
                      }
                      if (!current.affectedBySplitScreenResize()) {
                          continue;
                      }
                      if (mDockedStackResizing && !current.isTopActivityVisible()) {
                          // Non-visible stacks get resized once we're done with the resize
                          // interaction.
                          continue;
                      }
                      current.getStackDockedModeBounds(dockedBounds,
                              tempOtherTaskBounds /* currentTempTaskBounds */,
                              tempRect /* outStackBounds */,
                              otherTaskRect /* outTempTaskBounds */);
  
                      mRootActivityContainer.resizeStack(current,
                              !tempRect.isEmpty() ? tempRect : null,
                              !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
                              tempOtherTaskInsetBounds, preserveWindows,
                              true /* allowResizeInDockedMode */, deferResume);
                  }
              }
              if (!deferResume) {
                  stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
              }
          } finally {
              mAllowDockedStackResize = true;
              mWindowManager.continueSurfaceLayout();
              Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
          }
      }
  
      void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
          // TODO(multi-display): The display containing the stack should be passed in.
          final ActivityStack stack =
                  mRootActivityContainer.getDefaultDisplay().getPinnedStack();
          if (stack == null) {
              Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
              return;
          }
  
          // It is possible for the bounds animation from the WM to call this but be delayed by
          // another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be
          // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
          // for the AMS lock to be freed. So check and make sure these bounds are still good.
          final TaskStack stackController = stack.getTaskStack();
          if (stackController.pinnedStackResizeDisallowed()) {
              return;
          }
  
          Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
          mWindowManager.deferSurfaceLayout();
          try {
              ActivityRecord r = stack.topRunningActivityLocked();
              Rect insetBounds = null;
              if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) {
                  // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
                  insetBounds = tempRect;
                  insetBounds.top = 0;
                  insetBounds.left = 0;
                  insetBounds.right = tempPinnedTaskBounds.width();
                  insetBounds.bottom = tempPinnedTaskBounds.height();
              }
              if (pinnedBounds != null && tempPinnedTaskBounds == null) {
                  // We have finished the animation into PiP, and are resizing the tasks to match the
                  // stack bounds, while layouts are deferred, update any task state as a part of
                  // transitioning it from fullscreen into a floating state.
                  stack.onPipAnimationEndResize();
              }
              stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds);
              stack.ensureVisibleActivitiesConfigurationLocked(r, false);
          } finally {
              mWindowManager.continueSurfaceLayout();
              Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
          }
      }
  
      private void removeStackInSurfaceTransaction(ActivityStack stack) {
          final ArrayList<TaskRecord> tasks = stack.getAllTasks();
          if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
              /**
               * Workaround: Force-stop all the activities in the pinned stack before we reparent them
               * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
               * that the client receives onStop() before it is reparented.  We do this by detaching
               * the stack from the display so that it will be considered invisible when
               * ensureActivitiesVisibleLocked() is called, and all of its activitys will be marked
               * invisible as well and added to the stopping list.  After which we process the
               * stopping list by handling the idle.
               */
              stack.mForceHidden = true;
              stack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
              stack.mForceHidden = false;
              activityIdleInternalLocked(null, false /* fromTimeout */,
                      true /* processPausingActivites */, null /* configuration */);
  
              // Move all the tasks to the bottom of the fullscreen stack
              moveTasksToFullscreenStackLocked(stack, !ON_TOP);
          } else {
              for (int i = tasks.size() - 1; i >= 0; i--) {
                  removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
                          REMOVE_FROM_RECENTS, "remove-stack");
              }
          }
      }
  
      /**
       * Removes the stack associated with the given {@param stack}. If the {@param stack} is the
       * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
       * instead moved back onto the fullscreen stack.
       */
      void removeStack(ActivityStack stack) {
          mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stack));
      }
  
      /**
       * See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
       */
      boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
              String reason) {
          return removeTaskByIdLocked(taskId, killProcess, removeFromRecents, !PAUSE_IMMEDIATELY,
                  reason);
      }
  
      /**
       * Removes the task with the specified task id.
       *
       * @param taskId Identifier of the task to be removed.
       * @param killProcess Kill any process associated with the task if possible.
       * @param removeFromRecents Whether to also remove the task from recents.
       * @param pauseImmediately Pauses all task activities immediately without waiting for the
       *                         pause-complete callback from the activity.
       * @return Returns true if the given task was found and removed.
       */
      boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
              boolean pauseImmediately, String reason) {
          final TaskRecord tr =
                  mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
          if (tr != null) {
              tr.removeTaskActivitiesLocked(pauseImmediately, reason);
              cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
              mService.getLockTaskController().clearLockedTask(tr);
              if (tr.isPersistable) {
                  mService.notifyTaskPersisterLocked(null, true);
              }
              return true;
          }
          Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
          return false;
      }
  
      void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
          if (removeFromRecents) {
              mRecentTasks.remove(tr);
          }
          ComponentName component = tr.getBaseIntent().getComponent();
          if (component == null) {
              Slog.w(TAG, "No component for base intent of task: " + tr);
              return;
          }
  
          // Find any running services associated with this app and stop if needed.
          final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::cleanUpServices,
                  mService.mAmInternal, tr.userId, component, new Intent(tr.getBaseIntent()));
          mService.mH.sendMessage(msg);
  
          if (!killProcess) {
              return;
          }
  
          // Determine if the process(es) for this task should be killed.
          final String pkg = component.getPackageName();
          ArrayList<Object> procsToKill = new ArrayList<>();
          ArrayMap<String, SparseArray<WindowProcessController>> pmap =
                  mService.mProcessNames.getMap();
          for (int i = 0; i < pmap.size(); i++) {
  
              SparseArray<WindowProcessController> uids = pmap.valueAt(i);
              for (int j = 0; j < uids.size(); j++) {
                  WindowProcessController proc = uids.valueAt(j);
                  if (proc.mUserId != tr.userId) {
                      // Don't kill process for a different user.
                      continue;
                  }
                  if (proc == mService.mHomeProcess) {
                      // Don't kill the home process along with tasks from the same package.
                      continue;
                  }
                  if (!proc.mPkgList.contains(pkg)) {
                      // Don't kill process that is not associated with this task.
                      continue;
                  }
  
                  if (!proc.shouldKillProcessForRemovedTask(tr)) {
                      // Don't kill process(es) that has an activity in a different task that is also
                      // in recents, or has an activity not stopped.
                      return;
                  }
  
                  if (proc.hasForegroundServices()) {
                      // Don't kill process(es) with foreground service.
                      return;
                  }
  
                  // Add process to kill list.
                  procsToKill.add(proc);
              }
          }
  
          // Kill the running processes. Post on handle since we don't want to hold the service lock
          // while calling into AM.
          final Message m = PooledLambda.obtainMessage(
                  ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal,
                  procsToKill);
          mService.mH.sendMessage(m);
      }
  
      /**
       * Called to restore the state of the task into the stack that it's supposed to go into.
       *
       * @param task The recent task to be restored.
       * @param aOptions The activity options to use for restoration.
       * @param onTop If the stack for the task should be the topmost on the display.
       * @return true if the task has been restored successfully.
       */
      boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) {
          final ActivityStack stack =
                  mRootActivityContainer.getLaunchStack(null, aOptions, task, onTop);
          final ActivityStack currentStack = task.getStack();
          if (currentStack != null) {
              // Task has already been restored once. See if we need to do anything more
              if (currentStack == stack) {
                  // Nothing else to do since it is already restored in the right stack.
                  return true;
              }
              // Remove current stack association, so we can re-associate the task with the
              // right stack below.
              currentStack.removeTask(task, "restoreRecentTaskLocked", REMOVE_TASK_MODE_MOVING);
          }
  
          stack.addTask(task, onTop, "restoreRecentTask");
          // TODO: move call for creation here and other place into Stack.addTask()
          task.createTask(onTop, true /* showForAllUsers */);
          if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
                  "Added restored task=" + task + " to stack=" + stack);
          final ArrayList<ActivityRecord> activities = task.mActivities;
          for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
              activities.get(activityNdx).createAppWindowToken();
          }
          return true;
      }
  
      @Override
      public void onRecentTaskAdded(TaskRecord task) {
          task.touchActiveTime();
      }
  
      @Override
      public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
          if (wasTrimmed) {
              // Task was trimmed from the recent tasks list -- remove the active task record as well
              // since the user won't really be able to go back to it
              removeTaskByIdLocked(task.taskId, killProcess, false /* removeFromRecents */,
                      !PAUSE_IMMEDIATELY, "recent-task-trimmed");
          }
          task.removedFromRecents();
      }
  
      /**
       * Returns the reparent target stack, creating the stack if necessary.  This call also enforces
       * the various checks on tasks that are going to be reparented from one stack to another.
       */
      // TODO: Look into changing users to this method to ActivityDisplay.resolveWindowingMode()
      ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) {
          final ActivityStack prevStack = task.getStack();
          final int stackId = stack.mStackId;
          final boolean inMultiWindowMode = stack.inMultiWindowMode();
  
          // Check that we aren't reparenting to the same stack that the task is already in
          if (prevStack != null && prevStack.mStackId == stackId) {
              Slog.w(TAG, "Can not reparent to same stack, task=" + task
                      + " already in stackId=" + stackId);
              return prevStack;
          }
  
          // Ensure that we aren't trying to move into a multi-window stack without multi-window
          // support
          if (inMultiWindowMode && !mService.mSupportsMultiWindow) {
              throw new IllegalArgumentException("Device doesn't support multi-window, can not"
                      + " reparent task=" + task + " to stack=" + stack);
          }
  
          // Ensure that we're not moving a task to a dynamic stack if device doesn't support
          // multi-display.
          if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
              throw new IllegalArgumentException("Device doesn't support multi-display, can not"
                      + " reparent task=" + task + " to stackId=" + stackId);
          }
  
          // Ensure that we aren't trying to move into a freeform stack without freeform support
          if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
                  && !mService.mSupportsFreeformWindowManagement) {
              throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
                      + " task=" + task);
          }
  
          // Leave the task in its current stack or a fullscreen stack if it isn't resizeable and the
          // preferred stack is in multi-window mode.
          if (inMultiWindowMode && !task.isResizeable()) {
              Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window stack=" + stack
                      + " Moving to a fullscreen stack instead.");
              if (prevStack != null) {
                  return prevStack;
              }
              stack = stack.getDisplay().createStack(
                      WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
          }
          return stack;
      }
  
      void goingToSleepLocked() {
          scheduleSleepTimeout();
          if (!mGoingToSleepWakeLock.isHeld()) {
              mGoingToSleepWakeLock.acquire();
              if (mLaunchingActivityWakeLock.isHeld()) {
                  if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
                      throw new IllegalStateException("Calling must be system uid");
                  }
                  mLaunchingActivityWakeLock.release();
                  mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
              }
          }
  
          mRootActivityContainer.applySleepTokens(false /* applyToStacks */);
  
          checkReadyForSleepLocked(true /* allowDelay */);
      }
  
      boolean shutdownLocked(int timeout) {
          goingToSleepLocked();
  
          boolean timedout = false;
          final long endTime = System.currentTimeMillis() + timeout;
          while (true) {
              if (!mRootActivityContainer.putStacksToSleep(
                      true /* allowDelay */, true /* shuttingDown */)) {
                  long timeRemaining = endTime - System.currentTimeMillis();
                  if (timeRemaining > 0) {
                      try {
                          mService.mGlobalLock.wait(timeRemaining);
                      } catch (InterruptedException e) {
                      }
                  } else {
                      Slog.w(TAG, "Activity manager shutdown timed out");
                      timedout = true;
                      break;
                  }
              } else {
                  break;
              }
          }
  
          // Force checkReadyForSleep to complete.
          checkReadyForSleepLocked(false /* allowDelay */);
  
          return timedout;
      }
  
      void comeOutOfSleepIfNeededLocked() {
          removeSleepTimeouts();
          if (mGoingToSleepWakeLock.isHeld()) {
              mGoingToSleepWakeLock.release();
          }
      }
  
      void activitySleptLocked(ActivityRecord r) {
          mGoingToSleepActivities.remove(r);
          final ActivityStack s = r.getActivityStack();
          if (s != null) {
              s.checkReadyForSleep();
          } else {
              checkReadyForSleepLocked(true);
          }
      }
  
      void checkReadyForSleepLocked(boolean allowDelay) {
          if (!mService.isSleepingOrShuttingDownLocked()) {
              // Do not care.
              return;
          }
  
          if (!mRootActivityContainer.putStacksToSleep(
                  allowDelay, false /* shuttingDown */)) {
              return;
          }
  
          // Send launch end powerhint before going sleep
          mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
  
          removeSleepTimeouts();
  
          if (mGoingToSleepWakeLock.isHeld()) {
              mGoingToSleepWakeLock.release();
          }
          if (mService.mShuttingDown) {
              mService.mGlobalLock.notifyAll();
          }
      }
  
      boolean reportResumedActivityLocked(ActivityRecord r) {
          // A resumed activity cannot be stopping. remove from list
          mStoppingActivities.remove(r);
  
          final ActivityStack stack = r.getActivityStack();
          if (stack.getDisplay().allResumedActivitiesComplete()) {
              mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
              // Make sure activity & window visibility should be identical
              // for all displays in this stage.
              mRootActivityContainer.executeAppTransitionForAllDisplay();
              return true;
          }
          return false;
      }
  
      // Called when WindowManager has finished animating the launchingBehind activity to the back.
      private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
          final TaskRecord task = r.getTaskRecord();
          final ActivityStack stack = task.getStack();
  
          r.mLaunchTaskBehind = false;
          mRecentTasks.add(task);
          mService.getTaskChangeNotificationController().notifyTaskStackChanged();
          r.setVisibility(false);
  
          // When launching tasks behind, update the last active time of the top task after the new
          // task has been shown briefly
          final ActivityRecord top = stack.getTopActivity();
          if (top != null) {
              top.getTaskRecord().touchActiveTime();
          }
      }
  
      void scheduleLaunchTaskBehindComplete(IBinder token) {
          mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
      }
  
      /** Checks whether the userid is a profile of the current user. */
      boolean isCurrentProfileLocked(int userId) {
          if (userId == mRootActivityContainer.mCurrentUser) return true;
          return mService.mAmInternal.isCurrentProfile(userId);
      }
  
      /**
       * Returns whether a stopping activity is present that should be stopped after visible, rather
       * than idle.
       * @return {@code true} if such activity is present. {@code false} otherwise.
       */
      boolean isStoppingNoHistoryActivity() {
          // Activities that are marked as nohistory should be stopped immediately after the resumed
          // activity has become visible.
          for (ActivityRecord record : mStoppingActivities) {
              if (record.isNoHistory()) {
                  return true;
              }
          }
  
          return false;
      }
  
      final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
              boolean remove, boolean processPausingActivities) {
          ArrayList<ActivityRecord> stops = null;
  
          final boolean nowVisible = mRootActivityContainer.allResumedActivitiesVisible();
          for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
              ActivityRecord s = mStoppingActivities.get(activityNdx);
  
              final boolean animating = s.mAppWindowToken.isSelfAnimating();
  
              if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
                      + " animating=" + animating + " finishing=" + s.finishing);
              if (nowVisible && s.finishing) {
  
                  // If this activity is finishing, it is sitting on top of
                  // everyone else but we now know it is no longer needed...
                  // so get rid of it.  Otherwise, we need to go through the
                  // normal flow and hide it once we determine that it is
                  // hidden by the activities in front of it.
                  if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
                  s.setVisibility(false);
              }
              if (remove) {
                  final ActivityStack stack = s.getActivityStack();
                  final boolean shouldSleepOrShutDown = stack != null
                          ? stack.shouldSleepOrShutDownActivities()
                          : mService.isSleepingOrShuttingDownLocked();
                  if (!animating || shouldSleepOrShutDown) {
                      if (!processPausingActivities && s.isState(PAUSING)) {
                          // Defer processing pausing activities in this iteration and reschedule
                          // a delayed idle to reprocess it again
                          removeTimeoutsForActivityLocked(idleActivity);
                          scheduleIdleTimeoutLocked(idleActivity);
                          continue;
                      }
  
                      if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
                      if (stops == null) {
                          stops = new ArrayList<>();
                      }
                      stops.add(s);
  
                      mStoppingActivities.remove(activityNdx);
                  }
              }
          }
  
          return stops;
      }
  
      public void dump(PrintWriter pw, String prefix) {
          pw.println();
          pw.println("ActivityStackSupervisor state:");
          mRootActivityContainer.dump(pw, prefix);
          pw.print(prefix);
          pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
          pw.println(prefix + "mUserStackInFront=" + mRootActivityContainer.mUserStackInFront);
          if (!mWaitingForActivityVisible.isEmpty()) {
              pw.println(prefix + "mWaitingForActivityVisible=");
              for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
                  pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
              }
          }
          pw.print(prefix); pw.print("isHomeRecentsComponent=");
          pw.print(mRecentTasks.isRecentsComponentHomeActivity(mRootActivityContainer.mCurrentUser));
  
          getKeyguardController().dump(pw, prefix);
          mService.getLockTaskController().dump(pw, prefix);
      }
  
      static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
              boolean needSep, String prefix) {
          if (activity != null) {
              if (dumpPackage == null || dumpPackage.equals(activity.packageName)) {
                  if (needSep) {
                      pw.println();
                  }
                  pw.print(prefix);
                  pw.println(activity);
                  return true;
              }
          }
          return false;
      }
  
      static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
              String prefix, String label, boolean complete, boolean brief, boolean client,
              String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
          String innerPrefix = null;
          String[] args = null;
          boolean printed = false;
          for (int i=list.size()-1; i>=0; i--) {
              final ActivityRecord r = list.get(i);
              if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
                  continue;
              }
              if (innerPrefix == null) {
                  innerPrefix = prefix + "      ";
                  args = new String[0];
              }
              printed = true;
              final boolean full = !brief && (complete || !r.isInHistory());
              if (needNL) {
                  pw.println("");
                  needNL = false;
              }
              if (header != null) {
                  pw.println(header);
                  header = null;
              }
              if (lastTask != r.getTaskRecord()) {
                  lastTask = r.getTaskRecord();
                  pw.print(prefix);
                  pw.print(full ? "* " : "  ");
                  pw.println(lastTask);
                  if (full) {
                      lastTask.dump(pw, prefix + "  ");
                  } else if (complete) {
                      // Complete + brief == give a summary.  Isn't that obvious?!?
                      if (lastTask.intent != null) {
                          pw.print(prefix); pw.print("  ");
                                  pw.println(lastTask.intent.toInsecureStringWithClip());
                      }
                  }
              }
              pw.print(prefix); pw.print(full ? "  * " : "    "); pw.print(label);
              pw.print(" #"); pw.print(i); pw.print(": ");
              pw.println(r);
              if (full) {
                  r.dump(pw, innerPrefix);
              } else if (complete) {
                  // Complete + brief == give a summary.  Isn't that obvious?!?
                  pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
                  if (r.app != null) {
                      pw.print(innerPrefix); pw.println(r.app);
                  }
              }
              if (client && r.attachedToProcess()) {
                  // flush anything that is already in the PrintWriter since the thread is going
                  // to write to the file descriptor directly
                  pw.flush();
                  try {
                      TransferPipe tp = new TransferPipe();
                      try {
                          r.app.getThread().dumpActivity(
                                  tp.getWriteFd(), r.appToken, innerPrefix, args);
                          // Short timeout, since blocking here can deadlock with the application.
                          tp.go(fd, 2000);
                      } finally {
                          tp.kill();
                      }
                  } catch (IOException e) {
                      pw.println(innerPrefix + "Failure while dumping the activity: " + e);
                  } catch (RemoteException e) {
                      pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
                  }
                  needNL = true;
              }
          }
          return printed;
      }
  
      void scheduleIdleTimeoutLocked(ActivityRecord next) {
          if (DEBUG_IDLE) Slog.d(TAG_IDLE,
                  "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
          Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
          mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
      }
  
      final void scheduleIdleLocked() {
          mHandler.sendEmptyMessage(IDLE_NOW_MSG);
      }
  
      /**
       * Updates the record of top resumed activity when it changes and handles reporting of the
       * state changes to previous and new top activities. It will immediately dispatch top resumed
       * state loss message to previous top activity (if haven't done it already). After the previous
       * activity releases the top state and reports back, message about acquiring top state will be
       * sent to the new top resumed activity.
       */
      void updateTopResumedActivityIfNeeded() {
          final ActivityRecord prevTopActivity = mTopResumedActivity;
          final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
          if (topStack == null || topStack.mResumedActivity == prevTopActivity) {
              return;
          }
  
          // Ask previous activity to release the top state.
          final boolean prevActivityReceivedTopState =
                  prevTopActivity != null && !mTopResumedActivityWaitingForPrev;
          // mTopResumedActivityWaitingForPrev == true at this point would mean that an activity
          // before the prevTopActivity one hasn't reported back yet. So server never sent the top
          // resumed state change message to prevTopActivity.
          if (prevActivityReceivedTopState
                  && prevTopActivity.scheduleTopResumedActivityChanged(false /* onTop */)) {
              scheduleTopResumedStateLossTimeout(prevTopActivity);
              mTopResumedActivityWaitingForPrev = true;
          }
  
          // Update the current top activity.
          mTopResumedActivity = topStack.mResumedActivity;
          scheduleTopResumedActivityStateIfNeeded();
      }
  
      /** Schedule top resumed state change if previous top activity already reported back. */
      private void scheduleTopResumedActivityStateIfNeeded() {
          if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev) {
              mTopResumedActivity.scheduleTopResumedActivityChanged(true /* onTop */);
          }
      }
  
      /**
       * Limit the time given to the app to report handling of the state loss.
       */
      private void scheduleTopResumedStateLossTimeout(ActivityRecord r) {
          final Message msg = mHandler.obtainMessage(TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG);
          msg.obj = r;
          r.topResumedStateLossTime = SystemClock.uptimeMillis();
          mHandler.sendMessageDelayed(msg, TOP_RESUMED_STATE_LOSS_TIMEOUT);
          if (DEBUG_STATES) Slog.v(TAG_STATES, "Waiting for top state to be released by " + r);
      }
  
      /**
       * Handle a loss of top resumed state by an activity - update internal state and inform next top
       * activity if needed.
       */
      void handleTopResumedStateReleased(boolean timeout) {
          if (DEBUG_STATES) {
              Slog.v(TAG_STATES, "Top resumed state released "
                      + (timeout ? " (due to timeout)" : " (transition complete)"));
          }
          mHandler.removeMessages(TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG);
          if (!mTopResumedActivityWaitingForPrev) {
              // Top resumed activity state loss already handled.
              return;
          }
          mTopResumedActivityWaitingForPrev = false;
          scheduleTopResumedActivityStateIfNeeded();
      }
  
      void removeTimeoutsForActivityLocked(ActivityRecord r) {
          if (DEBUG_IDLE) Slog.d(TAG_IDLE, "removeTimeoutsForActivity: Callers="
                  + Debug.getCallers(4));
          mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
      }
  
      final void scheduleResumeTopActivities() {
          if (!mHandler.hasMessages(RESUME_TOP_ACTIVITY_MSG)) {
              mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
          }
      }
  
      void removeSleepTimeouts() {
          mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
      }
  
      final void scheduleSleepTimeout() {
          removeSleepTimeouts();
          mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
      }
  
      void removeRestartTimeouts(ActivityRecord r) {
          mHandler.removeMessages(RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG, r);
      }
  
      final void scheduleRestartTimeout(ActivityRecord r) {
          removeRestartTimeouts(r);
          mHandler.sendMessageDelayed(mHandler.obtainMessage(RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG, r),
                  WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION);
      }
  
      void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
              int preferredDisplayId, ActivityStack actualStack) {
          handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
                  actualStack, false /* forceNonResizable */);
      }
  
      void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
              int preferredDisplayId, ActivityStack actualStack, boolean forceNonResizable) {
          final boolean isSecondaryDisplayPreferred =
                  (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
          final boolean inSplitScreenMode = actualStack != null
                  && actualStack.getDisplay().hasSplitScreenPrimaryStack();
          if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
                  && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
              return;
          }
  
          // Handle incorrect launch/move to secondary display if needed.
          if (isSecondaryDisplayPreferred) {
              final int actualDisplayId = task.getStack().mDisplayId;
              if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
                  throw new IllegalStateException("Task resolved to incompatible display");
              }
  
              final ActivityDisplay preferredDisplay =
                      mRootActivityContainer.getActivityDisplay(preferredDisplayId);
  
              final boolean singleTaskInstance = preferredDisplay != null
                      && preferredDisplay.isSingleTaskInstance();
  
              if (preferredDisplayId != actualDisplayId) {
                  // Suppress the warning toast if the preferredDisplay was set to singleTask.
                  // The singleTaskInstance displays will only contain one task and any attempt to
                  // launch new task will re-route to the default display.
                  if (singleTaskInstance) {
                      mService.getTaskChangeNotificationController()
                              .notifyActivityLaunchOnSecondaryDisplayRerouted(task.getTaskInfo(),
                                      preferredDisplayId);
                      return;
                  }
  
                  Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
                  // Display a warning toast that we failed to put a task on a secondary display.
                  mService.getTaskChangeNotificationController()
                          .notifyActivityLaunchOnSecondaryDisplayFailed(task.getTaskInfo(),
                                  preferredDisplayId);
              } else if (!forceNonResizable) {
                  handleForcedResizableTaskIfNeeded(task, FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY);
              }
              // The information about not support secondary display should already be notified, we
              // don't want to show another message on default display about split-screen. And it may
              // be the case that a resizable activity is launched on a non-resizable task.
              return;
          }
  
          if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
              // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
              // we need to move it to top of fullscreen stack, otherwise it will be covered.
  
              final ActivityStack dockedStack =
                      task.getStack().getDisplay().getSplitScreenPrimaryStack();
              if (dockedStack != null) {
                  // Display a warning toast that we tried to put an app that doesn't support
                  // split-screen in split-screen.
                  mService.getTaskChangeNotificationController()
                          .notifyActivityDismissingDockedStack();
                  moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
              }
              return;
          }
  
          handleForcedResizableTaskIfNeeded(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN);
      }
  
      /** Notifies that the top activity of the task is forced to be resizeable. */
      private void handleForcedResizableTaskIfNeeded(TaskRecord task, int reason) {
          final ActivityRecord topActivity = task.getTopActivity();
          if (topActivity == null || topActivity.noDisplay
                  || !topActivity.isNonResizableOrForcedResizable()) {
              return;
          }
          mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
                  task.taskId, reason, topActivity.appInfo.packageName);
      }
  
      void activityRelaunchedLocked(IBinder token) {
          mWindowManager.notifyAppRelaunchingFinished(token);
          final ActivityRecord r = ActivityRecord.isInStackLocked(token);
          if (r != null) {
              if (r.getActivityStack().shouldSleepOrShutDownActivities()) {
                  r.setSleeping(true, true);
              }
          }
      }
  
      void activityRelaunchingLocked(ActivityRecord r) {
          mWindowManager.notifyAppRelaunching(r.appToken);
      }
  
      void logStackState() {
          mActivityMetricsLogger.logWindowState();
      }
  
      void scheduleUpdateMultiWindowMode(TaskRecord task) {
          // If the stack is animating in a way where we will be forcing a multi-mode change at the
          // end, then ensure that we defer all in between multi-window mode changes
          if (task.getStack().deferScheduleMultiWindowModeChanged()) {
              return;
          }
  
          for (int i = task.mActivities.size() - 1; i >= 0; i--) {
              final ActivityRecord r = task.mActivities.get(i);
              if (r.attachedToProcess()) {
                  mMultiWindowModeChangedActivities.add(r);
              }
          }
  
          if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
              mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
          }
      }
  
      void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, ActivityStack prevStack) {
          final ActivityStack stack = task.getStack();
          if (prevStack == null || prevStack == stack
                  || (!prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode())) {
              return;
          }
  
          scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getRequestedOverrideBounds());
      }
  
      void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
          for (int i = task.mActivities.size() - 1; i >= 0; i--) {
              final ActivityRecord r = task.mActivities.get(i);
              if (r.attachedToProcess()) {
                  mPipModeChangedActivities.add(r);
                  // If we are scheduling pip change, then remove this activity from multi-window
                  // change list as the processing of pip change will make sure multi-window changed
                  // message is processed in the right order relative to pip changed.
                  mMultiWindowModeChangedActivities.remove(r);
              }
          }
          mPipModeChangedTargetStackBounds = targetStackBounds;
  
          if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
              mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
          }
      }
  
      void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
          mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
          for (int i = task.mActivities.size() - 1; i >= 0; i--) {
              final ActivityRecord r = task.mActivities.get(i);
              if (r.attachedToProcess()) {
                  r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
              }
          }
      }
  
      void wakeUp(String reason) {
          mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION,
                  "android.server.am:TURN_ON:" + reason);
      }
  
      /**
       * Begin deferring resume to avoid duplicate resumes in one pass.
       */
      void beginDeferResume() {
          mDeferResumeCount++;
      }
  
      /**
       * End deferring resume and determine if resume can be called.
       */
      void endDeferResume() {
          mDeferResumeCount--;
      }
  
      /** @return True if resume can be called. */
      boolean readyToResume() {
          return mDeferResumeCount == 0;
      }
  
      private final class ActivityStackSupervisorHandler extends Handler {
  
          public ActivityStackSupervisorHandler(Looper looper) {
              super(looper);
          }
  
          void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
              synchronized (mService.mGlobalLock) {
                  activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
                          processPausingActivities, null);
              }
          }
  
          @Override
          public void handleMessage(Message msg) {
              switch (msg.what) {
                  case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
                      synchronized (mService.mGlobalLock) {
                          for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
                              final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
                              r.updateMultiWindowMode();
                          }
                      }
                  } break;
                  case REPORT_PIP_MODE_CHANGED_MSG: {
                      synchronized (mService.mGlobalLock) {
                          for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
                              final ActivityRecord r = mPipModeChangedActivities.remove(i);
                              r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
                                      false /* forceUpdate */);
                          }
                      }
                  } break;
                  case IDLE_TIMEOUT_MSG: {
                      if (DEBUG_IDLE) Slog.d(TAG_IDLE,
                              "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
                      // We don't at this point know if the activity is fullscreen,
                      // so we need to be conservative and assume it isn't.
                      activityIdleInternal((ActivityRecord) msg.obj,
                              true /* processPausingActivities */);
                  } break;
                  case IDLE_NOW_MSG: {
                      if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
                      activityIdleInternal((ActivityRecord) msg.obj,
                              false /* processPausingActivities */);
                  } break;
                  case RESUME_TOP_ACTIVITY_MSG: {
                      synchronized (mService.mGlobalLock) {
                          mRootActivityContainer.resumeFocusedStacksTopActivities();
                      }
                  } break;
                  case SLEEP_TIMEOUT_MSG: {
                      synchronized (mService.mGlobalLock) {
                          if (mService.isSleepingOrShuttingDownLocked()) {
                              Slog.w(TAG, "Sleep timeout!  Sleeping now.");
                              checkReadyForSleepLocked(false /* allowDelay */);
                          }
                      }
                  } break;
                  case LAUNCH_TIMEOUT_MSG: {
                      synchronized (mService.mGlobalLock) {
                          if (mLaunchingActivityWakeLock.isHeld()) {
                              Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
                              if (VALIDATE_WAKE_LOCK_CALLER
                                      && Binder.getCallingUid() != Process.myUid()) {
                                  throw new IllegalStateException("Calling must be system uid");
                              }
                              mLaunchingActivityWakeLock.release();
                          }
                      }
                  } break;
                  case LAUNCH_TASK_BEHIND_COMPLETE: {
                      synchronized (mService.mGlobalLock) {
                          ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
                          if (r != null) {
                              handleLaunchTaskBehindCompleteLocked(r);
                          }
                      }
                  } break;
                  case RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG: {
                      final ActivityRecord r = (ActivityRecord) msg.obj;
                      String processName = null;
                      int uid = 0;
                      synchronized (mService.mGlobalLock) {
                          if (r.attachedToProcess()
                                  && r.isState(ActivityStack.ActivityState.RESTARTING_PROCESS)) {
                              processName = r.app.mName;
                              uid = r.app.mUid;
                          }
                      }
                      if (processName != null) {
                          mService.mAmInternal.killProcess(processName, uid,
                                  "restartActivityProcessTimeout");
                      }
                  } break;
                  case REPORT_HOME_CHANGED_MSG: {
                      synchronized (mService.mGlobalLock) {
                          mHandler.removeMessages(REPORT_HOME_CHANGED_MSG);
  
                          // Start home activities on displays with no activities.
                          mRootActivityContainer.startHomeOnEmptyDisplays("homeChanged");
                      }
                  } break;
                  case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: {
                      ActivityRecord r = (ActivityRecord) msg.obj;
                      Slog.w(TAG, "Activity top resumed state loss timeout for " + r);
                      synchronized (mService.mGlobalLock) {
                          if (r.hasProcess()) {
                              mService.logAppTooSlow(r.app, r.topResumedStateLossTime,
                                      "top state loss for " + r);
                          }
                      }
                      handleTopResumedStateReleased(true /* timeout */);
                  } break;
              }
          }
      }
  
      /**
       * Puts a task into resizing mode during the next app transition.
       *
       * @param task The task to put into resizing mode
       */
      void setResizingDuringAnimation(TaskRecord task) {
          mResizingTasksDuringAnimation.add(task.taskId);
          task.setTaskDockedResizing(true);
      }
  
      int startActivityFromRecents(int callingPid, int callingUid, int taskId,
              SafeActivityOptions options) {
          TaskRecord task = null;
          final String callingPackage;
          final Intent intent;
          final int userId;
          int activityType = ACTIVITY_TYPE_UNDEFINED;
          int windowingMode = WINDOWING_MODE_UNDEFINED;
          final ActivityOptions activityOptions = options != null
                  ? options.getOptions(this)
                  : null;
          if (activityOptions != null) {
              activityType = activityOptions.getLaunchActivityType();
              windowingMode = activityOptions.getLaunchWindowingMode();
              if (activityOptions.freezeRecentTasksReordering()
                      && mRecentTasks.isCallerRecents(callingUid)) {
                  mRecentTasks.setFreezeTaskListReordering();
              }
          }
          if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
              throw new IllegalArgumentException("startActivityFromRecents: Task "
                      + taskId + " can't be launch in the home/recents stack.");
          }
  
          mWindowManager.deferSurfaceLayout();
          try {
              if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                  mWindowManager.setDockedStackCreateState(
                          activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
  
                  // Defer updating the stack in which recents is until the app transition is done, to
                  // not run into issues where we still need to draw the task in recents but the
                  // docked stack is already created.
                  deferUpdateRecentsHomeStackBounds();
                  // TODO(multi-display): currently recents animation only support default display.
                  mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
              }
  
              task = mRootActivityContainer.anyTaskForId(taskId,
                      MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
              if (task == null) {
                  continueUpdateRecentsHomeStackBounds();
                  mWindowManager.executeAppTransition();
                  throw new IllegalArgumentException(
                          "startActivityFromRecents: Task " + taskId + " not found.");
              }
  
              if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                  // We always want to return to the home activity instead of the recents activity
                  // from whatever is started from the recents activity, so move the home stack
                  // forward.
                  // TODO (b/115289124): Multi-display supports for recents.
                  mRootActivityContainer.getDefaultDisplay().moveHomeStackToFront(
                          "startActivityFromRecents");
              }
  
              // If the user must confirm credentials (e.g. when first launching a work app and the
              // Work Challenge is present) let startActivityInPackage handle the intercepting.
              if (!mService.mAmInternal.shouldConfirmCredentials(task.userId)
                      && task.getRootActivity() != null) {
                  final ActivityRecord targetActivity = task.getTopActivity();
  
                  mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
                          true /* forceSend */, targetActivity);
                  mActivityMetricsLogger.notifyActivityLaunching(task.intent);
                  try {
                      mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */,
                              task.taskId, 0, options, true /* fromRecents */);
                      // Apply options to prevent pendingOptions be taken by client to make sure
                      // the override pending app transition will be applied immediately.
                      targetActivity.applyOptionsLocked();
                  } finally {
                      mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
                              targetActivity);
                  }
  
                  mService.getActivityStartController().postStartActivityProcessingForLastStarter(
                          task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT,
                          task.getStack());
                  return ActivityManager.START_TASK_TO_FRONT;
              }
              callingPackage = task.mCallingPackage;
              intent = task.intent;
              intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
              userId = task.userId;
              return mService.getActivityStartController().startActivityInPackage(
                      task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
                      null, 0, 0, options, userId, task, "startActivityFromRecents",
                      false /* validateIncomingUser */, null /* originatingPendingIntent */,
                      false /* allowBackgroundActivityStart */);
          } finally {
              if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
                  // If we are launching the task in the docked stack, put it into resizing mode so
                  // the window renders full-screen with the background filling the void. Also only
                  // call this at the end to make sure that tasks exists on the window manager side.
                  setResizingDuringAnimation(task);
  
                  final ActivityDisplay display = task.getStack().getDisplay();
                  final ActivityStack topSecondaryStack =
                          display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
                  if (topSecondaryStack.isActivityTypeHome()) {
                      // If the home activity is the top split-screen secondary stack, then the
                      // primary split-screen stack is in the minimized mode which means it can't
                      // receive input keys, so we should move the focused app to the home app so that
                      // window manager can correctly calculate the focus window that can receive
                      // input keys.
                      display.moveHomeStackToFront(
                              "startActivityFromRecents: homeVisibleInSplitScreen");
  
                      // Immediately update the minimized docked stack mode, the upcoming animation
                      // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
                      // will do the animation to the target bounds
                      mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
                  }
              }
              mWindowManager.continueSurfaceLayout();
          }
      }
  
      /**
       * Internal container to store a match qualifier alongside a WaitResult.
       */
      static class WaitInfo {
          private final ComponentName mTargetComponent;
          private final WaitResult mResult;
          /** Time stamp when we started to wait for {@link WaitResult}. */
          private final long mStartTimeMs;
  
          WaitInfo(ComponentName targetComponent, WaitResult result, long startTimeMs) {
              this.mTargetComponent = targetComponent;
              this.mResult = result;
              this.mStartTimeMs = startTimeMs;
          }
  
          public boolean matches(ComponentName targetComponent) {
              return mTargetComponent == null || mTargetComponent.equals(targetComponent);
          }
  
          public WaitResult getResult() {
              return mResult;
          }
  
          public long getStartTime() {
              return mStartTimeMs;
          }
  
          public ComponentName getComponent() {
              return mTargetComponent;
          }
  
          public void dump(PrintWriter pw, String prefix) {
              pw.println(prefix + "WaitInfo:");
              pw.println(prefix + "  mTargetComponent=" + mTargetComponent);
              pw.println(prefix + "  mResult=");
              mResult.dump(pw, prefix);
          }
      }
  }
  