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