TaskRecord.java revision 096f36b8db3e513687b78da3e1dd0197fce148a3
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 /** 390 * Sets the last thumbnail. 391 * @return whether the thumbnail was set 392 */ 393 boolean setLastThumbnail(Bitmap thumbnail) { 394 if (mLastThumbnail != thumbnail) { 395 mLastThumbnail = thumbnail; 396 if (thumbnail == null) { 397 if (mLastThumbnailFile != null) { 398 mLastThumbnailFile.delete(); 399 } 400 } else { 401 mService.mTaskPersister.saveImage(thumbnail, mFilename); 402 } 403 return true; 404 } 405 return false; 406 } 407 408 void getLastThumbnail(TaskThumbnail thumbs) { 409 thumbs.mainThumbnail = mLastThumbnail; 410 thumbs.thumbnailFileDescriptor = null; 411 if (mLastThumbnail == null) { 412 thumbs.mainThumbnail = mService.mTaskPersister.getThumbnail(mFilename); 413 } 414 // Only load the thumbnail file if we don't have a thumbnail 415 if (thumbs.mainThumbnail == null && mLastThumbnailFile.exists()) { 416 try { 417 thumbs.thumbnailFileDescriptor = ParcelFileDescriptor.open(mLastThumbnailFile, 418 ParcelFileDescriptor.MODE_READ_ONLY); 419 } catch (IOException e) { 420 } 421 } 422 } 423 424 void freeLastThumbnail() { 425 mLastThumbnail = null; 426 } 427 428 void disposeThumbnail() { 429 mLastThumbnail = null; 430 lastDescription = null; 431 } 432 433 /** Returns the intent for the root activity for this task */ 434 Intent getBaseIntent() { 435 return intent != null ? intent : affinityIntent; 436 } 437 438 /** Returns the first non-finishing activity from the root. */ 439 ActivityRecord getRootActivity() { 440 for (int i = 0; i < mActivities.size(); i++) { 441 final ActivityRecord r = mActivities.get(i); 442 if (r.finishing) { 443 continue; 444 } 445 return r; 446 } 447 return null; 448 } 449 450 ActivityRecord getTopActivity() { 451 for (int i = mActivities.size() - 1; i >= 0; --i) { 452 final ActivityRecord r = mActivities.get(i); 453 if (r.finishing) { 454 continue; 455 } 456 return r; 457 } 458 return null; 459 } 460 461 ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { 462 for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { 463 ActivityRecord r = mActivities.get(activityNdx); 464 if (!r.finishing && r != notTop && stack.okToShowLocked(r)) { 465 return r; 466 } 467 } 468 return null; 469 } 470 471 /** Call after activity movement or finish to make sure that frontOfTask is set correctly */ 472 final void setFrontOfTask() { 473 boolean foundFront = false; 474 final int numActivities = mActivities.size(); 475 for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { 476 final ActivityRecord r = mActivities.get(activityNdx); 477 if (foundFront || r.finishing) { 478 r.frontOfTask = false; 479 } else { 480 r.frontOfTask = true; 481 // Set frontOfTask false for every following activity. 482 foundFront = true; 483 } 484 } 485 if (!foundFront && numActivities > 0) { 486 // All activities of this task are finishing. As we ought to have a frontOfTask 487 // activity, make the bottom activity front. 488 mActivities.get(0).frontOfTask = true; 489 } 490 } 491 492 /** 493 * Reorder the history stack so that the passed activity is brought to the front. 494 */ 495 final void moveActivityToFrontLocked(ActivityRecord newTop) { 496 if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop 497 + " to stack at top", new RuntimeException("here").fillInStackTrace()); 498 499 mActivities.remove(newTop); 500 mActivities.add(newTop); 501 updateEffectiveIntent(); 502 503 setFrontOfTask(); 504 } 505 506 void addActivityAtBottom(ActivityRecord r) { 507 addActivityAtIndex(0, r); 508 } 509 510 void addActivityToTop(ActivityRecord r) { 511 addActivityAtIndex(mActivities.size(), r); 512 } 513 514 void addActivityAtIndex(int index, ActivityRecord r) { 515 // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. 516 if (!mActivities.remove(r) && r.fullscreen) { 517 // Was not previously in list. 518 numFullscreen++; 519 } 520 // Only set this based on the first activity 521 if (mActivities.isEmpty()) { 522 taskType = r.mActivityType; 523 isPersistable = r.isPersistable(); 524 mCallingUid = r.launchedFromUid; 525 mCallingPackage = r.launchedFromPackage; 526 // Clamp to [1, 100]. 527 maxRecents = Math.min(Math.max(r.info.maxRecents, 1), 100); 528 } else { 529 // Otherwise make all added activities match this one. 530 r.mActivityType = taskType; 531 } 532 mActivities.add(index, r); 533 updateEffectiveIntent(); 534 if (r.isPersistable()) { 535 mService.notifyTaskPersisterLocked(this, false); 536 } 537 } 538 539 /** @return true if this was the last activity in the task */ 540 boolean removeActivity(ActivityRecord r) { 541 if (mActivities.remove(r) && r.fullscreen) { 542 // Was previously in list. 543 numFullscreen--; 544 } 545 if (r.isPersistable()) { 546 mService.notifyTaskPersisterLocked(this, false); 547 } 548 if (mActivities.isEmpty()) { 549 return !mReuseTask; 550 } 551 updateEffectiveIntent(); 552 return false; 553 } 554 555 boolean autoRemoveFromRecents() { 556 // We will automatically remove the task either if it has explicitly asked for 557 // this, or it is empty and has never contained an activity that got shown to 558 // the user. 559 return autoRemoveRecents || (mActivities.isEmpty() && !hasBeenVisible); 560 } 561 562 /** 563 * Completely remove all activities associated with an existing 564 * task starting at a specified index. 565 */ 566 final void performClearTaskAtIndexLocked(int activityNdx) { 567 int numActivities = mActivities.size(); 568 for ( ; activityNdx < numActivities; ++activityNdx) { 569 final ActivityRecord r = mActivities.get(activityNdx); 570 if (r.finishing) { 571 continue; 572 } 573 if (stack == null) { 574 // Task was restored from persistent storage. 575 r.takeFromHistory(); 576 mActivities.remove(activityNdx); 577 --activityNdx; 578 --numActivities; 579 } else if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", 580 false)) { 581 --activityNdx; 582 --numActivities; 583 } 584 } 585 } 586 587 /** 588 * Completely remove all activities associated with an existing task. 589 */ 590 final void performClearTaskLocked() { 591 mReuseTask = true; 592 performClearTaskAtIndexLocked(0); 593 mReuseTask = false; 594 } 595 596 /** 597 * Perform clear operation as requested by 598 * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the 599 * stack to the given task, then look for 600 * an instance of that activity in the stack and, if found, finish all 601 * activities on top of it and return the instance. 602 * 603 * @param newR Description of the new activity being started. 604 * @return Returns the old activity that should be continued to be used, 605 * or null if none was found. 606 */ 607 final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) { 608 int numActivities = mActivities.size(); 609 for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) { 610 ActivityRecord r = mActivities.get(activityNdx); 611 if (r.finishing) { 612 continue; 613 } 614 if (r.realActivity.equals(newR.realActivity)) { 615 // Here it is! Now finish everything in front... 616 final ActivityRecord ret = r; 617 618 for (++activityNdx; activityNdx < numActivities; ++activityNdx) { 619 r = mActivities.get(activityNdx); 620 if (r.finishing) { 621 continue; 622 } 623 ActivityOptions opts = r.takeOptionsLocked(); 624 if (opts != null) { 625 ret.updateOptionsLocked(opts); 626 } 627 if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", 628 false)) { 629 --activityNdx; 630 --numActivities; 631 } 632 } 633 634 // Finally, if this is a normal launch mode (that is, not 635 // expecting onNewIntent()), then we will finish the current 636 // instance of the activity so a new fresh one can be started. 637 if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE 638 && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { 639 if (!ret.finishing) { 640 stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null, 641 "clear", false); 642 return null; 643 } 644 } 645 646 return ret; 647 } 648 } 649 650 return null; 651 } 652 653 public TaskThumbnail getTaskThumbnailLocked() { 654 if (stack != null) { 655 final ActivityRecord resumedActivity = stack.mResumedActivity; 656 if (resumedActivity != null && resumedActivity.task == this) { 657 final Bitmap thumbnail = stack.screenshotActivities(resumedActivity); 658 setLastThumbnail(thumbnail); 659 } 660 } 661 final TaskThumbnail taskThumbnail = new TaskThumbnail(); 662 getLastThumbnail(taskThumbnail); 663 return taskThumbnail; 664 } 665 666 public void removeTaskActivitiesLocked() { 667 // Just remove the entire task. 668 performClearTaskAtIndexLocked(0); 669 } 670 671 boolean isHomeTask() { 672 return taskType == HOME_ACTIVITY_TYPE; 673 } 674 675 boolean isApplicationTask() { 676 return taskType == APPLICATION_ACTIVITY_TYPE; 677 } 678 679 boolean isOverHomeStack() { 680 return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE; 681 } 682 683 /** 684 * Find the activity in the history stack within the given task. Returns 685 * the index within the history at which it's found, or < 0 if not found. 686 */ 687 final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) { 688 final ComponentName realActivity = r.realActivity; 689 for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { 690 ActivityRecord candidate = mActivities.get(activityNdx); 691 if (candidate.finishing) { 692 continue; 693 } 694 if (candidate.realActivity.equals(realActivity)) { 695 return candidate; 696 } 697 } 698 return null; 699 } 700 701 /** Updates the last task description values. */ 702 void updateTaskDescription() { 703 // Traverse upwards looking for any break between main task activities and 704 // utility activities. 705 int activityNdx; 706 final int numActivities = mActivities.size(); 707 final boolean relinquish = numActivities == 0 ? false : 708 (mActivities.get(0).info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) != 0; 709 for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; 710 ++activityNdx) { 711 final ActivityRecord r = mActivities.get(activityNdx); 712 if (relinquish && (r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) { 713 // This will be the top activity for determining taskDescription. Pre-inc to 714 // overcome initial decrement below. 715 ++activityNdx; 716 break; 717 } 718 if (r.intent != null && 719 (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { 720 break; 721 } 722 } 723 if (activityNdx > 0) { 724 // Traverse downwards starting below break looking for set label, icon. 725 // Note that if there are activities in the task but none of them set the 726 // recent activity values, then we do not fall back to the last set 727 // values in the TaskRecord. 728 String label = null; 729 Bitmap icon = null; 730 int colorPrimary = 0; 731 for (--activityNdx; activityNdx >= 0; --activityNdx) { 732 final ActivityRecord r = mActivities.get(activityNdx); 733 if (r.taskDescription != null) { 734 if (label == null) { 735 label = r.taskDescription.getLabel(); 736 } 737 if (icon == null) { 738 icon = r.taskDescription.getIcon(); 739 } 740 if (colorPrimary == 0) { 741 colorPrimary = r.taskDescription.getPrimaryColor(); 742 } 743 } 744 } 745 lastTaskDescription = new ActivityManager.TaskDescription(label, icon, colorPrimary); 746 // Update the task affiliation color if we are the parent of the group 747 if (taskId == mAffiliatedTaskId) { 748 mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor(); 749 } 750 } 751 } 752 753 int findEffectiveRootIndex() { 754 int activityNdx; 755 final int topActivityNdx = mActivities.size() - 1; 756 for (activityNdx = 0; activityNdx < topActivityNdx; ++activityNdx) { 757 final ActivityRecord r = mActivities.get(activityNdx); 758 if (r.finishing) { 759 continue; 760 } 761 if ((r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) { 762 break; 763 } 764 } 765 return activityNdx; 766 } 767 768 void updateEffectiveIntent() { 769 final int effectiveRootIndex = findEffectiveRootIndex(); 770 final ActivityRecord r = mActivities.get(effectiveRootIndex); 771 setIntent(r); 772 } 773 774 void saveTaskDescription(ActivityManager.TaskDescription taskDescription, 775 String iconFilename, XmlSerializer out) throws IOException { 776 if (taskDescription != null) { 777 final String label = taskDescription.getLabel(); 778 if (label != null) { 779 out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label); 780 } 781 final int colorPrimary = taskDescription.getPrimaryColor(); 782 if (colorPrimary != 0) { 783 out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary)); 784 } 785 final Bitmap icon = taskDescription.getIcon(); 786 if (icon != null) { 787 mService.mTaskPersister.saveImage(icon, iconFilename); 788 } 789 } 790 } 791 792 static boolean readTaskDescriptionAttribute(ActivityManager.TaskDescription taskDescription, 793 String attrName, String attrValue) { 794 if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) { 795 taskDescription.setLabel(attrValue); 796 } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) { 797 taskDescription.setPrimaryColor((int) Long.parseLong(attrValue, 16)); 798 } else { 799 return false; 800 } 801 return true; 802 } 803 804 private static String createLastTaskDescriptionIconFilename(int taskId, long lastActiveTime) { 805 return String.valueOf(taskId) + LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime + 806 TaskPersister.IMAGE_EXTENSION; 807 } 808 809 void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { 810 if (ActivityManagerService.DEBUG_RECENTS) Slog.i(TAG, "Saving task=" + this); 811 812 out.attribute(null, ATTR_TASKID, String.valueOf(taskId)); 813 if (realActivity != null) { 814 out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString()); 815 } 816 if (origActivity != null) { 817 out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString()); 818 } 819 if (affinity != null) { 820 out.attribute(null, ATTR_AFFINITY, affinity); 821 } 822 out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset)); 823 out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents)); 824 out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode)); 825 out.attribute(null, ATTR_USERID, String.valueOf(userId)); 826 out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType)); 827 out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime)); 828 out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime)); 829 out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved)); 830 out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity)); 831 if (lastDescription != null) { 832 out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString()); 833 } 834 if (lastTaskDescription != null) { 835 saveTaskDescription(lastTaskDescription, createLastTaskDescriptionIconFilename(taskId, 836 lastActiveTime), out); 837 } 838 out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor)); 839 out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId)); 840 out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId)); 841 out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId)); 842 out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid)); 843 out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage); 844 845 if (affinityIntent != null) { 846 out.startTag(null, TAG_AFFINITYINTENT); 847 affinityIntent.saveToXml(out); 848 out.endTag(null, TAG_AFFINITYINTENT); 849 } 850 851 out.startTag(null, TAG_INTENT); 852 intent.saveToXml(out); 853 out.endTag(null, TAG_INTENT); 854 855 final ArrayList<ActivityRecord> activities = mActivities; 856 final int numActivities = activities.size(); 857 for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { 858 final ActivityRecord r = activities.get(activityNdx); 859 if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() || 860 ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) && 861 activityNdx > 0) { 862 // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET). 863 break; 864 } 865 out.startTag(null, TAG_ACTIVITY); 866 r.saveToXml(out); 867 out.endTag(null, TAG_ACTIVITY); 868 } 869 } 870 871 static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor) 872 throws IOException, XmlPullParserException { 873 Intent intent = null; 874 Intent affinityIntent = null; 875 ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(); 876 ComponentName realActivity = null; 877 ComponentName origActivity = null; 878 String affinity = null; 879 boolean rootHasReset = false; 880 boolean autoRemoveRecents = false; 881 boolean askedCompatMode = false; 882 int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE; 883 int userId = 0; 884 String lastDescription = null; 885 long firstActiveTime = -1; 886 long lastActiveTime = -1; 887 long lastTimeOnTop = 0; 888 boolean neverRelinquishIdentity = true; 889 int taskId = -1; 890 final int outerDepth = in.getDepth(); 891 ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription(); 892 int taskAffiliation = -1; 893 int taskAffiliationColor = 0; 894 int prevTaskId = -1; 895 int nextTaskId = -1; 896 int callingUid = -1; 897 String callingPackage = ""; 898 899 for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) { 900 final String attrName = in.getAttributeName(attrNdx); 901 final String attrValue = in.getAttributeValue(attrNdx); 902 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" + 903 attrName + " value=" + attrValue); 904 if (ATTR_TASKID.equals(attrName)) { 905 taskId = Integer.valueOf(attrValue); 906 } else if (ATTR_REALACTIVITY.equals(attrName)) { 907 realActivity = ComponentName.unflattenFromString(attrValue); 908 } else if (ATTR_ORIGACTIVITY.equals(attrName)) { 909 origActivity = ComponentName.unflattenFromString(attrValue); 910 } else if (ATTR_AFFINITY.equals(attrName)) { 911 affinity = attrValue; 912 } else if (ATTR_ROOTHASRESET.equals(attrName)) { 913 rootHasReset = Boolean.valueOf(attrValue); 914 } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) { 915 autoRemoveRecents = Boolean.valueOf(attrValue); 916 } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) { 917 askedCompatMode = Boolean.valueOf(attrValue); 918 } else if (ATTR_USERID.equals(attrName)) { 919 userId = Integer.valueOf(attrValue); 920 } else if (ATTR_TASKTYPE.equals(attrName)) { 921 taskType = Integer.valueOf(attrValue); 922 } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) { 923 firstActiveTime = Long.valueOf(attrValue); 924 } else if (ATTR_LASTACTIVETIME.equals(attrName)) { 925 lastActiveTime = Long.valueOf(attrValue); 926 } else if (ATTR_LASTDESCRIPTION.equals(attrName)) { 927 lastDescription = attrValue; 928 } else if (ATTR_LASTTIMEMOVED.equals(attrName)) { 929 lastTimeOnTop = Long.valueOf(attrValue); 930 } else if (ATTR_NEVERRELINQUISH.equals(attrName)) { 931 neverRelinquishIdentity = Boolean.valueOf(attrValue); 932 } else if (readTaskDescriptionAttribute(taskDescription, attrName, attrValue)) { 933 // Completed in TaskPersister.readTaskDescriptionAttribute() 934 } else if (ATTR_TASK_AFFILIATION.equals(attrName)) { 935 taskAffiliation = Integer.valueOf(attrValue); 936 } else if (ATTR_PREV_AFFILIATION.equals(attrName)) { 937 prevTaskId = Integer.valueOf(attrValue); 938 } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) { 939 nextTaskId = Integer.valueOf(attrValue); 940 } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) { 941 taskAffiliationColor = Integer.valueOf(attrValue); 942 } else if (ATTR_CALLING_UID.equals(attrName)) { 943 callingUid = Integer.valueOf(attrValue); 944 } else if (ATTR_CALLING_PACKAGE.equals(attrName)) { 945 callingPackage = attrValue; 946 } else { 947 Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName); 948 } 949 } 950 951 int event; 952 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && 953 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { 954 if (event == XmlPullParser.START_TAG) { 955 final String name = in.getName(); 956 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" + 957 name); 958 if (TAG_AFFINITYINTENT.equals(name)) { 959 affinityIntent = Intent.restoreFromXml(in); 960 } else if (TAG_INTENT.equals(name)) { 961 intent = Intent.restoreFromXml(in); 962 } else if (TAG_ACTIVITY.equals(name)) { 963 ActivityRecord activity = 964 ActivityRecord.restoreFromXml(in, taskId, stackSupervisor); 965 if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" + 966 activity); 967 if (activity != null) { 968 activities.add(activity); 969 } 970 } else { 971 Slog.e(TAG, "restoreTask: Unexpected name=" + name); 972 XmlUtils.skipCurrentTag(in); 973 } 974 } 975 } 976 977 if (lastActiveTime >= 0) { 978 taskDescription.setIcon(TaskPersister.restoreImage( 979 createLastTaskDescriptionIconFilename(taskId, lastActiveTime))); 980 } 981 982 final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent, 983 affinityIntent, affinity, realActivity, origActivity, rootHasReset, 984 autoRemoveRecents, askedCompatMode, taskType, userId, lastDescription, activities, 985 firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity, 986 taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, 987 callingUid, callingPackage); 988 989 for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { 990 activities.get(activityNdx).task = task; 991 } 992 993 if (ActivityManagerService.DEBUG_RECENTS) Slog.d(TAG, "Restored task=" + task); 994 return task; 995 } 996 997 void dump(PrintWriter pw, String prefix) { 998 pw.print(prefix); pw.print("userId="); pw.print(userId); 999 pw.print(" creatorUid="); pw.print(creatorUid); 1000 pw.print(" mCallingUid="); pw.print(mCallingUid); 1001 pw.print(" mCallingPackage="); pw.println(mCallingPackage); 1002 if (affinity != null) { 1003 pw.print(prefix); pw.print("affinity="); pw.println(affinity); 1004 } 1005 if (voiceSession != null || voiceInteractor != null) { 1006 pw.print(prefix); pw.print("VOICE: session=0x"); 1007 pw.print(Integer.toHexString(System.identityHashCode(voiceSession))); 1008 pw.print(" interactor=0x"); 1009 pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor))); 1010 } 1011 if (intent != null) { 1012 StringBuilder sb = new StringBuilder(128); 1013 sb.append(prefix); sb.append("intent={"); 1014 intent.toShortString(sb, false, true, false, true); 1015 sb.append('}'); 1016 pw.println(sb.toString()); 1017 } 1018 if (affinityIntent != null) { 1019 StringBuilder sb = new StringBuilder(128); 1020 sb.append(prefix); sb.append("affinityIntent={"); 1021 affinityIntent.toShortString(sb, false, true, false, true); 1022 sb.append('}'); 1023 pw.println(sb.toString()); 1024 } 1025 if (origActivity != null) { 1026 pw.print(prefix); pw.print("origActivity="); 1027 pw.println(origActivity.flattenToShortString()); 1028 } 1029 if (realActivity != null) { 1030 pw.print(prefix); pw.print("realActivity="); 1031 pw.println(realActivity.flattenToShortString()); 1032 } 1033 if (autoRemoveRecents || taskType != 0 || mTaskToReturnTo != 0 || numFullscreen != 0) { 1034 pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents); 1035 pw.print(" numFullscreen="); pw.print(numFullscreen); 1036 pw.print(" taskType="); pw.print(taskType); 1037 pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo); 1038 } 1039 if (rootWasReset || mNeverRelinquishIdentity || mReuseTask) { 1040 pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset); 1041 pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity); 1042 pw.print(" mReuseTask="); pw.println(mReuseTask); 1043 } 1044 pw.print(prefix); pw.print("Activities="); pw.println(mActivities); 1045 if (!askedCompatMode) { 1046 pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); 1047 } 1048 pw.print(prefix); pw.print("lastThumbnail="); pw.print(mLastThumbnail); 1049 pw.print(" lastThumbnailFile="); pw.print(mLastThumbnailFile); 1050 pw.print(" lastDescription="); pw.println(lastDescription); 1051 pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible); 1052 pw.print(" firstActiveTime="); pw.print(lastActiveTime); 1053 pw.print(" lastActiveTime="); pw.print(lastActiveTime); 1054 pw.print(" (inactive for "); 1055 pw.print((getInactiveDuration()/1000)); pw.println("s)"); 1056 pw.print(prefix); pw.print("isPersistable="); pw.print(isPersistable); 1057 pw.print(" affiliation="); pw.print(mAffiliatedTaskId); 1058 pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId); 1059 pw.print(" nextAffiliation="); pw.println(mNextAffiliateTaskId); 1060 } 1061 1062 @Override 1063 public String toString() { 1064 StringBuilder sb = new StringBuilder(128); 1065 if (stringName != null) { 1066 sb.append(stringName); 1067 sb.append(" U="); 1068 sb.append(userId); 1069 sb.append(" sz="); 1070 sb.append(mActivities.size()); 1071 sb.append('}'); 1072 return sb.toString(); 1073 } 1074 sb.append("TaskRecord{"); 1075 sb.append(Integer.toHexString(System.identityHashCode(this))); 1076 sb.append(" #"); 1077 sb.append(taskId); 1078 if (affinity != null) { 1079 sb.append(" A="); 1080 sb.append(affinity); 1081 } else if (intent != null) { 1082 sb.append(" I="); 1083 sb.append(intent.getComponent().flattenToShortString()); 1084 } else if (affinityIntent != null) { 1085 sb.append(" aI="); 1086 sb.append(affinityIntent.getComponent().flattenToShortString()); 1087 } else { 1088 sb.append(" ??"); 1089 } 1090 stringName = sb.toString(); 1091 return toString(); 1092 } 1093} 1094