TaskRecord.java revision 648f69b95ce7fc95f551f6e08a2408d6e57dbab9
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.ActivityManager.TaskDescription; 29import android.app.ActivityOptions; 30import android.app.AppGlobals; 31import android.content.ComponentName; 32import android.content.Intent; 33import android.content.pm.ActivityInfo; 34import android.content.pm.ApplicationInfo; 35import android.content.pm.IPackageManager; 36import android.content.pm.PackageManager; 37import android.graphics.Bitmap; 38import android.os.ParcelFileDescriptor; 39import android.os.RemoteException; 40import android.os.UserHandle; 41import android.service.voice.IVoiceInteractionSession; 42import android.util.Slog; 43import com.android.internal.app.IVoiceInteractor; 44import com.android.internal.util.XmlUtils; 45import org.xmlpull.v1.XmlPullParser; 46import org.xmlpull.v1.XmlPullParserException; 47import org.xmlpull.v1.XmlSerializer; 48 49import java.io.File; 50import java.io.IOException; 51import java.io.PrintWriter; 52import java.util.ArrayList; 53 54final 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 if (intent != null && 328 (intent.getFlags()&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 ((intent.getFlags()&(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 activityNdx; 785 final int topActivityNdx = mActivities.size() - 1; 786 for (activityNdx = 0; activityNdx < topActivityNdx; ++activityNdx) { 787 final ActivityRecord r = mActivities.get(activityNdx); 788 if (r.finishing) { 789 continue; 790 } 791 if ((r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) { 792 break; 793 } 794 } 795 return activityNdx; 796 } 797 798 void updateEffectiveIntent() { 799 final int effectiveRootIndex = findEffectiveRootIndex(); 800 final ActivityRecord r = mActivities.get(effectiveRootIndex); 801 setIntent(r); 802 } 803 804 void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { 805 if (ActivityManagerService.DEBUG_RECENTS) Slog.i(TAG, "Saving task=" + this); 806 807 out.attribute(null, ATTR_TASKID, String.valueOf(taskId)); 808 if (realActivity != null) { 809 out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString()); 810 } 811 if (origActivity != null) { 812 out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString()); 813 } 814 // Write affinity, and root affinity if it is different from affinity. 815 // We use the special string "@" for a null root affinity, so we can identify 816 // later whether we were given a root affinity or should just make it the 817 // same as the affinity. 818 if (affinity != null) { 819 out.attribute(null, ATTR_AFFINITY, affinity); 820 if (!affinity.equals(rootAffinity)) { 821 out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@"); 822 } 823 } else if (rootAffinity != null) { 824 out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@"); 825 } 826 out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset)); 827 out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents)); 828 out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode)); 829 out.attribute(null, ATTR_USERID, String.valueOf(userId)); 830 out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid)); 831 out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType)); 832 out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime)); 833 out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime)); 834 out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved)); 835 out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity)); 836 if (lastDescription != null) { 837 out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString()); 838 } 839 if (lastTaskDescription != null) { 840 lastTaskDescription.saveToXml(out); 841 } 842 out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor)); 843 out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId)); 844 out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId)); 845 out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId)); 846 out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid)); 847 out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage); 848 849 if (affinityIntent != null) { 850 out.startTag(null, TAG_AFFINITYINTENT); 851 affinityIntent.saveToXml(out); 852 out.endTag(null, TAG_AFFINITYINTENT); 853 } 854 855 out.startTag(null, TAG_INTENT); 856 intent.saveToXml(out); 857 out.endTag(null, TAG_INTENT); 858 859 final ArrayList<ActivityRecord> activities = mActivities; 860 final int numActivities = activities.size(); 861 for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { 862 final ActivityRecord r = activities.get(activityNdx); 863 if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() || 864 ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) && 865 activityNdx > 0) { 866 // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET). 867 break; 868 } 869 out.startTag(null, TAG_ACTIVITY); 870 r.saveToXml(out); 871 out.endTag(null, TAG_ACTIVITY); 872 } 873 } 874 875 static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor) 876 throws IOException, XmlPullParserException { 877 Intent intent = null; 878 Intent affinityIntent = null; 879 ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(); 880 ComponentName realActivity = null; 881 ComponentName origActivity = null; 882 String affinity = null; 883 String rootAffinity = null; 884 boolean hasRootAffinity = false; 885 boolean rootHasReset = false; 886 boolean autoRemoveRecents = false; 887 boolean askedCompatMode = false; 888 int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE; 889 int userId = 0; 890 int effectiveUid = -1; 891 String lastDescription = null; 892 long firstActiveTime = -1; 893 long lastActiveTime = -1; 894 long lastTimeOnTop = 0; 895 boolean neverRelinquishIdentity = true; 896 int taskId = -1; 897 final int outerDepth = in.getDepth(); 898 TaskDescription taskDescription = new TaskDescription(); 899 int taskAffiliation = -1; 900 int taskAffiliationColor = 0; 901 int prevTaskId = -1; 902 int nextTaskId = -1; 903 int callingUid = -1; 904 String callingPackage = ""; 905 906 for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) { 907 final String attrName = in.getAttributeName(attrNdx); 908 final String attrValue = in.getAttributeValue(attrNdx); 909 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" + 910 attrName + " value=" + attrValue); 911 if (ATTR_TASKID.equals(attrName)) { 912 taskId = Integer.valueOf(attrValue); 913 } else if (ATTR_REALACTIVITY.equals(attrName)) { 914 realActivity = ComponentName.unflattenFromString(attrValue); 915 } else if (ATTR_ORIGACTIVITY.equals(attrName)) { 916 origActivity = ComponentName.unflattenFromString(attrValue); 917 } else if (ATTR_AFFINITY.equals(attrName)) { 918 affinity = attrValue; 919 } else if (ATTR_ROOT_AFFINITY.equals(attrName)) { 920 rootAffinity = attrValue; 921 hasRootAffinity = true; 922 } else if (ATTR_ROOTHASRESET.equals(attrName)) { 923 rootHasReset = Boolean.valueOf(attrValue); 924 } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) { 925 autoRemoveRecents = Boolean.valueOf(attrValue); 926 } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) { 927 askedCompatMode = Boolean.valueOf(attrValue); 928 } else if (ATTR_USERID.equals(attrName)) { 929 userId = Integer.valueOf(attrValue); 930 } else if (ATTR_EFFECTIVE_UID.equals(attrName)) { 931 effectiveUid = Integer.valueOf(attrValue); 932 } else if (ATTR_TASKTYPE.equals(attrName)) { 933 taskType = Integer.valueOf(attrValue); 934 } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) { 935 firstActiveTime = Long.valueOf(attrValue); 936 } else if (ATTR_LASTACTIVETIME.equals(attrName)) { 937 lastActiveTime = Long.valueOf(attrValue); 938 } else if (ATTR_LASTDESCRIPTION.equals(attrName)) { 939 lastDescription = attrValue; 940 } else if (ATTR_LASTTIMEMOVED.equals(attrName)) { 941 lastTimeOnTop = Long.valueOf(attrValue); 942 } else if (ATTR_NEVERRELINQUISH.equals(attrName)) { 943 neverRelinquishIdentity = Boolean.valueOf(attrValue); 944 } else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) { 945 taskDescription.restoreFromXml(attrName, attrValue); 946 } else if (ATTR_TASK_AFFILIATION.equals(attrName)) { 947 taskAffiliation = Integer.valueOf(attrValue); 948 } else if (ATTR_PREV_AFFILIATION.equals(attrName)) { 949 prevTaskId = Integer.valueOf(attrValue); 950 } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) { 951 nextTaskId = Integer.valueOf(attrValue); 952 } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) { 953 taskAffiliationColor = Integer.valueOf(attrValue); 954 } else if (ATTR_CALLING_UID.equals(attrName)) { 955 callingUid = Integer.valueOf(attrValue); 956 } else if (ATTR_CALLING_PACKAGE.equals(attrName)) { 957 callingPackage = attrValue; 958 } else { 959 Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName); 960 } 961 } 962 963 int event; 964 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && 965 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { 966 if (event == XmlPullParser.START_TAG) { 967 final String name = in.getName(); 968 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" + 969 name); 970 if (TAG_AFFINITYINTENT.equals(name)) { 971 affinityIntent = Intent.restoreFromXml(in); 972 } else if (TAG_INTENT.equals(name)) { 973 intent = Intent.restoreFromXml(in); 974 } else if (TAG_ACTIVITY.equals(name)) { 975 ActivityRecord activity = 976 ActivityRecord.restoreFromXml(in, taskId, stackSupervisor); 977 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" + 978 activity); 979 if (activity != null) { 980 activities.add(activity); 981 } 982 } else { 983 Slog.e(TAG, "restoreTask: Unexpected name=" + name); 984 XmlUtils.skipCurrentTag(in); 985 } 986 } 987 } 988 989 if (!hasRootAffinity) { 990 rootAffinity = affinity; 991 } else if ("@".equals(rootAffinity)) { 992 rootAffinity = null; 993 } 994 995 if (effectiveUid <= 0) { 996 Intent checkIntent = intent != null ? intent : affinityIntent; 997 effectiveUid = 0; 998 if (checkIntent != null) { 999 IPackageManager pm = AppGlobals.getPackageManager(); 1000 try { 1001 ApplicationInfo ai = pm.getApplicationInfo( 1002 checkIntent.getComponent().getPackageName(), 1003 PackageManager.GET_UNINSTALLED_PACKAGES 1004 | PackageManager.GET_DISABLED_COMPONENTS, userId); 1005 if (ai != null) { 1006 effectiveUid = ai.uid; 1007 } 1008 } catch (RemoteException e) { 1009 } 1010 } 1011 Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent 1012 + ": effectiveUid=" + effectiveUid); 1013 } 1014 1015 final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent, 1016 affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset, 1017 autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription, 1018 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity, 1019 taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, 1020 callingUid, callingPackage); 1021 1022 for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { 1023 activities.get(activityNdx).task = task; 1024 } 1025 1026 if (ActivityManagerService.DEBUG_RECENTS) Slog.d(TAG, "Restored task=" + task); 1027 return task; 1028 } 1029 1030 void dump(PrintWriter pw, String prefix) { 1031 pw.print(prefix); pw.print("userId="); pw.print(userId); 1032 pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid); 1033 pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid); 1034 pw.print(" mCallingPackage="); pw.println(mCallingPackage); 1035 if (affinity != null || rootAffinity != null) { 1036 pw.print(prefix); pw.print("affinity="); pw.print(affinity); 1037 if (affinity == null || !affinity.equals(rootAffinity)) { 1038 pw.print(" root="); pw.println(rootAffinity); 1039 } else { 1040 pw.println(); 1041 } 1042 } 1043 if (voiceSession != null || voiceInteractor != null) { 1044 pw.print(prefix); pw.print("VOICE: session=0x"); 1045 pw.print(Integer.toHexString(System.identityHashCode(voiceSession))); 1046 pw.print(" interactor=0x"); 1047 pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor))); 1048 } 1049 if (intent != null) { 1050 StringBuilder sb = new StringBuilder(128); 1051 sb.append(prefix); sb.append("intent={"); 1052 intent.toShortString(sb, false, true, false, true); 1053 sb.append('}'); 1054 pw.println(sb.toString()); 1055 } 1056 if (affinityIntent != null) { 1057 StringBuilder sb = new StringBuilder(128); 1058 sb.append(prefix); sb.append("affinityIntent={"); 1059 affinityIntent.toShortString(sb, false, true, false, true); 1060 sb.append('}'); 1061 pw.println(sb.toString()); 1062 } 1063 if (origActivity != null) { 1064 pw.print(prefix); pw.print("origActivity="); 1065 pw.println(origActivity.flattenToShortString()); 1066 } 1067 if (realActivity != null) { 1068 pw.print(prefix); pw.print("realActivity="); 1069 pw.println(realActivity.flattenToShortString()); 1070 } 1071 if (autoRemoveRecents || isPersistable || taskType != 0 || mTaskToReturnTo != 0 1072 || numFullscreen != 0) { 1073 pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents); 1074 pw.print(" isPersistable="); pw.print(isPersistable); 1075 pw.print(" numFullscreen="); pw.print(numFullscreen); 1076 pw.print(" taskType="); pw.print(taskType); 1077 pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo); 1078 } 1079 if (rootWasReset || mNeverRelinquishIdentity || mReuseTask) { 1080 pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset); 1081 pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity); 1082 pw.print(" mReuseTask="); pw.println(mReuseTask); 1083 } 1084 if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != -1 || mPrevAffiliate != null 1085 || mNextAffiliateTaskId != -1 || mNextAffiliate != null) { 1086 pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId); 1087 pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId); 1088 pw.print(" ("); 1089 if (mPrevAffiliate == null) { 1090 pw.print("null"); 1091 } else { 1092 pw.print(Integer.toHexString(System.identityHashCode(mPrevAffiliate))); 1093 } 1094 pw.print(") nextAffiliation="); pw.print(mNextAffiliateTaskId); 1095 pw.print(" ("); 1096 if (mNextAffiliate == null) { 1097 pw.print("null"); 1098 } else { 1099 pw.print(Integer.toHexString(System.identityHashCode(mNextAffiliate))); 1100 } 1101 pw.println(")"); 1102 } 1103 pw.print(prefix); pw.print("Activities="); pw.println(mActivities); 1104 if (!askedCompatMode || !inRecents || !isAvailable) { 1105 pw.print(prefix); pw.print("askedCompatMode="); pw.print(askedCompatMode); 1106 pw.print(" inRecents="); pw.print(inRecents); 1107 pw.print(" isAvailable="); pw.println(isAvailable); 1108 } 1109 pw.print(prefix); pw.print("lastThumbnail="); pw.print(mLastThumbnail); 1110 pw.print(" lastThumbnailFile="); pw.println(mLastThumbnailFile); 1111 if (lastDescription != null) { 1112 pw.print(prefix); pw.print("lastDescription="); pw.println(lastDescription); 1113 } 1114 pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible); 1115 pw.print(" firstActiveTime="); pw.print(lastActiveTime); 1116 pw.print(" lastActiveTime="); pw.print(lastActiveTime); 1117 pw.print(" (inactive for "); 1118 pw.print((getInactiveDuration()/1000)); pw.println("s)"); 1119 } 1120 1121 @Override 1122 public String toString() { 1123 StringBuilder sb = new StringBuilder(128); 1124 if (stringName != null) { 1125 sb.append(stringName); 1126 sb.append(" U="); 1127 sb.append(userId); 1128 sb.append(" sz="); 1129 sb.append(mActivities.size()); 1130 sb.append('}'); 1131 return sb.toString(); 1132 } 1133 sb.append("TaskRecord{"); 1134 sb.append(Integer.toHexString(System.identityHashCode(this))); 1135 sb.append(" #"); 1136 sb.append(taskId); 1137 if (affinity != null) { 1138 sb.append(" A="); 1139 sb.append(affinity); 1140 } else if (intent != null) { 1141 sb.append(" I="); 1142 sb.append(intent.getComponent().flattenToShortString()); 1143 } else if (affinityIntent != null) { 1144 sb.append(" aI="); 1145 sb.append(affinityIntent.getComponent().flattenToShortString()); 1146 } else { 1147 sb.append(" ??"); 1148 } 1149 stringName = sb.toString(); 1150 return toString(); 1151 } 1152} 1153