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