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