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