1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.am;
18
19import android.annotation.IntDef;
20import android.annotation.Nullable;
21import android.app.Activity;
22import android.app.ActivityManager;
23import android.app.ActivityManager.StackId;
24import android.app.ActivityManager.TaskDescription;
25import android.app.ActivityManager.TaskSnapshot;
26import android.app.ActivityManager.TaskThumbnail;
27import android.app.ActivityManager.TaskThumbnailInfo;
28import android.app.ActivityOptions;
29import android.app.AppGlobals;
30import android.app.IActivityManager;
31import android.content.ComponentName;
32import android.content.Intent;
33import android.content.pm.ActivityInfo;
34import android.content.pm.ApplicationInfo;
35import android.content.pm.IPackageManager;
36import android.content.pm.PackageManager;
37import android.content.res.Configuration;
38import android.graphics.Bitmap;
39import android.graphics.Point;
40import android.graphics.Rect;
41import android.os.Debug;
42import android.os.ParcelFileDescriptor;
43import android.os.RemoteException;
44import android.os.Trace;
45import android.os.UserHandle;
46import android.provider.Settings;
47import android.service.voice.IVoiceInteractionSession;
48import android.util.DisplayMetrics;
49import android.util.Slog;
50
51import com.android.internal.annotations.VisibleForTesting;
52import com.android.internal.app.IVoiceInteractor;
53import com.android.internal.util.XmlUtils;
54
55import com.android.server.wm.AppWindowContainerController;
56import com.android.server.wm.StackWindowController;
57import com.android.server.wm.TaskWindowContainerController;
58import com.android.server.wm.TaskWindowContainerListener;
59import com.android.server.wm.WindowManagerService;
60
61import org.xmlpull.v1.XmlPullParser;
62import org.xmlpull.v1.XmlPullParserException;
63import org.xmlpull.v1.XmlSerializer;
64
65import java.io.File;
66import java.io.IOException;
67import java.io.PrintWriter;
68import java.lang.annotation.Retention;
69import java.lang.annotation.RetentionPolicy;
70import java.util.ArrayList;
71import java.util.Objects;
72
73import static android.app.ActivityManager.RESIZE_MODE_FORCED;
74import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
75import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
76import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
77import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
78import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
79import static android.app.ActivityManager.StackId.HOME_STACK_ID;
80import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
81import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
82import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
83import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
84import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
85import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
86import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
87import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
88import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
89import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
90import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
91import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
92import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
93import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
94import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
95import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
96import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
97import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
98import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
99import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
100import static android.view.Display.DEFAULT_DISPLAY;
101
102import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
103import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
104import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
105import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
106import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
107import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
108import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
109import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
110import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
111import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
112import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
113import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
114import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
115import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
116import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
117import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
118import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP;
119import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
120import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
121
122import static java.lang.Integer.MAX_VALUE;
123
124final class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
125    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
126    private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
127    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
128    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
129    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
130
131    private static final String ATTR_TASKID = "task_id";
132    private static final String TAG_INTENT = "intent";
133    private static final String TAG_AFFINITYINTENT = "affinity_intent";
134    private static final String ATTR_REALACTIVITY = "real_activity";
135    private static final String ATTR_REALACTIVITY_SUSPENDED = "real_activity_suspended";
136    private static final String ATTR_ORIGACTIVITY = "orig_activity";
137    private static final String TAG_ACTIVITY = "activity";
138    private static final String ATTR_AFFINITY = "affinity";
139    private static final String ATTR_ROOT_AFFINITY = "root_affinity";
140    private static final String ATTR_ROOTHASRESET = "root_has_reset";
141    private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
142    private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
143    private static final String ATTR_USERID = "user_id";
144    private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete";
145    private static final String ATTR_EFFECTIVE_UID = "effective_uid";
146    private static final String ATTR_TASKTYPE = "task_type";
147    private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
148    private static final String ATTR_LASTACTIVETIME = "last_active_time";
149    private static final String ATTR_LASTDESCRIPTION = "last_description";
150    private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
151    private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
152    private static final String ATTR_TASK_AFFILIATION = "task_affiliation";
153    private static final String ATTR_PREV_AFFILIATION = "prev_affiliation";
154    private static final String ATTR_NEXT_AFFILIATION = "next_affiliation";
155    private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
156    private static final String ATTR_CALLING_UID = "calling_uid";
157    private static final String ATTR_CALLING_PACKAGE = "calling_package";
158    private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
159    private static final String ATTR_RESIZE_MODE = "resize_mode";
160    private static final String ATTR_PRIVILEGED = "privileged";
161    private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
162    private static final String ATTR_MIN_WIDTH = "min_width";
163    private static final String ATTR_MIN_HEIGHT = "min_height";
164    private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
165
166    // Current version of the task record we persist. Used to check if we need to run any upgrade
167    // code.
168    private static final int PERSIST_TASK_VERSION = 1;
169    private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
170
171    static final int INVALID_TASK_ID = -1;
172    private static final int INVALID_MIN_SIZE = -1;
173
174    /**
175     * The modes to control how the stack is moved to the front when calling
176     * {@link TaskRecord#reparent}.
177     */
178    @Retention(RetentionPolicy.SOURCE)
179    @IntDef({
180            REPARENT_MOVE_STACK_TO_FRONT,
181            REPARENT_KEEP_STACK_AT_FRONT,
182            REPARENT_LEAVE_STACK_IN_PLACE
183    })
184    public @interface ReparentMoveStackMode {}
185    // Moves the stack to the front if it was not at the front
186    public static final int REPARENT_MOVE_STACK_TO_FRONT = 0;
187    // Only moves the stack to the front if it was focused or front most already
188    public static final int REPARENT_KEEP_STACK_AT_FRONT = 1;
189    // Do not move the stack as a part of reparenting
190    public static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
191
192    final int taskId;       // Unique identifier for this task.
193    String affinity;        // The affinity name for this task, or null; may change identity.
194    String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
195    final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
196    final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
197    Intent intent;          // The original intent that started the task.
198    Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
199    int effectiveUid;       // The current effective uid of the identity of this task.
200    ComponentName origActivity; // The non-alias activity component of the intent.
201    ComponentName realActivity; // The actual activity component that started the task.
202    boolean realActivitySuspended; // True if the actual activity component that started the
203                                   // task is suspended.
204    long firstActiveTime;   // First time this task was active.
205    long lastActiveTime;    // Last time this task was active, including sleep.
206    boolean inRecents;      // Actually in the recents list?
207    boolean isAvailable;    // Is the activity available to be launched?
208    boolean rootWasReset;   // True if the intent at the root of the task had
209                            // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
210    boolean autoRemoveRecents;  // If true, we should automatically remove the task from
211                                // recents when activity finishes
212    boolean askedCompatMode;// Have asked the user about compat mode for this task.
213    boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
214
215    String stringName;      // caching of toString() result.
216    int userId;             // user for which this task was created
217    boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
218                                // was changed.
219
220    int numFullscreen;      // Number of fullscreen activities.
221
222    int mResizeMode;        // The resize mode of this task and its activities.
223                            // Based on the {@link ActivityInfo#resizeMode} of the root activity.
224    private boolean mSupportsPictureInPicture;  // Whether or not this task and its activities
225            // support PiP. Based on the {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag
226            // of the root activity.
227    boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize
228                                     // changes on a temporary basis.
229    private int mLockTaskMode;  // Which tasklock mode to launch this task in. One of
230                                // ActivityManager.LOCK_TASK_LAUNCH_MODE_*
231    private boolean mPrivileged;    // The root activity application of this task holds
232                                    // privileged permissions.
233
234    /** Can't be put in lockTask mode. */
235    final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
236    /** Can enter app pinning with user approval. Can never start over existing lockTask task. */
237    final static int LOCK_TASK_AUTH_PINNABLE = 1;
238    /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
239    final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
240    /** Can enter lockTask without user approval. Can start over existing lockTask task. */
241    final static int LOCK_TASK_AUTH_WHITELISTED = 3;
242    /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing
243     * lockTask task. */
244    final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4;
245    int mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
246
247    int mLockTaskUid = -1;  // The uid of the application that called startLockTask().
248
249    // This represents the last resolved activity values for this task
250    // NOTE: This value needs to be persisted with each task
251    TaskDescription lastTaskDescription = new TaskDescription();
252
253    /** List of all activities in the task arranged in history order */
254    final ArrayList<ActivityRecord> mActivities;
255
256    /** Current stack. Setter must always be used to update the value. */
257    private ActivityStack mStack;
258
259    /** Takes on same set of values as ActivityRecord.mActivityType */
260    int taskType;
261
262    /** Takes on same value as first root activity */
263    boolean isPersistable = false;
264    int maxRecents;
265
266    /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
267     * determining the order when restoring. Sign indicates whether last task movement was to front
268     * (positive) or back (negative). Absolute value indicates time. */
269    long mLastTimeMoved = System.currentTimeMillis();
270
271    /** Indication of what to run next when task exits. Use ActivityRecord types.
272     * ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the
273     * task stack. */
274    private int mTaskToReturnTo = APPLICATION_ACTIVITY_TYPE;
275
276    /** If original intent did not allow relinquishing task identity, save that information */
277    private boolean mNeverRelinquishIdentity = true;
278
279    // Used in the unique case where we are clearing the task in order to reuse it. In that case we
280    // do not want to delete the stack when the task goes empty.
281    private boolean mReuseTask = false;
282
283    private Bitmap mLastThumbnail; // Last thumbnail captured for this item.
284    private final File mLastThumbnailFile; // File containing last thumbnail.
285    private final String mFilename;
286    private TaskThumbnailInfo mLastThumbnailInfo;
287    CharSequence lastDescription; // Last description captured for this item.
288
289    int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
290    int mAffiliatedTaskColor; // color of the parent task affiliation.
291    TaskRecord mPrevAffiliate; // previous task in affiliated chain.
292    int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
293    TaskRecord mNextAffiliate; // next task in affiliated chain.
294    int mNextAffiliateTaskId = INVALID_TASK_ID; // next id for persistence.
295
296    // For relaunching the task from recents as though it was launched by the original launcher.
297    int mCallingUid;
298    String mCallingPackage;
299
300    final ActivityManagerService mService;
301
302    // Whether or not this task covers the entire screen; by default tasks are fullscreen.
303    boolean mFullscreen = true;
304
305    // Bounds of the Task. null for fullscreen tasks.
306    Rect mBounds = null;
307    private final Rect mTmpStableBounds = new Rect();
308    private final Rect mTmpNonDecorBounds = new Rect();
309    private final Rect mTmpRect = new Rect();
310
311    // Last non-fullscreen bounds the task was launched in or resized to.
312    // The information is persisted and used to determine the appropriate stack to launch the
313    // task into on restore.
314    Rect mLastNonFullscreenBounds = null;
315    // Minimal width and height of this task when it's resizeable. -1 means it should use the
316    // default minimal width/height.
317    int mMinWidth;
318    int mMinHeight;
319
320    // Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
321    // This number will be assigned when we evaluate OOM scores for all visible tasks.
322    int mLayerRank = -1;
323
324    /** Helper object used for updating override configuration. */
325    private Configuration mTmpConfig = new Configuration();
326
327    private TaskWindowContainerController mWindowContainerController;
328
329    TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
330            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, int type) {
331        mService = service;
332        mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
333                TaskPersister.IMAGE_EXTENSION;
334        userId = UserHandle.getUserId(info.applicationInfo.uid);
335        mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(userId), mFilename);
336        mLastThumbnailInfo = new TaskThumbnailInfo();
337        taskId = _taskId;
338        mAffiliatedTaskId = _taskId;
339        voiceSession = _voiceSession;
340        voiceInteractor = _voiceInteractor;
341        isAvailable = true;
342        mActivities = new ArrayList<>();
343        mCallingUid = info.applicationInfo.uid;
344        mCallingPackage = info.packageName;
345        taskType = type;
346        setIntent(_intent, info);
347        setMinDimensions(info);
348        touchActiveTime();
349        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
350    }
351
352    TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
353            TaskDescription _taskDescription, TaskThumbnailInfo thumbnailInfo) {
354        mService = service;
355        mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
356                TaskPersister.IMAGE_EXTENSION;
357        userId = UserHandle.getUserId(info.applicationInfo.uid);
358        mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(userId), mFilename);
359        mLastThumbnailInfo = thumbnailInfo;
360        taskId = _taskId;
361        mAffiliatedTaskId = _taskId;
362        voiceSession = null;
363        voiceInteractor = null;
364        isAvailable = true;
365        mActivities = new ArrayList<>();
366        mCallingUid = info.applicationInfo.uid;
367        mCallingPackage = info.packageName;
368        setIntent(_intent, info);
369        setMinDimensions(info);
370
371        isPersistable = true;
372        // Clamp to [1, max].
373        maxRecents = Math.min(Math.max(info.maxRecents, 1),
374                ActivityManager.getMaxAppRecentsLimitStatic());
375
376        taskType = APPLICATION_ACTIVITY_TYPE;
377        mTaskToReturnTo = HOME_ACTIVITY_TYPE;
378        lastTaskDescription = _taskDescription;
379        touchActiveTime();
380        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
381    }
382
383    private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
384            Intent _affinityIntent, String _affinity, String _rootAffinity,
385            ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
386            boolean _autoRemoveRecents, boolean _askedCompatMode, int _taskType, int _userId,
387            int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
388            long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
389            boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
390            TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
391            int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
392            int resizeMode, boolean supportsPictureInPicture, boolean privileged,
393            boolean _realActivitySuspended, boolean userSetupComplete, int minWidth,
394            int minHeight) {
395        mService = service;
396        mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
397                TaskPersister.IMAGE_EXTENSION;
398        mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(_userId), mFilename);
399        mLastThumbnailInfo = lastThumbnailInfo;
400        taskId = _taskId;
401        intent = _intent;
402        affinityIntent = _affinityIntent;
403        affinity = _affinity;
404        rootAffinity = _rootAffinity;
405        voiceSession = null;
406        voiceInteractor = null;
407        realActivity = _realActivity;
408        realActivitySuspended = _realActivitySuspended;
409        origActivity = _origActivity;
410        rootWasReset = _rootWasReset;
411        isAvailable = true;
412        autoRemoveRecents = _autoRemoveRecents;
413        askedCompatMode = _askedCompatMode;
414        taskType = _taskType;
415        mTaskToReturnTo = HOME_ACTIVITY_TYPE;
416        userId = _userId;
417        mUserSetupComplete = userSetupComplete;
418        effectiveUid = _effectiveUid;
419        firstActiveTime = _firstActiveTime;
420        lastActiveTime = _lastActiveTime;
421        lastDescription = _lastDescription;
422        mActivities = activities;
423        mLastTimeMoved = lastTimeMoved;
424        mNeverRelinquishIdentity = neverRelinquishIdentity;
425        lastTaskDescription = _lastTaskDescription;
426        mAffiliatedTaskId = taskAffiliation;
427        mAffiliatedTaskColor = taskAffiliationColor;
428        mPrevAffiliateTaskId = prevTaskId;
429        mNextAffiliateTaskId = nextTaskId;
430        mCallingUid = callingUid;
431        mCallingPackage = callingPackage;
432        mResizeMode = resizeMode;
433        mSupportsPictureInPicture = supportsPictureInPicture;
434        mPrivileged = privileged;
435        mMinWidth = minWidth;
436        mMinHeight = minHeight;
437        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
438    }
439
440    TaskWindowContainerController getWindowContainerController() {
441        return mWindowContainerController;
442    }
443
444    void createWindowContainer(boolean onTop, boolean showForAllUsers) {
445        if (mWindowContainerController != null) {
446            throw new IllegalArgumentException("Window container=" + mWindowContainerController
447                    + " already created for task=" + this);
448        }
449
450        final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
451        final Configuration overrideConfig = getOverrideConfiguration();
452        setWindowContainerController(new TaskWindowContainerController(taskId, this,
453                getStack().getWindowContainerController(), userId, bounds, overrideConfig,
454                mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers,
455                lastTaskDescription));
456    }
457
458    /**
459     * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}.
460     */
461    @VisibleForTesting
462    protected void setWindowContainerController(TaskWindowContainerController controller) {
463        if (mWindowContainerController != null) {
464            throw new IllegalArgumentException("Window container=" + mWindowContainerController
465                    + " already created for task=" + this);
466        }
467
468        mWindowContainerController = controller;
469    }
470
471    void removeWindowContainer() {
472        mService.mStackSupervisor.removeLockedTaskLocked(this);
473        mWindowContainerController.removeContainer();
474        if (!StackId.persistTaskBounds(getStackId())) {
475            // Reset current bounds for task whose bounds shouldn't be persisted so it uses
476            // default configuration the next time it launches.
477            updateOverrideConfiguration(null);
478        }
479        mService.mTaskChangeNotificationController.notifyTaskRemoved(taskId);
480        mWindowContainerController = null;
481    }
482
483    @Override
484    public void onSnapshotChanged(TaskSnapshot snapshot) {
485        mService.mTaskChangeNotificationController.notifyTaskSnapshotChanged(taskId, snapshot);
486    }
487
488    void setResizeMode(int resizeMode) {
489        if (mResizeMode == resizeMode) {
490            return;
491        }
492        mResizeMode = resizeMode;
493        mWindowContainerController.setResizeable(resizeMode);
494        mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
495        mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
496    }
497
498    void setTaskDockedResizing(boolean resizing) {
499        mWindowContainerController.setTaskDockedResizing(resizing);
500    }
501
502    // TODO: Consolidate this with the resize() method below.
503    @Override
504    public void requestResize(Rect bounds, int resizeMode) {
505        mService.resizeTask(taskId, bounds, resizeMode);
506    }
507
508    boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) {
509        if (!isResizeable()) {
510            Slog.w(TAG, "resizeTask: task " + this + " not resizeable.");
511            return true;
512        }
513
514        // If this is a forced resize, let it go through even if the bounds is not changing,
515        // as we might need a relayout due to surface size change (to/from fullscreen).
516        final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
517        if (Objects.equals(mBounds, bounds) && !forced) {
518            // Nothing to do here...
519            return true;
520        }
521        bounds = validateBounds(bounds);
522
523        if (mWindowContainerController == null) {
524            // Task doesn't exist in window manager yet (e.g. was restored from recents).
525            // All we can do for now is update the bounds so it can be used when the task is
526            // added to window manager.
527            updateOverrideConfiguration(bounds);
528            if (getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
529                // re-restore the task so it can have the proper stack association.
530                mService.mStackSupervisor.restoreRecentTaskLocked(this,
531                        FREEFORM_WORKSPACE_STACK_ID);
532            }
533            return true;
534        }
535
536        if (!canResizeToBounds(bounds)) {
537            throw new IllegalArgumentException("resizeTask: Can not resize task=" + this
538                    + " to bounds=" + bounds + " resizeMode=" + mResizeMode);
539        }
540
541        // Do not move the task to another stack here.
542        // This method assumes that the task is already placed in the right stack.
543        // we do not mess with that decision and we only do the resize!
544
545        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId);
546
547        final boolean updatedConfig = updateOverrideConfiguration(bounds);
548        // This variable holds information whether the configuration didn't change in a significant
549        // way and the activity was kept the way it was. If it's false, it means the activity had
550        // to be relaunched due to configuration change.
551        boolean kept = true;
552        if (updatedConfig) {
553            final ActivityRecord r = topRunningActivityLocked();
554            if (r != null && !deferResume) {
555                kept = r.ensureActivityConfigurationLocked(0 /* globalChanges */, preserveWindow);
556                mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
557                if (!kept) {
558                    mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
559                }
560            }
561        }
562        mWindowContainerController.resize(mBounds, getOverrideConfiguration(), kept, forced);
563
564        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
565        return kept;
566    }
567
568    // TODO: Investigate combining with the resize() method above.
569    void resizeWindowContainer() {
570        mWindowContainerController.resize(mBounds, getOverrideConfiguration(), false /* relayout */,
571                false /* forced */);
572    }
573
574    void getWindowContainerBounds(Rect bounds) {
575        mWindowContainerController.getBounds(bounds);
576    }
577
578    /**
579     * Convenience method to reparent a task to the top or bottom position of the stack.
580     */
581    boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
582            boolean animate, boolean deferResume, String reason) {
583        return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
584                deferResume, true /* schedulePictureInPictureModeChange */, reason);
585    }
586
587    /**
588     * Convenience method to reparent a task to the top or bottom position of the stack, with
589     * an option to skip scheduling the picture-in-picture mode change.
590     */
591    boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
592            boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
593            String reason) {
594        return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
595                deferResume, schedulePictureInPictureModeChange, reason);
596    }
597
598    /**
599     * Convenience method to reparent a task to a specific position of the stack.
600     */
601    boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
602            boolean animate, boolean deferResume, String reason) {
603        return reparent(preferredStackId, position, moveStackMode, animate, deferResume,
604                true /* schedulePictureInPictureModeChange */, reason);
605    }
606
607    /**
608     * Reparents the task into a preferred stack, creating it if necessary.
609     *
610     * @param preferredStackId the stack id of the target stack to move this task
611     * @param position the position to place this task in the new stack
612     * @param animate whether or not we should wait for the new window created as a part of the
613     *            reparenting to be drawn and animated in
614     * @param moveStackMode whether or not to move the stack to the front always, only if it was
615     *            previously focused & in front, or never
616     * @param deferResume whether or not to update the visibility of other tasks and stacks that may
617     *            have changed as a result of this reparenting
618     * @param schedulePictureInPictureModeChange specifies whether or not to schedule the PiP mode
619     *            change. Callers may set this to false if they are explicitly scheduling PiP mode
620     *            changes themselves, like during the PiP animation
621     * @param reason the caller of this reparenting
622     * @return whether the task was reparented
623     */
624    boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
625            boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange,
626            String reason) {
627        final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
628        final WindowManagerService windowManager = mService.mWindowManager;
629        final ActivityStack sourceStack = getStack();
630        final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStackId,
631                position == MAX_VALUE);
632        if (toStack == sourceStack) {
633            return false;
634        }
635
636        final int sourceStackId = getStackId();
637        final int stackId = toStack.getStackId();
638        final ActivityRecord topActivity = getTopActivity();
639
640        final boolean mightReplaceWindow = StackId.replaceWindowsOnTaskMove(sourceStackId, stackId)
641                && topActivity != null;
642        if (mightReplaceWindow) {
643            // We are about to relaunch the activity because its configuration changed due to
644            // being maximized, i.e. size change. The activity will first remove the old window
645            // and then add a new one. This call will tell window manager about this, so it can
646            // preserve the old window until the new one is drawn. This prevents having a gap
647            // between the removal and addition, in which no window is visible. We also want the
648            // entrance of the new window to be properly animated.
649            // Note here we always set the replacing window first, as the flags might be needed
650            // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
651            windowManager.setWillReplaceWindow(topActivity.appToken, animate);
652        }
653
654        windowManager.deferSurfaceLayout();
655        boolean kept = true;
656        try {
657            final ActivityRecord r = topRunningActivityLocked();
658            final boolean wasFocused = r != null && supervisor.isFocusedStack(sourceStack)
659                    && (topRunningActivityLocked() == r);
660            final boolean wasResumed = r != null && sourceStack.mResumedActivity == r;
661            final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
662
663            // In some cases the focused stack isn't the front stack. E.g. pinned stack.
664            // Whenever we are moving the top activity from the front stack we want to make sure to
665            // move the stack to the front.
666            final boolean wasFront = r != null && supervisor.isFrontStackOnDisplay(sourceStack)
667                    && (sourceStack.topRunningActivityLocked() == r);
668
669            // Adjust the position for the new parent stack as needed.
670            position = toStack.getAdjustedPositionForTask(this, position, null /* starting */);
671
672            // Must reparent first in window manager to avoid a situation where AM can delete the
673            // we are coming from in WM before we reparent because it became empty.
674            mWindowContainerController.reparent(toStack.getWindowContainerController(), position,
675                    moveStackMode == REPARENT_MOVE_STACK_TO_FRONT);
676
677            final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
678                    || (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
679            // Move the task
680            sourceStack.removeTask(this, reason, moveStackToFront
681                    ? REMOVE_TASK_MODE_MOVING_TO_TOP : REMOVE_TASK_MODE_MOVING);
682            toStack.addTask(this, position, false /* schedulePictureInPictureModeChange */, reason);
683
684            if (schedulePictureInPictureModeChange) {
685                // Notify of picture-in-picture mode changes
686                supervisor.scheduleUpdatePictureInPictureModeIfNeeded(this, sourceStack);
687            }
688
689            // TODO: Ensure that this is actually necessary here
690            // Notify the voice session if required
691            if (voiceSession != null) {
692                try {
693                    voiceSession.taskStarted(intent, taskId);
694                } catch (RemoteException e) {
695                }
696            }
697
698            // If the task had focus before (or we're requested to move focus), move focus to the
699            // new stack by moving the stack to the front.
700            if (r != null) {
701                toStack.moveToFrontAndResumeStateIfNeeded(r, moveStackToFront, wasResumed,
702                        wasPaused, reason);
703            }
704            if (!animate) {
705                toStack.mNoAnimActivities.add(topActivity);
706            }
707
708            // We might trigger a configuration change. Save the current task bounds for freezing.
709            // TODO: Should this call be moved inside the resize method in WM?
710            toStack.prepareFreezingTaskBounds();
711
712            // Make sure the task has the appropriate bounds/size for the stack it is in.
713            if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
714                    && !Objects.equals(mBounds, toStack.mBounds)) {
715                kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
716                        deferResume);
717            } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
718                Rect bounds = getLaunchBounds();
719                if (bounds == null) {
720                    toStack.layoutTaskInStack(this, null);
721                    bounds = mBounds;
722                }
723                kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
724            } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
725                if (stackId == DOCKED_STACK_ID && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
726                    // Move recents to front so it is not behind home stack when going into docked
727                    // mode
728                    mService.mStackSupervisor.moveRecentsStackToFront(reason);
729                }
730                kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
731                        deferResume);
732            }
733        } finally {
734            windowManager.continueSurfaceLayout();
735        }
736
737        if (mightReplaceWindow) {
738            // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
739            // window), we need to clear the replace window settings. Otherwise, we schedule a
740            // timeout to remove the old window if the replacing window is not coming in time.
741            windowManager.scheduleClearWillReplaceWindows(topActivity.appToken, !kept);
742        }
743
744        if (!deferResume) {
745            // The task might have already been running and its visibility needs to be synchronized
746            // with the visibility of the stack / windows.
747            supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
748            supervisor.resumeFocusedStackTopActivityLocked();
749        }
750
751        // TODO: Handle incorrect request to move before the actual move, not after.
752        supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, DEFAULT_DISPLAY, stackId);
753
754        boolean successful = (preferredStackId == stackId);
755        if (successful && stackId == DOCKED_STACK_ID) {
756            // If task moved to docked stack - show recents if needed.
757            mService.mWindowManager.showRecentApps(false /* fromHome */);
758        }
759        return successful;
760    }
761
762    void cancelWindowTransition() {
763        mWindowContainerController.cancelWindowTransition();
764    }
765
766    void cancelThumbnailTransition() {
767        mWindowContainerController.cancelThumbnailTransition();
768    }
769
770    /**
771     * DO NOT HOLD THE ACTIVITY MANAGER LOCK WHEN CALLING THIS METHOD!
772     */
773    TaskSnapshot getSnapshot(boolean reducedResolution) {
774
775        // TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
776        // synchronized between AM and WM.
777        return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution);
778    }
779
780    void touchActiveTime() {
781        lastActiveTime = System.currentTimeMillis();
782        if (firstActiveTime == 0) {
783            firstActiveTime = lastActiveTime;
784        }
785    }
786
787    long getInactiveDuration() {
788        return System.currentTimeMillis() - lastActiveTime;
789    }
790
791    /** Sets the original intent, and the calling uid and package. */
792    void setIntent(ActivityRecord r) {
793        mCallingUid = r.launchedFromUid;
794        mCallingPackage = r.launchedFromPackage;
795        setIntent(r.intent, r.info);
796    }
797
798    /** Sets the original intent, _without_ updating the calling uid or package. */
799    private void setIntent(Intent _intent, ActivityInfo info) {
800        if (intent == null) {
801            mNeverRelinquishIdentity =
802                    (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
803        } else if (mNeverRelinquishIdentity) {
804            return;
805        }
806
807        affinity = info.taskAffinity;
808        if (intent == null) {
809            // If this task already has an intent associated with it, don't set the root
810            // affinity -- we don't want it changing after initially set, but the initially
811            // set value may be null.
812            rootAffinity = affinity;
813        }
814        effectiveUid = info.applicationInfo.uid;
815        stringName = null;
816
817        if (info.targetActivity == null) {
818            if (_intent != null) {
819                // If this Intent has a selector, we want to clear it for the
820                // recent task since it is not relevant if the user later wants
821                // to re-launch the app.
822                if (_intent.getSelector() != null || _intent.getSourceBounds() != null) {
823                    _intent = new Intent(_intent);
824                    _intent.setSelector(null);
825                    _intent.setSourceBounds(null);
826                }
827            }
828            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Setting Intent of " + this + " to " + _intent);
829            intent = _intent;
830            realActivity = _intent != null ? _intent.getComponent() : null;
831            origActivity = null;
832        } else {
833            ComponentName targetComponent = new ComponentName(
834                    info.packageName, info.targetActivity);
835            if (_intent != null) {
836                Intent targetIntent = new Intent(_intent);
837                targetIntent.setComponent(targetComponent);
838                targetIntent.setSelector(null);
839                targetIntent.setSourceBounds(null);
840                if (DEBUG_TASKS) Slog.v(TAG_TASKS,
841                        "Setting Intent of " + this + " to target " + targetIntent);
842                intent = targetIntent;
843                realActivity = targetComponent;
844                origActivity = _intent.getComponent();
845            } else {
846                intent = null;
847                realActivity = targetComponent;
848                origActivity = new ComponentName(info.packageName, info.name);
849            }
850        }
851
852        final int intentFlags = intent == null ? 0 : intent.getFlags();
853        if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
854            // Once we are set to an Intent with this flag, we count this
855            // task as having a true root activity.
856            rootWasReset = true;
857        }
858        userId = UserHandle.getUserId(info.applicationInfo.uid);
859        mUserSetupComplete = Settings.Secure.getIntForUser(mService.mContext.getContentResolver(),
860                USER_SETUP_COMPLETE, 0, userId) != 0;
861        if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
862            // If the activity itself has requested auto-remove, then just always do it.
863            autoRemoveRecents = true;
864        } else if ((intentFlags & (FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS))
865                == FLAG_ACTIVITY_NEW_DOCUMENT) {
866            // If the caller has not asked for the document to be retained, then we may
867            // want to turn on auto-remove, depending on whether the target has set its
868            // own document launch mode.
869            if (info.documentLaunchMode != ActivityInfo.DOCUMENT_LAUNCH_NONE) {
870                autoRemoveRecents = false;
871            } else {
872                autoRemoveRecents = true;
873            }
874        } else {
875            autoRemoveRecents = false;
876        }
877        mResizeMode = info.resizeMode;
878        mSupportsPictureInPicture = info.supportsPictureInPicture();
879        mLockTaskMode = info.lockTaskLaunchMode;
880        mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
881        setLockTaskAuth();
882    }
883
884    /** Sets the original minimal width and height. */
885    private void setMinDimensions(ActivityInfo info) {
886        if (info != null && info.windowLayout != null) {
887            mMinWidth = info.windowLayout.minWidth;
888            mMinHeight = info.windowLayout.minHeight;
889        } else {
890            mMinWidth = INVALID_MIN_SIZE;
891            mMinHeight = INVALID_MIN_SIZE;
892        }
893    }
894
895    /**
896     * Return true if the input activity has the same intent filter as the intent this task
897     * record is based on (normally the root activity intent).
898     */
899    boolean isSameIntentFilter(ActivityRecord r) {
900        final Intent intent = new Intent(r.intent);
901        // Correct the activity intent for aliasing. The task record intent will always be based on
902        // the real activity that will be launched not the alias, so we need to use an intent with
903        // the component name pointing to the real activity not the alias in the activity record.
904        intent.setComponent(r.realActivity);
905        return this.intent.filterEquals(intent);
906    }
907
908    void setTaskToReturnTo(int taskToReturnTo) {
909        mTaskToReturnTo = (taskToReturnTo == RECENTS_ACTIVITY_TYPE)
910                ? HOME_ACTIVITY_TYPE : taskToReturnTo;
911    }
912
913    void setTaskToReturnTo(ActivityRecord source) {
914        if (source.isRecentsActivity()) {
915            setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
916        } else if (source.isAssistantActivity()) {
917            setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE);
918        }
919    }
920
921    int getTaskToReturnTo() {
922        return mTaskToReturnTo;
923    }
924
925    void setPrevAffiliate(TaskRecord prevAffiliate) {
926        mPrevAffiliate = prevAffiliate;
927        mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.taskId;
928    }
929
930    void setNextAffiliate(TaskRecord nextAffiliate) {
931        mNextAffiliate = nextAffiliate;
932        mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
933    }
934
935    ActivityStack getStack() {
936        return mStack;
937    }
938
939    /**
940     * Must be used for setting parent stack because it performs configuration updates.
941     * Must be called after adding task as a child to the stack.
942     */
943    void setStack(ActivityStack stack) {
944        if (stack != null && !stack.isInStackLocked(this)) {
945            throw new IllegalStateException("Task must be added as a Stack child first.");
946        }
947        mStack = stack;
948        onParentChanged();
949    }
950
951    /**
952     * @return Id of current stack, {@link INVALID_STACK_ID} if no stack is set.
953     */
954    int getStackId() {
955        return mStack != null ? mStack.mStackId : INVALID_STACK_ID;
956    }
957
958    @Override
959    protected int getChildCount() {
960        return mActivities.size();
961    }
962
963    @Override
964    protected ConfigurationContainer getChildAt(int index) {
965        return mActivities.get(index);
966    }
967
968    @Override
969    protected ConfigurationContainer getParent() {
970        return mStack;
971    }
972
973    @Override
974    void onParentChanged() {
975        super.onParentChanged();
976        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
977    }
978
979    // Close up recents linked list.
980    private void closeRecentsChain() {
981        if (mPrevAffiliate != null) {
982            mPrevAffiliate.setNextAffiliate(mNextAffiliate);
983        }
984        if (mNextAffiliate != null) {
985            mNextAffiliate.setPrevAffiliate(mPrevAffiliate);
986        }
987        setPrevAffiliate(null);
988        setNextAffiliate(null);
989    }
990
991    void removedFromRecents() {
992        disposeThumbnail();
993        closeRecentsChain();
994        if (inRecents) {
995            inRecents = false;
996            mService.notifyTaskPersisterLocked(this, false);
997        }
998
999        // TODO: Use window container controller once tasks are better synced between AM and WM
1000        mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);
1001    }
1002
1003    void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
1004        closeRecentsChain();
1005        mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
1006        mAffiliatedTaskColor = taskToAffiliateWith.mAffiliatedTaskColor;
1007        // Find the end
1008        while (taskToAffiliateWith.mNextAffiliate != null) {
1009            final TaskRecord nextRecents = taskToAffiliateWith.mNextAffiliate;
1010            if (nextRecents.mAffiliatedTaskId != mAffiliatedTaskId) {
1011                Slog.e(TAG, "setTaskToAffiliateWith: nextRecents=" + nextRecents + " affilTaskId="
1012                        + nextRecents.mAffiliatedTaskId + " should be " + mAffiliatedTaskId);
1013                if (nextRecents.mPrevAffiliate == taskToAffiliateWith) {
1014                    nextRecents.setPrevAffiliate(null);
1015                }
1016                taskToAffiliateWith.setNextAffiliate(null);
1017                break;
1018            }
1019            taskToAffiliateWith = nextRecents;
1020        }
1021        taskToAffiliateWith.setNextAffiliate(this);
1022        setPrevAffiliate(taskToAffiliateWith);
1023        setNextAffiliate(null);
1024    }
1025
1026    /**
1027     * Sets the last thumbnail with the current task bounds and the system orientation.
1028     * @return whether the thumbnail was set
1029     */
1030    boolean setLastThumbnailLocked(Bitmap thumbnail) {
1031        int taskWidth = 0;
1032        int taskHeight = 0;
1033        if (mBounds != null) {
1034            // Non-fullscreen tasks
1035            taskWidth = mBounds.width();
1036            taskHeight = mBounds.height();
1037        } else if (mStack != null) {
1038            // Fullscreen tasks
1039            final Point displaySize = new Point();
1040            mStack.getDisplaySize(displaySize);
1041            taskWidth = displaySize.x;
1042            taskHeight = displaySize.y;
1043        } else {
1044            Slog.e(TAG, "setLastThumbnailLocked() called on Task without stack");
1045        }
1046        // We need to provide the current orientation of the display on which this task resides,
1047        // not the orientation of the task.
1048        final int orientation =
1049                getStack().mActivityContainer.mActivityDisplay.getConfiguration().orientation;
1050        return setLastThumbnailLocked(thumbnail, taskWidth, taskHeight, orientation);
1051    }
1052
1053    /**
1054     * Sets the last thumbnail with the current task bounds.
1055     * @return whether the thumbnail was set
1056     */
1057    private boolean setLastThumbnailLocked(Bitmap thumbnail, int taskWidth, int taskHeight,
1058            int screenOrientation) {
1059        if (mLastThumbnail != thumbnail) {
1060            mLastThumbnail = thumbnail;
1061            mLastThumbnailInfo.taskWidth = taskWidth;
1062            mLastThumbnailInfo.taskHeight = taskHeight;
1063            mLastThumbnailInfo.screenOrientation = screenOrientation;
1064            if (thumbnail == null) {
1065                if (mLastThumbnailFile != null) {
1066                    mLastThumbnailFile.delete();
1067                }
1068            } else {
1069                mService.mRecentTasks.saveImage(thumbnail, mLastThumbnailFile.getAbsolutePath());
1070            }
1071            return true;
1072        }
1073        return false;
1074    }
1075
1076    void getLastThumbnail(TaskThumbnail thumbs) {
1077        thumbs.mainThumbnail = mLastThumbnail;
1078        thumbs.thumbnailInfo = mLastThumbnailInfo;
1079        thumbs.thumbnailFileDescriptor = null;
1080        if (mLastThumbnail == null) {
1081            thumbs.mainThumbnail = mService.mRecentTasks.getImageFromWriteQueue(
1082                    mLastThumbnailFile.getAbsolutePath());
1083        }
1084        // Only load the thumbnail file if we don't have a thumbnail
1085        if (thumbs.mainThumbnail == null && mLastThumbnailFile.exists()) {
1086            try {
1087                thumbs.thumbnailFileDescriptor = ParcelFileDescriptor.open(mLastThumbnailFile,
1088                        ParcelFileDescriptor.MODE_READ_ONLY);
1089            } catch (IOException e) {
1090            }
1091        }
1092    }
1093
1094    /**
1095     * Removes in-memory thumbnail data when the max number of in-memory task thumbnails is reached.
1096     */
1097    void freeLastThumbnail() {
1098        mLastThumbnail = null;
1099    }
1100
1101    /**
1102     * Removes all associated thumbnail data when a task is removed or pruned from recents.
1103     */
1104    void disposeThumbnail() {
1105        mLastThumbnailInfo.reset();
1106        mLastThumbnail = null;
1107        lastDescription = null;
1108    }
1109
1110    /** Returns the intent for the root activity for this task */
1111    Intent getBaseIntent() {
1112        return intent != null ? intent : affinityIntent;
1113    }
1114
1115    /** Returns the first non-finishing activity from the root. */
1116    ActivityRecord getRootActivity() {
1117        for (int i = 0; i < mActivities.size(); i++) {
1118            final ActivityRecord r = mActivities.get(i);
1119            if (r.finishing) {
1120                continue;
1121            }
1122            return r;
1123        }
1124        return null;
1125    }
1126
1127    ActivityRecord getTopActivity() {
1128        for (int i = mActivities.size() - 1; i >= 0; --i) {
1129            final ActivityRecord r = mActivities.get(i);
1130            if (r.finishing) {
1131                continue;
1132            }
1133            return r;
1134        }
1135        return null;
1136    }
1137
1138    ActivityRecord topRunningActivityLocked() {
1139        if (mStack != null) {
1140            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
1141                ActivityRecord r = mActivities.get(activityNdx);
1142                if (!r.finishing && r.okToShowLocked()) {
1143                    return r;
1144                }
1145            }
1146        }
1147        return null;
1148    }
1149
1150    ActivityRecord topRunningActivityWithStartingWindowLocked() {
1151        if (mStack != null) {
1152            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
1153                ActivityRecord r = mActivities.get(activityNdx);
1154                if (r.mStartingWindowState != STARTING_WINDOW_SHOWN
1155                        || r.finishing || !r.okToShowLocked()) {
1156                    continue;
1157                }
1158                return r;
1159            }
1160        }
1161        return null;
1162    }
1163
1164    boolean okToShowLocked() {
1165        // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
1166        // okay to show the activity when locked.
1167        return mService.mStackSupervisor.isCurrentProfileLocked(userId)
1168                || topRunningActivityLocked() != null;
1169    }
1170
1171    /** Call after activity movement or finish to make sure that frontOfTask is set correctly */
1172    final void setFrontOfTask() {
1173        boolean foundFront = false;
1174        final int numActivities = mActivities.size();
1175        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
1176            final ActivityRecord r = mActivities.get(activityNdx);
1177            if (foundFront || r.finishing) {
1178                r.frontOfTask = false;
1179            } else {
1180                r.frontOfTask = true;
1181                // Set frontOfTask false for every following activity.
1182                foundFront = true;
1183            }
1184        }
1185        if (!foundFront && numActivities > 0) {
1186            // All activities of this task are finishing. As we ought to have a frontOfTask
1187            // activity, make the bottom activity front.
1188            mActivities.get(0).frontOfTask = true;
1189        }
1190    }
1191
1192    /**
1193     * Reorder the history stack so that the passed activity is brought to the front.
1194     */
1195    final void moveActivityToFrontLocked(ActivityRecord newTop) {
1196        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
1197                "Removing and adding activity " + newTop
1198                + " to stack at top callers=" + Debug.getCallers(4));
1199
1200        mActivities.remove(newTop);
1201        mActivities.add(newTop);
1202        updateEffectiveIntent();
1203
1204        setFrontOfTask();
1205    }
1206
1207    void addActivityAtBottom(ActivityRecord r) {
1208        addActivityAtIndex(0, r);
1209    }
1210
1211    void addActivityToTop(ActivityRecord r) {
1212        addActivityAtIndex(mActivities.size(), r);
1213    }
1214
1215    /**
1216     * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either
1217     * be in the current task or unparented to any task.
1218     */
1219    void addActivityAtIndex(int index, ActivityRecord r) {
1220        TaskRecord task = r.getTask();
1221        if (task != null && task != this) {
1222            throw new IllegalArgumentException("Can not add r=" + " to task=" + this
1223                    + " current parent=" + task);
1224        }
1225
1226        r.setTask(this);
1227
1228        // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
1229        if (!mActivities.remove(r) && r.fullscreen) {
1230            // Was not previously in list.
1231            numFullscreen++;
1232        }
1233        // Only set this based on the first activity
1234        if (mActivities.isEmpty()) {
1235            taskType = r.mActivityType;
1236            isPersistable = r.isPersistable();
1237            mCallingUid = r.launchedFromUid;
1238            mCallingPackage = r.launchedFromPackage;
1239            // Clamp to [1, max].
1240            maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
1241                    ActivityManager.getMaxAppRecentsLimitStatic());
1242        } else {
1243            // Otherwise make all added activities match this one.
1244            r.mActivityType = taskType;
1245        }
1246
1247        final int size = mActivities.size();
1248
1249        if (index == size && size > 0) {
1250            final ActivityRecord top = mActivities.get(size - 1);
1251            if (top.mTaskOverlay) {
1252                // Place below the task overlay activity since the overlay activity should always
1253                // be on top.
1254                index--;
1255            }
1256        }
1257
1258        index = Math.min(size, index);
1259        mActivities.add(index, r);
1260        updateEffectiveIntent();
1261        if (r.isPersistable()) {
1262            mService.notifyTaskPersisterLocked(this, false);
1263        }
1264
1265        // Sync. with window manager
1266        updateOverrideConfigurationFromLaunchBounds();
1267        final AppWindowContainerController appController = r.getWindowContainerController();
1268        if (appController != null) {
1269            // Only attempt to move in WM if the child has a controller. It is possible we haven't
1270            // created controller for the activity we are starting yet.
1271            mWindowContainerController.positionChildAt(appController, index);
1272        }
1273        r.onOverrideConfigurationSent();
1274
1275        // Make sure the list of display UID whitelists is updated
1276        // now that this record is in a new task.
1277        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
1278    }
1279
1280    /**
1281     * Removes the specified activity from this task.
1282     * @param r The {@link ActivityRecord} to remove.
1283     * @return true if this was the last activity in the task.
1284     */
1285    boolean removeActivity(ActivityRecord r) {
1286        return removeActivity(r, false /*reparenting*/);
1287    }
1288
1289    boolean removeActivity(ActivityRecord r, boolean reparenting) {
1290        if (r.getTask() != this) {
1291            throw new IllegalArgumentException(
1292                    "Activity=" + r + " does not belong to task=" + this);
1293        }
1294
1295        r.setTask(null /*task*/, reparenting);
1296
1297        if (mActivities.remove(r) && r.fullscreen) {
1298            // Was previously in list.
1299            numFullscreen--;
1300        }
1301        if (r.isPersistable()) {
1302            mService.notifyTaskPersisterLocked(this, false);
1303        }
1304
1305        if (getStackId() == PINNED_STACK_ID) {
1306            // We normally notify listeners of task stack changes on pause, however pinned stack
1307            // activities are normally in the paused state so no notification will be sent there
1308            // before the activity is removed. We send it here so instead.
1309            mService.mTaskChangeNotificationController.notifyTaskStackChanged();
1310        }
1311
1312        if (mActivities.isEmpty()) {
1313            return !mReuseTask;
1314        }
1315        updateEffectiveIntent();
1316        return false;
1317    }
1318
1319    /**
1320     * @return whether or not there are ONLY task overlay activities in the stack.
1321     *         If {@param excludeFinishing} is set, then ignore finishing activities in the check.
1322     *         If there are no task overlay activities, this call returns false.
1323     */
1324    boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
1325        int count = 0;
1326        for (int i = mActivities.size() - 1; i >= 0; i--) {
1327            final ActivityRecord r = mActivities.get(i);
1328            if (excludeFinishing && r.finishing) {
1329                continue;
1330            }
1331            if (!r.mTaskOverlay) {
1332                return false;
1333            }
1334            count++;
1335        }
1336        return count > 0;
1337    }
1338
1339    boolean autoRemoveFromRecents() {
1340        // We will automatically remove the task either if it has explicitly asked for
1341        // this, or it is empty and has never contained an activity that got shown to
1342        // the user.
1343        return autoRemoveRecents || (mActivities.isEmpty() && !hasBeenVisible);
1344    }
1345
1346    /**
1347     * Completely remove all activities associated with an existing
1348     * task starting at a specified index.
1349     */
1350    final void performClearTaskAtIndexLocked(int activityNdx, boolean pauseImmediately) {
1351        int numActivities = mActivities.size();
1352        for ( ; activityNdx < numActivities; ++activityNdx) {
1353            final ActivityRecord r = mActivities.get(activityNdx);
1354            if (r.finishing) {
1355                continue;
1356            }
1357            if (mStack == null) {
1358                // Task was restored from persistent storage.
1359                r.takeFromHistory();
1360                mActivities.remove(activityNdx);
1361                --activityNdx;
1362                --numActivities;
1363            } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null,
1364                    "clear-task-index", false, pauseImmediately)) {
1365                --activityNdx;
1366                --numActivities;
1367            }
1368        }
1369    }
1370
1371    /**
1372     * Completely remove all activities associated with an existing task.
1373     */
1374    final void performClearTaskLocked() {
1375        mReuseTask = true;
1376        performClearTaskAtIndexLocked(0, !PAUSE_IMMEDIATELY);
1377        mReuseTask = false;
1378    }
1379
1380    ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) {
1381        mReuseTask = true;
1382        final ActivityRecord result = performClearTaskLocked(newR, launchFlags);
1383        mReuseTask = false;
1384        return result;
1385    }
1386
1387    /**
1388     * Perform clear operation as requested by
1389     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
1390     * stack to the given task, then look for
1391     * an instance of that activity in the stack and, if found, finish all
1392     * activities on top of it and return the instance.
1393     *
1394     * @param newR Description of the new activity being started.
1395     * @return Returns the old activity that should be continued to be used,
1396     * or null if none was found.
1397     */
1398    final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
1399        int numActivities = mActivities.size();
1400        for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
1401            ActivityRecord r = mActivities.get(activityNdx);
1402            if (r.finishing) {
1403                continue;
1404            }
1405            if (r.realActivity.equals(newR.realActivity)) {
1406                // Here it is!  Now finish everything in front...
1407                final ActivityRecord ret = r;
1408
1409                for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
1410                    r = mActivities.get(activityNdx);
1411                    if (r.finishing) {
1412                        continue;
1413                    }
1414                    ActivityOptions opts = r.takeOptionsLocked();
1415                    if (opts != null) {
1416                        ret.updateOptionsLocked(opts);
1417                    }
1418                    if (mStack != null && mStack.finishActivityLocked(
1419                            r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
1420                        --activityNdx;
1421                        --numActivities;
1422                    }
1423                }
1424
1425                // Finally, if this is a normal launch mode (that is, not
1426                // expecting onNewIntent()), then we will finish the current
1427                // instance of the activity so a new fresh one can be started.
1428                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
1429                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
1430                        && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
1431                    if (!ret.finishing) {
1432                        if (mStack != null) {
1433                            mStack.finishActivityLocked(
1434                                    ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
1435                        }
1436                        return null;
1437                    }
1438                }
1439
1440                return ret;
1441            }
1442        }
1443
1444        return null;
1445    }
1446
1447    TaskThumbnail getTaskThumbnailLocked() {
1448        if (mStack != null) {
1449            final ActivityRecord resumedActivity = mStack.mResumedActivity;
1450            if (resumedActivity != null && resumedActivity.getTask() == this) {
1451                final Bitmap thumbnail = resumedActivity.screenshotActivityLocked();
1452                setLastThumbnailLocked(thumbnail);
1453            }
1454        }
1455        final TaskThumbnail taskThumbnail = new TaskThumbnail();
1456        getLastThumbnail(taskThumbnail);
1457        return taskThumbnail;
1458    }
1459
1460    void removeTaskActivitiesLocked(boolean pauseImmediately) {
1461        // Just remove the entire task.
1462        performClearTaskAtIndexLocked(0, pauseImmediately);
1463    }
1464
1465    String lockTaskAuthToString() {
1466        switch (mLockTaskAuth) {
1467            case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";
1468            case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE";
1469            case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE";
1470            case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED";
1471            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV";
1472            default: return "unknown=" + mLockTaskAuth;
1473        }
1474    }
1475
1476    void setLockTaskAuth() {
1477        if (!mPrivileged &&
1478                (mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS ||
1479                        mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
1480            // Non-priv apps are not allowed to use always or never, fall back to default
1481            mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
1482        }
1483        switch (mLockTaskMode) {
1484            case LOCK_TASK_LAUNCH_MODE_DEFAULT:
1485                mLockTaskAuth = isLockTaskWhitelistedLocked() ?
1486                    LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
1487                break;
1488
1489            case LOCK_TASK_LAUNCH_MODE_NEVER:
1490                mLockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
1491                break;
1492
1493            case LOCK_TASK_LAUNCH_MODE_ALWAYS:
1494                mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
1495                break;
1496
1497            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
1498                mLockTaskAuth = isLockTaskWhitelistedLocked() ?
1499                        LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
1500                break;
1501        }
1502        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
1503                " mLockTaskAuth=" + lockTaskAuthToString());
1504    }
1505
1506    boolean isLockTaskWhitelistedLocked() {
1507        String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
1508        if (pkg == null) {
1509            return false;
1510        }
1511        String[] packages = mService.mLockTaskPackages.get(userId);
1512        if (packages == null) {
1513            return false;
1514        }
1515        for (int i = packages.length - 1; i >= 0; --i) {
1516            if (pkg.equals(packages[i])) {
1517                return true;
1518            }
1519        }
1520        return false;
1521    }
1522
1523    boolean isHomeTask() {
1524        return taskType == HOME_ACTIVITY_TYPE;
1525    }
1526
1527    boolean isRecentsTask() {
1528        return taskType == RECENTS_ACTIVITY_TYPE;
1529    }
1530
1531    boolean isAssistantTask() {
1532        return taskType == ASSISTANT_ACTIVITY_TYPE;
1533    }
1534
1535    boolean isApplicationTask() {
1536        return taskType == APPLICATION_ACTIVITY_TYPE;
1537    }
1538
1539    boolean isOverHomeStack() {
1540        return mTaskToReturnTo == HOME_ACTIVITY_TYPE;
1541    }
1542
1543    boolean isOverAssistantStack() {
1544        return mTaskToReturnTo == ASSISTANT_ACTIVITY_TYPE;
1545    }
1546
1547    private boolean isResizeable(boolean checkSupportsPip) {
1548        return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
1549                || (checkSupportsPip && mSupportsPictureInPicture)) && !mTemporarilyUnresizable;
1550    }
1551
1552    boolean isResizeable() {
1553        return isResizeable(true /* checkSupportsPip */);
1554    }
1555
1556    boolean supportsSplitScreen() {
1557        // A task can not be docked even if it is considered resizeable because it only supports
1558        // picture-in-picture mode but has a non-resizeable resizeMode
1559        return mService.mSupportsSplitScreenMultiWindow
1560                && isResizeable(false /* checkSupportsPip */)
1561                && !ActivityInfo.isPreserveOrientationMode(mResizeMode);
1562    }
1563
1564    /**
1565     * Check whether this task can be launched on the specified display.
1566     * @param displayId Target display id.
1567     * @return {@code true} if either it is the default display or this activity is resizeable and
1568     *         can be put a secondary screen.
1569     */
1570    boolean canBeLaunchedOnDisplay(int displayId) {
1571        return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
1572                isResizeable(false /* checkSupportsPip */));
1573    }
1574
1575    /**
1576     * Check that a given bounds matches the application requested orientation.
1577     *
1578     * @param bounds The bounds to be tested.
1579     * @return True if the requested bounds are okay for a resizing request.
1580     */
1581    private boolean canResizeToBounds(Rect bounds) {
1582        if (bounds == null || getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
1583            // Note: If not on the freeform workspace, we ignore the bounds.
1584            return true;
1585        }
1586        final boolean landscape = bounds.width() > bounds.height();
1587        if (mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION) {
1588            return mBounds == null || landscape == (mBounds.width() > mBounds.height());
1589        }
1590        return (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY || !landscape)
1591                && (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY || landscape);
1592    }
1593
1594    /**
1595     * Find the activity in the history stack within the given task.  Returns
1596     * the index within the history at which it's found, or < 0 if not found.
1597     */
1598    final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
1599        final ComponentName realActivity = r.realActivity;
1600        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
1601            ActivityRecord candidate = mActivities.get(activityNdx);
1602            if (candidate.finishing) {
1603                continue;
1604            }
1605            if (candidate.realActivity.equals(realActivity)) {
1606                return candidate;
1607            }
1608        }
1609        return null;
1610    }
1611
1612    /** Updates the last task description values. */
1613    void updateTaskDescription() {
1614        // Traverse upwards looking for any break between main task activities and
1615        // utility activities.
1616        int activityNdx;
1617        final int numActivities = mActivities.size();
1618        final boolean relinquish = numActivities != 0 &&
1619                (mActivities.get(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
1620        for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
1621                ++activityNdx) {
1622            final ActivityRecord r = mActivities.get(activityNdx);
1623            if (relinquish && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
1624                // This will be the top activity for determining taskDescription. Pre-inc to
1625                // overcome initial decrement below.
1626                ++activityNdx;
1627                break;
1628            }
1629            if (r.intent != null &&
1630                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
1631                break;
1632            }
1633        }
1634        if (activityNdx > 0) {
1635            // Traverse downwards starting below break looking for set label, icon.
1636            // Note that if there are activities in the task but none of them set the
1637            // recent activity values, then we do not fall back to the last set
1638            // values in the TaskRecord.
1639            String label = null;
1640            String iconFilename = null;
1641            int colorPrimary = 0;
1642            int colorBackground = 0;
1643            int statusBarColor = 0;
1644            int navigationBarColor = 0;
1645            boolean topActivity = true;
1646            for (--activityNdx; activityNdx >= 0; --activityNdx) {
1647                final ActivityRecord r = mActivities.get(activityNdx);
1648                if (r.taskDescription != null) {
1649                    if (label == null) {
1650                        label = r.taskDescription.getLabel();
1651                    }
1652                    if (iconFilename == null) {
1653                        iconFilename = r.taskDescription.getIconFilename();
1654                    }
1655                    if (colorPrimary == 0) {
1656                        colorPrimary = r.taskDescription.getPrimaryColor();
1657                    }
1658                    if (topActivity) {
1659                        colorBackground = r.taskDescription.getBackgroundColor();
1660                        statusBarColor = r.taskDescription.getStatusBarColor();
1661                        navigationBarColor = r.taskDescription.getNavigationBarColor();
1662                    }
1663                }
1664                topActivity = false;
1665            }
1666            lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
1667                    colorBackground, statusBarColor, navigationBarColor);
1668            if (mWindowContainerController != null) {
1669                mWindowContainerController.setTaskDescription(lastTaskDescription);
1670            }
1671            // Update the task affiliation color if we are the parent of the group
1672            if (taskId == mAffiliatedTaskId) {
1673                mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
1674            }
1675        }
1676    }
1677
1678    int findEffectiveRootIndex() {
1679        int effectiveNdx = 0;
1680        final int topActivityNdx = mActivities.size() - 1;
1681        for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) {
1682            final ActivityRecord r = mActivities.get(activityNdx);
1683            if (r.finishing) {
1684                continue;
1685            }
1686            effectiveNdx = activityNdx;
1687            if ((r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
1688                break;
1689            }
1690        }
1691        return effectiveNdx;
1692    }
1693
1694    void updateEffectiveIntent() {
1695        final int effectiveRootIndex = findEffectiveRootIndex();
1696        final ActivityRecord r = mActivities.get(effectiveRootIndex);
1697        setIntent(r);
1698    }
1699
1700    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
1701        if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
1702
1703        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
1704        if (realActivity != null) {
1705            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
1706        }
1707        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
1708        if (origActivity != null) {
1709            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
1710        }
1711        // Write affinity, and root affinity if it is different from affinity.
1712        // We use the special string "@" for a null root affinity, so we can identify
1713        // later whether we were given a root affinity or should just make it the
1714        // same as the affinity.
1715        if (affinity != null) {
1716            out.attribute(null, ATTR_AFFINITY, affinity);
1717            if (!affinity.equals(rootAffinity)) {
1718                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
1719            }
1720        } else if (rootAffinity != null) {
1721            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
1722        }
1723        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
1724        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
1725        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
1726        out.attribute(null, ATTR_USERID, String.valueOf(userId));
1727        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
1728        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
1729        out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
1730        out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
1731        out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
1732        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
1733        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
1734        if (lastDescription != null) {
1735            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
1736        }
1737        if (lastTaskDescription != null) {
1738            lastTaskDescription.saveToXml(out);
1739        }
1740        mLastThumbnailInfo.saveToXml(out);
1741        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
1742        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
1743        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
1744        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
1745        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
1746        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
1747        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
1748        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
1749                String.valueOf(mSupportsPictureInPicture));
1750        out.attribute(null, ATTR_PRIVILEGED, String.valueOf(mPrivileged));
1751        if (mLastNonFullscreenBounds != null) {
1752            out.attribute(
1753                    null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
1754        }
1755        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
1756        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
1757        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
1758
1759        if (affinityIntent != null) {
1760            out.startTag(null, TAG_AFFINITYINTENT);
1761            affinityIntent.saveToXml(out);
1762            out.endTag(null, TAG_AFFINITYINTENT);
1763        }
1764
1765        out.startTag(null, TAG_INTENT);
1766        intent.saveToXml(out);
1767        out.endTag(null, TAG_INTENT);
1768
1769        final ArrayList<ActivityRecord> activities = mActivities;
1770        final int numActivities = activities.size();
1771        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
1772            final ActivityRecord r = activities.get(activityNdx);
1773            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
1774                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
1775                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
1776                            activityNdx > 0) {
1777                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
1778                break;
1779            }
1780            out.startTag(null, TAG_ACTIVITY);
1781            r.saveToXml(out);
1782            out.endTag(null, TAG_ACTIVITY);
1783        }
1784    }
1785
1786    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
1787            throws IOException, XmlPullParserException {
1788        Intent intent = null;
1789        Intent affinityIntent = null;
1790        ArrayList<ActivityRecord> activities = new ArrayList<>();
1791        ComponentName realActivity = null;
1792        boolean realActivitySuspended = false;
1793        ComponentName origActivity = null;
1794        String affinity = null;
1795        String rootAffinity = null;
1796        boolean hasRootAffinity = false;
1797        boolean rootHasReset = false;
1798        boolean autoRemoveRecents = false;
1799        boolean askedCompatMode = false;
1800        int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
1801        int userId = 0;
1802        boolean userSetupComplete = true;
1803        int effectiveUid = -1;
1804        String lastDescription = null;
1805        long firstActiveTime = -1;
1806        long lastActiveTime = -1;
1807        long lastTimeOnTop = 0;
1808        boolean neverRelinquishIdentity = true;
1809        int taskId = INVALID_TASK_ID;
1810        final int outerDepth = in.getDepth();
1811        TaskDescription taskDescription = new TaskDescription();
1812        TaskThumbnailInfo thumbnailInfo = new TaskThumbnailInfo();
1813        int taskAffiliation = INVALID_TASK_ID;
1814        int taskAffiliationColor = 0;
1815        int prevTaskId = INVALID_TASK_ID;
1816        int nextTaskId = INVALID_TASK_ID;
1817        int callingUid = -1;
1818        String callingPackage = "";
1819        int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
1820        boolean supportsPictureInPicture = false;
1821        boolean privileged = false;
1822        Rect bounds = null;
1823        int minWidth = INVALID_MIN_SIZE;
1824        int minHeight = INVALID_MIN_SIZE;
1825        int persistTaskVersion = 0;
1826
1827        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
1828            final String attrName = in.getAttributeName(attrNdx);
1829            final String attrValue = in.getAttributeValue(attrNdx);
1830            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
1831                    attrName + " value=" + attrValue);
1832            if (ATTR_TASKID.equals(attrName)) {
1833                if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
1834            } else if (ATTR_REALACTIVITY.equals(attrName)) {
1835                realActivity = ComponentName.unflattenFromString(attrValue);
1836            } else if (ATTR_REALACTIVITY_SUSPENDED.equals(attrName)) {
1837                realActivitySuspended = Boolean.valueOf(attrValue);
1838            } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
1839                origActivity = ComponentName.unflattenFromString(attrValue);
1840            } else if (ATTR_AFFINITY.equals(attrName)) {
1841                affinity = attrValue;
1842            } else if (ATTR_ROOT_AFFINITY.equals(attrName)) {
1843                rootAffinity = attrValue;
1844                hasRootAffinity = true;
1845            } else if (ATTR_ROOTHASRESET.equals(attrName)) {
1846                rootHasReset = Boolean.parseBoolean(attrValue);
1847            } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) {
1848                autoRemoveRecents = Boolean.parseBoolean(attrValue);
1849            } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
1850                askedCompatMode = Boolean.parseBoolean(attrValue);
1851            } else if (ATTR_USERID.equals(attrName)) {
1852                userId = Integer.parseInt(attrValue);
1853            } else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) {
1854                userSetupComplete = Boolean.parseBoolean(attrValue);
1855            } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
1856                effectiveUid = Integer.parseInt(attrValue);
1857            } else if (ATTR_TASKTYPE.equals(attrName)) {
1858                taskType = Integer.parseInt(attrValue);
1859            } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
1860                firstActiveTime = Long.parseLong(attrValue);
1861            } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
1862                lastActiveTime = Long.parseLong(attrValue);
1863            } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
1864                lastDescription = attrValue;
1865            } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
1866                lastTimeOnTop = Long.parseLong(attrValue);
1867            } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
1868                neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
1869            } else if (attrName.startsWith(TaskThumbnailInfo.ATTR_TASK_THUMBNAILINFO_PREFIX)) {
1870                thumbnailInfo.restoreFromXml(attrName, attrValue);
1871            } else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
1872                taskDescription.restoreFromXml(attrName, attrValue);
1873            } else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
1874                taskAffiliation = Integer.parseInt(attrValue);
1875            } else if (ATTR_PREV_AFFILIATION.equals(attrName)) {
1876                prevTaskId = Integer.parseInt(attrValue);
1877            } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
1878                nextTaskId = Integer.parseInt(attrValue);
1879            } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) {
1880                taskAffiliationColor = Integer.parseInt(attrValue);
1881            } else if (ATTR_CALLING_UID.equals(attrName)) {
1882                callingUid = Integer.parseInt(attrValue);
1883            } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
1884                callingPackage = attrValue;
1885            } else if (ATTR_RESIZE_MODE.equals(attrName)) {
1886                resizeMode = Integer.parseInt(attrValue);
1887            } else if (ATTR_SUPPORTS_PICTURE_IN_PICTURE.equals(attrName)) {
1888                supportsPictureInPicture = Boolean.parseBoolean(attrValue);
1889            } else if (ATTR_PRIVILEGED.equals(attrName)) {
1890                privileged = Boolean.parseBoolean(attrValue);
1891            } else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
1892                bounds = Rect.unflattenFromString(attrValue);
1893            } else if (ATTR_MIN_WIDTH.equals(attrName)) {
1894                minWidth = Integer.parseInt(attrValue);
1895            } else if (ATTR_MIN_HEIGHT.equals(attrName)) {
1896                minHeight = Integer.parseInt(attrValue);
1897            } else if (ATTR_PERSIST_TASK_VERSION.equals(attrName)) {
1898                persistTaskVersion = Integer.parseInt(attrValue);
1899            } else {
1900                Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
1901            }
1902        }
1903
1904        int event;
1905        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
1906                (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
1907            if (event == XmlPullParser.START_TAG) {
1908                final String name = in.getName();
1909                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
1910                        name);
1911                if (TAG_AFFINITYINTENT.equals(name)) {
1912                    affinityIntent = Intent.restoreFromXml(in);
1913                } else if (TAG_INTENT.equals(name)) {
1914                    intent = Intent.restoreFromXml(in);
1915                } else if (TAG_ACTIVITY.equals(name)) {
1916                    ActivityRecord activity = ActivityRecord.restoreFromXml(in, stackSupervisor);
1917                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
1918                            activity);
1919                    if (activity != null) {
1920                        activities.add(activity);
1921                    }
1922                } else {
1923                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
1924                    XmlUtils.skipCurrentTag(in);
1925                }
1926            }
1927        }
1928        if (!hasRootAffinity) {
1929            rootAffinity = affinity;
1930        } else if ("@".equals(rootAffinity)) {
1931            rootAffinity = null;
1932        }
1933        if (effectiveUid <= 0) {
1934            Intent checkIntent = intent != null ? intent : affinityIntent;
1935            effectiveUid = 0;
1936            if (checkIntent != null) {
1937                IPackageManager pm = AppGlobals.getPackageManager();
1938                try {
1939                    ApplicationInfo ai = pm.getApplicationInfo(
1940                            checkIntent.getComponent().getPackageName(),
1941                            PackageManager.MATCH_UNINSTALLED_PACKAGES
1942                                    | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
1943                    if (ai != null) {
1944                        effectiveUid = ai.uid;
1945                    }
1946                } catch (RemoteException e) {
1947                }
1948            }
1949            Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
1950                    + ": effectiveUid=" + effectiveUid);
1951        }
1952
1953        if (persistTaskVersion < 1) {
1954            // We need to convert the resize mode of home activities saved before version one if
1955            // they are marked as RESIZE_MODE_RESIZEABLE to RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
1956            // since we didn't have that differentiation before version 1 and the system didn't
1957            // resize home activities before then.
1958            if (taskType == HOME_ACTIVITY_TYPE && resizeMode == RESIZE_MODE_RESIZEABLE) {
1959                resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
1960            }
1961        } else {
1962            // This activity has previously marked itself explicitly as both resizeable and
1963            // supporting picture-in-picture.  Since there is no longer a requirement for
1964            // picture-in-picture activities to be resizeable, we can mark this simply as
1965            // resizeable and supporting picture-in-picture separately.
1966            if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
1967                resizeMode = RESIZE_MODE_RESIZEABLE;
1968                supportsPictureInPicture = true;
1969            }
1970        }
1971
1972        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
1973                affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
1974                autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
1975                activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
1976                taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
1977                taskAffiliationColor, callingUid, callingPackage, resizeMode,
1978                supportsPictureInPicture, privileged, realActivitySuspended, userSetupComplete,
1979                minWidth, minHeight);
1980        task.updateOverrideConfiguration(bounds);
1981
1982        for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
1983            activities.get(activityNdx).setTask(task);
1984        }
1985
1986        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
1987        return task;
1988    }
1989
1990    private void adjustForMinimalTaskDimensions(Rect bounds) {
1991        if (bounds == null) {
1992            return;
1993        }
1994        int minWidth = mMinWidth;
1995        int minHeight = mMinHeight;
1996        // If the task has no requested minimal size, we'd like to enforce a minimal size
1997        // so that the user can not render the task too small to manipulate. We don't need
1998        // to do this for the pinned stack as the bounds are controlled by the system.
1999        if (getStackId() != PINNED_STACK_ID) {
2000            if (minWidth == INVALID_MIN_SIZE) {
2001                minWidth = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
2002            }
2003            if (minHeight == INVALID_MIN_SIZE) {
2004                minHeight = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
2005            }
2006        }
2007        final boolean adjustWidth = minWidth > bounds.width();
2008        final boolean adjustHeight = minHeight > bounds.height();
2009        if (!(adjustWidth || adjustHeight)) {
2010            return;
2011        }
2012
2013        if (adjustWidth) {
2014            if (mBounds != null && bounds.right == mBounds.right) {
2015                bounds.left = bounds.right - minWidth;
2016            } else {
2017                // Either left bounds match, or neither match, or the previous bounds were
2018                // fullscreen and we default to keeping left.
2019                bounds.right = bounds.left + minWidth;
2020            }
2021        }
2022        if (adjustHeight) {
2023            if (mBounds != null && bounds.bottom == mBounds.bottom) {
2024                bounds.top = bounds.bottom - minHeight;
2025            } else {
2026                // Either top bounds match, or neither match, or the previous bounds were
2027                // fullscreen and we default to keeping top.
2028                bounds.bottom = bounds.top + minHeight;
2029            }
2030        }
2031    }
2032
2033    /**
2034     * @return a new Configuration for this Task, given the provided {@param bounds} and
2035     *         {@param insetBounds}.
2036     */
2037    Configuration computeNewOverrideConfigurationForBounds(Rect bounds, Rect insetBounds) {
2038        // Compute a new override configuration for the given bounds, if fullscreen bounds
2039        // (bounds == null), then leave the override config unset
2040        final Configuration newOverrideConfig = new Configuration();
2041        if (bounds != null) {
2042            newOverrideConfig.setTo(getOverrideConfiguration());
2043            mTmpRect.set(bounds);
2044            adjustForMinimalTaskDimensions(mTmpRect);
2045            computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds,
2046                    mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
2047        }
2048
2049        return newOverrideConfig;
2050    }
2051
2052    /**
2053     * Update task's override configuration based on the bounds.
2054     * @param bounds The bounds of the task.
2055     * @return True if the override configuration was updated.
2056     */
2057    boolean updateOverrideConfiguration(Rect bounds) {
2058        return updateOverrideConfiguration(bounds, null /* insetBounds */);
2059    }
2060
2061    /**
2062     * Update task's override configuration based on the bounds.
2063     * @param bounds The bounds of the task.
2064     * @param insetBounds The bounds used to calculate the system insets, which is used here to
2065     *                    subtract the navigation bar/status bar size from the screen size reported
2066     *                    to the application. See {@link IActivityManager#resizeDockedStack}.
2067     * @return True if the override configuration was updated.
2068     */
2069    boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
2070        if (Objects.equals(mBounds, bounds)) {
2071            return false;
2072        }
2073        mTmpConfig.setTo(getOverrideConfiguration());
2074        final boolean oldFullscreen = mFullscreen;
2075        final Configuration newConfig = getOverrideConfiguration();
2076
2077        mFullscreen = bounds == null;
2078        if (mFullscreen) {
2079            if (mBounds != null && StackId.persistTaskBounds(mStack.mStackId)) {
2080                mLastNonFullscreenBounds = mBounds;
2081            }
2082            mBounds = null;
2083            newConfig.unset();
2084        } else {
2085            mTmpRect.set(bounds);
2086            adjustForMinimalTaskDimensions(mTmpRect);
2087            if (mBounds == null) {
2088                mBounds = new Rect(mTmpRect);
2089            } else {
2090                mBounds.set(mTmpRect);
2091            }
2092            if (mStack == null || StackId.persistTaskBounds(mStack.mStackId)) {
2093                mLastNonFullscreenBounds = mBounds;
2094            }
2095            computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
2096                    mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
2097        }
2098        onOverrideConfigurationChanged(newConfig);
2099
2100        if (mFullscreen != oldFullscreen) {
2101            mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
2102        }
2103
2104        return !mTmpConfig.equals(newConfig);
2105    }
2106
2107    /** Clears passed config and fills it with new override values. */
2108    // TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
2109    // depend on task or stacks, but uses those object to get the display to base the calculation
2110    // on. Probably best to centralize calculations like this in ConfigurationContainer.
2111    void computeOverrideConfiguration(Configuration config, Rect bounds, Rect insetBounds,
2112            boolean overrideWidth, boolean overrideHeight) {
2113        mTmpNonDecorBounds.set(bounds);
2114        mTmpStableBounds.set(bounds);
2115
2116        config.unset();
2117        final Configuration parentConfig = getParent().getConfiguration();
2118
2119        final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
2120
2121        if (mStack != null) {
2122            final StackWindowController stackController = mStack.getWindowContainerController();
2123            stackController.adjustConfigurationForBounds(bounds, insetBounds,
2124                    mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
2125                    config, parentConfig);
2126        } else {
2127            throw new IllegalArgumentException("Expected stack when calculating override config");
2128        }
2129
2130        config.orientation = (config.screenWidthDp <= config.screenHeightDp)
2131                ? Configuration.ORIENTATION_PORTRAIT
2132                : Configuration.ORIENTATION_LANDSCAPE;
2133
2134        // For calculating screen layout, we need to use the non-decor inset screen area for the
2135        // calculation for compatibility reasons, i.e. screen area without system bars that could
2136        // never go away in Honeycomb.
2137        final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
2138        final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
2139        // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start override
2140        // calculation with partial default.
2141        final int sl = Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_XLARGE;
2142        final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
2143        final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
2144        config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
2145
2146    }
2147
2148    Rect updateOverrideConfigurationFromLaunchBounds() {
2149        final Rect bounds = validateBounds(getLaunchBounds());
2150        updateOverrideConfiguration(bounds);
2151        if (bounds != null) {
2152            bounds.set(mBounds);
2153        }
2154        return bounds;
2155    }
2156
2157    static Rect validateBounds(Rect bounds) {
2158        if (bounds != null && bounds.isEmpty()) {
2159            Slog.wtf(TAG, "Received strange task bounds: " + bounds, new Throwable());
2160            return null;
2161        }
2162        return bounds;
2163    }
2164
2165    /** Updates the task's bounds and override configuration to match what is expected for the
2166     * input stack. */
2167    void updateOverrideConfigurationForStack(ActivityStack inStack) {
2168        if (mStack != null && mStack == inStack) {
2169            return;
2170        }
2171
2172        if (inStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
2173            if (!isResizeable()) {
2174                throw new IllegalArgumentException("Can not position non-resizeable task="
2175                        + this + " in stack=" + inStack);
2176            }
2177            if (mBounds != null) {
2178                return;
2179            }
2180            if (mLastNonFullscreenBounds != null) {
2181                updateOverrideConfiguration(mLastNonFullscreenBounds);
2182            } else {
2183                inStack.layoutTaskInStack(this, null);
2184            }
2185        } else {
2186            updateOverrideConfiguration(inStack.mBounds);
2187        }
2188    }
2189
2190    /**
2191     * Returns the correct stack to use based on task type and currently set bounds,
2192     * regardless of the focused stack and current stack association of the task.
2193     * The task will be moved (and stack focus changed) later if necessary.
2194     */
2195    int getLaunchStackId() {
2196        if (isRecentsTask()) {
2197            return RECENTS_STACK_ID;
2198        }
2199        if (isHomeTask()) {
2200            return HOME_STACK_ID;
2201        }
2202        if (isAssistantTask()) {
2203            return ASSISTANT_STACK_ID;
2204        }
2205        if (mBounds != null) {
2206            return FREEFORM_WORKSPACE_STACK_ID;
2207        }
2208        return FULLSCREEN_WORKSPACE_STACK_ID;
2209    }
2210
2211    /** Returns the bounds that should be used to launch this task. */
2212    Rect getLaunchBounds() {
2213        if (mStack == null) {
2214            return null;
2215        }
2216
2217        final int stackId = mStack.mStackId;
2218        if (stackId == HOME_STACK_ID
2219                || stackId == RECENTS_STACK_ID
2220                || stackId == ASSISTANT_STACK_ID
2221                || stackId == FULLSCREEN_WORKSPACE_STACK_ID
2222                || (stackId == DOCKED_STACK_ID && !isResizeable())) {
2223            return isResizeable() ? mStack.mBounds : null;
2224        } else if (!StackId.persistTaskBounds(stackId)) {
2225            return mStack.mBounds;
2226        }
2227        return mLastNonFullscreenBounds;
2228    }
2229
2230    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
2231        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
2232            final ActivityRecord r = mActivities.get(activityNdx);
2233            if (r.visible) {
2234                r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
2235            }
2236        }
2237    }
2238
2239    void dump(PrintWriter pw, String prefix) {
2240        pw.print(prefix); pw.print("userId="); pw.print(userId);
2241                pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
2242                pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
2243                pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
2244                pw.print(" mCallingPackage="); pw.println(mCallingPackage);
2245        if (affinity != null || rootAffinity != null) {
2246            pw.print(prefix); pw.print("affinity="); pw.print(affinity);
2247            if (affinity == null || !affinity.equals(rootAffinity)) {
2248                pw.print(" root="); pw.println(rootAffinity);
2249            } else {
2250                pw.println();
2251            }
2252        }
2253        if (voiceSession != null || voiceInteractor != null) {
2254            pw.print(prefix); pw.print("VOICE: session=0x");
2255            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
2256            pw.print(" interactor=0x");
2257            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
2258        }
2259        if (intent != null) {
2260            StringBuilder sb = new StringBuilder(128);
2261            sb.append(prefix); sb.append("intent={");
2262            intent.toShortString(sb, false, true, false, true);
2263            sb.append('}');
2264            pw.println(sb.toString());
2265        }
2266        if (affinityIntent != null) {
2267            StringBuilder sb = new StringBuilder(128);
2268            sb.append(prefix); sb.append("affinityIntent={");
2269            affinityIntent.toShortString(sb, false, true, false, true);
2270            sb.append('}');
2271            pw.println(sb.toString());
2272        }
2273        if (origActivity != null) {
2274            pw.print(prefix); pw.print("origActivity=");
2275            pw.println(origActivity.flattenToShortString());
2276        }
2277        if (realActivity != null) {
2278            pw.print(prefix); pw.print("realActivity=");
2279            pw.println(realActivity.flattenToShortString());
2280        }
2281        if (autoRemoveRecents || isPersistable || taskType != 0 || mTaskToReturnTo != 0
2282                || numFullscreen != 0) {
2283            pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents);
2284                    pw.print(" isPersistable="); pw.print(isPersistable);
2285                    pw.print(" numFullscreen="); pw.print(numFullscreen);
2286                    pw.print(" taskType="); pw.print(taskType);
2287                    pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
2288        }
2289        if (rootWasReset || mNeverRelinquishIdentity || mReuseTask
2290                || mLockTaskAuth != LOCK_TASK_AUTH_PINNABLE) {
2291            pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset);
2292                    pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity);
2293                    pw.print(" mReuseTask="); pw.print(mReuseTask);
2294                    pw.print(" mLockTaskAuth="); pw.println(lockTaskAuthToString());
2295        }
2296        if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != INVALID_TASK_ID
2297                || mPrevAffiliate != null || mNextAffiliateTaskId != INVALID_TASK_ID
2298                || mNextAffiliate != null) {
2299            pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId);
2300                    pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId);
2301                    pw.print(" (");
2302                    if (mPrevAffiliate == null) {
2303                        pw.print("null");
2304                    } else {
2305                        pw.print(Integer.toHexString(System.identityHashCode(mPrevAffiliate)));
2306                    }
2307                    pw.print(") nextAffiliation="); pw.print(mNextAffiliateTaskId);
2308                    pw.print(" (");
2309                    if (mNextAffiliate == null) {
2310                        pw.print("null");
2311                    } else {
2312                        pw.print(Integer.toHexString(System.identityHashCode(mNextAffiliate)));
2313                    }
2314                    pw.println(")");
2315        }
2316        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
2317        if (!askedCompatMode || !inRecents || !isAvailable) {
2318            pw.print(prefix); pw.print("askedCompatMode="); pw.print(askedCompatMode);
2319                    pw.print(" inRecents="); pw.print(inRecents);
2320                    pw.print(" isAvailable="); pw.println(isAvailable);
2321        }
2322        pw.print(prefix); pw.print("lastThumbnail="); pw.print(mLastThumbnail);
2323                pw.print(" lastThumbnailFile="); pw.println(mLastThumbnailFile);
2324        if (lastDescription != null) {
2325            pw.print(prefix); pw.print("lastDescription="); pw.println(lastDescription);
2326        }
2327        pw.print(prefix); pw.print("stackId="); pw.println(getStackId());
2328        pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
2329                pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
2330                pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
2331                pw.print(" isResizeable=" + isResizeable());
2332                pw.print(" firstActiveTime=" + lastActiveTime);
2333                pw.print(" lastActiveTime=" + lastActiveTime);
2334                pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
2335    }
2336
2337    @Override
2338    public String toString() {
2339        StringBuilder sb = new StringBuilder(128);
2340        if (stringName != null) {
2341            sb.append(stringName);
2342            sb.append(" U=");
2343            sb.append(userId);
2344            sb.append(" StackId=");
2345            sb.append(getStackId());
2346            sb.append(" sz=");
2347            sb.append(mActivities.size());
2348            sb.append('}');
2349            return sb.toString();
2350        }
2351        sb.append("TaskRecord{");
2352        sb.append(Integer.toHexString(System.identityHashCode(this)));
2353        sb.append(" #");
2354        sb.append(taskId);
2355        if (affinity != null) {
2356            sb.append(" A=");
2357            sb.append(affinity);
2358        } else if (intent != null) {
2359            sb.append(" I=");
2360            sb.append(intent.getComponent().flattenToShortString());
2361        } else if (affinityIntent != null) {
2362            sb.append(" aI=");
2363            sb.append(affinityIntent.getComponent().flattenToShortString());
2364        } else {
2365            sb.append(" ??");
2366        }
2367        stringName = sb.toString();
2368        return toString();
2369    }
2370}
2371