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