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