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 
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent, IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor)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 
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent, TaskDescription _taskDescription)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 
TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent, String _affinity, String _rootAffinity, ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents, boolean _askedCompatMode, int _taskType, int _userId, int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities, long _firstActiveTime, long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage)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 
touchActiveTime()258     void touchActiveTime() {
259         lastActiveTime = System.currentTimeMillis();
260         if (firstActiveTime == 0) {
261             firstActiveTime = lastActiveTime;
262         }
263     }
264 
getInactiveDuration()265     long getInactiveDuration() {
266         return System.currentTimeMillis() - lastActiveTime;
267     }
268 
269     /** Sets the original intent, and the calling uid and package. */
setIntent(ActivityRecord r)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. */
setIntent(Intent _intent, ActivityInfo info)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 
setTaskToReturnTo(int taskToReturnTo)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 
getTaskToReturnTo()364     int getTaskToReturnTo() {
365         return mTaskToReturnTo;
366     }
367 
setPrevAffiliate(TaskRecord prevAffiliate)368     void setPrevAffiliate(TaskRecord prevAffiliate) {
369         mPrevAffiliate = prevAffiliate;
370         mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.taskId;
371     }
372 
setNextAffiliate(TaskRecord nextAffiliate)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.
closeRecentsChain()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 
removedFromRecents()390     void removedFromRecents() {
391         disposeThumbnail();
392         closeRecentsChain();
393         if (inRecents) {
394             inRecents = false;
395             mService.notifyTaskPersisterLocked(this, false);
396         }
397     }
398 
setTaskToAffiliateWith(TaskRecord taskToAffiliateWith)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      */
setLastThumbnail(Bitmap thumbnail)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 
getLastThumbnail(TaskThumbnail thumbs)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 
freeLastThumbnail()457     void freeLastThumbnail() {
458         mLastThumbnail = null;
459     }
460 
disposeThumbnail()461     void disposeThumbnail() {
462         mLastThumbnail = null;
463         lastDescription = null;
464     }
465 
466     /** Returns the intent for the root activity for this task */
getBaseIntent()467     Intent getBaseIntent() {
468         return intent != null ? intent : affinityIntent;
469     }
470 
471     /** Returns the first non-finishing activity from the root. */
getRootActivity()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 
getTopActivity()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 
topRunningActivityLocked(ActivityRecord notTop)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 */
setFrontOfTask()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      */
moveActivityToFrontLocked(ActivityRecord newTop)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 
addActivityAtBottom(ActivityRecord r)539     void addActivityAtBottom(ActivityRecord r) {
540         addActivityAtIndex(0, r);
541     }
542 
addActivityToTop(ActivityRecord r)543     void addActivityToTop(ActivityRecord r) {
544         addActivityAtIndex(mActivities.size(), r);
545     }
546 
addActivityAtIndex(int index, ActivityRecord r)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 */
removeActivity(ActivityRecord r)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 
autoRemoveFromRecents()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      */
performClearTaskAtIndexLocked(int activityNdx)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      */
performClearTaskLocked()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      */
performClearTaskLocked(ActivityRecord newR, int launchFlags)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 
getTaskThumbnailLocked()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 
removeTaskActivitiesLocked()700     public void removeTaskActivitiesLocked() {
701         // Just remove the entire task.
702         performClearTaskAtIndexLocked(0);
703     }
704 
isHomeTask()705     boolean isHomeTask() {
706         return taskType == HOME_ACTIVITY_TYPE;
707     }
708 
isApplicationTask()709     boolean isApplicationTask() {
710         return taskType == APPLICATION_ACTIVITY_TYPE;
711     }
712 
isOverHomeStack()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      */
findActivityInHistoryLocked(ActivityRecord r)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. */
updateTaskDescription()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 
findEffectiveRootIndex()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 
updateEffectiveIntent()803     void updateEffectiveIntent() {
804         final int effectiveRootIndex = findEffectiveRootIndex();
805         final ActivityRecord r = mActivities.get(effectiveRootIndex);
806         setIntent(r);
807     }
808 
saveToXml(XmlSerializer out)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 
restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)880     static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
881             throws IOException, XmlPullParserException {
882         return restoreFromXml(in, stackSupervisor, INVALID_TASK_ID);
883     }
restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor, int inTaskId)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 
dump(PrintWriter pw, String prefix)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
toString()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