TaskRecord.java revision ffcfcaadfefec2fb56f67a0a614a54bf4599d62b
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.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
21
22import android.app.Activity;
23import android.app.ActivityManager;
24import android.app.ActivityOptions;
25import android.app.IThumbnailRetriever;
26import android.content.ComponentName;
27import android.content.Intent;
28import android.content.pm.ActivityInfo;
29import android.graphics.Bitmap;
30import android.os.SystemClock;
31import android.os.UserHandle;
32import android.service.voice.IVoiceInteractionSession;
33import android.util.Slog;
34import com.android.internal.app.IVoiceInteractor;
35import com.android.internal.util.XmlUtils;
36import org.xmlpull.v1.XmlPullParser;
37import org.xmlpull.v1.XmlPullParserException;
38import org.xmlpull.v1.XmlSerializer;
39
40import java.io.IOException;
41import java.io.PrintWriter;
42import java.util.ArrayList;
43
44final class TaskRecord extends ThumbnailHolder {
45    private static final String TAG_TASK = "task";
46    private static final String ATTR_TASKID = "task_id";
47    private static final String TAG_INTENT = "intent";
48    private static final String TAG_AFFINITYINTENT = "affinity_intent";
49    private static final String ATTR_REALACTIVITY = "real_activity";
50    private static final String ATTR_ORIGACTIVITY = "orig_activity";
51    private static final String TAG_ACTIVITY = "activity";
52    private static final String ATTR_AFFINITY = "affinity";
53    private static final String ATTR_ROOTHASRESET = "root_has_reset";
54    private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
55    private static final String ATTR_USERID = "user_id";
56    private static final String ATTR_TASKTYPE = "task_type";
57    private static final String ATTR_ONTOPOFHOME = "on_top_of_home";
58    private static final String ATTR_LASTDESCRIPTION = "last_description";
59    private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
60
61    private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
62
63    final int taskId;       // Unique identifier for this task.
64    final String affinity;  // The affinity name for this task, or null.
65    final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
66    final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
67    Intent intent;          // The original intent that started the task.
68    Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
69    ComponentName origActivity; // The non-alias activity component of the intent.
70    ComponentName realActivity; // The actual activity component that started the task.
71    int numActivities;      // Current number of activities in this task.
72    long lastActiveTime;    // Last time this task was active, including sleep.
73    boolean rootWasReset;   // True if the intent at the root of the task had
74                            // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
75    boolean askedCompatMode;// Have asked the user about compat mode for this task.
76
77    String stringName;      // caching of toString() result.
78    int userId;             // user for which this task was created
79    int creatorUid;         // The app uid that originally created the task
80
81    int numFullscreen;      // Number of fullscreen activities.
82
83    // This represents the last resolved activity values for this task
84    // NOTE: This value needs to be persisted with each task
85    ActivityManager.TaskDescription lastTaskDescription =
86            new ActivityManager.TaskDescription();
87
88    /** List of all activities in the task arranged in history order */
89    final ArrayList<ActivityRecord> mActivities;
90
91    /** Current stack */
92    ActivityStack stack;
93
94    /** Takes on same set of values as ActivityRecord.mActivityType */
95    int taskType;
96
97    /** Takes on same value as first root activity */
98    boolean isPersistable = false;
99    int maxRecents;
100
101    /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
102     * determining the order when restoring. Sign indicates whether last task movement was to front
103     * (positive) or back (negative). Absolute value indicates time. */
104    long mLastTimeMoved = System.currentTimeMillis();
105
106    /** True if persistable, has changed, and has not yet been persisted */
107    boolean needsPersisting = false;
108
109    /** Launch the home activity when leaving this task. Will be false for tasks that are not on
110     * Display.DEFAULT_DISPLAY. */
111    boolean mOnTopOfHome = false;
112
113    final ActivityManagerService mService;
114
115    TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
116            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
117        mService = service;
118        taskId = _taskId;
119        affinity = info.taskAffinity;
120        voiceSession = _voiceSession;
121        voiceInteractor = _voiceInteractor;
122        setIntent(_intent, info);
123        mActivities = new ArrayList<ActivityRecord>();
124    }
125
126    TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
127            String _affinity, ComponentName _realActivity, ComponentName _origActivity,
128            boolean _rootWasReset, boolean _askedCompatMode, int _taskType, boolean _onTopOfHome,
129            int _userId, String _lastDescription, ArrayList<ActivityRecord> activities,
130            long lastTimeMoved) {
131        mService = service;
132        taskId = _taskId;
133        intent = _intent;
134        affinityIntent = _affinityIntent;
135        affinity = _affinity;
136        voiceSession = null;
137        voiceInteractor = null;
138        realActivity = _realActivity;
139        origActivity = _origActivity;
140        rootWasReset = _rootWasReset;
141        askedCompatMode = _askedCompatMode;
142        taskType = _taskType;
143        mOnTopOfHome = _onTopOfHome;
144        userId = _userId;
145        lastDescription = _lastDescription;
146        mActivities = activities;
147        mLastTimeMoved = lastTimeMoved;
148    }
149
150    void touchActiveTime() {
151        lastActiveTime = android.os.SystemClock.elapsedRealtime();
152    }
153
154    long getInactiveDuration() {
155        return android.os.SystemClock.elapsedRealtime() - lastActiveTime;
156    }
157
158    void setIntent(Intent _intent, ActivityInfo info) {
159        stringName = null;
160
161        if (info.targetActivity == null) {
162            if (_intent != null) {
163                // If this Intent has a selector, we want to clear it for the
164                // recent task since it is not relevant if the user later wants
165                // to re-launch the app.
166                if (_intent.getSelector() != null || _intent.getSourceBounds() != null) {
167                    _intent = new Intent(_intent);
168                    _intent.setSelector(null);
169                    _intent.setSourceBounds(null);
170                }
171            }
172            if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG,
173                    "Setting Intent of " + this + " to " + _intent);
174            intent = _intent;
175            realActivity = _intent != null ? _intent.getComponent() : null;
176            origActivity = null;
177        } else {
178            ComponentName targetComponent = new ComponentName(
179                    info.packageName, info.targetActivity);
180            if (_intent != null) {
181                Intent targetIntent = new Intent(_intent);
182                targetIntent.setComponent(targetComponent);
183                targetIntent.setSelector(null);
184                targetIntent.setSourceBounds(null);
185                if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG,
186                        "Setting Intent of " + this + " to target " + targetIntent);
187                intent = targetIntent;
188                realActivity = targetComponent;
189                origActivity = _intent.getComponent();
190            } else {
191                intent = null;
192                realActivity = targetComponent;
193                origActivity = new ComponentName(info.packageName, info.name);
194            }
195        }
196
197        if (intent != null &&
198                (intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
199            // Once we are set to an Intent with this flag, we count this
200            // task as having a true root activity.
201            rootWasReset = true;
202        }
203
204        userId = UserHandle.getUserId(info.applicationInfo.uid);
205        creatorUid = info.applicationInfo.uid;
206        if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
207            intent.addFlags(Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
208        }
209    }
210
211    void disposeThumbnail() {
212        super.disposeThumbnail();
213        for (int i=mActivities.size()-1; i>=0; i--) {
214            ThumbnailHolder thumb = mActivities.get(i).thumbHolder;
215            if (thumb != this) {
216                thumb.disposeThumbnail();
217            }
218        }
219    }
220
221    /** Returns the intent for the root activity for this task */
222    Intent getBaseIntent() {
223        return intent != null ? intent : affinityIntent;
224    }
225
226    /** Returns the first non-finishing activity from the root. */
227    ActivityRecord getRootActivity() {
228        for (int i = 0; i < mActivities.size(); i++) {
229            final ActivityRecord r = mActivities.get(i);
230            if (r.finishing) {
231                continue;
232            }
233            return r;
234        }
235        return null;
236    }
237
238    ActivityRecord getTopActivity() {
239        for (int i = mActivities.size() - 1; i >= 0; --i) {
240            final ActivityRecord r = mActivities.get(i);
241            if (r.finishing) {
242                continue;
243            }
244            return r;
245        }
246        return null;
247    }
248
249    ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
250        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
251            ActivityRecord r = mActivities.get(activityNdx);
252            if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
253                return r;
254            }
255        }
256        return null;
257    }
258
259    /** Call after activity movement or finish to make sure that frontOfTask is set correctly */
260    final void setFrontOfTask() {
261        boolean foundFront = false;
262        final int numActivities = mActivities.size();
263        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
264            final ActivityRecord r = mActivities.get(activityNdx);
265            if (foundFront || r.finishing) {
266                r.frontOfTask = false;
267            } else {
268                r.frontOfTask = true;
269                // Set frontOfTask false for every following activity.
270                foundFront = true;
271            }
272        }
273    }
274
275    /**
276     * Reorder the history stack so that the passed activity is brought to the front.
277     */
278    final void moveActivityToFrontLocked(ActivityRecord newTop) {
279        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop
280            + " to stack at top", new RuntimeException("here").fillInStackTrace());
281
282        mActivities.remove(newTop);
283        mActivities.add(newTop);
284
285        setFrontOfTask();
286    }
287
288    void addActivityAtBottom(ActivityRecord r) {
289        addActivityAtIndex(0, r);
290    }
291
292    void addActivityToTop(ActivityRecord r) {
293        addActivityAtIndex(mActivities.size(), r);
294    }
295
296    void addActivityAtIndex(int index, ActivityRecord r) {
297        // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
298        if (!mActivities.remove(r) && r.fullscreen) {
299            // Was not previously in list.
300            numFullscreen++;
301        }
302        // Only set this based on the first activity
303        if (mActivities.isEmpty()) {
304            taskType = r.mActivityType;
305            isPersistable = r.isPersistable();
306            // Clamp to [1, 100].
307            maxRecents = Math.min(Math.max(r.info.maxRecents, 1), 100);
308        } else {
309            // Otherwise make all added activities match this one.
310            r.mActivityType = taskType;
311        }
312        mActivities.add(index, r);
313        if (r.isPersistable()) {
314            mService.notifyTaskPersisterLocked(this, false);
315        }
316    }
317
318    /** @return true if this was the last activity in the task */
319    boolean removeActivity(ActivityRecord r) {
320        if (mActivities.remove(r) && r.fullscreen) {
321            // Was previously in list.
322            numFullscreen--;
323        }
324        if (r.isPersistable()) {
325            mService.notifyTaskPersisterLocked(this, false);
326        }
327        return mActivities.size() == 0;
328    }
329
330    boolean autoRemoveFromRecents() {
331        return intent != null &&
332                (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0;
333    }
334
335    /**
336     * Completely remove all activities associated with an existing
337     * task starting at a specified index.
338     */
339    final void performClearTaskAtIndexLocked(int activityNdx) {
340        int numActivities = mActivities.size();
341        for ( ; activityNdx < numActivities; ++activityNdx) {
342            final ActivityRecord r = mActivities.get(activityNdx);
343            if (r.finishing) {
344                continue;
345            }
346            if (stack == null) {
347                // Task was restored from persistent storage.
348                r.takeFromHistory();
349                mActivities.remove(activityNdx);
350                --activityNdx;
351                --numActivities;
352            } else if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
353                    false)) {
354                --activityNdx;
355                --numActivities;
356            }
357        }
358    }
359
360    /**
361     * Completely remove all activities associated with an existing task.
362     */
363    final void performClearTaskLocked() {
364        performClearTaskAtIndexLocked(0);
365    }
366
367    /**
368     * Perform clear operation as requested by
369     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
370     * stack to the given task, then look for
371     * an instance of that activity in the stack and, if found, finish all
372     * activities on top of it and return the instance.
373     *
374     * @param newR Description of the new activity being started.
375     * @return Returns the old activity that should be continued to be used,
376     * or null if none was found.
377     */
378    final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
379        int numActivities = mActivities.size();
380        for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
381            ActivityRecord r = mActivities.get(activityNdx);
382            if (r.finishing) {
383                continue;
384            }
385            if (r.realActivity.equals(newR.realActivity)) {
386                // Here it is!  Now finish everything in front...
387                final ActivityRecord ret = r;
388
389                for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
390                    r = mActivities.get(activityNdx);
391                    if (r.finishing) {
392                        continue;
393                    }
394                    ActivityOptions opts = r.takeOptionsLocked();
395                    if (opts != null) {
396                        ret.updateOptionsLocked(opts);
397                    }
398                    if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
399                            false)) {
400                        --activityNdx;
401                        --numActivities;
402                    }
403                }
404
405                // Finally, if this is a normal launch mode (that is, not
406                // expecting onNewIntent()), then we will finish the current
407                // instance of the activity so a new fresh one can be started.
408                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
409                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
410                    if (!ret.finishing) {
411                        stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null,
412                                "clear", false);
413                        return null;
414                    }
415                }
416
417                return ret;
418            }
419        }
420
421        return null;
422    }
423
424    public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() {
425        TaskAccessInfo info = getTaskAccessInfoLocked();
426        final ActivityRecord resumedActivity = stack.mResumedActivity;
427        if (resumedActivity != null && resumedActivity.thumbHolder == this) {
428            info.mainThumbnail = stack.screenshotActivities(resumedActivity);
429        }
430        if (info.mainThumbnail == null) {
431            info.mainThumbnail = lastThumbnail;
432        }
433        return info;
434    }
435
436    public Bitmap getTaskTopThumbnailLocked() {
437        if (stack != null) {
438            final ActivityRecord resumedActivity = stack.mResumedActivity;
439            if (resumedActivity != null && resumedActivity.task == this) {
440                // This task is the current resumed task, we just need to take
441                // a screenshot of it and return that.
442                return stack.screenshotActivities(resumedActivity);
443            }
444        }
445        // Return the information about the task, to figure out the top
446        // thumbnail to return.
447        TaskAccessInfo info = getTaskAccessInfoLocked();
448        if (info.numSubThumbbails <= 0) {
449            return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail;
450        }
451        return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail;
452    }
453
454    public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex,
455            boolean taskRequired) {
456        TaskAccessInfo info = getTaskAccessInfoLocked();
457        if (info.root == null) {
458            if (taskRequired) {
459                Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
460            }
461            return null;
462        }
463
464        if (subTaskIndex < 0) {
465            // Just remove the entire task.
466            performClearTaskAtIndexLocked(info.rootIndex);
467            return info.root;
468        }
469
470        if (subTaskIndex >= info.subtasks.size()) {
471            if (taskRequired) {
472                Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex);
473            }
474            return null;
475        }
476
477        // Remove all of this task's activities starting at the sub task.
478        TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex);
479        performClearTaskAtIndexLocked(subtask.index);
480        return subtask.activity;
481    }
482
483    boolean isHomeTask() {
484        return taskType == ActivityRecord.HOME_ACTIVITY_TYPE;
485    }
486
487    boolean isApplicationTask() {
488        return taskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
489    }
490
491    public TaskAccessInfo getTaskAccessInfoLocked() {
492        final TaskAccessInfo thumbs = new TaskAccessInfo();
493        // How many different sub-thumbnails?
494        final int NA = mActivities.size();
495        int j = 0;
496        ThumbnailHolder holder = null;
497        while (j < NA) {
498            ActivityRecord ar = mActivities.get(j);
499            if (!ar.finishing) {
500                thumbs.root = ar;
501                thumbs.rootIndex = j;
502                holder = ar.thumbHolder;
503                if (holder != null) {
504                    thumbs.mainThumbnail = holder.lastThumbnail;
505                }
506                j++;
507                break;
508            }
509            j++;
510        }
511
512        if (j >= NA) {
513            return thumbs;
514        }
515
516        ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>();
517        thumbs.subtasks = subtasks;
518        while (j < NA) {
519            ActivityRecord ar = mActivities.get(j);
520            j++;
521            if (ar.finishing) {
522                continue;
523            }
524            if (ar.thumbHolder != holder && holder != null) {
525                thumbs.numSubThumbbails++;
526                holder = ar.thumbHolder;
527                TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask();
528                sub.holder = holder;
529                sub.activity = ar;
530                sub.index = j-1;
531                subtasks.add(sub);
532            }
533        }
534        if (thumbs.numSubThumbbails > 0) {
535            thumbs.retriever = new IThumbnailRetriever.Stub() {
536                @Override
537                public Bitmap getThumbnail(int index) {
538                    if (index < 0 || index >= thumbs.subtasks.size()) {
539                        return null;
540                    }
541                    TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index);
542                    ActivityRecord resumedActivity = stack.mResumedActivity;
543                    if (resumedActivity != null && resumedActivity.thumbHolder == sub.holder) {
544                        return stack.screenshotActivities(resumedActivity);
545                    }
546                    return sub.holder.lastThumbnail;
547                }
548            };
549        }
550        return thumbs;
551    }
552
553    /**
554     * Find the activity in the history stack within the given task.  Returns
555     * the index within the history at which it's found, or < 0 if not found.
556     */
557    final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
558        final ComponentName realActivity = r.realActivity;
559        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
560            ActivityRecord candidate = mActivities.get(activityNdx);
561            if (candidate.finishing) {
562                continue;
563            }
564            if (candidate.realActivity.equals(realActivity)) {
565                return candidate;
566            }
567        }
568        return null;
569    }
570
571    /** Updates the last task description values. */
572    void updateTaskDescription() {
573        // Traverse upwards looking for any break between main task activities and
574        // utility activities.
575        int activityNdx;
576        final int numActivities = mActivities.size();
577        for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
578                ++activityNdx) {
579            final ActivityRecord r = mActivities.get(activityNdx);
580            if (r.intent != null &&
581                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
582                            != 0) {
583                break;
584            }
585        }
586        if (activityNdx > 0) {
587            // Traverse downwards starting below break looking for set label, icon.
588            // Note that if there are activities in the task but none of them set the
589            // recent activity values, then we do not fall back to the last set
590            // values in the TaskRecord.
591            String label = null;
592            Bitmap icon = null;
593            int colorPrimary = 0;
594            for (--activityNdx; activityNdx >= 0; --activityNdx) {
595                final ActivityRecord r = mActivities.get(activityNdx);
596                if (r.taskDescription != null) {
597                    if (label == null) {
598                        label = r.taskDescription.getLabel();
599                    }
600                    if (icon == null) {
601                        icon = r.taskDescription.getIcon();
602                    }
603                    if (colorPrimary == 0) {
604                        colorPrimary = r.taskDescription.getPrimaryColor();
605
606                    }
607                }
608            }
609            lastTaskDescription = new ActivityManager.TaskDescription(label, icon, colorPrimary);
610        }
611    }
612
613    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
614        Slog.i(TAG, "Saving task=" + this);
615
616        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
617        if (realActivity != null) {
618            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
619        }
620        if (origActivity != null) {
621            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
622        }
623        if (affinity != null) {
624            out.attribute(null, ATTR_AFFINITY, affinity);
625        }
626        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
627        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
628        out.attribute(null, ATTR_USERID, String.valueOf(userId));
629        out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
630        out.attribute(null, ATTR_ONTOPOFHOME, String.valueOf(mOnTopOfHome));
631        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
632        if (lastDescription != null) {
633            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
634        }
635
636        if (affinityIntent != null) {
637            out.startTag(null, TAG_AFFINITYINTENT);
638            affinityIntent.saveToXml(out);
639            out.endTag(null, TAG_AFFINITYINTENT);
640        }
641
642        out.startTag(null, TAG_INTENT);
643        intent.saveToXml(out);
644        out.endTag(null, TAG_INTENT);
645
646        final ArrayList<ActivityRecord> activities = mActivities;
647        final int numActivities = activities.size();
648        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
649            final ActivityRecord r = activities.get(activityNdx);
650            if (!r.isPersistable() || (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
651                    Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) {
652                break;
653            }
654            out.startTag(null, TAG_ACTIVITY);
655            r.saveToXml(out);
656            out.endTag(null, TAG_ACTIVITY);
657        }
658
659        final Bitmap thumbnail = getTaskTopThumbnailLocked();
660        if (thumbnail != null) {
661            TaskPersister.saveImage(thumbnail, String.valueOf(taskId) + TASK_THUMBNAIL_SUFFIX);
662        }
663    }
664
665    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
666            throws IOException, XmlPullParserException {
667        Intent intent = null;
668        Intent affinityIntent = null;
669        ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
670        ComponentName realActivity = null;
671        ComponentName origActivity = null;
672        String affinity = null;
673        boolean rootHasReset = false;
674        boolean askedCompatMode = false;
675        int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
676        boolean onTopOfHome = true;
677        int userId = 0;
678        String lastDescription = null;
679        long lastTimeOnTop = 0;
680        int taskId = -1;
681        final int outerDepth = in.getDepth();
682
683        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
684            final String attrName = in.getAttributeName(attrNdx);
685            final String attrValue = in.getAttributeValue(attrNdx);
686            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
687                    attrName + " value=" + attrValue);
688            if (ATTR_TASKID.equals(attrName)) {
689                taskId = Integer.valueOf(attrValue);
690            } else if (ATTR_REALACTIVITY.equals(attrName)) {
691                realActivity = ComponentName.unflattenFromString(attrValue);
692            } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
693                origActivity = ComponentName.unflattenFromString(attrValue);
694            } else if (ATTR_AFFINITY.equals(attrName)) {
695                affinity = attrValue;
696            } else if (ATTR_ROOTHASRESET.equals(attrName)) {
697                rootHasReset = Boolean.valueOf(attrValue);
698            } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
699                askedCompatMode = Boolean.valueOf(attrValue);
700            } else if (ATTR_USERID.equals(attrName)) {
701                userId = Integer.valueOf(attrValue);
702            } else if (ATTR_TASKTYPE.equals(attrName)) {
703                taskType = Integer.valueOf(attrValue);
704            } else if (ATTR_ONTOPOFHOME.equals(attrName)) {
705                onTopOfHome = Boolean.valueOf(attrValue);
706            } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
707                lastDescription = attrValue;
708            } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
709                lastTimeOnTop = Long.valueOf(attrValue);
710            } else {
711                Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
712            }
713        }
714
715        int event;
716        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
717                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
718            if (event == XmlPullParser.START_TAG) {
719                final String name = in.getName();
720                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
721                        name);
722                if (TAG_AFFINITYINTENT.equals(name)) {
723                    affinityIntent = Intent.restoreFromXml(in);
724                } else if (TAG_INTENT.equals(name)) {
725                    intent = Intent.restoreFromXml(in);
726                } else if (TAG_ACTIVITY.equals(name)) {
727                    ActivityRecord activity =
728                            ActivityRecord.restoreFromXml(in, taskId, stackSupervisor);
729                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
730                            activity);
731                    if (activity != null) {
732                        activities.add(activity);
733                    }
734                } else {
735                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
736                    XmlUtils.skipCurrentTag(in);
737                }
738            }
739        }
740
741        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
742                affinityIntent, affinity, realActivity, origActivity, rootHasReset,
743                askedCompatMode, taskType, onTopOfHome, userId, lastDescription, activities,
744                lastTimeOnTop);
745
746        for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
747            final ActivityRecord r = activities.get(activityNdx);
748            r.thumbHolder = r.task = task;
749        }
750
751        task.lastThumbnail = TaskPersister.restoreImage(taskId + TASK_THUMBNAIL_SUFFIX);
752
753        Slog.i(TAG, "Restored task=" + task);
754        return task;
755    }
756
757    void dump(PrintWriter pw, String prefix) {
758        if (rootWasReset || userId != 0 || numFullscreen != 0) {
759            pw.print(prefix); pw.print(" rootWasReset="); pw.print(rootWasReset);
760                    pw.print(" userId="); pw.print(userId);
761                    pw.print(" taskType="); pw.print(taskType);
762                    pw.print(" numFullscreen="); pw.print(numFullscreen);
763                    pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome);
764        }
765        if (affinity != null) {
766            pw.print(prefix); pw.print("affinity="); pw.println(affinity);
767        }
768        if (voiceSession != null || voiceInteractor != null) {
769            pw.print(prefix); pw.print("VOICE: session=0x");
770            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
771            pw.print(" interactor=0x");
772            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
773        }
774        if (intent != null) {
775            StringBuilder sb = new StringBuilder(128);
776            sb.append(prefix); sb.append("intent={");
777            intent.toShortString(sb, false, true, false, true);
778            sb.append('}');
779            pw.println(sb.toString());
780        }
781        if (affinityIntent != null) {
782            StringBuilder sb = new StringBuilder(128);
783            sb.append(prefix); sb.append("affinityIntent={");
784            affinityIntent.toShortString(sb, false, true, false, true);
785            sb.append('}');
786            pw.println(sb.toString());
787        }
788        if (origActivity != null) {
789            pw.print(prefix); pw.print("origActivity=");
790            pw.println(origActivity.flattenToShortString());
791        }
792        if (realActivity != null) {
793            pw.print(prefix); pw.print("realActivity=");
794            pw.println(realActivity.flattenToShortString());
795        }
796        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
797        if (!askedCompatMode) {
798            pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
799        }
800        pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail);
801                pw.print(" lastDescription="); pw.println(lastDescription);
802        pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
803                pw.print(" (inactive for ");
804                pw.print((getInactiveDuration()/1000)); pw.println("s)");
805    }
806
807    @Override
808    public String toString() {
809        StringBuilder sb = new StringBuilder(128);
810        if (stringName != null) {
811            sb.append(stringName);
812            sb.append(" U=");
813            sb.append(userId);
814            sb.append(" sz=");
815            sb.append(mActivities.size());
816            sb.append('}');
817            return sb.toString();
818        }
819        sb.append("TaskRecord{");
820        sb.append(Integer.toHexString(System.identityHashCode(this)));
821        sb.append(" #");
822        sb.append(taskId);
823        if (affinity != null) {
824            sb.append(" A=");
825            sb.append(affinity);
826        } else if (intent != null) {
827            sb.append(" I=");
828            sb.append(intent.getComponent().flattenToShortString());
829        } else if (affinityIntent != null) {
830            sb.append(" aI=");
831            sb.append(affinityIntent.getComponent().flattenToShortString());
832        } else {
833            sb.append(" ??");
834        }
835        stringName = sb.toString();
836        return toString();
837    }
838}
839