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