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