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