TaskRecord.java revision 3bad5cc0f5b8ac3018e413ce480692e40039027c
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    private static String createLastTaskDescriptionIconFilename(int taskId, long lastActiveTime) {
768        return String.valueOf(taskId) + LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime +
769                TaskPersister.IMAGE_EXTENSION;
770    }
771
772    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
773        Slog.i(TAG, "Saving task=" + this);
774
775        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
776        if (realActivity != null) {
777            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
778        }
779        if (origActivity != null) {
780            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
781        }
782        if (affinity != null) {
783            out.attribute(null, ATTR_AFFINITY, affinity);
784        }
785        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
786        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
787        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
788        out.attribute(null, ATTR_USERID, String.valueOf(userId));
789        out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
790        out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
791        out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
792        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
793        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
794        if (lastDescription != null) {
795            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
796        }
797        if (lastTaskDescription != null) {
798            saveTaskDescription(lastTaskDescription, createLastTaskDescriptionIconFilename(taskId,
799                    lastActiveTime), out);
800        }
801        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
802        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
803        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
804        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
805        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
806        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
807
808        if (affinityIntent != null) {
809            out.startTag(null, TAG_AFFINITYINTENT);
810            affinityIntent.saveToXml(out);
811            out.endTag(null, TAG_AFFINITYINTENT);
812        }
813
814        out.startTag(null, TAG_INTENT);
815        intent.saveToXml(out);
816        out.endTag(null, TAG_INTENT);
817
818        final ArrayList<ActivityRecord> activities = mActivities;
819        final int numActivities = activities.size();
820        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
821            final ActivityRecord r = activities.get(activityNdx);
822            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
823                    ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
824                            activityNdx > 0) {
825                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
826                break;
827            }
828            out.startTag(null, TAG_ACTIVITY);
829            r.saveToXml(out);
830            out.endTag(null, TAG_ACTIVITY);
831        }
832    }
833
834    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
835            throws IOException, XmlPullParserException {
836        Intent intent = null;
837        Intent affinityIntent = null;
838        ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
839        ComponentName realActivity = null;
840        ComponentName origActivity = null;
841        String affinity = null;
842        boolean rootHasReset = false;
843        boolean autoRemoveRecents = false;
844        boolean askedCompatMode = false;
845        int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
846        int userId = 0;
847        String lastDescription = null;
848        long firstActiveTime = -1;
849        long lastActiveTime = -1;
850        long lastTimeOnTop = 0;
851        boolean neverRelinquishIdentity = true;
852        int taskId = -1;
853        final int outerDepth = in.getDepth();
854        ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription();
855        int taskAffiliation = -1;
856        int taskAffiliationColor = 0;
857        int prevTaskId = -1;
858        int nextTaskId = -1;
859        int callingUid = -1;
860        String callingPackage = "";
861
862        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
863            final String attrName = in.getAttributeName(attrNdx);
864            final String attrValue = in.getAttributeValue(attrNdx);
865            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
866                    attrName + " value=" + attrValue);
867            if (ATTR_TASKID.equals(attrName)) {
868                taskId = Integer.valueOf(attrValue);
869            } else if (ATTR_REALACTIVITY.equals(attrName)) {
870                realActivity = ComponentName.unflattenFromString(attrValue);
871            } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
872                origActivity = ComponentName.unflattenFromString(attrValue);
873            } else if (ATTR_AFFINITY.equals(attrName)) {
874                affinity = attrValue;
875            } else if (ATTR_ROOTHASRESET.equals(attrName)) {
876                rootHasReset = Boolean.valueOf(attrValue);
877            } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) {
878                autoRemoveRecents = Boolean.valueOf(attrValue);
879            } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
880                askedCompatMode = Boolean.valueOf(attrValue);
881            } else if (ATTR_USERID.equals(attrName)) {
882                userId = Integer.valueOf(attrValue);
883            } else if (ATTR_TASKTYPE.equals(attrName)) {
884                taskType = Integer.valueOf(attrValue);
885            } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
886                firstActiveTime = Long.valueOf(attrValue);
887            } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
888                lastActiveTime = Long.valueOf(attrValue);
889            } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
890                lastDescription = attrValue;
891            } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
892                lastTimeOnTop = Long.valueOf(attrValue);
893            } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
894                neverRelinquishIdentity = Boolean.valueOf(attrValue);
895            } else if (readTaskDescriptionAttribute(taskDescription, attrName, attrValue)) {
896                // Completed in TaskPersister.readTaskDescriptionAttribute()
897            } else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
898                taskAffiliation = Integer.valueOf(attrValue);
899            } else if (ATTR_PREV_AFFILIATION.equals(attrName)) {
900                prevTaskId = Integer.valueOf(attrValue);
901            } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
902                nextTaskId = Integer.valueOf(attrValue);
903            } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) {
904                taskAffiliationColor = Integer.valueOf(attrValue);
905            } else if (ATTR_CALLING_UID.equals(attrName)) {
906                callingUid = Integer.valueOf(attrValue);
907            } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
908                callingPackage = attrValue;
909            } else {
910                Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
911            }
912        }
913
914        int event;
915        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
916                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
917            if (event == XmlPullParser.START_TAG) {
918                final String name = in.getName();
919                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
920                        name);
921                if (TAG_AFFINITYINTENT.equals(name)) {
922                    affinityIntent = Intent.restoreFromXml(in);
923                } else if (TAG_INTENT.equals(name)) {
924                    intent = Intent.restoreFromXml(in);
925                } else if (TAG_ACTIVITY.equals(name)) {
926                    ActivityRecord activity =
927                            ActivityRecord.restoreFromXml(in, taskId, stackSupervisor);
928                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
929                            activity);
930                    if (activity != null) {
931                        activities.add(activity);
932                    }
933                } else {
934                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
935                    XmlUtils.skipCurrentTag(in);
936                }
937            }
938        }
939
940        if (lastActiveTime >= 0) {
941            taskDescription.setIcon(TaskPersister.restoreImage(
942                    createLastTaskDescriptionIconFilename(taskId, lastActiveTime)));
943        }
944
945        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
946                affinityIntent, affinity, realActivity, origActivity, rootHasReset,
947                autoRemoveRecents, askedCompatMode, taskType, userId, lastDescription, activities,
948                firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
949                taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
950                callingUid, callingPackage);
951
952        for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
953            activities.get(activityNdx).task = task;
954        }
955
956        Slog.i(TAG, "Restored task=" + task);
957        return task;
958    }
959
960    void dump(PrintWriter pw, String prefix) {
961        if (rootWasReset || userId != 0 || numFullscreen != 0) {
962            pw.print(prefix); pw.print(" rootWasReset="); pw.print(rootWasReset);
963                    pw.print(" userId="); pw.print(userId);
964                    pw.print(" taskType="); pw.print(taskType);
965                    pw.print(" numFullscreen="); pw.print(numFullscreen);
966                    pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
967        }
968        if (affinity != null) {
969            pw.print(prefix); pw.print("affinity="); pw.println(affinity);
970        }
971        if (voiceSession != null || voiceInteractor != null) {
972            pw.print(prefix); pw.print("VOICE: session=0x");
973            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
974            pw.print(" interactor=0x");
975            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
976        }
977        if (intent != null) {
978            StringBuilder sb = new StringBuilder(128);
979            sb.append(prefix); sb.append("intent={");
980            intent.toShortString(sb, false, true, false, true);
981            sb.append('}');
982            pw.println(sb.toString());
983        }
984        if (affinityIntent != null) {
985            StringBuilder sb = new StringBuilder(128);
986            sb.append(prefix); sb.append("affinityIntent={");
987            affinityIntent.toShortString(sb, false, true, false, true);
988            sb.append('}');
989            pw.println(sb.toString());
990        }
991        if (origActivity != null) {
992            pw.print(prefix); pw.print("origActivity=");
993            pw.println(origActivity.flattenToShortString());
994        }
995        if (realActivity != null) {
996            pw.print(prefix); pw.print("realActivity=");
997            pw.println(realActivity.flattenToShortString());
998        }
999        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
1000        if (!askedCompatMode) {
1001            pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
1002        }
1003        pw.print(prefix); pw.print("lastThumbnail="); pw.print(mLastThumbnail);
1004                pw.print(" lastThumbnailFile="); pw.print(mLastThumbnailFile);
1005                pw.print(" lastDescription="); pw.println(lastDescription);
1006        pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible);
1007                pw.print(" firstActiveTime="); pw.print(lastActiveTime);
1008                pw.print(" lastActiveTime="); pw.print(lastActiveTime);
1009                pw.print(" (inactive for ");
1010                pw.print((getInactiveDuration()/1000)); pw.println("s)");
1011        pw.print(prefix); pw.print("isPersistable="); pw.print(isPersistable);
1012                pw.print(" affiliation="); pw.print(mAffiliatedTaskId);
1013                pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId);
1014                pw.print(" nextAffiliation="); pw.println(mNextAffiliateTaskId);
1015    }
1016
1017    @Override
1018    public String toString() {
1019        StringBuilder sb = new StringBuilder(128);
1020        if (stringName != null) {
1021            sb.append(stringName);
1022            sb.append(" U=");
1023            sb.append(userId);
1024            sb.append(" sz=");
1025            sb.append(mActivities.size());
1026            sb.append('}');
1027            return sb.toString();
1028        }
1029        sb.append("TaskRecord{");
1030        sb.append(Integer.toHexString(System.identityHashCode(this)));
1031        sb.append(" #");
1032        sb.append(taskId);
1033        if (affinity != null) {
1034            sb.append(" A=");
1035            sb.append(affinity);
1036        } else if (intent != null) {
1037            sb.append(" I=");
1038            sb.append(intent.getComponent().flattenToShortString());
1039        } else if (affinityIntent != null) {
1040            sb.append(" aI=");
1041            sb.append(affinityIntent.getComponent().flattenToShortString());
1042        } else {
1043            sb.append(" ??");
1044        }
1045        stringName = sb.toString();
1046        return toString();
1047    }
1048}
1049