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