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