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