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