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