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