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