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