TaskRecord.java revision 9158825f9c41869689d6b1786d7c7aa8bdd524ce
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. */
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