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