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