TaskRecord.java revision 4767f4b3a97ce468c6ef9abf8fb06e0b8051dba1
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 static com.android.server.am.ActivityManagerService.TAG;
20import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
21import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
22import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
23import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
24
25import android.app.Activity;
26import android.app.ActivityManager;
27import android.app.ActivityManager.TaskThumbnail;
28import android.app.ActivityOptions;
29import android.app.AppGlobals;
30import android.content.ComponentName;
31import android.content.Intent;
32import android.content.pm.ActivityInfo;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageManager;
35import android.content.pm.PackageManager;
36import android.graphics.Bitmap;
37import android.os.ParcelFileDescriptor;
38import android.os.RemoteException;
39import android.os.UserHandle;
40import android.service.voice.IVoiceInteractionSession;
41import android.util.Slog;
42import com.android.internal.app.IVoiceInteractor;
43import com.android.internal.util.XmlUtils;
44import org.xmlpull.v1.XmlPullParser;
45import org.xmlpull.v1.XmlPullParserException;
46import org.xmlpull.v1.XmlSerializer;
47
48import java.io.File;
49import java.io.IOException;
50import java.io.PrintWriter;
51import java.util.ArrayList;
52
53final class TaskRecord {
54    private static final String ATTR_TASKID = "task_id";
55    private static final String TAG_INTENT = "intent";
56    private static final String TAG_AFFINITYINTENT = "affinity_intent";
57    private static final String ATTR_REALACTIVITY = "real_activity";
58    private static final String ATTR_ORIGACTIVITY = "orig_activity";
59    private static final String TAG_ACTIVITY = "activity";
60    private static final String ATTR_AFFINITY = "affinity";
61    private static final String ATTR_ROOT_AFFINITY = "root_affinity";
62    private static final String ATTR_ROOTHASRESET = "root_has_reset";
63    private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
64    private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
65    private static final String ATTR_USERID = "user_id";
66    private static final String ATTR_EFFECTIVE_UID = "effective_uid";
67    private static final String ATTR_TASKTYPE = "task_type";
68    private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
69    private static final String ATTR_LASTACTIVETIME = "last_active_time";
70    private static final String ATTR_LASTDESCRIPTION = "last_description";
71    private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
72    private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
73    private static final String ATTR_TASKDESCRIPTIONLABEL = "task_description_label";
74    private static final String ATTR_TASKDESCRIPTIONCOLOR = "task_description_color";
75    private static final String ATTR_TASK_AFFILIATION = "task_affiliation";
76    private static final String ATTR_PREV_AFFILIATION = "prev_affiliation";
77    private static final String ATTR_NEXT_AFFILIATION = "next_affiliation";
78    private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
79    private static final String ATTR_CALLING_UID = "calling_uid";
80    private static final String ATTR_CALLING_PACKAGE = "calling_package";
81    private static final String LAST_ACTIVITY_ICON_SUFFIX = "_last_activity_icon_";
82
83    private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
84
85    static final boolean IGNORE_RETURN_TO_RECENTS = true;
86
87    final int taskId;       // Unique identifier for this task.
88    String affinity;        // The affinity name for this task, or null; may change identity.
89    String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
90    final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
91    final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
92    Intent intent;          // The original intent that started the task.
93    Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
94    int effectiveUid;       // The current effective uid of the identity of this task.
95    ComponentName origActivity; // The non-alias activity component of the intent.
96    ComponentName realActivity; // The actual activity component that started the task.
97    long firstActiveTime;   // First time this task was active.
98    long lastActiveTime;    // Last time this task was active, including sleep.
99    boolean inRecents;      // Actually in the recents list?
100    boolean isAvailable;    // Is the activity available to be launched?
101    boolean rootWasReset;   // True if the intent at the root of the task had
102                            // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
103    boolean autoRemoveRecents;  // If true, we should automatically remove the task from
104                                // recents when activity finishes
105    boolean askedCompatMode;// Have asked the user about compat mode for this task.
106    boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
107
108    String stringName;      // caching of toString() result.
109    int userId;             // user for which this task was created
110    int creatorUid;         // The app uid that originally created the task
111
112    int numFullscreen;      // Number of fullscreen activities.
113
114    // This represents the last resolved activity values for this task
115    // NOTE: This value needs to be persisted with each task
116    ActivityManager.TaskDescription lastTaskDescription =
117            new ActivityManager.TaskDescription();
118
119    /** List of all activities in the task arranged in history order */
120    final ArrayList<ActivityRecord> mActivities;
121
122    /** Current stack */
123    ActivityStack stack;
124
125    /** Takes on same set of values as ActivityRecord.mActivityType */
126    int taskType;
127
128    /** Takes on same value as first root activity */
129    boolean isPersistable = false;
130    int maxRecents;
131
132    /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
133     * determining the order when restoring. Sign indicates whether last task movement was to front
134     * (positive) or back (negative). Absolute value indicates time. */
135    long mLastTimeMoved = System.currentTimeMillis();
136
137    /** Indication of what to run next when task exits. Use ActivityRecord types.
138     * ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the
139     * task stack. */
140    private int mTaskToReturnTo = APPLICATION_ACTIVITY_TYPE;
141
142    /** If original intent did not allow relinquishing task identity, save that information */
143    boolean mNeverRelinquishIdentity = true;
144
145    // Used in the unique case where we are clearing the task in order to reuse it. In that case we
146    // do not want to delete the stack when the task goes empty.
147    boolean mReuseTask = false;
148
149    private Bitmap mLastThumbnail; // Last thumbnail captured for this item.
150    private final File mLastThumbnailFile; // File containing last thubmnail.
151    private final String mFilename;
152    CharSequence lastDescription; // Last description captured for this item.
153
154    int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
155    int mAffiliatedTaskColor; // color of the parent task affiliation.
156    TaskRecord mPrevAffiliate; // previous task in affiliated chain.
157    int mPrevAffiliateTaskId = -1; // previous id for persistence.
158    TaskRecord mNextAffiliate; // next task in affiliated chain.
159    int mNextAffiliateTaskId = -1; // next id for persistence.
160
161    // For relaunching the task from recents as though it was launched by the original launcher.
162    int mCallingUid;
163    String mCallingPackage;
164
165    final ActivityManagerService mService;
166
167    TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
168            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
169        mService = service;
170        mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
171                TaskPersister.IMAGE_EXTENSION;
172        mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
173        taskId = _taskId;
174        mAffiliatedTaskId = _taskId;
175        voiceSession = _voiceSession;
176        voiceInteractor = _voiceInteractor;
177        isAvailable = true;
178        mActivities = new ArrayList<ActivityRecord>();
179        setIntent(_intent, info);
180    }
181
182    TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
183            ActivityManager.TaskDescription _taskDescription) {
184        mService = service;
185        mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
186                TaskPersister.IMAGE_EXTENSION;
187        mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
188        taskId = _taskId;
189        mAffiliatedTaskId = _taskId;
190        voiceSession = null;
191        voiceInteractor = null;
192        isAvailable = true;
193        mActivities = new ArrayList<ActivityRecord>();
194        setIntent(_intent, info);
195
196        taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
197        isPersistable = true;
198        mCallingUid = info.applicationInfo.uid;
199        mCallingPackage = info.packageName;
200        // Clamp to [1, max].
201        maxRecents = Math.min(Math.max(info.maxRecents, 1),
202                ActivityManager.getMaxAppRecentsLimitStatic());
203
204        taskType = APPLICATION_ACTIVITY_TYPE;
205        mTaskToReturnTo = HOME_ACTIVITY_TYPE;
206        userId = UserHandle.getUserId(info.applicationInfo.uid);
207        lastTaskDescription = _taskDescription;
208        mCallingUid = info.applicationInfo.uid;
209        mCallingPackage = info.packageName;
210    }
211
212    TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
213            String _affinity, String _rootAffinity, ComponentName _realActivity,
214            ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents,
215            boolean _askedCompatMode, int _taskType, int _userId, int _effectiveUid,
216            String _lastDescription, ArrayList<ActivityRecord> activities, long _firstActiveTime,
217            long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity,
218            ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
219            int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid,
220            String callingPackage) {
221        mService = service;
222        mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
223                TaskPersister.IMAGE_EXTENSION;
224        mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
225        taskId = _taskId;
226        intent = _intent;
227        affinityIntent = _affinityIntent;
228        affinity = _affinity;
229        rootAffinity = _affinity;
230        voiceSession = null;
231        voiceInteractor = null;
232        realActivity = _realActivity;
233        origActivity = _origActivity;
234        rootWasReset = _rootWasReset;
235        isAvailable = true;
236        autoRemoveRecents = _autoRemoveRecents;
237        askedCompatMode = _askedCompatMode;
238        taskType = _taskType;
239        mTaskToReturnTo = HOME_ACTIVITY_TYPE;
240        userId = _userId;
241        effectiveUid = _effectiveUid;
242        firstActiveTime = _firstActiveTime;
243        lastActiveTime = _lastActiveTime;
244        lastDescription = _lastDescription;
245        mActivities = activities;
246        mLastTimeMoved = lastTimeMoved;
247        mNeverRelinquishIdentity = neverRelinquishIdentity;
248        lastTaskDescription = _lastTaskDescription;
249        mAffiliatedTaskId = taskAffiliation;
250        mAffiliatedTaskColor = taskAffiliationColor;
251        mPrevAffiliateTaskId = prevTaskId;
252        mNextAffiliateTaskId = nextTaskId;
253        mCallingUid = callingUid;
254        mCallingPackage = callingPackage;
255    }
256
257    void touchActiveTime() {
258        lastActiveTime = System.currentTimeMillis();
259        if (firstActiveTime == 0) {
260            firstActiveTime = lastActiveTime;
261        }
262    }
263
264    long getInactiveDuration() {
265        return System.currentTimeMillis() - lastActiveTime;
266    }
267
268    /** Sets the original intent, and the calling uid and package. */
269    void setIntent(ActivityRecord r) {
270        setIntent(r.intent, r.info);
271        mCallingUid = r.launchedFromUid;
272        mCallingPackage = r.launchedFromPackage;
273    }
274
275    /** Sets the original intent, _without_ updating the calling uid or package. */
276    private void setIntent(Intent _intent, ActivityInfo info) {
277        if (intent == null) {
278            mNeverRelinquishIdentity =
279                    (info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0;
280        } else if (mNeverRelinquishIdentity) {
281            return;
282        }
283
284        affinity = info.taskAffinity;
285        if (intent == null) {
286            // If this task already has an intent associated with it, don't set the root
287            // affinity -- we don't want it changing after initially set, but the initially
288            // set value may be null.
289            rootAffinity = affinity;
290        }
291        effectiveUid = info.applicationInfo.uid;
292        stringName = null;
293
294        if (info.targetActivity == null) {
295            if (_intent != null) {
296                // If this Intent has a selector, we want to clear it for the
297                // recent task since it is not relevant if the user later wants
298                // to re-launch the app.
299                if (_intent.getSelector() != null || _intent.getSourceBounds() != null) {
300                    _intent = new Intent(_intent);
301                    _intent.setSelector(null);
302                    _intent.setSourceBounds(null);
303                }
304            }
305            if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG,
306                    "Setting Intent of " + this + " to " + _intent);
307            intent = _intent;
308            realActivity = _intent != null ? _intent.getComponent() : null;
309            origActivity = null;
310        } else {
311            ComponentName targetComponent = new ComponentName(
312                    info.packageName, info.targetActivity);
313            if (_intent != null) {
314                Intent targetIntent = new Intent(_intent);
315                targetIntent.setComponent(targetComponent);
316                targetIntent.setSelector(null);
317                targetIntent.setSourceBounds(null);
318                if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG,
319                        "Setting Intent of " + this + " to target " + targetIntent);
320                intent = targetIntent;
321                realActivity = targetComponent;
322                origActivity = _intent.getComponent();
323            } else {
324                intent = null;
325                realActivity = targetComponent;
326                origActivity = new ComponentName(info.packageName, info.name);
327            }
328        }
329
330        if (intent != null &&
331                (intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
332            // Once we are set to an Intent with this flag, we count this
333            // task as having a true root activity.
334            rootWasReset = true;
335        }
336
337        userId = UserHandle.getUserId(info.applicationInfo.uid);
338        if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
339            // If the activity itself has requested auto-remove, then just always do it.
340            autoRemoveRecents = true;
341        } else if ((intent.getFlags()&(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
342                |Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT) {
343            // If the caller has not asked for the document to be retained, then we may
344            // want to turn on auto-remove, depending on whether the target has set its
345            // own document launch mode.
346            if (info.documentLaunchMode != ActivityInfo.DOCUMENT_LAUNCH_NONE) {
347                autoRemoveRecents = false;
348            } else {
349                autoRemoveRecents = true;
350            }
351        } else {
352            autoRemoveRecents = false;
353        }
354    }
355
356    void setTaskToReturnTo(int taskToReturnTo) {
357        if (IGNORE_RETURN_TO_RECENTS && taskToReturnTo == RECENTS_ACTIVITY_TYPE) {
358            taskToReturnTo = HOME_ACTIVITY_TYPE;
359        }
360        mTaskToReturnTo = taskToReturnTo;
361    }
362
363    int getTaskToReturnTo() {
364        return mTaskToReturnTo;
365    }
366
367    void setPrevAffiliate(TaskRecord prevAffiliate) {
368        mPrevAffiliate = prevAffiliate;
369        mPrevAffiliateTaskId = prevAffiliate == null ? -1 : prevAffiliate.taskId;
370    }
371
372    void setNextAffiliate(TaskRecord nextAffiliate) {
373        mNextAffiliate = nextAffiliate;
374        mNextAffiliateTaskId = nextAffiliate == null ? -1 : nextAffiliate.taskId;
375    }
376
377    // Close up recents linked list.
378    void closeRecentsChain() {
379        if (mPrevAffiliate != null) {
380            mPrevAffiliate.setNextAffiliate(mNextAffiliate);
381        }
382        if (mNextAffiliate != null) {
383            mNextAffiliate.setPrevAffiliate(mPrevAffiliate);
384        }
385        setPrevAffiliate(null);
386        setNextAffiliate(null);
387    }
388
389    void removedFromRecents(TaskPersister persister) {
390        disposeThumbnail();
391        closeRecentsChain();
392        if (inRecents) {
393            inRecents = false;
394            persister.wakeup(this, false);
395        }
396    }
397
398    void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
399        closeRecentsChain();
400        mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
401        mAffiliatedTaskColor = taskToAffiliateWith.mAffiliatedTaskColor;
402        // Find the end
403        while (taskToAffiliateWith.mNextAffiliate != null) {
404            final TaskRecord nextRecents = taskToAffiliateWith.mNextAffiliate;
405            if (nextRecents.mAffiliatedTaskId != mAffiliatedTaskId) {
406                Slog.e(TAG, "setTaskToAffiliateWith: nextRecents=" + nextRecents + " affilTaskId="
407                        + nextRecents.mAffiliatedTaskId + " should be " + mAffiliatedTaskId);
408                if (nextRecents.mPrevAffiliate == taskToAffiliateWith) {
409                    nextRecents.setPrevAffiliate(null);
410                }
411                taskToAffiliateWith.setNextAffiliate(null);
412                break;
413            }
414            taskToAffiliateWith = nextRecents;
415        }
416        taskToAffiliateWith.setNextAffiliate(this);
417        setPrevAffiliate(taskToAffiliateWith);
418        setNextAffiliate(null);
419    }
420
421    /**
422     * Sets the last thumbnail.
423     * @return whether the thumbnail was set
424     */
425    boolean setLastThumbnail(Bitmap thumbnail) {
426        if (mLastThumbnail != thumbnail) {
427            mLastThumbnail = thumbnail;
428            if (thumbnail == null) {
429                if (mLastThumbnailFile != null) {
430                    mLastThumbnailFile.delete();
431                }
432            } else {
433                mService.mTaskPersister.saveImage(thumbnail, mFilename);
434            }
435            return true;
436        }
437        return false;
438    }
439
440    void getLastThumbnail(TaskThumbnail thumbs) {
441        thumbs.mainThumbnail = mLastThumbnail;
442        thumbs.thumbnailFileDescriptor = null;
443        if (mLastThumbnail == null) {
444            thumbs.mainThumbnail = mService.mTaskPersister.getThumbnail(mFilename);
445        }
446        // Only load the thumbnail file if we don't have a thumbnail
447        if (thumbs.mainThumbnail == null && mLastThumbnailFile.exists()) {
448            try {
449                thumbs.thumbnailFileDescriptor = ParcelFileDescriptor.open(mLastThumbnailFile,
450                        ParcelFileDescriptor.MODE_READ_ONLY);
451            } catch (IOException e) {
452            }
453        }
454    }
455
456    void freeLastThumbnail() {
457        mLastThumbnail = null;
458    }
459
460    void disposeThumbnail() {
461        mLastThumbnail = null;
462        lastDescription = null;
463    }
464
465    /** Returns the intent for the root activity for this task */
466    Intent getBaseIntent() {
467        return intent != null ? intent : affinityIntent;
468    }
469
470    /** Returns the first non-finishing activity from the root. */
471    ActivityRecord getRootActivity() {
472        for (int i = 0; i < mActivities.size(); i++) {
473            final ActivityRecord r = mActivities.get(i);
474            if (r.finishing) {
475                continue;
476            }
477            return r;
478        }
479        return null;
480    }
481
482    ActivityRecord getTopActivity() {
483        for (int i = mActivities.size() - 1; i >= 0; --i) {
484            final ActivityRecord r = mActivities.get(i);
485            if (r.finishing) {
486                continue;
487            }
488            return r;
489        }
490        return null;
491    }
492
493    ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
494        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
495            ActivityRecord r = mActivities.get(activityNdx);
496            if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
497                return r;
498            }
499        }
500        return null;
501    }
502
503    /** Call after activity movement or finish to make sure that frontOfTask is set correctly */
504    final void setFrontOfTask() {
505        boolean foundFront = false;
506        final int numActivities = mActivities.size();
507        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
508            final ActivityRecord r = mActivities.get(activityNdx);
509            if (foundFront || r.finishing) {
510                r.frontOfTask = false;
511            } else {
512                r.frontOfTask = true;
513                // Set frontOfTask false for every following activity.
514                foundFront = true;
515            }
516        }
517        if (!foundFront && numActivities > 0) {
518            // All activities of this task are finishing. As we ought to have a frontOfTask
519            // activity, make the bottom activity front.
520            mActivities.get(0).frontOfTask = true;
521        }
522    }
523
524    /**
525     * Reorder the history stack so that the passed activity is brought to the front.
526     */
527    final void moveActivityToFrontLocked(ActivityRecord newTop) {
528        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop
529            + " to stack at top", new RuntimeException("here").fillInStackTrace());
530
531        mActivities.remove(newTop);
532        mActivities.add(newTop);
533        updateEffectiveIntent();
534
535        setFrontOfTask();
536    }
537
538    void addActivityAtBottom(ActivityRecord r) {
539        addActivityAtIndex(0, r);
540    }
541
542    void addActivityToTop(ActivityRecord r) {
543        addActivityAtIndex(mActivities.size(), r);
544    }
545
546    void addActivityAtIndex(int index, ActivityRecord r) {
547        // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
548        if (!mActivities.remove(r) && r.fullscreen) {
549            // Was not previously in list.
550            numFullscreen++;
551        }
552        // Only set this based on the first activity
553        if (mActivities.isEmpty()) {
554            taskType = r.mActivityType;
555            isPersistable = r.isPersistable();
556            mCallingUid = r.launchedFromUid;
557            mCallingPackage = r.launchedFromPackage;
558            // Clamp to [1, max].
559            maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
560                    ActivityManager.getMaxAppRecentsLimitStatic());
561        } else {
562            // Otherwise make all added activities match this one.
563            r.mActivityType = taskType;
564        }
565        mActivities.add(index, r);
566        updateEffectiveIntent();
567        if (r.isPersistable()) {
568            mService.notifyTaskPersisterLocked(this, false);
569        }
570    }
571
572    /** @return true if this was the last activity in the task */
573    boolean removeActivity(ActivityRecord r) {
574        if (mActivities.remove(r) && r.fullscreen) {
575            // Was previously in list.
576            numFullscreen--;
577        }
578        if (r.isPersistable()) {
579            mService.notifyTaskPersisterLocked(this, false);
580        }
581        if (mActivities.isEmpty()) {
582            return !mReuseTask;
583        }
584        updateEffectiveIntent();
585        return false;
586    }
587
588    boolean autoRemoveFromRecents() {
589        // We will automatically remove the task either if it has explicitly asked for
590        // this, or it is empty and has never contained an activity that got shown to
591        // the user.
592        return autoRemoveRecents || (mActivities.isEmpty() && !hasBeenVisible);
593    }
594
595    /**
596     * Completely remove all activities associated with an existing
597     * task starting at a specified index.
598     */
599    final void performClearTaskAtIndexLocked(int activityNdx) {
600        int numActivities = mActivities.size();
601        for ( ; activityNdx < numActivities; ++activityNdx) {
602            final ActivityRecord r = mActivities.get(activityNdx);
603            if (r.finishing) {
604                continue;
605            }
606            if (stack == null) {
607                // Task was restored from persistent storage.
608                r.takeFromHistory();
609                mActivities.remove(activityNdx);
610                --activityNdx;
611                --numActivities;
612            } else if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
613                    false)) {
614                --activityNdx;
615                --numActivities;
616            }
617        }
618    }
619
620    /**
621     * Completely remove all activities associated with an existing task.
622     */
623    final void performClearTaskLocked() {
624        mReuseTask = true;
625        performClearTaskAtIndexLocked(0);
626        mReuseTask = false;
627    }
628
629    /**
630     * Perform clear operation as requested by
631     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
632     * stack to the given task, then look for
633     * an instance of that activity in the stack and, if found, finish all
634     * activities on top of it and return the instance.
635     *
636     * @param newR Description of the new activity being started.
637     * @return Returns the old activity that should be continued to be used,
638     * or null if none was found.
639     */
640    final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
641        int numActivities = mActivities.size();
642        for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
643            ActivityRecord r = mActivities.get(activityNdx);
644            if (r.finishing) {
645                continue;
646            }
647            if (r.realActivity.equals(newR.realActivity)) {
648                // Here it is!  Now finish everything in front...
649                final ActivityRecord ret = r;
650
651                for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
652                    r = mActivities.get(activityNdx);
653                    if (r.finishing) {
654                        continue;
655                    }
656                    ActivityOptions opts = r.takeOptionsLocked();
657                    if (opts != null) {
658                        ret.updateOptionsLocked(opts);
659                    }
660                    if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
661                            false)) {
662                        --activityNdx;
663                        --numActivities;
664                    }
665                }
666
667                // Finally, if this is a normal launch mode (that is, not
668                // expecting onNewIntent()), then we will finish the current
669                // instance of the activity so a new fresh one can be started.
670                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
671                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
672                    if (!ret.finishing) {
673                        stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null,
674                                "clear", false);
675                        return null;
676                    }
677                }
678
679                return ret;
680            }
681        }
682
683        return null;
684    }
685
686    public TaskThumbnail getTaskThumbnailLocked() {
687        if (stack != null) {
688            final ActivityRecord resumedActivity = stack.mResumedActivity;
689            if (resumedActivity != null && resumedActivity.task == this) {
690                final Bitmap thumbnail = stack.screenshotActivities(resumedActivity);
691                setLastThumbnail(thumbnail);
692            }
693        }
694        final TaskThumbnail taskThumbnail = new TaskThumbnail();
695        getLastThumbnail(taskThumbnail);
696        return taskThumbnail;
697    }
698
699    public void removeTaskActivitiesLocked() {
700        // Just remove the entire task.
701        performClearTaskAtIndexLocked(0);
702    }
703
704    boolean isHomeTask() {
705        return taskType == HOME_ACTIVITY_TYPE;
706    }
707
708    boolean isApplicationTask() {
709        return taskType == APPLICATION_ACTIVITY_TYPE;
710    }
711
712    boolean isOverHomeStack() {
713        return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE;
714    }
715
716    /**
717     * Find the activity in the history stack within the given task.  Returns
718     * the index within the history at which it's found, or < 0 if not found.
719     */
720    final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
721        final ComponentName realActivity = r.realActivity;
722        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
723            ActivityRecord candidate = mActivities.get(activityNdx);
724            if (candidate.finishing) {
725                continue;
726            }
727            if (candidate.realActivity.equals(realActivity)) {
728                return candidate;
729            }
730        }
731        return null;
732    }
733
734    /** Updates the last task description values. */
735    void updateTaskDescription() {
736        // Traverse upwards looking for any break between main task activities and
737        // utility activities.
738        int activityNdx;
739        final int numActivities = mActivities.size();
740        final boolean relinquish = numActivities == 0 ? false :
741                (mActivities.get(0).info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) != 0;
742        for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
743                ++activityNdx) {
744            final ActivityRecord r = mActivities.get(activityNdx);
745            if (relinquish && (r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
746                // This will be the top activity for determining taskDescription. Pre-inc to
747                // overcome initial decrement below.
748                ++activityNdx;
749                break;
750            }
751            if (r.intent != null &&
752                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
753                break;
754            }
755        }
756        if (activityNdx > 0) {
757            // Traverse downwards starting below break looking for set label, icon.
758            // Note that if there are activities in the task but none of them set the
759            // recent activity values, then we do not fall back to the last set
760            // values in the TaskRecord.
761            String label = null;
762            Bitmap icon = null;
763            int colorPrimary = 0;
764            for (--activityNdx; activityNdx >= 0; --activityNdx) {
765                final ActivityRecord r = mActivities.get(activityNdx);
766                if (r.taskDescription != null) {
767                    if (label == null) {
768                        label = r.taskDescription.getLabel();
769                    }
770                    if (icon == null) {
771                        icon = r.taskDescription.getIcon();
772                    }
773                    if (colorPrimary == 0) {
774                        colorPrimary = r.taskDescription.getPrimaryColor();
775                    }
776                }
777            }
778            lastTaskDescription = new ActivityManager.TaskDescription(label, icon, colorPrimary);
779            // Update the task affiliation color if we are the parent of the group
780            if (taskId == mAffiliatedTaskId) {
781                mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
782            }
783        }
784    }
785
786    int findEffectiveRootIndex() {
787        int effectiveNdx = 0;
788        final int topActivityNdx = mActivities.size() - 1;
789        for (int activityNdx = 0; activityNdx < topActivityNdx; ++activityNdx) {
790            final ActivityRecord r = mActivities.get(activityNdx);
791            if (r.finishing) {
792                continue;
793            }
794            effectiveNdx = activityNdx;
795            if ((r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
796                break;
797            }
798        }
799        return effectiveNdx;
800    }
801
802    void updateEffectiveIntent() {
803        final int effectiveRootIndex = findEffectiveRootIndex();
804        final ActivityRecord r = mActivities.get(effectiveRootIndex);
805        setIntent(r);
806    }
807
808    void saveTaskDescription(ActivityManager.TaskDescription taskDescription,
809            String iconFilename, XmlSerializer out) throws IOException {
810        if (taskDescription != null) {
811            final String label = taskDescription.getLabel();
812            if (label != null) {
813                out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label);
814            }
815            final int colorPrimary = taskDescription.getPrimaryColor();
816            if (colorPrimary != 0) {
817                out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary));
818            }
819            final Bitmap icon = taskDescription.getIcon();
820            if (icon != null) {
821                mService.mTaskPersister.saveImage(icon, iconFilename);
822            }
823        }
824    }
825
826    static boolean readTaskDescriptionAttribute(ActivityManager.TaskDescription taskDescription,
827            String attrName, String attrValue) {
828        if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
829            taskDescription.setLabel(attrValue);
830        } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
831            taskDescription.setPrimaryColor((int) Long.parseLong(attrValue, 16));
832        } else {
833            return false;
834        }
835        return true;
836    }
837
838    private static String createLastTaskDescriptionIconFilename(int taskId, long lastActiveTime) {
839        return String.valueOf(taskId) + LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime +
840                TaskPersister.IMAGE_EXTENSION;
841    }
842
843    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
844        if (ActivityManagerService.DEBUG_RECENTS) Slog.i(TAG, "Saving task=" + this);
845
846        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
847        if (realActivity != null) {
848            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
849        }
850        if (origActivity != null) {
851            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
852        }
853        // Write affinity, and root affinity if it is different from affinity.
854        // We use the special string "@" for a null root affinity, so we can identify
855        // later whether we were given a root affinity or should just make it the
856        // same as the affinity.
857        if (affinity != null) {
858            out.attribute(null, ATTR_AFFINITY, affinity);
859            if (!affinity.equals(rootAffinity)) {
860                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
861            }
862        } else if (rootAffinity != null) {
863            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
864        }
865        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
866        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
867        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
868        out.attribute(null, ATTR_USERID, String.valueOf(userId));
869        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
870        out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
871        out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
872        out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
873        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
874        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
875        if (lastDescription != null) {
876            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
877        }
878        if (lastTaskDescription != null) {
879            saveTaskDescription(lastTaskDescription, createLastTaskDescriptionIconFilename(taskId,
880                    lastActiveTime), out);
881        }
882        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
883        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
884        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
885        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
886        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
887        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
888
889        if (affinityIntent != null) {
890            out.startTag(null, TAG_AFFINITYINTENT);
891            affinityIntent.saveToXml(out);
892            out.endTag(null, TAG_AFFINITYINTENT);
893        }
894
895        out.startTag(null, TAG_INTENT);
896        intent.saveToXml(out);
897        out.endTag(null, TAG_INTENT);
898
899        final ArrayList<ActivityRecord> activities = mActivities;
900        final int numActivities = activities.size();
901        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
902            final ActivityRecord r = activities.get(activityNdx);
903            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
904                    ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
905                            activityNdx > 0) {
906                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
907                break;
908            }
909            out.startTag(null, TAG_ACTIVITY);
910            r.saveToXml(out);
911            out.endTag(null, TAG_ACTIVITY);
912        }
913    }
914
915    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
916            throws IOException, XmlPullParserException {
917        Intent intent = null;
918        Intent affinityIntent = null;
919        ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
920        ComponentName realActivity = null;
921        ComponentName origActivity = null;
922        String affinity = null;
923        String rootAffinity = null;
924        boolean hasRootAffinity = false;
925        boolean rootHasReset = false;
926        boolean autoRemoveRecents = false;
927        boolean askedCompatMode = false;
928        int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
929        int userId = 0;
930        int effectiveUid = -1;
931        String lastDescription = null;
932        long firstActiveTime = -1;
933        long lastActiveTime = -1;
934        long lastTimeOnTop = 0;
935        boolean neverRelinquishIdentity = true;
936        int taskId = -1;
937        final int outerDepth = in.getDepth();
938        ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription();
939        int taskAffiliation = -1;
940        int taskAffiliationColor = 0;
941        int prevTaskId = -1;
942        int nextTaskId = -1;
943        int callingUid = -1;
944        String callingPackage = "";
945
946        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
947            final String attrName = in.getAttributeName(attrNdx);
948            final String attrValue = in.getAttributeValue(attrNdx);
949            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
950                    attrName + " value=" + attrValue);
951            if (ATTR_TASKID.equals(attrName)) {
952                taskId = Integer.valueOf(attrValue);
953            } else if (ATTR_REALACTIVITY.equals(attrName)) {
954                realActivity = ComponentName.unflattenFromString(attrValue);
955            } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
956                origActivity = ComponentName.unflattenFromString(attrValue);
957            } else if (ATTR_AFFINITY.equals(attrName)) {
958                affinity = attrValue;
959            } else if (ATTR_ROOT_AFFINITY.equals(attrName)) {
960                rootAffinity = attrValue;
961                hasRootAffinity = true;
962            } else if (ATTR_ROOTHASRESET.equals(attrName)) {
963                rootHasReset = Boolean.valueOf(attrValue);
964            } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) {
965                autoRemoveRecents = Boolean.valueOf(attrValue);
966            } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
967                askedCompatMode = Boolean.valueOf(attrValue);
968            } else if (ATTR_USERID.equals(attrName)) {
969                userId = Integer.valueOf(attrValue);
970            } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
971                effectiveUid = Integer.valueOf(attrValue);
972            } else if (ATTR_TASKTYPE.equals(attrName)) {
973                taskType = Integer.valueOf(attrValue);
974            } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
975                firstActiveTime = Long.valueOf(attrValue);
976            } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
977                lastActiveTime = Long.valueOf(attrValue);
978            } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
979                lastDescription = attrValue;
980            } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
981                lastTimeOnTop = Long.valueOf(attrValue);
982            } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
983                neverRelinquishIdentity = Boolean.valueOf(attrValue);
984            } else if (readTaskDescriptionAttribute(taskDescription, attrName, attrValue)) {
985                // Completed in TaskPersister.readTaskDescriptionAttribute()
986            } else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
987                taskAffiliation = Integer.valueOf(attrValue);
988            } else if (ATTR_PREV_AFFILIATION.equals(attrName)) {
989                prevTaskId = Integer.valueOf(attrValue);
990            } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
991                nextTaskId = Integer.valueOf(attrValue);
992            } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) {
993                taskAffiliationColor = Integer.valueOf(attrValue);
994            } else if (ATTR_CALLING_UID.equals(attrName)) {
995                callingUid = Integer.valueOf(attrValue);
996            } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
997                callingPackage = attrValue;
998            } else {
999                Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
1000            }
1001        }
1002
1003        int event;
1004        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
1005                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
1006            if (event == XmlPullParser.START_TAG) {
1007                final String name = in.getName();
1008                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
1009                        name);
1010                if (TAG_AFFINITYINTENT.equals(name)) {
1011                    affinityIntent = Intent.restoreFromXml(in);
1012                } else if (TAG_INTENT.equals(name)) {
1013                    intent = Intent.restoreFromXml(in);
1014                } else if (TAG_ACTIVITY.equals(name)) {
1015                    ActivityRecord activity =
1016                            ActivityRecord.restoreFromXml(in, taskId, stackSupervisor);
1017                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
1018                            activity);
1019                    if (activity != null) {
1020                        activities.add(activity);
1021                    }
1022                } else {
1023                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
1024                    XmlUtils.skipCurrentTag(in);
1025                }
1026            }
1027        }
1028
1029        if (lastActiveTime >= 0) {
1030            taskDescription.setIcon(TaskPersister.restoreImage(
1031                    createLastTaskDescriptionIconFilename(taskId, lastActiveTime)));
1032        }
1033
1034        if (!hasRootAffinity) {
1035            rootAffinity = affinity;
1036        } else if ("@".equals(rootAffinity)) {
1037            rootAffinity = null;
1038        }
1039
1040        if (effectiveUid <= 0) {
1041            Intent checkIntent = intent != null ? intent : affinityIntent;
1042            effectiveUid = 0;
1043            if (checkIntent != null) {
1044                IPackageManager pm = AppGlobals.getPackageManager();
1045                try {
1046                    ApplicationInfo ai = pm.getApplicationInfo(
1047                            checkIntent.getComponent().getPackageName(),
1048                            PackageManager.GET_UNINSTALLED_PACKAGES
1049                                    | PackageManager.GET_DISABLED_COMPONENTS, userId);
1050                    if (ai != null) {
1051                        effectiveUid = ai.uid;
1052                    }
1053                } catch (RemoteException e) {
1054                }
1055            }
1056            Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
1057                    + ": effectiveUid=" + effectiveUid);
1058        }
1059
1060        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
1061                affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
1062                autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
1063                activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
1064                taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
1065                callingUid, callingPackage);
1066
1067        for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
1068            activities.get(activityNdx).task = task;
1069        }
1070
1071        if (ActivityManagerService.DEBUG_RECENTS) Slog.d(TAG, "Restored task=" + task);
1072        return task;
1073    }
1074
1075    void dump(PrintWriter pw, String prefix) {
1076        pw.print(prefix); pw.print("userId="); pw.print(userId);
1077                pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
1078                pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
1079                pw.print(" mCallingPackage="); pw.println(mCallingPackage);
1080        if (affinity != null || rootAffinity != null) {
1081            pw.print(prefix); pw.print("affinity="); pw.print(affinity);
1082            if (affinity == null || !affinity.equals(rootAffinity)) {
1083                pw.print(" root="); pw.println(rootAffinity);
1084            } else {
1085                pw.println();
1086            }
1087        }
1088        if (voiceSession != null || voiceInteractor != null) {
1089            pw.print(prefix); pw.print("VOICE: session=0x");
1090            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
1091            pw.print(" interactor=0x");
1092            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
1093        }
1094        if (intent != null) {
1095            StringBuilder sb = new StringBuilder(128);
1096            sb.append(prefix); sb.append("intent={");
1097            intent.toShortString(sb, false, true, false, true);
1098            sb.append('}');
1099            pw.println(sb.toString());
1100        }
1101        if (affinityIntent != null) {
1102            StringBuilder sb = new StringBuilder(128);
1103            sb.append(prefix); sb.append("affinityIntent={");
1104            affinityIntent.toShortString(sb, false, true, false, true);
1105            sb.append('}');
1106            pw.println(sb.toString());
1107        }
1108        if (origActivity != null) {
1109            pw.print(prefix); pw.print("origActivity=");
1110            pw.println(origActivity.flattenToShortString());
1111        }
1112        if (realActivity != null) {
1113            pw.print(prefix); pw.print("realActivity=");
1114            pw.println(realActivity.flattenToShortString());
1115        }
1116        if (autoRemoveRecents || isPersistable || taskType != 0 || mTaskToReturnTo != 0
1117                || numFullscreen != 0) {
1118            pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents);
1119                    pw.print(" isPersistable="); pw.print(isPersistable);
1120                    pw.print(" numFullscreen="); pw.print(numFullscreen);
1121                    pw.print(" taskType="); pw.print(taskType);
1122                    pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
1123        }
1124        if (rootWasReset || mNeverRelinquishIdentity || mReuseTask) {
1125            pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset);
1126                    pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity);
1127                    pw.print(" mReuseTask="); pw.println(mReuseTask);
1128        }
1129        if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != -1 || mPrevAffiliate != null
1130                || mNextAffiliateTaskId != -1 || mNextAffiliate != null) {
1131            pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId);
1132                    pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId);
1133                    pw.print(" (");
1134                    if (mPrevAffiliate == null) {
1135                        pw.print("null");
1136                    } else {
1137                        pw.print(Integer.toHexString(System.identityHashCode(mPrevAffiliate)));
1138                    }
1139                    pw.print(") nextAffiliation="); pw.print(mNextAffiliateTaskId);
1140                    pw.print(" (");
1141                    if (mNextAffiliate == null) {
1142                        pw.print("null");
1143                    } else {
1144                        pw.print(Integer.toHexString(System.identityHashCode(mNextAffiliate)));
1145                    }
1146                    pw.println(")");
1147        }
1148        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
1149        if (!askedCompatMode || !inRecents || !isAvailable) {
1150            pw.print(prefix); pw.print("askedCompatMode="); pw.print(askedCompatMode);
1151                    pw.print(" inRecents="); pw.print(inRecents);
1152                    pw.print(" isAvailable="); pw.println(isAvailable);
1153        }
1154        pw.print(prefix); pw.print("lastThumbnail="); pw.print(mLastThumbnail);
1155                pw.print(" lastThumbnailFile="); pw.println(mLastThumbnailFile);
1156        if (lastDescription != null) {
1157            pw.print(prefix); pw.print("lastDescription="); pw.println(lastDescription);
1158        }
1159        pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible);
1160                pw.print(" firstActiveTime="); pw.print(lastActiveTime);
1161                pw.print(" lastActiveTime="); pw.print(lastActiveTime);
1162                pw.print(" (inactive for ");
1163                pw.print((getInactiveDuration()/1000)); pw.println("s)");
1164    }
1165
1166    @Override
1167    public String toString() {
1168        StringBuilder sb = new StringBuilder(128);
1169        if (stringName != null) {
1170            sb.append(stringName);
1171            sb.append(" U=");
1172            sb.append(userId);
1173            sb.append(" sz=");
1174            sb.append(mActivities.size());
1175            sb.append('}');
1176            return sb.toString();
1177        }
1178        sb.append("TaskRecord{");
1179        sb.append(Integer.toHexString(System.identityHashCode(this)));
1180        sb.append(" #");
1181        sb.append(taskId);
1182        if (affinity != null) {
1183            sb.append(" A=");
1184            sb.append(affinity);
1185        } else if (intent != null) {
1186            sb.append(" I=");
1187            sb.append(intent.getComponent().flattenToShortString());
1188        } else if (affinityIntent != null) {
1189            sb.append(" aI=");
1190            sb.append(affinityIntent.getComponent().flattenToShortString());
1191        } else {
1192            sb.append(" ??");
1193        }
1194        stringName = sb.toString();
1195        return toString();
1196    }
1197}
1198