diff options
Diffstat (limited to 'services/core/java/com/android/server/am/TaskRecord.java')
-rw-r--r-- | services/core/java/com/android/server/am/TaskRecord.java | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java new file mode 100644 index 0000000..9740812 --- /dev/null +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static com.android.server.am.ActivityManagerService.TAG; +import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.IThumbnailRetriever; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.graphics.Bitmap; +import android.os.UserHandle; +import android.util.Slog; + +import java.io.PrintWriter; +import java.util.ArrayList; + +final class TaskRecord extends ThumbnailHolder { + final int taskId; // Unique identifier for this task. + final String affinity; // The affinity name for this task, or null. + Intent intent; // The original intent that started the task. + Intent affinityIntent; // Intent of affinity-moved activity that started this task. + ComponentName origActivity; // The non-alias activity component of the intent. + ComponentName realActivity; // The actual activity component that started the task. + int numActivities; // Current number of activities in this task. + long lastActiveTime; // Last time this task was active, including sleep. + boolean rootWasReset; // True if the intent at the root of the task had + // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag. + boolean askedCompatMode;// Have asked the user about compat mode for this task. + + String stringName; // caching of toString() result. + int userId; // user for which this task was created + + int numFullscreen; // Number of fullscreen activities. + + /** List of all activities in the task arranged in history order */ + final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>(); + + /** Current stack */ + ActivityStack stack; + + /** Takes on same set of values as ActivityRecord.mActivityType */ + private int mTaskType; + + /** Launch the home activity when leaving this task. Will be false for tasks that are not on + * Display.DEFAULT_DISPLAY. */ + boolean mOnTopOfHome = false; + + TaskRecord(int _taskId, ActivityInfo info, Intent _intent) { + taskId = _taskId; + affinity = info.taskAffinity; + setIntent(_intent, info); + } + + void touchActiveTime() { + lastActiveTime = android.os.SystemClock.elapsedRealtime(); + } + + long getInactiveDuration() { + return android.os.SystemClock.elapsedRealtime() - lastActiveTime; + } + + void setIntent(Intent _intent, ActivityInfo info) { + stringName = null; + + if (info.targetActivity == null) { + if (_intent != null) { + // If this Intent has a selector, we want to clear it for the + // recent task since it is not relevant if the user later wants + // to re-launch the app. + if (_intent.getSelector() != null || _intent.getSourceBounds() != null) { + _intent = new Intent(_intent); + _intent.setSelector(null); + _intent.setSourceBounds(null); + } + } + if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG, + "Setting Intent of " + this + " to " + _intent); + intent = _intent; + realActivity = _intent != null ? _intent.getComponent() : null; + origActivity = null; + } else { + ComponentName targetComponent = new ComponentName( + info.packageName, info.targetActivity); + if (_intent != null) { + Intent targetIntent = new Intent(_intent); + targetIntent.setComponent(targetComponent); + targetIntent.setSelector(null); + targetIntent.setSourceBounds(null); + if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG, + "Setting Intent of " + this + " to target " + targetIntent); + intent = targetIntent; + realActivity = targetComponent; + origActivity = _intent.getComponent(); + } else { + intent = null; + realActivity = targetComponent; + origActivity = new ComponentName(info.packageName, info.name); + } + } + + if (intent != null && + (intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { + // Once we are set to an Intent with this flag, we count this + // task as having a true root activity. + rootWasReset = true; + } + + if (info.applicationInfo != null) { + userId = UserHandle.getUserId(info.applicationInfo.uid); + } + } + + void disposeThumbnail() { + super.disposeThumbnail(); + for (int i=mActivities.size()-1; i>=0; i--) { + ThumbnailHolder thumb = mActivities.get(i).thumbHolder; + if (thumb != this) { + thumb.disposeThumbnail(); + } + } + } + + ActivityRecord getTopActivity() { + for (int i = mActivities.size() - 1; i >= 0; --i) { + final ActivityRecord r = mActivities.get(i); + if (r.finishing) { + continue; + } + return r; + } + return null; + } + + ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { + for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord r = mActivities.get(activityNdx); + if (!r.finishing && r != notTop && stack.okToShow(r)) { + return r; + } + } + return null; + } + + /** Call after activity movement or finish to make sure that frontOfTask is set correctly */ + final void setFrontOfTask() { + boolean foundFront = false; + final int numActivities = mActivities.size(); + for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { + final ActivityRecord r = mActivities.get(activityNdx); + if (foundFront || r.finishing) { + r.frontOfTask = false; + } else { + r.frontOfTask = true; + // Set frontOfTask false for every following activity. + foundFront = true; + } + } + } + + /** + * Reorder the history stack so that the passed activity is brought to the front. + */ + final void moveActivityToFrontLocked(ActivityRecord newTop) { + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop + + " to stack at top", new RuntimeException("here").fillInStackTrace()); + + mActivities.remove(newTop); + mActivities.add(newTop); + + setFrontOfTask(); + } + + void addActivityAtBottom(ActivityRecord r) { + addActivityAtIndex(0, r); + } + + void addActivityToTop(ActivityRecord r) { + addActivityAtIndex(mActivities.size(), r); + } + + void addActivityAtIndex(int index, ActivityRecord r) { + // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. + if (!mActivities.remove(r) && r.fullscreen) { + // Was not previously in list. + numFullscreen++; + } + // Only set this based on the first activity + if (mActivities.isEmpty()) { + mTaskType = r.mActivityType; + } else { + // Otherwise make all added activities match this one. + r.mActivityType = mTaskType; + } + mActivities.add(index, r); + } + + /** @return true if this was the last activity in the task */ + boolean removeActivity(ActivityRecord r) { + if (mActivities.remove(r) && r.fullscreen) { + // Was previously in list. + numFullscreen--; + } + return mActivities.size() == 0; + } + + /** + * Completely remove all activities associated with an existing + * task starting at a specified index. + */ + final void performClearTaskAtIndexLocked(int activityNdx) { + int numActivities = mActivities.size(); + for ( ; activityNdx < numActivities; ++activityNdx) { + final ActivityRecord r = mActivities.get(activityNdx); + if (r.finishing) { + continue; + } + if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) { + --activityNdx; + --numActivities; + } + } + } + + /** + * Completely remove all activities associated with an existing task. + */ + final void performClearTaskLocked() { + performClearTaskAtIndexLocked(0); + } + + /** + * Perform clear operation as requested by + * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the + * stack to the given task, then look for + * an instance of that activity in the stack and, if found, finish all + * activities on top of it and return the instance. + * + * @param newR Description of the new activity being started. + * @return Returns the old activity that should be continued to be used, + * or null if none was found. + */ + final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) { + int numActivities = mActivities.size(); + for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord r = mActivities.get(activityNdx); + if (r.finishing) { + continue; + } + if (r.realActivity.equals(newR.realActivity)) { + // Here it is! Now finish everything in front... + final ActivityRecord ret = r; + + for (++activityNdx; activityNdx < numActivities; ++activityNdx) { + r = mActivities.get(activityNdx); + if (r.finishing) { + continue; + } + ActivityOptions opts = r.takeOptionsLocked(); + if (opts != null) { + ret.updateOptionsLocked(opts); + } + if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", + false)) { + --activityNdx; + --numActivities; + } + } + + // Finally, if this is a normal launch mode (that is, not + // expecting onNewIntent()), then we will finish the current + // instance of the activity so a new fresh one can be started. + if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE + && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { + if (!ret.finishing) { + stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null, + "clear", false); + return null; + } + } + + return ret; + } + } + + return null; + } + + public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() { + TaskAccessInfo info = getTaskAccessInfoLocked(true); + final ActivityRecord resumedActivity = stack.mResumedActivity; + if (resumedActivity != null && resumedActivity.thumbHolder == this) { + info.mainThumbnail = stack.screenshotActivities(resumedActivity); + } + if (info.mainThumbnail == null) { + info.mainThumbnail = lastThumbnail; + } + return info; + } + + public Bitmap getTaskTopThumbnailLocked() { + final ActivityRecord resumedActivity = stack.mResumedActivity; + if (resumedActivity != null && resumedActivity.task == this) { + // This task is the current resumed task, we just need to take + // a screenshot of it and return that. + return stack.screenshotActivities(resumedActivity); + } + // Return the information about the task, to figure out the top + // thumbnail to return. + TaskAccessInfo info = getTaskAccessInfoLocked(true); + if (info.numSubThumbbails <= 0) { + return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail; + } + return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail; + } + + public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex, + boolean taskRequired) { + TaskAccessInfo info = getTaskAccessInfoLocked(false); + if (info.root == null) { + if (taskRequired) { + Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId); + } + return null; + } + + if (subTaskIndex < 0) { + // Just remove the entire task. + performClearTaskAtIndexLocked(info.rootIndex); + return info.root; + } + + if (subTaskIndex >= info.subtasks.size()) { + if (taskRequired) { + Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex); + } + return null; + } + + // Remove all of this task's activities starting at the sub task. + TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex); + performClearTaskAtIndexLocked(subtask.index); + return subtask.activity; + } + + boolean isHomeTask() { + return mTaskType == ActivityRecord.HOME_ACTIVITY_TYPE; + } + + boolean isApplicationTask() { + return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE; + } + + public TaskAccessInfo getTaskAccessInfoLocked(boolean inclThumbs) { + final TaskAccessInfo thumbs = new TaskAccessInfo(); + // How many different sub-thumbnails? + final int NA = mActivities.size(); + int j = 0; + ThumbnailHolder holder = null; + while (j < NA) { + ActivityRecord ar = mActivities.get(j); + if (!ar.finishing) { + thumbs.root = ar; + thumbs.rootIndex = j; + holder = ar.thumbHolder; + if (holder != null) { + thumbs.mainThumbnail = holder.lastThumbnail; + } + j++; + break; + } + j++; + } + + if (j >= NA) { + return thumbs; + } + + ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>(); + thumbs.subtasks = subtasks; + while (j < NA) { + ActivityRecord ar = mActivities.get(j); + j++; + if (ar.finishing) { + continue; + } + if (ar.thumbHolder != holder && holder != null) { + thumbs.numSubThumbbails++; + holder = ar.thumbHolder; + TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask(); + sub.holder = holder; + sub.activity = ar; + sub.index = j-1; + subtasks.add(sub); + } + } + if (thumbs.numSubThumbbails > 0) { + thumbs.retriever = new IThumbnailRetriever.Stub() { + @Override + public Bitmap getThumbnail(int index) { + if (index < 0 || index >= thumbs.subtasks.size()) { + return null; + } + TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index); + ActivityRecord resumedActivity = stack.mResumedActivity; + if (resumedActivity != null && resumedActivity.thumbHolder == sub.holder) { + return stack.screenshotActivities(resumedActivity); + } + return sub.holder.lastThumbnail; + } + }; + } + return thumbs; + } + + /** + * Find the activity in the history stack within the given task. Returns + * the index within the history at which it's found, or < 0 if not found. + */ + final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) { + final ComponentName realActivity = r.realActivity; + for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord candidate = mActivities.get(activityNdx); + if (candidate.finishing) { + continue; + } + if (candidate.realActivity.equals(realActivity)) { + return candidate; + } + } + return null; + } + + void dump(PrintWriter pw, String prefix) { + if (numActivities != 0 || rootWasReset || userId != 0 || numFullscreen != 0) { + pw.print(prefix); pw.print("numActivities="); pw.print(numActivities); + pw.print(" rootWasReset="); pw.print(rootWasReset); + pw.print(" userId="); pw.print(userId); + pw.print(" mTaskType="); pw.print(mTaskType); + pw.print(" numFullscreen="); pw.print(numFullscreen); + pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome); + } + if (affinity != null) { + pw.print(prefix); pw.print("affinity="); pw.println(affinity); + } + if (intent != null) { + StringBuilder sb = new StringBuilder(128); + sb.append(prefix); sb.append("intent={"); + intent.toShortString(sb, false, true, false, true); + sb.append('}'); + pw.println(sb.toString()); + } + if (affinityIntent != null) { + StringBuilder sb = new StringBuilder(128); + sb.append(prefix); sb.append("affinityIntent={"); + affinityIntent.toShortString(sb, false, true, false, true); + sb.append('}'); + pw.println(sb.toString()); + } + if (origActivity != null) { + pw.print(prefix); pw.print("origActivity="); + pw.println(origActivity.flattenToShortString()); + } + if (realActivity != null) { + pw.print(prefix); pw.print("realActivity="); + pw.println(realActivity.flattenToShortString()); + } + pw.print(prefix); pw.print("Activities="); pw.println(mActivities); + if (!askedCompatMode) { + pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); + } + pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail); + pw.print(" lastDescription="); pw.println(lastDescription); + pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); + pw.print(" (inactive for "); + pw.print((getInactiveDuration()/1000)); pw.println("s)"); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + if (stringName != null) { + sb.append(stringName); + sb.append(" U="); + sb.append(userId); + sb.append(" sz="); + sb.append(mActivities.size()); + sb.append('}'); + return sb.toString(); + } + sb.append("TaskRecord{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(" #"); + sb.append(taskId); + if (affinity != null) { + sb.append(" A="); + sb.append(affinity); + } else if (intent != null) { + sb.append(" I="); + sb.append(intent.getComponent().flattenToShortString()); + } else if (affinityIntent != null) { + sb.append(" aI="); + sb.append(affinityIntent.getComponent().flattenToShortString()); + } else { + sb.append(" ??"); + } + stringName = sb.toString(); + return toString(); + } +} |