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