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