TaskRecord.java revision 47b20ba8f667a0cf3ed9a1e34c93dcfdfd50eacb
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        final int intentFlags = intent == null ? 0 : intent.getFlags();
331        if ((intentFlags & 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 ((intentFlags & (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 activityNdx;
788        final int topActivityNdx = mActivities.size() - 1;
789        for (activityNdx = 0; activityNdx < topActivityNdx; ++activityNdx) {
790            final ActivityRecord r = mActivities.get(activityNdx);
791            if (r.finishing) {
792                continue;
793            }
794            if ((r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
795                break;
796            }
797        }
798        return activityNdx;
799    }
800
801    void updateEffectiveIntent() {
802        final int effectiveRootIndex = findEffectiveRootIndex();
803        final ActivityRecord r = mActivities.get(effectiveRootIndex);
804        setIntent(r);
805    }
806
807    void saveTaskDescription(ActivityManager.TaskDescription taskDescription,
808            String iconFilename, XmlSerializer out) throws IOException {
809        if (taskDescription != null) {
810            final String label = taskDescription.getLabel();
811            if (label != null) {
812                out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label);
813            }
814            final int colorPrimary = taskDescription.getPrimaryColor();
815            if (colorPrimary != 0) {
816                out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary));
817            }
818            final Bitmap icon = taskDescription.getIcon();
819            if (icon != null) {
820                mService.mTaskPersister.saveImage(icon, iconFilename);
821            }
822        }
823    }
824
825    static boolean readTaskDescriptionAttribute(ActivityManager.TaskDescription taskDescription,
826            String attrName, String attrValue) {
827        if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
828            taskDescription.setLabel(attrValue);
829        } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
830            taskDescription.setPrimaryColor((int) Long.parseLong(attrValue, 16));
831        } else {
832            return false;
833        }
834        return true;
835    }
836
837    private static String createLastTaskDescriptionIconFilename(int taskId, long lastActiveTime) {
838        return String.valueOf(taskId) + LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime +
839                TaskPersister.IMAGE_EXTENSION;
840    }
841
842    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
843        if (ActivityManagerService.DEBUG_RECENTS) Slog.i(TAG, "Saving task=" + this);
844
845        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
846        if (realActivity != null) {
847            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
848        }
849        if (origActivity != null) {
850            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
851        }
852        // Write affinity, and root affinity if it is different from affinity.
853        // We use the special string "@" for a null root affinity, so we can identify
854        // later whether we were given a root affinity or should just make it the
855        // same as the affinity.
856        if (affinity != null) {
857            out.attribute(null, ATTR_AFFINITY, affinity);
858            if (!affinity.equals(rootAffinity)) {
859                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
860            }
861        } else if (rootAffinity != null) {
862            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
863        }
864        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
865        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
866        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
867        out.attribute(null, ATTR_USERID, String.valueOf(userId));
868        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
869        out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
870        out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
871        out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
872        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
873        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
874        if (lastDescription != null) {
875            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
876        }
877        if (lastTaskDescription != null) {
878            saveTaskDescription(lastTaskDescription, createLastTaskDescriptionIconFilename(taskId,
879                    lastActiveTime), out);
880        }
881        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
882        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
883        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
884        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
885        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
886        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
887
888        if (affinityIntent != null) {
889            out.startTag(null, TAG_AFFINITYINTENT);
890            affinityIntent.saveToXml(out);
891            out.endTag(null, TAG_AFFINITYINTENT);
892        }
893
894        out.startTag(null, TAG_INTENT);
895        intent.saveToXml(out);
896        out.endTag(null, TAG_INTENT);
897
898        final ArrayList<ActivityRecord> activities = mActivities;
899        final int numActivities = activities.size();
900        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
901            final ActivityRecord r = activities.get(activityNdx);
902            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
903                    ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
904                            activityNdx > 0) {
905                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
906                break;
907            }
908            out.startTag(null, TAG_ACTIVITY);
909            r.saveToXml(out);
910            out.endTag(null, TAG_ACTIVITY);
911        }
912    }
913
914    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
915            throws IOException, XmlPullParserException {
916        Intent intent = null;
917        Intent affinityIntent = null;
918        ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
919        ComponentName realActivity = null;
920        ComponentName origActivity = null;
921        String affinity = null;
922        String rootAffinity = null;
923        boolean hasRootAffinity = false;
924        boolean rootHasReset = false;
925        boolean autoRemoveRecents = false;
926        boolean askedCompatMode = false;
927        int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
928        int userId = 0;
929        int effectiveUid = -1;
930        String lastDescription = null;
931        long firstActiveTime = -1;
932        long lastActiveTime = -1;
933        long lastTimeOnTop = 0;
934        boolean neverRelinquishIdentity = true;
935        int taskId = -1;
936        final int outerDepth = in.getDepth();
937        ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription();
938        int taskAffiliation = -1;
939        int taskAffiliationColor = 0;
940        int prevTaskId = -1;
941        int nextTaskId = -1;
942        int callingUid = -1;
943        String callingPackage = "";
944
945        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
946            final String attrName = in.getAttributeName(attrNdx);
947            final String attrValue = in.getAttributeValue(attrNdx);
948            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
949                    attrName + " value=" + attrValue);
950            if (ATTR_TASKID.equals(attrName)) {
951                taskId = Integer.valueOf(attrValue);
952            } else if (ATTR_REALACTIVITY.equals(attrName)) {
953                realActivity = ComponentName.unflattenFromString(attrValue);
954            } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
955                origActivity = ComponentName.unflattenFromString(attrValue);
956            } else if (ATTR_AFFINITY.equals(attrName)) {
957                affinity = attrValue;
958            } else if (ATTR_ROOT_AFFINITY.equals(attrName)) {
959                rootAffinity = attrValue;
960                hasRootAffinity = true;
961            } else if (ATTR_ROOTHASRESET.equals(attrName)) {
962                rootHasReset = Boolean.valueOf(attrValue);
963            } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) {
964                autoRemoveRecents = Boolean.valueOf(attrValue);
965            } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
966                askedCompatMode = Boolean.valueOf(attrValue);
967            } else if (ATTR_USERID.equals(attrName)) {
968                userId = Integer.valueOf(attrValue);
969            } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
970                effectiveUid = Integer.valueOf(attrValue);
971            } else if (ATTR_TASKTYPE.equals(attrName)) {
972                taskType = Integer.valueOf(attrValue);
973            } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
974                firstActiveTime = Long.valueOf(attrValue);
975            } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
976                lastActiveTime = Long.valueOf(attrValue);
977            } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
978                lastDescription = attrValue;
979            } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
980                lastTimeOnTop = Long.valueOf(attrValue);
981            } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
982                neverRelinquishIdentity = Boolean.valueOf(attrValue);
983            } else if (readTaskDescriptionAttribute(taskDescription, attrName, attrValue)) {
984                // Completed in TaskPersister.readTaskDescriptionAttribute()
985            } else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
986                taskAffiliation = Integer.valueOf(attrValue);
987            } else if (ATTR_PREV_AFFILIATION.equals(attrName)) {
988                prevTaskId = Integer.valueOf(attrValue);
989            } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
990                nextTaskId = Integer.valueOf(attrValue);
991            } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) {
992                taskAffiliationColor = Integer.valueOf(attrValue);
993            } else if (ATTR_CALLING_UID.equals(attrName)) {
994                callingUid = Integer.valueOf(attrValue);
995            } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
996                callingPackage = attrValue;
997            } else {
998                Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
999            }
1000        }
1001
1002        int event;
1003        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
1004                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
1005            if (event == XmlPullParser.START_TAG) {
1006                final String name = in.getName();
1007                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
1008                        name);
1009                if (TAG_AFFINITYINTENT.equals(name)) {
1010                    affinityIntent = Intent.restoreFromXml(in);
1011                } else if (TAG_INTENT.equals(name)) {
1012                    intent = Intent.restoreFromXml(in);
1013                } else if (TAG_ACTIVITY.equals(name)) {
1014                    ActivityRecord activity =
1015                            ActivityRecord.restoreFromXml(in, taskId, stackSupervisor);
1016                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
1017                            activity);
1018                    if (activity != null) {
1019                        activities.add(activity);
1020                    }
1021                } else {
1022                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
1023                    XmlUtils.skipCurrentTag(in);
1024                }
1025            }
1026        }
1027
1028        if (lastActiveTime >= 0) {
1029            taskDescription.setIcon(TaskPersister.restoreImage(
1030                    createLastTaskDescriptionIconFilename(taskId, lastActiveTime)));
1031        }
1032
1033        if (!hasRootAffinity) {
1034            rootAffinity = affinity;
1035        } else if ("@".equals(rootAffinity)) {
1036            rootAffinity = null;
1037        }
1038
1039        if (effectiveUid <= 0) {
1040            Intent checkIntent = intent != null ? intent : affinityIntent;
1041            effectiveUid = 0;
1042            if (checkIntent != null) {
1043                IPackageManager pm = AppGlobals.getPackageManager();
1044                try {
1045                    ApplicationInfo ai = pm.getApplicationInfo(
1046                            checkIntent.getComponent().getPackageName(),
1047                            PackageManager.GET_UNINSTALLED_PACKAGES
1048                                    | PackageManager.GET_DISABLED_COMPONENTS, userId);
1049                    if (ai != null) {
1050                        effectiveUid = ai.uid;
1051                    }
1052                } catch (RemoteException e) {
1053                }
1054            }
1055            Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
1056                    + ": effectiveUid=" + effectiveUid);
1057        }
1058
1059        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
1060                affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
1061                autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
1062                activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
1063                taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
1064                callingUid, callingPackage);
1065
1066        for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
1067            activities.get(activityNdx).task = task;
1068        }
1069
1070        if (ActivityManagerService.DEBUG_RECENTS) Slog.d(TAG, "Restored task=" + task);
1071        return task;
1072    }
1073
1074    void dump(PrintWriter pw, String prefix) {
1075        pw.print(prefix); pw.print("userId="); pw.print(userId);
1076                pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
1077                pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
1078                pw.print(" mCallingPackage="); pw.println(mCallingPackage);
1079        if (affinity != null || rootAffinity != null) {
1080            pw.print(prefix); pw.print("affinity="); pw.print(affinity);
1081            if (affinity == null || !affinity.equals(rootAffinity)) {
1082                pw.print(" root="); pw.println(rootAffinity);
1083            } else {
1084                pw.println();
1085            }
1086        }
1087        if (voiceSession != null || voiceInteractor != null) {
1088            pw.print(prefix); pw.print("VOICE: session=0x");
1089            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
1090            pw.print(" interactor=0x");
1091            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
1092        }
1093        if (intent != null) {
1094            StringBuilder sb = new StringBuilder(128);
1095            sb.append(prefix); sb.append("intent={");
1096            intent.toShortString(sb, false, true, false, true);
1097            sb.append('}');
1098            pw.println(sb.toString());
1099        }
1100        if (affinityIntent != null) {
1101            StringBuilder sb = new StringBuilder(128);
1102            sb.append(prefix); sb.append("affinityIntent={");
1103            affinityIntent.toShortString(sb, false, true, false, true);
1104            sb.append('}');
1105            pw.println(sb.toString());
1106        }
1107        if (origActivity != null) {
1108            pw.print(prefix); pw.print("origActivity=");
1109            pw.println(origActivity.flattenToShortString());
1110        }
1111        if (realActivity != null) {
1112            pw.print(prefix); pw.print("realActivity=");
1113            pw.println(realActivity.flattenToShortString());
1114        }
1115        if (autoRemoveRecents || isPersistable || taskType != 0 || mTaskToReturnTo != 0
1116                || numFullscreen != 0) {
1117            pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents);
1118                    pw.print(" isPersistable="); pw.print(isPersistable);
1119                    pw.print(" numFullscreen="); pw.print(numFullscreen);
1120                    pw.print(" taskType="); pw.print(taskType);
1121                    pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
1122        }
1123        if (rootWasReset || mNeverRelinquishIdentity || mReuseTask) {
1124            pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset);
1125                    pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity);
1126                    pw.print(" mReuseTask="); pw.println(mReuseTask);
1127        }
1128        if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != -1 || mPrevAffiliate != null
1129                || mNextAffiliateTaskId != -1 || mNextAffiliate != null) {
1130            pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId);
1131                    pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId);
1132                    pw.print(" (");
1133                    if (mPrevAffiliate == null) {
1134                        pw.print("null");
1135                    } else {
1136                        pw.print(Integer.toHexString(System.identityHashCode(mPrevAffiliate)));
1137                    }
1138                    pw.print(") nextAffiliation="); pw.print(mNextAffiliateTaskId);
1139                    pw.print(" (");
1140                    if (mNextAffiliate == null) {
1141                        pw.print("null");
1142                    } else {
1143                        pw.print(Integer.toHexString(System.identityHashCode(mNextAffiliate)));
1144                    }
1145                    pw.println(")");
1146        }
1147        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
1148        if (!askedCompatMode || !inRecents || !isAvailable) {
1149            pw.print(prefix); pw.print("askedCompatMode="); pw.print(askedCompatMode);
1150                    pw.print(" inRecents="); pw.print(inRecents);
1151                    pw.print(" isAvailable="); pw.println(isAvailable);
1152        }
1153        pw.print(prefix); pw.print("lastThumbnail="); pw.print(mLastThumbnail);
1154                pw.print(" lastThumbnailFile="); pw.println(mLastThumbnailFile);
1155        if (lastDescription != null) {
1156            pw.print(prefix); pw.print("lastDescription="); pw.println(lastDescription);
1157        }
1158        pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible);
1159                pw.print(" firstActiveTime="); pw.print(lastActiveTime);
1160                pw.print(" lastActiveTime="); pw.print(lastActiveTime);
1161                pw.print(" (inactive for ");
1162                pw.print((getInactiveDuration()/1000)); pw.println("s)");
1163    }
1164
1165    @Override
1166    public String toString() {
1167        StringBuilder sb = new StringBuilder(128);
1168        if (stringName != null) {
1169            sb.append(stringName);
1170            sb.append(" U=");
1171            sb.append(userId);
1172            sb.append(" sz=");
1173            sb.append(mActivities.size());
1174            sb.append('}');
1175            return sb.toString();
1176        }
1177        sb.append("TaskRecord{");
1178        sb.append(Integer.toHexString(System.identityHashCode(this)));
1179        sb.append(" #");
1180        sb.append(taskId);
1181        if (affinity != null) {
1182            sb.append(" A=");
1183            sb.append(affinity);
1184        } else if (intent != null) {
1185            sb.append(" I=");
1186            sb.append(intent.getComponent().flattenToShortString());
1187        } else if (affinityIntent != null) {
1188            sb.append(" aI=");
1189            sb.append(affinityIntent.getComponent().flattenToShortString());
1190        } else {
1191            sb.append(" ??");
1192        }
1193        stringName = sb.toString();
1194        return toString();
1195    }
1196}
1197