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