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