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