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