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