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