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