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