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