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