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 /** 163 * Reorder the history stack so that the activity at the given index is 164 * brought to the front. 165 */ 166 final void moveActivityToFrontLocked(ActivityRecord newTop) { 167 if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop 168 + " to stack at top", new RuntimeException("here").fillInStackTrace()); 169 170 getTopActivity().frontOfTask = false; 171 mActivities.remove(newTop); 172 mActivities.add(newTop); 173 newTop.frontOfTask = true; 174 } 175 176 void addActivityAtBottom(ActivityRecord r) { 177 addActivityAtIndex(0, r); 178 } 179 180 void addActivityToTop(ActivityRecord r) { 181 addActivityAtIndex(mActivities.size(), r); 182 } 183 184 void addActivityAtIndex(int index, ActivityRecord r) { 185 // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. 186 if (!mActivities.remove(r) && r.fullscreen) { 187 // Was not previously in list. 188 numFullscreen++; 189 } 190 // Only set this based on the first activity 191 if (mActivities.isEmpty()) { 192 mTaskType = r.mActivityType; 193 } else { 194 // Otherwise make all added activities match this one. 195 r.mActivityType = mTaskType; 196 } 197 mActivities.add(index, r); 198 } 199 200 /** @return true if this was the last activity in the task */ 201 boolean removeActivity(ActivityRecord r) { 202 if (mActivities.remove(r) && r.fullscreen) { 203 // Was previously in list. 204 numFullscreen--; 205 } 206 return mActivities.size() == 0; 207 } 208 209 /** 210 * Completely remove all activities associated with an existing 211 * task starting at a specified index. 212 */ 213 final void performClearTaskAtIndexLocked(int activityNdx) { 214 int numActivities = mActivities.size(); 215 for ( ; activityNdx < numActivities; ++activityNdx) { 216 final ActivityRecord r = mActivities.get(activityNdx); 217 if (r.finishing) { 218 continue; 219 } 220 if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) { 221 --activityNdx; 222 --numActivities; 223 } 224 } 225 } 226 227 /** 228 * Completely remove all activities associated with an existing task. 229 */ 230 final void performClearTaskLocked() { 231 performClearTaskAtIndexLocked(0); 232 } 233 234 /** 235 * Perform clear operation as requested by 236 * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the 237 * stack to the given task, then look for 238 * an instance of that activity in the stack and, if found, finish all 239 * activities on top of it and return the instance. 240 * 241 * @param newR Description of the new activity being started. 242 * @return Returns the old activity that should be continued to be used, 243 * or null if none was found. 244 */ 245 final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) { 246 int numActivities = mActivities.size(); 247 for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) { 248 ActivityRecord r = mActivities.get(activityNdx); 249 if (r.finishing) { 250 continue; 251 } 252 if (r.realActivity.equals(newR.realActivity)) { 253 // Here it is! Now finish everything in front... 254 final ActivityRecord ret = r; 255 256 for (++activityNdx; activityNdx < numActivities; ++activityNdx) { 257 r = mActivities.get(activityNdx); 258 if (r.finishing) { 259 continue; 260 } 261 ActivityOptions opts = r.takeOptionsLocked(); 262 if (opts != null) { 263 ret.updateOptionsLocked(opts); 264 } 265 if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", 266 false)) { 267 --activityNdx; 268 --numActivities; 269 } 270 } 271 272 // Finally, if this is a normal launch mode (that is, not 273 // expecting onNewIntent()), then we will finish the current 274 // instance of the activity so a new fresh one can be started. 275 if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE 276 && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { 277 if (!ret.finishing) { 278 stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null, 279 "clear", false); 280 return null; 281 } 282 } 283 284 return ret; 285 } 286 } 287 288 return null; 289 } 290 291 public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() { 292 TaskAccessInfo info = getTaskAccessInfoLocked(true); 293 final ActivityRecord resumedActivity = stack.mResumedActivity; 294 if (resumedActivity != null && resumedActivity.thumbHolder == this) { 295 info.mainThumbnail = stack.screenshotActivities(resumedActivity); 296 } 297 if (info.mainThumbnail == null) { 298 info.mainThumbnail = lastThumbnail; 299 } 300 return info; 301 } 302 303 public Bitmap getTaskTopThumbnailLocked() { 304 final ActivityRecord resumedActivity = stack.mResumedActivity; 305 if (resumedActivity != null && resumedActivity.task == this) { 306 // This task is the current resumed task, we just need to take 307 // a screenshot of it and return that. 308 return stack.screenshotActivities(resumedActivity); 309 } 310 // Return the information about the task, to figure out the top 311 // thumbnail to return. 312 TaskAccessInfo info = getTaskAccessInfoLocked(true); 313 if (info.numSubThumbbails <= 0) { 314 return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail; 315 } 316 return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail; 317 } 318 319 public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex, 320 boolean taskRequired) { 321 TaskAccessInfo info = getTaskAccessInfoLocked(false); 322 if (info.root == null) { 323 if (taskRequired) { 324 Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId); 325 } 326 return null; 327 } 328 329 if (subTaskIndex < 0) { 330 // Just remove the entire task. 331 performClearTaskAtIndexLocked(info.rootIndex); 332 return info.root; 333 } 334 335 if (subTaskIndex >= info.subtasks.size()) { 336 if (taskRequired) { 337 Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex); 338 } 339 return null; 340 } 341 342 // Remove all of this task's activities starting at the sub task. 343 TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex); 344 performClearTaskAtIndexLocked(subtask.index); 345 return subtask.activity; 346 } 347 348 boolean isHomeTask() { 349 return mTaskType == ActivityRecord.HOME_ACTIVITY_TYPE; 350 } 351 352 boolean isApplicationTask() { 353 return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE; 354 } 355 356 public TaskAccessInfo getTaskAccessInfoLocked(boolean inclThumbs) { 357 final TaskAccessInfo thumbs = new TaskAccessInfo(); 358 // How many different sub-thumbnails? 359 final int NA = mActivities.size(); 360 int j = 0; 361 ThumbnailHolder holder = null; 362 while (j < NA) { 363 ActivityRecord ar = mActivities.get(j); 364 if (!ar.finishing) { 365 thumbs.root = ar; 366 thumbs.rootIndex = j; 367 holder = ar.thumbHolder; 368 if (holder != null) { 369 thumbs.mainThumbnail = holder.lastThumbnail; 370 } 371 j++; 372 break; 373 } 374 j++; 375 } 376 377 if (j >= NA) { 378 return thumbs; 379 } 380 381 ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>(); 382 thumbs.subtasks = subtasks; 383 while (j < NA) { 384 ActivityRecord ar = mActivities.get(j); 385 j++; 386 if (ar.finishing) { 387 continue; 388 } 389 if (ar.thumbHolder != holder && holder != null) { 390 thumbs.numSubThumbbails++; 391 holder = ar.thumbHolder; 392 TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask(); 393 sub.holder = holder; 394 sub.activity = ar; 395 sub.index = j-1; 396 subtasks.add(sub); 397 } 398 } 399 if (thumbs.numSubThumbbails > 0) { 400 thumbs.retriever = new IThumbnailRetriever.Stub() { 401 @Override 402 public Bitmap getThumbnail(int index) { 403 if (index < 0 || index >= thumbs.subtasks.size()) { 404 return null; 405 } 406 TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index); 407 ActivityRecord resumedActivity = stack.mResumedActivity; 408 if (resumedActivity != null && resumedActivity.thumbHolder == sub.holder) { 409 return stack.screenshotActivities(resumedActivity); 410 } 411 return sub.holder.lastThumbnail; 412 } 413 }; 414 } 415 return thumbs; 416 } 417 418 /** 419 * Find the activity in the history stack within the given task. Returns 420 * the index within the history at which it's found, or < 0 if not found. 421 */ 422 final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) { 423 final ComponentName realActivity = r.realActivity; 424 for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { 425 ActivityRecord candidate = mActivities.get(activityNdx); 426 if (candidate.finishing) { 427 continue; 428 } 429 if (candidate.realActivity.equals(realActivity)) { 430 return candidate; 431 } 432 } 433 return null; 434 } 435 436 void dump(PrintWriter pw, String prefix) { 437 if (numActivities != 0 || rootWasReset || userId != 0 || numFullscreen != 0) { 438 pw.print(prefix); pw.print("numActivities="); pw.print(numActivities); 439 pw.print(" rootWasReset="); pw.print(rootWasReset); 440 pw.print(" userId="); pw.print(userId); 441 pw.print(" mTaskType="); pw.print(mTaskType); 442 pw.print(" numFullscreen="); pw.print(numFullscreen); 443 pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome); 444 } 445 if (affinity != null) { 446 pw.print(prefix); pw.print("affinity="); pw.println(affinity); 447 } 448 if (intent != null) { 449 StringBuilder sb = new StringBuilder(128); 450 sb.append(prefix); sb.append("intent={"); 451 intent.toShortString(sb, false, true, false, true); 452 sb.append('}'); 453 pw.println(sb.toString()); 454 } 455 if (affinityIntent != null) { 456 StringBuilder sb = new StringBuilder(128); 457 sb.append(prefix); sb.append("affinityIntent={"); 458 affinityIntent.toShortString(sb, false, true, false, true); 459 sb.append('}'); 460 pw.println(sb.toString()); 461 } 462 if (origActivity != null) { 463 pw.print(prefix); pw.print("origActivity="); 464 pw.println(origActivity.flattenToShortString()); 465 } 466 if (realActivity != null) { 467 pw.print(prefix); pw.print("realActivity="); 468 pw.println(realActivity.flattenToShortString()); 469 } 470 pw.print(prefix); pw.print("Activities="); pw.println(mActivities); 471 if (!askedCompatMode) { 472 pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); 473 } 474 pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail); 475 pw.print(" lastDescription="); pw.println(lastDescription); 476 pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); 477 pw.print(" (inactive for "); 478 pw.print((getInactiveDuration()/1000)); pw.println("s)"); 479 } 480 481 @Override 482 public String toString() { 483 StringBuilder sb = new StringBuilder(128); 484 if (stringName != null) { 485 sb.append(stringName); 486 sb.append(" U="); 487 sb.append(userId); 488 sb.append(" sz="); 489 sb.append(mActivities.size()); 490 sb.append('}'); 491 return sb.toString(); 492 } 493 sb.append("TaskRecord{"); 494 sb.append(Integer.toHexString(System.identityHashCode(this))); 495 sb.append(" #"); 496 sb.append(taskId); 497 if (affinity != null) { 498 sb.append(" A="); 499 sb.append(affinity); 500 } else if (intent != null) { 501 sb.append(" I="); 502 sb.append(intent.getComponent().flattenToShortString()); 503 } else if (affinityIntent != null) { 504 sb.append(" aI="); 505 sb.append(affinityIntent.getComponent().flattenToShortString()); 506 } else { 507 sb.append(" ??"); 508 } 509 stringName = sb.toString(); 510 return toString(); 511 } 512 } 513