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