TaskRecord.java revision 41db4a77fa4659d60ad055ec1819a410ce35bf28
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.ActivityStackSupervisor.DEBUG_ADD_REMOVE; 21 22import android.app.Activity; 23import android.app.ActivityManager; 24import android.app.ActivityOptions; 25import android.app.IThumbnailRetriever; 26import android.content.ComponentName; 27import android.content.Intent; 28import android.content.pm.ActivityInfo; 29import android.graphics.Bitmap; 30import android.os.UserHandle; 31import android.service.voice.IVoiceInteractionSession; 32import android.util.Slog; 33import com.android.internal.app.IVoiceInteractor; 34 35import java.io.PrintWriter; 36import java.util.ArrayList; 37 38final class TaskRecord extends ThumbnailHolder { 39 final int taskId; // Unique identifier for this task. 40 final String affinity; // The affinity name for this task, or null. 41 final IVoiceInteractionSession voiceSession; // Voice interaction session driving task 42 final IVoiceInteractor voiceInteractor; // Associated interactor to provide to app 43 Intent intent; // The original intent that started the task. 44 Intent affinityIntent; // Intent of affinity-moved activity that started this task. 45 ComponentName origActivity; // The non-alias activity component of the intent. 46 ComponentName realActivity; // The actual activity component that started the task. 47 int numActivities; // Current number of activities in this task. 48 long lastActiveTime; // Last time this task was active, including sleep. 49 boolean rootWasReset; // True if the intent at the root of the task had 50 // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag. 51 boolean askedCompatMode;// Have asked the user about compat mode for this task. 52 53 String stringName; // caching of toString() result. 54 int userId; // user for which this task was created 55 int creatorUid; // The app uid that originally created the task 56 57 int numFullscreen; // Number of fullscreen activities. 58 59 // This represents the last resolved activity values for this task 60 // NOTE: This value needs to be persisted with each task 61 ActivityManager.RecentsActivityValues lastActivityValues = 62 new ActivityManager.RecentsActivityValues(); 63 64 /** List of all activities in the task arranged in history order */ 65 final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>(); 66 67 /** Current stack */ 68 ActivityStack stack; 69 70 /** Takes on same set of values as ActivityRecord.mActivityType */ 71 private int mTaskType; 72 73 /** Launch the home activity when leaving this task. Will be false for tasks that are not on 74 * Display.DEFAULT_DISPLAY. */ 75 boolean mOnTopOfHome = false; 76 77 TaskRecord(int _taskId, ActivityInfo info, Intent _intent, 78 IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) { 79 taskId = _taskId; 80 affinity = info.taskAffinity; 81 voiceSession = _voiceSession; 82 voiceInteractor = _voiceInteractor; 83 setIntent(_intent, info); 84 } 85 86 void touchActiveTime() { 87 lastActiveTime = android.os.SystemClock.elapsedRealtime(); 88 } 89 90 long getInactiveDuration() { 91 return android.os.SystemClock.elapsedRealtime() - lastActiveTime; 92 } 93 94 void setIntent(Intent _intent, ActivityInfo info) { 95 stringName = null; 96 97 if (info.targetActivity == null) { 98 if (_intent != null) { 99 // If this Intent has a selector, we want to clear it for the 100 // recent task since it is not relevant if the user later wants 101 // to re-launch the app. 102 if (_intent.getSelector() != null || _intent.getSourceBounds() != null) { 103 _intent = new Intent(_intent); 104 _intent.setSelector(null); 105 _intent.setSourceBounds(null); 106 } 107 } 108 if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG, 109 "Setting Intent of " + this + " to " + _intent); 110 intent = _intent; 111 realActivity = _intent != null ? _intent.getComponent() : null; 112 origActivity = null; 113 } else { 114 ComponentName targetComponent = new ComponentName( 115 info.packageName, info.targetActivity); 116 if (_intent != null) { 117 Intent targetIntent = new Intent(_intent); 118 targetIntent.setComponent(targetComponent); 119 targetIntent.setSelector(null); 120 targetIntent.setSourceBounds(null); 121 if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG, 122 "Setting Intent of " + this + " to target " + targetIntent); 123 intent = targetIntent; 124 realActivity = targetComponent; 125 origActivity = _intent.getComponent(); 126 } else { 127 intent = null; 128 realActivity = targetComponent; 129 origActivity = new ComponentName(info.packageName, info.name); 130 } 131 } 132 133 if (intent != null && 134 (intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { 135 // Once we are set to an Intent with this flag, we count this 136 // task as having a true root activity. 137 rootWasReset = true; 138 } 139 140 userId = UserHandle.getUserId(info.applicationInfo.uid); 141 creatorUid = info.applicationInfo.uid; 142 if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) { 143 intent.addFlags(Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS); 144 } 145 } 146 147 void disposeThumbnail() { 148 super.disposeThumbnail(); 149 for (int i=mActivities.size()-1; i>=0; i--) { 150 ThumbnailHolder thumb = mActivities.get(i).thumbHolder; 151 if (thumb != this) { 152 thumb.disposeThumbnail(); 153 } 154 } 155 } 156 157 /** Returns the first non-finishing activity from the root. */ 158 ActivityRecord getRootActivity() { 159 for (int i = 0; i < mActivities.size(); i++) { 160 final ActivityRecord r = mActivities.get(i); 161 if (r.finishing) { 162 continue; 163 } 164 return r; 165 } 166 return null; 167 } 168 169 ActivityRecord getTopActivity() { 170 for (int i = mActivities.size() - 1; i >= 0; --i) { 171 final ActivityRecord r = mActivities.get(i); 172 if (r.finishing) { 173 continue; 174 } 175 return r; 176 } 177 return null; 178 } 179 180 ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { 181 for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { 182 ActivityRecord r = mActivities.get(activityNdx); 183 if (!r.finishing && r != notTop && stack.okToShowLocked(r)) { 184 return r; 185 } 186 } 187 return null; 188 } 189 190 /** Call after activity movement or finish to make sure that frontOfTask is set correctly */ 191 final void setFrontOfTask() { 192 boolean foundFront = false; 193 final int numActivities = mActivities.size(); 194 for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { 195 final ActivityRecord r = mActivities.get(activityNdx); 196 if (foundFront || r.finishing) { 197 r.frontOfTask = false; 198 } else { 199 r.frontOfTask = true; 200 // Set frontOfTask false for every following activity. 201 foundFront = true; 202 } 203 } 204 } 205 206 /** 207 * Reorder the history stack so that the passed activity is brought to the front. 208 */ 209 final void moveActivityToFrontLocked(ActivityRecord newTop) { 210 if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop 211 + " to stack at top", new RuntimeException("here").fillInStackTrace()); 212 213 mActivities.remove(newTop); 214 mActivities.add(newTop); 215 216 setFrontOfTask(); 217 } 218 219 void addActivityAtBottom(ActivityRecord r) { 220 addActivityAtIndex(0, r); 221 } 222 223 void addActivityToTop(ActivityRecord r) { 224 addActivityAtIndex(mActivities.size(), r); 225 } 226 227 void addActivityAtIndex(int index, ActivityRecord r) { 228 // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. 229 if (!mActivities.remove(r) && r.fullscreen) { 230 // Was not previously in list. 231 numFullscreen++; 232 } 233 // Only set this based on the first activity 234 if (mActivities.isEmpty()) { 235 mTaskType = r.mActivityType; 236 } else { 237 // Otherwise make all added activities match this one. 238 r.mActivityType = mTaskType; 239 } 240 mActivities.add(index, r); 241 } 242 243 /** @return true if this was the last activity in the task */ 244 boolean removeActivity(ActivityRecord r) { 245 if (mActivities.remove(r) && r.fullscreen) { 246 // Was previously in list. 247 numFullscreen--; 248 } 249 return mActivities.size() == 0; 250 } 251 252 boolean autoRemoveFromRecents() { 253 return intent != null && 254 (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0; 255 } 256 257 /** 258 * Completely remove all activities associated with an existing 259 * task starting at a specified index. 260 */ 261 final void performClearTaskAtIndexLocked(int activityNdx) { 262 int numActivities = mActivities.size(); 263 for ( ; activityNdx < numActivities; ++activityNdx) { 264 final ActivityRecord r = mActivities.get(activityNdx); 265 if (r.finishing) { 266 continue; 267 } 268 if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) { 269 --activityNdx; 270 --numActivities; 271 } 272 } 273 } 274 275 /** 276 * Completely remove all activities associated with an existing task. 277 */ 278 final void performClearTaskLocked() { 279 performClearTaskAtIndexLocked(0); 280 } 281 282 /** 283 * Perform clear operation as requested by 284 * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the 285 * stack to the given task, then look for 286 * an instance of that activity in the stack and, if found, finish all 287 * activities on top of it and return the instance. 288 * 289 * @param newR Description of the new activity being started. 290 * @return Returns the old activity that should be continued to be used, 291 * or null if none was found. 292 */ 293 final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) { 294 int numActivities = mActivities.size(); 295 for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) { 296 ActivityRecord r = mActivities.get(activityNdx); 297 if (r.finishing) { 298 continue; 299 } 300 if (r.realActivity.equals(newR.realActivity)) { 301 // Here it is! Now finish everything in front... 302 final ActivityRecord ret = r; 303 304 for (++activityNdx; activityNdx < numActivities; ++activityNdx) { 305 r = mActivities.get(activityNdx); 306 if (r.finishing) { 307 continue; 308 } 309 ActivityOptions opts = r.takeOptionsLocked(); 310 if (opts != null) { 311 ret.updateOptionsLocked(opts); 312 } 313 if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", 314 false)) { 315 --activityNdx; 316 --numActivities; 317 } 318 } 319 320 // Finally, if this is a normal launch mode (that is, not 321 // expecting onNewIntent()), then we will finish the current 322 // instance of the activity so a new fresh one can be started. 323 if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE 324 && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { 325 if (!ret.finishing) { 326 stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null, 327 "clear", false); 328 return null; 329 } 330 } 331 332 return ret; 333 } 334 } 335 336 return null; 337 } 338 339 public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() { 340 TaskAccessInfo info = getTaskAccessInfoLocked(); 341 final ActivityRecord resumedActivity = stack.mResumedActivity; 342 if (resumedActivity != null && resumedActivity.thumbHolder == this) { 343 info.mainThumbnail = stack.screenshotActivities(resumedActivity); 344 } 345 if (info.mainThumbnail == null) { 346 info.mainThumbnail = lastThumbnail; 347 } 348 return info; 349 } 350 351 public Bitmap getTaskTopThumbnailLocked() { 352 final ActivityRecord resumedActivity = stack.mResumedActivity; 353 if (resumedActivity != null && resumedActivity.task == this) { 354 // This task is the current resumed task, we just need to take 355 // a screenshot of it and return that. 356 return stack.screenshotActivities(resumedActivity); 357 } 358 // Return the information about the task, to figure out the top 359 // thumbnail to return. 360 TaskAccessInfo info = getTaskAccessInfoLocked(); 361 if (info.numSubThumbbails <= 0) { 362 return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail; 363 } 364 return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail; 365 } 366 367 public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex, 368 boolean taskRequired) { 369 TaskAccessInfo info = getTaskAccessInfoLocked(); 370 if (info.root == null) { 371 if (taskRequired) { 372 Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId); 373 } 374 return null; 375 } 376 377 if (subTaskIndex < 0) { 378 // Just remove the entire task. 379 performClearTaskAtIndexLocked(info.rootIndex); 380 return info.root; 381 } 382 383 if (subTaskIndex >= info.subtasks.size()) { 384 if (taskRequired) { 385 Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex); 386 } 387 return null; 388 } 389 390 // Remove all of this task's activities starting at the sub task. 391 TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex); 392 performClearTaskAtIndexLocked(subtask.index); 393 return subtask.activity; 394 } 395 396 boolean isHomeTask() { 397 return mTaskType == ActivityRecord.HOME_ACTIVITY_TYPE; 398 } 399 400 boolean isApplicationTask() { 401 return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE; 402 } 403 404 public TaskAccessInfo getTaskAccessInfoLocked() { 405 final TaskAccessInfo thumbs = new TaskAccessInfo(); 406 // How many different sub-thumbnails? 407 final int NA = mActivities.size(); 408 int j = 0; 409 ThumbnailHolder holder = null; 410 while (j < NA) { 411 ActivityRecord ar = mActivities.get(j); 412 if (!ar.finishing) { 413 thumbs.root = ar; 414 thumbs.rootIndex = j; 415 holder = ar.thumbHolder; 416 if (holder != null) { 417 thumbs.mainThumbnail = holder.lastThumbnail; 418 } 419 j++; 420 break; 421 } 422 j++; 423 } 424 425 if (j >= NA) { 426 return thumbs; 427 } 428 429 ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>(); 430 thumbs.subtasks = subtasks; 431 while (j < NA) { 432 ActivityRecord ar = mActivities.get(j); 433 j++; 434 if (ar.finishing) { 435 continue; 436 } 437 if (ar.thumbHolder != holder && holder != null) { 438 thumbs.numSubThumbbails++; 439 holder = ar.thumbHolder; 440 TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask(); 441 sub.holder = holder; 442 sub.activity = ar; 443 sub.index = j-1; 444 subtasks.add(sub); 445 } 446 } 447 if (thumbs.numSubThumbbails > 0) { 448 thumbs.retriever = new IThumbnailRetriever.Stub() { 449 @Override 450 public Bitmap getThumbnail(int index) { 451 if (index < 0 || index >= thumbs.subtasks.size()) { 452 return null; 453 } 454 TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index); 455 ActivityRecord resumedActivity = stack.mResumedActivity; 456 if (resumedActivity != null && resumedActivity.thumbHolder == sub.holder) { 457 return stack.screenshotActivities(resumedActivity); 458 } 459 return sub.holder.lastThumbnail; 460 } 461 }; 462 } 463 return thumbs; 464 } 465 466 /** 467 * Find the activity in the history stack within the given task. Returns 468 * the index within the history at which it's found, or < 0 if not found. 469 */ 470 final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) { 471 final ComponentName realActivity = r.realActivity; 472 for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { 473 ActivityRecord candidate = mActivities.get(activityNdx); 474 if (candidate.finishing) { 475 continue; 476 } 477 if (candidate.realActivity.equals(realActivity)) { 478 return candidate; 479 } 480 } 481 return null; 482 } 483 484 void dump(PrintWriter pw, String prefix) { 485 if (numActivities != 0 || rootWasReset || userId != 0 || numFullscreen != 0) { 486 pw.print(prefix); pw.print("numActivities="); pw.print(numActivities); 487 pw.print(" rootWasReset="); pw.print(rootWasReset); 488 pw.print(" userId="); pw.print(userId); 489 pw.print(" mTaskType="); pw.print(mTaskType); 490 pw.print(" numFullscreen="); pw.print(numFullscreen); 491 pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome); 492 } 493 if (affinity != null) { 494 pw.print(prefix); pw.print("affinity="); pw.println(affinity); 495 } 496 if (voiceSession != null || voiceInteractor != null) { 497 pw.print(prefix); pw.print("VOICE: session=0x"); 498 pw.print(Integer.toHexString(System.identityHashCode(voiceSession))); 499 pw.print(" interactor=0x"); 500 pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor))); 501 } 502 if (intent != null) { 503 StringBuilder sb = new StringBuilder(128); 504 sb.append(prefix); sb.append("intent={"); 505 intent.toShortString(sb, false, true, false, true); 506 sb.append('}'); 507 pw.println(sb.toString()); 508 } 509 if (affinityIntent != null) { 510 StringBuilder sb = new StringBuilder(128); 511 sb.append(prefix); sb.append("affinityIntent={"); 512 affinityIntent.toShortString(sb, false, true, false, true); 513 sb.append('}'); 514 pw.println(sb.toString()); 515 } 516 if (origActivity != null) { 517 pw.print(prefix); pw.print("origActivity="); 518 pw.println(origActivity.flattenToShortString()); 519 } 520 if (realActivity != null) { 521 pw.print(prefix); pw.print("realActivity="); 522 pw.println(realActivity.flattenToShortString()); 523 } 524 pw.print(prefix); pw.print("Activities="); pw.println(mActivities); 525 if (!askedCompatMode) { 526 pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); 527 } 528 pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail); 529 pw.print(" lastDescription="); pw.println(lastDescription); 530 pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); 531 pw.print(" (inactive for "); 532 pw.print((getInactiveDuration()/1000)); pw.println("s)"); 533 } 534 535 @Override 536 public String toString() { 537 StringBuilder sb = new StringBuilder(128); 538 if (stringName != null) { 539 sb.append(stringName); 540 sb.append(" U="); 541 sb.append(userId); 542 sb.append(" sz="); 543 sb.append(mActivities.size()); 544 sb.append('}'); 545 return sb.toString(); 546 } 547 sb.append("TaskRecord{"); 548 sb.append(Integer.toHexString(System.identityHashCode(this))); 549 sb.append(" #"); 550 sb.append(taskId); 551 if (affinity != null) { 552 sb.append(" A="); 553 sb.append(affinity); 554 } else if (intent != null) { 555 sb.append(" I="); 556 sb.append(intent.getComponent().flattenToShortString()); 557 } else if (affinityIntent != null) { 558 sb.append(" aI="); 559 sb.append(affinityIntent.getComponent().flattenToShortString()); 560 } else { 561 sb.append(" ??"); 562 } 563 stringName = sb.toString(); 564 return toString(); 565 } 566} 567