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