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