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