TaskRecord.java revision 2cb86c723195e24278f983241cd6b1e307acf159
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.ActivityOptions; 28import android.app.IThumbnailRetriever; 29import android.content.ComponentName; 30import android.content.Intent; 31import android.content.pm.ActivityInfo; 32import android.graphics.Bitmap; 33import android.os.UserHandle; 34import android.service.voice.IVoiceInteractionSession; 35import android.util.Slog; 36import com.android.internal.app.IVoiceInteractor; 37import com.android.internal.util.XmlUtils; 38import org.xmlpull.v1.XmlPullParser; 39import org.xmlpull.v1.XmlPullParserException; 40import org.xmlpull.v1.XmlSerializer; 41 42import java.io.IOException; 43import java.io.PrintWriter; 44import java.util.ArrayList; 45 46final class TaskRecord extends ThumbnailHolder { 47 private static final String TAG_TASK = "task"; 48 private static final String ATTR_TASKID = "task_id"; 49 private static final String TAG_INTENT = "intent"; 50 private static final String TAG_AFFINITYINTENT = "affinity_intent"; 51 private static final String ATTR_REALACTIVITY = "real_activity"; 52 private static final String ATTR_ORIGACTIVITY = "orig_activity"; 53 private static final String TAG_ACTIVITY = "activity"; 54 private static final String ATTR_AFFINITY = "affinity"; 55 private static final String ATTR_ROOTHASRESET = "root_has_reset"; 56 private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode"; 57 private static final String ATTR_USERID = "user_id"; 58 private static final String ATTR_TASKTYPE = "task_type"; 59 private static final String ATTR_LASTACTIVETIME = "last_active_time"; 60 private static final String ATTR_LASTDESCRIPTION = "last_description"; 61 private static final String ATTR_LASTTIMEMOVED = "last_time_moved"; 62 private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity"; 63 private static final String LAST_ACTIVITY_ICON_SUFFIX = "_last_activity_icon_"; 64 65 private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail"; 66 67 final int taskId; // Unique identifier for this task. 68 String affinity; // The affinity name for this task, or null. 69 final IVoiceInteractionSession voiceSession; // Voice interaction session driving task 70 final IVoiceInteractor voiceInteractor; // Associated interactor to provide to app 71 Intent intent; // The original intent that started the task. 72 Intent affinityIntent; // Intent of affinity-moved activity that started this task. 73 ComponentName origActivity; // The non-alias activity component of the intent. 74 ComponentName realActivity; // The actual activity component that started the task. 75 int numActivities; // Current number of activities in this task. 76 long lastActiveTime; // Last time this task was active, including sleep. 77 boolean rootWasReset; // True if the intent at the root of the task had 78 // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag. 79 boolean askedCompatMode;// Have asked the user about compat mode for this task. 80 boolean hasBeenVisible; // Set if any activities in the task have been visible to the user. 81 82 String stringName; // caching of toString() result. 83 int userId; // user for which this task was created 84 int creatorUid; // The app uid that originally created the task 85 86 int numFullscreen; // Number of fullscreen activities. 87 88 // This represents the last resolved activity values for this task 89 // NOTE: This value needs to be persisted with each task 90 ActivityManager.TaskDescription lastTaskDescription = 91 new ActivityManager.TaskDescription(); 92 93 /** List of all activities in the task arranged in history order */ 94 final ArrayList<ActivityRecord> mActivities; 95 96 /** Current stack */ 97 ActivityStack stack; 98 99 /** Takes on same set of values as ActivityRecord.mActivityType */ 100 int taskType; 101 102 /** Takes on same value as first root activity */ 103 boolean isPersistable = false; 104 int maxRecents; 105 106 /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for 107 * determining the order when restoring. Sign indicates whether last task movement was to front 108 * (positive) or back (negative). Absolute value indicates time. */ 109 long mLastTimeMoved = System.currentTimeMillis(); 110 111 /** True if persistable, has changed, and has not yet been persisted */ 112 boolean needsPersisting = false; 113 114 /** Indication of what to run next when task exits. Use ActivityRecord types. 115 * ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the 116 * task stack. */ 117 private int mTaskToReturnTo = APPLICATION_ACTIVITY_TYPE; 118 119 /** If original intent did not allow relinquishing task identity, save that information */ 120 boolean mNeverRelinquishIdentity = true; 121 122 // Used in the unique case where we are clearing the task in order to reuse it. In that case we 123 // do not want to delete the stack when the task goes empty. 124 boolean mReuseTask = false; 125 126 final ActivityManagerService mService; 127 128 TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent, 129 IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) { 130 mService = service; 131 taskId = _taskId; 132 voiceSession = _voiceSession; 133 voiceInteractor = _voiceInteractor; 134 setIntent(_intent, info); 135 mActivities = new ArrayList<ActivityRecord>(); 136 } 137 138 TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent, 139 String _affinity, ComponentName _realActivity, ComponentName _origActivity, 140 boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId, 141 String _lastDescription, ArrayList<ActivityRecord> activities, long _lastActiveTime, 142 long lastTimeMoved, boolean neverRelinquishIdentity, 143 ActivityManager.TaskDescription _lastTaskDescription) { 144 mService = service; 145 taskId = _taskId; 146 intent = _intent; 147 affinityIntent = _affinityIntent; 148 affinity = _affinity; 149 voiceSession = null; 150 voiceInteractor = null; 151 realActivity = _realActivity; 152 origActivity = _origActivity; 153 rootWasReset = _rootWasReset; 154 askedCompatMode = _askedCompatMode; 155 taskType = _taskType; 156 mTaskToReturnTo = HOME_ACTIVITY_TYPE; 157 userId = _userId; 158 lastActiveTime = _lastActiveTime; 159 lastDescription = _lastDescription; 160 mActivities = activities; 161 mLastTimeMoved = lastTimeMoved; 162 mNeverRelinquishIdentity = neverRelinquishIdentity; 163 lastTaskDescription = _lastTaskDescription; 164 } 165 166 void touchActiveTime() { 167 lastActiveTime = android.os.SystemClock.elapsedRealtime(); 168 } 169 170 long getInactiveDuration() { 171 return android.os.SystemClock.elapsedRealtime() - lastActiveTime; 172 } 173 174 void setIntent(Intent _intent, ActivityInfo info) { 175 if (intent == null) { 176 mNeverRelinquishIdentity = 177 (info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0; 178 } else if (mNeverRelinquishIdentity) { 179 return; 180 } 181 182 affinity = info.taskAffinity; 183 stringName = null; 184 185 if (info.targetActivity == null) { 186 if (_intent != null) { 187 // If this Intent has a selector, we want to clear it for the 188 // recent task since it is not relevant if the user later wants 189 // to re-launch the app. 190 if (_intent.getSelector() != null || _intent.getSourceBounds() != null) { 191 _intent = new Intent(_intent); 192 _intent.setSelector(null); 193 _intent.setSourceBounds(null); 194 } 195 } 196 if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG, 197 "Setting Intent of " + this + " to " + _intent); 198 intent = _intent; 199 realActivity = _intent != null ? _intent.getComponent() : null; 200 origActivity = null; 201 } else { 202 ComponentName targetComponent = new ComponentName( 203 info.packageName, info.targetActivity); 204 if (_intent != null) { 205 Intent targetIntent = new Intent(_intent); 206 targetIntent.setComponent(targetComponent); 207 targetIntent.setSelector(null); 208 targetIntent.setSourceBounds(null); 209 if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG, 210 "Setting Intent of " + this + " to target " + targetIntent); 211 intent = targetIntent; 212 realActivity = targetComponent; 213 origActivity = _intent.getComponent(); 214 } else { 215 intent = null; 216 realActivity = targetComponent; 217 origActivity = new ComponentName(info.packageName, info.name); 218 } 219 } 220 221 if (intent != null && 222 (intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { 223 // Once we are set to an Intent with this flag, we count this 224 // task as having a true root activity. 225 rootWasReset = true; 226 } 227 228 userId = UserHandle.getUserId(info.applicationInfo.uid); 229 creatorUid = info.applicationInfo.uid; 230 if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) { 231 intent.addFlags(Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS); 232 } 233 } 234 235 void setTaskToReturnTo(int taskToReturnTo) { 236 mTaskToReturnTo = taskToReturnTo; 237 } 238 239 int getTaskToReturnTo() { 240 return mTaskToReturnTo; 241 } 242 243 void disposeThumbnail() { 244 super.disposeThumbnail(); 245 for (int i=mActivities.size()-1; i>=0; i--) { 246 ThumbnailHolder thumb = mActivities.get(i).thumbHolder; 247 if (thumb != this) { 248 thumb.disposeThumbnail(); 249 } 250 } 251 } 252 253 /** Returns the intent for the root activity for this task */ 254 Intent getBaseIntent() { 255 return intent != null ? intent : affinityIntent; 256 } 257 258 /** Returns the first non-finishing activity from the root. */ 259 ActivityRecord getRootActivity() { 260 for (int i = 0; i < mActivities.size(); i++) { 261 final ActivityRecord r = mActivities.get(i); 262 if (r.finishing) { 263 continue; 264 } 265 return r; 266 } 267 return null; 268 } 269 270 ActivityRecord getTopActivity() { 271 for (int i = mActivities.size() - 1; i >= 0; --i) { 272 final ActivityRecord r = mActivities.get(i); 273 if (r.finishing) { 274 continue; 275 } 276 return r; 277 } 278 return null; 279 } 280 281 ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { 282 for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { 283 ActivityRecord r = mActivities.get(activityNdx); 284 if (!r.finishing && r != notTop && stack.okToShowLocked(r)) { 285 return r; 286 } 287 } 288 return null; 289 } 290 291 /** Call after activity movement or finish to make sure that frontOfTask is set correctly */ 292 final void setFrontOfTask() { 293 boolean foundFront = false; 294 final int numActivities = mActivities.size(); 295 for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { 296 final ActivityRecord r = mActivities.get(activityNdx); 297 if (foundFront || r.finishing) { 298 r.frontOfTask = false; 299 } else { 300 r.frontOfTask = true; 301 // Set frontOfTask false for every following activity. 302 foundFront = true; 303 } 304 } 305 if (!foundFront && numActivities > 0) { 306 // All activities of this task are finishing. As we ought to have a frontOfTask 307 // activity, make the bottom activity front. 308 mActivities.get(0).frontOfTask = true; 309 } 310 } 311 312 /** 313 * Reorder the history stack so that the passed activity is brought to the front. 314 */ 315 final void moveActivityToFrontLocked(ActivityRecord newTop) { 316 if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop 317 + " to stack at top", new RuntimeException("here").fillInStackTrace()); 318 319 mActivities.remove(newTop); 320 mActivities.add(newTop); 321 updateEffectiveIntent(); 322 323 setFrontOfTask(); 324 } 325 326 void addActivityAtBottom(ActivityRecord r) { 327 addActivityAtIndex(0, r); 328 } 329 330 void addActivityToTop(ActivityRecord r) { 331 addActivityAtIndex(mActivities.size(), r); 332 } 333 334 void addActivityAtIndex(int index, ActivityRecord r) { 335 // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. 336 if (!mActivities.remove(r) && r.fullscreen) { 337 // Was not previously in list. 338 numFullscreen++; 339 } 340 // Only set this based on the first activity 341 if (mActivities.isEmpty()) { 342 taskType = r.mActivityType; 343 isPersistable = r.isPersistable(); 344 // Clamp to [1, 100]. 345 maxRecents = Math.min(Math.max(r.info.maxRecents, 1), 100); 346 } else { 347 // Otherwise make all added activities match this one. 348 r.mActivityType = taskType; 349 } 350 mActivities.add(index, r); 351 updateEffectiveIntent(); 352 if (r.isPersistable()) { 353 mService.notifyTaskPersisterLocked(this, false); 354 } 355 } 356 357 /** @return true if this was the last activity in the task */ 358 boolean removeActivity(ActivityRecord r) { 359 if (mActivities.remove(r) && r.fullscreen) { 360 // Was previously in list. 361 numFullscreen--; 362 } 363 if (r.isPersistable()) { 364 mService.notifyTaskPersisterLocked(this, false); 365 } 366 if (mActivities.isEmpty()) { 367 return !mReuseTask; 368 } 369 updateEffectiveIntent(); 370 return false; 371 } 372 373 boolean autoRemoveFromRecents() { 374 // We will automatically remove the task either if it has explicitly asked for 375 // this, or it is empty and has never contained an activity that got shown to 376 // the user. 377 return (intent != null && 378 (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0) || 379 (mActivities.isEmpty() && !hasBeenVisible); 380 } 381 382 /** 383 * Completely remove all activities associated with an existing 384 * task starting at a specified index. 385 */ 386 final void performClearTaskAtIndexLocked(int activityNdx) { 387 int numActivities = mActivities.size(); 388 for ( ; activityNdx < numActivities; ++activityNdx) { 389 final ActivityRecord r = mActivities.get(activityNdx); 390 if (r.finishing) { 391 continue; 392 } 393 if (stack == null) { 394 // Task was restored from persistent storage. 395 r.takeFromHistory(); 396 mActivities.remove(activityNdx); 397 --activityNdx; 398 --numActivities; 399 } else if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", 400 false)) { 401 --activityNdx; 402 --numActivities; 403 } 404 } 405 } 406 407 /** 408 * Completely remove all activities associated with an existing task. 409 */ 410 final void performClearTaskLocked() { 411 mReuseTask = true; 412 performClearTaskAtIndexLocked(0); 413 mReuseTask = false; 414 } 415 416 /** 417 * Perform clear operation as requested by 418 * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the 419 * stack to the given task, then look for 420 * an instance of that activity in the stack and, if found, finish all 421 * activities on top of it and return the instance. 422 * 423 * @param newR Description of the new activity being started. 424 * @return Returns the old activity that should be continued to be used, 425 * or null if none was found. 426 */ 427 final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) { 428 int numActivities = mActivities.size(); 429 for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) { 430 ActivityRecord r = mActivities.get(activityNdx); 431 if (r.finishing) { 432 continue; 433 } 434 if (r.realActivity.equals(newR.realActivity)) { 435 // Here it is! Now finish everything in front... 436 final ActivityRecord ret = r; 437 438 for (++activityNdx; activityNdx < numActivities; ++activityNdx) { 439 r = mActivities.get(activityNdx); 440 if (r.finishing) { 441 continue; 442 } 443 ActivityOptions opts = r.takeOptionsLocked(); 444 if (opts != null) { 445 ret.updateOptionsLocked(opts); 446 } 447 if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", 448 false)) { 449 --activityNdx; 450 --numActivities; 451 } 452 } 453 454 // Finally, if this is a normal launch mode (that is, not 455 // expecting onNewIntent()), then we will finish the current 456 // instance of the activity so a new fresh one can be started. 457 if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE 458 && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { 459 if (!ret.finishing) { 460 stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null, 461 "clear", false); 462 return null; 463 } 464 } 465 466 return ret; 467 } 468 } 469 470 return null; 471 } 472 473 public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() { 474 TaskAccessInfo info = getTaskAccessInfoLocked(); 475 final ActivityRecord resumedActivity = stack.mResumedActivity; 476 if (resumedActivity != null && resumedActivity.thumbHolder == this) { 477 info.mainThumbnail = stack.screenshotActivities(resumedActivity); 478 } 479 if (info.mainThumbnail == null) { 480 info.mainThumbnail = lastThumbnail; 481 } 482 return info; 483 } 484 485 public Bitmap getTaskTopThumbnailLocked() { 486 if (stack != null) { 487 final ActivityRecord resumedActivity = stack.mResumedActivity; 488 if (resumedActivity != null && resumedActivity.task == this) { 489 // This task is the current resumed task, we just need to take 490 // a screenshot of it and return that. 491 return stack.screenshotActivities(resumedActivity); 492 } 493 } 494 // Return the information about the task, to figure out the top 495 // thumbnail to return. 496 TaskAccessInfo info = getTaskAccessInfoLocked(); 497 if (info.numSubThumbbails <= 0) { 498 return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail; 499 } 500 return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail; 501 } 502 503 public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex, 504 boolean taskRequired) { 505 TaskAccessInfo info = getTaskAccessInfoLocked(); 506 if (info.root == null) { 507 if (taskRequired) { 508 Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId); 509 } 510 return null; 511 } 512 513 if (subTaskIndex < 0) { 514 // Just remove the entire task. 515 performClearTaskAtIndexLocked(info.rootIndex); 516 return info.root; 517 } 518 519 if (subTaskIndex >= info.subtasks.size()) { 520 if (taskRequired) { 521 Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex); 522 } 523 return null; 524 } 525 526 // Remove all of this task's activities starting at the sub task. 527 TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex); 528 performClearTaskAtIndexLocked(subtask.index); 529 return subtask.activity; 530 } 531 532 boolean isHomeTask() { 533 return taskType == HOME_ACTIVITY_TYPE; 534 } 535 536 boolean isApplicationTask() { 537 return taskType == APPLICATION_ACTIVITY_TYPE; 538 } 539 540 boolean isOverHomeStack() { 541 return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE; 542 } 543 544 public TaskAccessInfo getTaskAccessInfoLocked() { 545 final TaskAccessInfo thumbs = new TaskAccessInfo(); 546 // How many different sub-thumbnails? 547 final int NA = mActivities.size(); 548 int j = 0; 549 ThumbnailHolder holder = null; 550 while (j < NA) { 551 ActivityRecord ar = mActivities.get(j); 552 if (!ar.finishing) { 553 thumbs.root = ar; 554 thumbs.rootIndex = j; 555 holder = ar.thumbHolder; 556 if (holder != null) { 557 thumbs.mainThumbnail = holder.lastThumbnail; 558 } 559 j++; 560 break; 561 } 562 j++; 563 } 564 565 if (j >= NA) { 566 return thumbs; 567 } 568 569 ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>(); 570 thumbs.subtasks = subtasks; 571 while (j < NA) { 572 ActivityRecord ar = mActivities.get(j); 573 j++; 574 if (ar.finishing) { 575 continue; 576 } 577 if (ar.thumbHolder != holder && holder != null) { 578 thumbs.numSubThumbbails++; 579 holder = ar.thumbHolder; 580 TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask(); 581 sub.holder = holder; 582 sub.activity = ar; 583 sub.index = j-1; 584 subtasks.add(sub); 585 } 586 } 587 if (thumbs.numSubThumbbails > 0) { 588 thumbs.retriever = new IThumbnailRetriever.Stub() { 589 @Override 590 public Bitmap getThumbnail(int index) { 591 if (index < 0 || index >= thumbs.subtasks.size()) { 592 return null; 593 } 594 TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index); 595 ActivityRecord resumedActivity = stack.mResumedActivity; 596 if (resumedActivity != null && resumedActivity.thumbHolder == sub.holder) { 597 return stack.screenshotActivities(resumedActivity); 598 } 599 return sub.holder.lastThumbnail; 600 } 601 }; 602 } 603 return thumbs; 604 } 605 606 /** 607 * Find the activity in the history stack within the given task. Returns 608 * the index within the history at which it's found, or < 0 if not found. 609 */ 610 final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) { 611 final ComponentName realActivity = r.realActivity; 612 for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { 613 ActivityRecord candidate = mActivities.get(activityNdx); 614 if (candidate.finishing) { 615 continue; 616 } 617 if (candidate.realActivity.equals(realActivity)) { 618 return candidate; 619 } 620 } 621 return null; 622 } 623 624 /** Updates the last task description values. */ 625 void updateTaskDescription() { 626 // Traverse upwards looking for any break between main task activities and 627 // utility activities. 628 int activityNdx; 629 final int numActivities = mActivities.size(); 630 final boolean relinquish = numActivities == 0 ? false : 631 (mActivities.get(0).info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) != 0; 632 for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; 633 ++activityNdx) { 634 final ActivityRecord r = mActivities.get(activityNdx); 635 if (relinquish && (r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) { 636 // This will be the top activity for determining taskDescription. Pre-inc to 637 // overcome initial decrement below. 638 ++activityNdx; 639 break; 640 } 641 if (r.intent != null && 642 (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { 643 break; 644 } 645 } 646 if (activityNdx > 0) { 647 // Traverse downwards starting below break looking for set label, icon. 648 // Note that if there are activities in the task but none of them set the 649 // recent activity values, then we do not fall back to the last set 650 // values in the TaskRecord. 651 String label = null; 652 Bitmap icon = null; 653 int colorPrimary = 0; 654 for (--activityNdx; activityNdx >= 0; --activityNdx) { 655 final ActivityRecord r = mActivities.get(activityNdx); 656 if (r.taskDescription != null) { 657 if (label == null) { 658 label = r.taskDescription.getLabel(); 659 } 660 if (icon == null) { 661 icon = r.taskDescription.getIcon(); 662 } 663 if (colorPrimary == 0) { 664 colorPrimary = r.taskDescription.getPrimaryColor(); 665 666 } 667 } 668 } 669 lastTaskDescription = new ActivityManager.TaskDescription(label, icon, colorPrimary); 670 } 671 } 672 673 int findEffectiveRootIndex() { 674 int activityNdx; 675 final int topActivityNdx = mActivities.size() - 1; 676 for (activityNdx = 0; activityNdx < topActivityNdx; ++activityNdx) { 677 final ActivityRecord r = mActivities.get(activityNdx); 678 if (r.finishing) { 679 continue; 680 } 681 if ((r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) { 682 break; 683 } 684 } 685 return activityNdx; 686 } 687 688 void updateEffectiveIntent() { 689 final int effectiveRootIndex = findEffectiveRootIndex(); 690 final ActivityRecord r = mActivities.get(effectiveRootIndex); 691 setIntent(r.intent, r.info); 692 } 693 694 void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { 695 Slog.i(TAG, "Saving task=" + this); 696 697 out.attribute(null, ATTR_TASKID, String.valueOf(taskId)); 698 if (realActivity != null) { 699 out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString()); 700 } 701 if (origActivity != null) { 702 out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString()); 703 } 704 if (affinity != null) { 705 out.attribute(null, ATTR_AFFINITY, affinity); 706 } 707 out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset)); 708 out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode)); 709 out.attribute(null, ATTR_USERID, String.valueOf(userId)); 710 out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType)); 711 out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime)); 712 out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved)); 713 out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity)); 714 if (lastDescription != null) { 715 out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString()); 716 } 717 718 if (lastTaskDescription != null) { 719 TaskPersister.saveTaskDescription(lastTaskDescription, String.valueOf(taskId) + 720 LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime, out); 721 } 722 723 if (affinityIntent != null) { 724 out.startTag(null, TAG_AFFINITYINTENT); 725 affinityIntent.saveToXml(out); 726 out.endTag(null, TAG_AFFINITYINTENT); 727 } 728 729 out.startTag(null, TAG_INTENT); 730 intent.saveToXml(out); 731 out.endTag(null, TAG_INTENT); 732 733 final ArrayList<ActivityRecord> activities = mActivities; 734 final int numActivities = activities.size(); 735 for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { 736 final ActivityRecord r = activities.get(activityNdx); 737 if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() || 738 ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) && 739 activityNdx > 0) { 740 // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET). 741 break; 742 } 743 out.startTag(null, TAG_ACTIVITY); 744 r.saveToXml(out); 745 out.endTag(null, TAG_ACTIVITY); 746 } 747 748 final Bitmap thumbnail = getTaskTopThumbnailLocked(); 749 if (thumbnail != null) { 750 TaskPersister.saveImage(thumbnail, String.valueOf(taskId) + TASK_THUMBNAIL_SUFFIX); 751 } 752 } 753 754 static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor) 755 throws IOException, XmlPullParserException { 756 Intent intent = null; 757 Intent affinityIntent = null; 758 ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(); 759 ComponentName realActivity = null; 760 ComponentName origActivity = null; 761 String affinity = null; 762 boolean rootHasReset = false; 763 boolean askedCompatMode = false; 764 int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE; 765 int userId = 0; 766 String lastDescription = null; 767 long lastActiveTime = -1; 768 long lastTimeOnTop = 0; 769 boolean neverRelinquishIdentity = true; 770 int taskId = -1; 771 final int outerDepth = in.getDepth(); 772 ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription(); 773 774 for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) { 775 final String attrName = in.getAttributeName(attrNdx); 776 final String attrValue = in.getAttributeValue(attrNdx); 777 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" + 778 attrName + " value=" + attrValue); 779 if (ATTR_TASKID.equals(attrName)) { 780 taskId = Integer.valueOf(attrValue); 781 } else if (ATTR_REALACTIVITY.equals(attrName)) { 782 realActivity = ComponentName.unflattenFromString(attrValue); 783 } else if (ATTR_ORIGACTIVITY.equals(attrName)) { 784 origActivity = ComponentName.unflattenFromString(attrValue); 785 } else if (ATTR_AFFINITY.equals(attrName)) { 786 affinity = attrValue; 787 } else if (ATTR_ROOTHASRESET.equals(attrName)) { 788 rootHasReset = Boolean.valueOf(attrValue); 789 } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) { 790 askedCompatMode = Boolean.valueOf(attrValue); 791 } else if (ATTR_USERID.equals(attrName)) { 792 userId = Integer.valueOf(attrValue); 793 } else if (ATTR_TASKTYPE.equals(attrName)) { 794 taskType = Integer.valueOf(attrValue); 795 } else if (ATTR_LASTACTIVETIME.equals(attrName)) { 796 lastActiveTime = Long.valueOf(attrValue); 797 } else if (ATTR_LASTDESCRIPTION.equals(attrName)) { 798 lastDescription = attrValue; 799 } else if (ATTR_LASTTIMEMOVED.equals(attrName)) { 800 lastTimeOnTop = Long.valueOf(attrValue); 801 } else if (ATTR_NEVERRELINQUISH.equals(attrName)) { 802 neverRelinquishIdentity = Boolean.valueOf(attrValue); 803 } else if (TaskPersister.readTaskDescriptionAttribute(taskDescription, attrName, 804 attrValue)) { 805 // Completed in TaskPersister.readTaskDescriptionAttribute() 806 } else { 807 Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName); 808 } 809 } 810 811 int event; 812 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && 813 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { 814 if (event == XmlPullParser.START_TAG) { 815 final String name = in.getName(); 816 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" + 817 name); 818 if (TAG_AFFINITYINTENT.equals(name)) { 819 affinityIntent = Intent.restoreFromXml(in); 820 } else if (TAG_INTENT.equals(name)) { 821 intent = Intent.restoreFromXml(in); 822 } else if (TAG_ACTIVITY.equals(name)) { 823 ActivityRecord activity = 824 ActivityRecord.restoreFromXml(in, taskId, stackSupervisor); 825 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" + 826 activity); 827 if (activity != null) { 828 activities.add(activity); 829 } 830 } else { 831 Slog.e(TAG, "restoreTask: Unexpected name=" + name); 832 XmlUtils.skipCurrentTag(in); 833 } 834 } 835 } 836 837 if (lastActiveTime >= 0) { 838 taskDescription.setIcon(TaskPersister.restoreImage(String.valueOf(taskId) + 839 LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime)); 840 } 841 842 final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent, 843 affinityIntent, affinity, realActivity, origActivity, rootHasReset, 844 askedCompatMode, taskType, userId, lastDescription, activities, lastActiveTime, 845 lastTimeOnTop, neverRelinquishIdentity, taskDescription); 846 847 for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { 848 final ActivityRecord r = activities.get(activityNdx); 849 r.thumbHolder = r.task = task; 850 } 851 852 task.lastThumbnail = TaskPersister.restoreImage(taskId + TASK_THUMBNAIL_SUFFIX); 853 854 Slog.i(TAG, "Restored task=" + task); 855 return task; 856 } 857 858 void dump(PrintWriter pw, String prefix) { 859 if (rootWasReset || userId != 0 || numFullscreen != 0) { 860 pw.print(prefix); pw.print(" rootWasReset="); pw.print(rootWasReset); 861 pw.print(" userId="); pw.print(userId); 862 pw.print(" taskType="); pw.print(taskType); 863 pw.print(" numFullscreen="); pw.print(numFullscreen); 864 pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo); 865 } 866 if (affinity != null) { 867 pw.print(prefix); pw.print("affinity="); pw.println(affinity); 868 } 869 if (voiceSession != null || voiceInteractor != null) { 870 pw.print(prefix); pw.print("VOICE: session=0x"); 871 pw.print(Integer.toHexString(System.identityHashCode(voiceSession))); 872 pw.print(" interactor=0x"); 873 pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor))); 874 } 875 if (intent != null) { 876 StringBuilder sb = new StringBuilder(128); 877 sb.append(prefix); sb.append("intent={"); 878 intent.toShortString(sb, false, true, false, true); 879 sb.append('}'); 880 pw.println(sb.toString()); 881 } 882 if (affinityIntent != null) { 883 StringBuilder sb = new StringBuilder(128); 884 sb.append(prefix); sb.append("affinityIntent={"); 885 affinityIntent.toShortString(sb, false, true, false, true); 886 sb.append('}'); 887 pw.println(sb.toString()); 888 } 889 if (origActivity != null) { 890 pw.print(prefix); pw.print("origActivity="); 891 pw.println(origActivity.flattenToShortString()); 892 } 893 if (realActivity != null) { 894 pw.print(prefix); pw.print("realActivity="); 895 pw.println(realActivity.flattenToShortString()); 896 } 897 pw.print(prefix); pw.print("Activities="); pw.println(mActivities); 898 if (!askedCompatMode) { 899 pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); 900 } 901 pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail); 902 pw.print(" lastDescription="); pw.println(lastDescription); 903 pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible); 904 pw.print(" lastActiveTime="); pw.print(lastActiveTime); 905 pw.print(" (inactive for "); 906 pw.print((getInactiveDuration()/1000)); pw.println("s)"); 907 } 908 909 @Override 910 public String toString() { 911 StringBuilder sb = new StringBuilder(128); 912 if (stringName != null) { 913 sb.append(stringName); 914 sb.append(" U="); 915 sb.append(userId); 916 sb.append(" sz="); 917 sb.append(mActivities.size()); 918 sb.append('}'); 919 return sb.toString(); 920 } 921 sb.append("TaskRecord{"); 922 sb.append(Integer.toHexString(System.identityHashCode(this))); 923 sb.append(" #"); 924 sb.append(taskId); 925 if (affinity != null) { 926 sb.append(" A="); 927 sb.append(affinity); 928 } else if (intent != null) { 929 sb.append(" I="); 930 sb.append(intent.getComponent().flattenToShortString()); 931 } else if (affinityIntent != null) { 932 sb.append(" aI="); 933 sb.append(affinityIntent.getComponent().flattenToShortString()); 934 } else { 935 sb.append(" ??"); 936 } 937 stringName = sb.toString(); 938 return toString(); 939 } 940} 941