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