TaskRecord.java revision a449dc033b79775b8945d9cc5a035a6deb145065
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.TaskDescription lastTaskDescription =
62            new ActivityManager.TaskDescription();
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 intent for the root activity for this task */
158    Intent getBaseIntent() {
159        return intent != null ? intent : affinityIntent;
160    }
161
162    /** Returns the first non-finishing activity from the root. */
163    ActivityRecord getRootActivity() {
164        for (int i = 0; i < mActivities.size(); i++) {
165            final ActivityRecord r = mActivities.get(i);
166            if (r.finishing) {
167                continue;
168            }
169            return r;
170        }
171        return null;
172    }
173
174    ActivityRecord getTopActivity() {
175        for (int i = mActivities.size() - 1; i >= 0; --i) {
176            final ActivityRecord r = mActivities.get(i);
177            if (r.finishing) {
178                continue;
179            }
180            return r;
181        }
182        return null;
183    }
184
185    ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
186        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
187            ActivityRecord r = mActivities.get(activityNdx);
188            if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
189                return r;
190            }
191        }
192        return null;
193    }
194
195    /** Call after activity movement or finish to make sure that frontOfTask is set correctly */
196    final void setFrontOfTask() {
197        boolean foundFront = false;
198        final int numActivities = mActivities.size();
199        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
200            final ActivityRecord r = mActivities.get(activityNdx);
201            if (foundFront || r.finishing) {
202                r.frontOfTask = false;
203            } else {
204                r.frontOfTask = true;
205                // Set frontOfTask false for every following activity.
206                foundFront = true;
207            }
208        }
209    }
210
211    /**
212     * Reorder the history stack so that the passed activity is brought to the front.
213     */
214    final void moveActivityToFrontLocked(ActivityRecord newTop) {
215        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop
216            + " to stack at top", new RuntimeException("here").fillInStackTrace());
217
218        mActivities.remove(newTop);
219        mActivities.add(newTop);
220
221        setFrontOfTask();
222    }
223
224    void addActivityAtBottom(ActivityRecord r) {
225        addActivityAtIndex(0, r);
226    }
227
228    void addActivityToTop(ActivityRecord r) {
229        addActivityAtIndex(mActivities.size(), r);
230    }
231
232    void addActivityAtIndex(int index, ActivityRecord r) {
233        // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
234        if (!mActivities.remove(r) && r.fullscreen) {
235            // Was not previously in list.
236            numFullscreen++;
237        }
238        // Only set this based on the first activity
239        if (mActivities.isEmpty()) {
240            mTaskType = r.mActivityType;
241        } else {
242            // Otherwise make all added activities match this one.
243            r.mActivityType = mTaskType;
244        }
245        mActivities.add(index, r);
246    }
247
248    /** @return true if this was the last activity in the task */
249    boolean removeActivity(ActivityRecord r) {
250        if (mActivities.remove(r) && r.fullscreen) {
251            // Was previously in list.
252            numFullscreen--;
253        }
254        return mActivities.size() == 0;
255    }
256
257    boolean autoRemoveFromRecents() {
258        return intent != null &&
259                (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0;
260    }
261
262    /**
263     * Completely remove all activities associated with an existing
264     * task starting at a specified index.
265     */
266    final void performClearTaskAtIndexLocked(int activityNdx) {
267        int numActivities = mActivities.size();
268        for ( ; activityNdx < numActivities; ++activityNdx) {
269            final ActivityRecord r = mActivities.get(activityNdx);
270            if (r.finishing) {
271                continue;
272            }
273            if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) {
274                --activityNdx;
275                --numActivities;
276            }
277        }
278    }
279
280    /**
281     * Completely remove all activities associated with an existing task.
282     */
283    final void performClearTaskLocked() {
284        performClearTaskAtIndexLocked(0);
285    }
286
287    /**
288     * Perform clear operation as requested by
289     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
290     * stack to the given task, then look for
291     * an instance of that activity in the stack and, if found, finish all
292     * activities on top of it and return the instance.
293     *
294     * @param newR Description of the new activity being started.
295     * @return Returns the old activity that should be continued to be used,
296     * or null if none was found.
297     */
298    final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
299        int numActivities = mActivities.size();
300        for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
301            ActivityRecord r = mActivities.get(activityNdx);
302            if (r.finishing) {
303                continue;
304            }
305            if (r.realActivity.equals(newR.realActivity)) {
306                // Here it is!  Now finish everything in front...
307                final ActivityRecord ret = r;
308
309                for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
310                    r = mActivities.get(activityNdx);
311                    if (r.finishing) {
312                        continue;
313                    }
314                    ActivityOptions opts = r.takeOptionsLocked();
315                    if (opts != null) {
316                        ret.updateOptionsLocked(opts);
317                    }
318                    if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
319                            false)) {
320                        --activityNdx;
321                        --numActivities;
322                    }
323                }
324
325                // Finally, if this is a normal launch mode (that is, not
326                // expecting onNewIntent()), then we will finish the current
327                // instance of the activity so a new fresh one can be started.
328                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
329                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
330                    if (!ret.finishing) {
331                        stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null,
332                                "clear", false);
333                        return null;
334                    }
335                }
336
337                return ret;
338            }
339        }
340
341        return null;
342    }
343
344    public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() {
345        TaskAccessInfo info = getTaskAccessInfoLocked();
346        final ActivityRecord resumedActivity = stack.mResumedActivity;
347        if (resumedActivity != null && resumedActivity.thumbHolder == this) {
348            info.mainThumbnail = stack.screenshotActivities(resumedActivity);
349        }
350        if (info.mainThumbnail == null) {
351            info.mainThumbnail = lastThumbnail;
352        }
353        return info;
354    }
355
356    public Bitmap getTaskTopThumbnailLocked() {
357        final ActivityRecord resumedActivity = stack.mResumedActivity;
358        if (resumedActivity != null && resumedActivity.task == this) {
359            // This task is the current resumed task, we just need to take
360            // a screenshot of it and return that.
361            return stack.screenshotActivities(resumedActivity);
362        }
363        // Return the information about the task, to figure out the top
364        // thumbnail to return.
365        TaskAccessInfo info = getTaskAccessInfoLocked();
366        if (info.numSubThumbbails <= 0) {
367            return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail;
368        }
369        return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail;
370    }
371
372    public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex,
373            boolean taskRequired) {
374        TaskAccessInfo info = getTaskAccessInfoLocked();
375        if (info.root == null) {
376            if (taskRequired) {
377                Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
378            }
379            return null;
380        }
381
382        if (subTaskIndex < 0) {
383            // Just remove the entire task.
384            performClearTaskAtIndexLocked(info.rootIndex);
385            return info.root;
386        }
387
388        if (subTaskIndex >= info.subtasks.size()) {
389            if (taskRequired) {
390                Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex);
391            }
392            return null;
393        }
394
395        // Remove all of this task's activities starting at the sub task.
396        TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex);
397        performClearTaskAtIndexLocked(subtask.index);
398        return subtask.activity;
399    }
400
401    boolean isHomeTask() {
402        return mTaskType == ActivityRecord.HOME_ACTIVITY_TYPE;
403    }
404
405    boolean isApplicationTask() {
406        return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
407    }
408
409    public TaskAccessInfo getTaskAccessInfoLocked() {
410        final TaskAccessInfo thumbs = new TaskAccessInfo();
411        // How many different sub-thumbnails?
412        final int NA = mActivities.size();
413        int j = 0;
414        ThumbnailHolder holder = null;
415        while (j < NA) {
416            ActivityRecord ar = mActivities.get(j);
417            if (!ar.finishing) {
418                thumbs.root = ar;
419                thumbs.rootIndex = j;
420                holder = ar.thumbHolder;
421                if (holder != null) {
422                    thumbs.mainThumbnail = holder.lastThumbnail;
423                }
424                j++;
425                break;
426            }
427            j++;
428        }
429
430        if (j >= NA) {
431            return thumbs;
432        }
433
434        ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>();
435        thumbs.subtasks = subtasks;
436        while (j < NA) {
437            ActivityRecord ar = mActivities.get(j);
438            j++;
439            if (ar.finishing) {
440                continue;
441            }
442            if (ar.thumbHolder != holder && holder != null) {
443                thumbs.numSubThumbbails++;
444                holder = ar.thumbHolder;
445                TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask();
446                sub.holder = holder;
447                sub.activity = ar;
448                sub.index = j-1;
449                subtasks.add(sub);
450            }
451        }
452        if (thumbs.numSubThumbbails > 0) {
453            thumbs.retriever = new IThumbnailRetriever.Stub() {
454                @Override
455                public Bitmap getThumbnail(int index) {
456                    if (index < 0 || index >= thumbs.subtasks.size()) {
457                        return null;
458                    }
459                    TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index);
460                    ActivityRecord resumedActivity = stack.mResumedActivity;
461                    if (resumedActivity != null && resumedActivity.thumbHolder == sub.holder) {
462                        return stack.screenshotActivities(resumedActivity);
463                    }
464                    return sub.holder.lastThumbnail;
465                }
466            };
467        }
468        return thumbs;
469    }
470
471    /**
472     * Find the activity in the history stack within the given task.  Returns
473     * the index within the history at which it's found, or < 0 if not found.
474     */
475    final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
476        final ComponentName realActivity = r.realActivity;
477        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
478            ActivityRecord candidate = mActivities.get(activityNdx);
479            if (candidate.finishing) {
480                continue;
481            }
482            if (candidate.realActivity.equals(realActivity)) {
483                return candidate;
484            }
485        }
486        return null;
487    }
488
489    /** Updates the last task description values. */
490    void updateTaskDescription() {
491        // Traverse upwards looking for any break between main task activities and
492        // utility activities.
493        int activityNdx;
494        final int numActivities = mActivities.size();
495        for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
496             ++activityNdx) {
497            final ActivityRecord r = mActivities.get(activityNdx);
498            if (r.intent != null &&
499                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
500                            != 0) {
501                break;
502            }
503        }
504        if (activityNdx > 0) {
505            // Traverse downwards starting below break looking for set label, icon.
506            // Note that if there are activities in the task but none of them set the
507            // recent activity values, then we do not fall back to the last set
508            // values in the TaskRecord.
509            String label = null;
510            Bitmap icon = null;
511            int colorPrimary = 0;
512            for (--activityNdx; activityNdx >= 0; --activityNdx) {
513                final ActivityRecord r = mActivities.get(activityNdx);
514                if (r.taskDescription != null) {
515                    if (label == null) {
516                        label = r.taskDescription.getLabel();
517                    }
518                    if (icon == null) {
519                        icon = r.taskDescription.getIcon();
520                    }
521                    if (colorPrimary == 0) {
522                        colorPrimary = r.taskDescription.getPrimaryColor();
523
524                    }
525                }
526            }
527            lastTaskDescription = new ActivityManager.TaskDescription(label, icon, colorPrimary);
528        }
529    }
530
531    void dump(PrintWriter pw, String prefix) {
532        if (numActivities != 0 || rootWasReset || userId != 0 || numFullscreen != 0) {
533            pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
534                    pw.print(" rootWasReset="); pw.print(rootWasReset);
535                    pw.print(" userId="); pw.print(userId);
536                    pw.print(" mTaskType="); pw.print(mTaskType);
537                    pw.print(" numFullscreen="); pw.print(numFullscreen);
538                    pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome);
539        }
540        if (affinity != null) {
541            pw.print(prefix); pw.print("affinity="); pw.println(affinity);
542        }
543        if (voiceSession != null || voiceInteractor != null) {
544            pw.print(prefix); pw.print("VOICE: session=0x");
545            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
546            pw.print(" interactor=0x");
547            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
548        }
549        if (intent != null) {
550            StringBuilder sb = new StringBuilder(128);
551            sb.append(prefix); sb.append("intent={");
552            intent.toShortString(sb, false, true, false, true);
553            sb.append('}');
554            pw.println(sb.toString());
555        }
556        if (affinityIntent != null) {
557            StringBuilder sb = new StringBuilder(128);
558            sb.append(prefix); sb.append("affinityIntent={");
559            affinityIntent.toShortString(sb, false, true, false, true);
560            sb.append('}');
561            pw.println(sb.toString());
562        }
563        if (origActivity != null) {
564            pw.print(prefix); pw.print("origActivity=");
565            pw.println(origActivity.flattenToShortString());
566        }
567        if (realActivity != null) {
568            pw.print(prefix); pw.print("realActivity=");
569            pw.println(realActivity.flattenToShortString());
570        }
571        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
572        if (!askedCompatMode) {
573            pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
574        }
575        pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail);
576                pw.print(" lastDescription="); pw.println(lastDescription);
577        pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
578                pw.print(" (inactive for ");
579                pw.print((getInactiveDuration()/1000)); pw.println("s)");
580    }
581
582    @Override
583    public String toString() {
584        StringBuilder sb = new StringBuilder(128);
585        if (stringName != null) {
586            sb.append(stringName);
587            sb.append(" U=");
588            sb.append(userId);
589            sb.append(" sz=");
590            sb.append(mActivities.size());
591            sb.append('}');
592            return sb.toString();
593        }
594        sb.append("TaskRecord{");
595        sb.append(Integer.toHexString(System.identityHashCode(this)));
596        sb.append(" #");
597        sb.append(taskId);
598        if (affinity != null) {
599            sb.append(" A=");
600            sb.append(affinity);
601        } else if (intent != null) {
602            sb.append(" I=");
603            sb.append(intent.getComponent().flattenToShortString());
604        } else if (affinityIntent != null) {
605            sb.append(" aI=");
606            sb.append(affinityIntent.getComponent().flattenToShortString());
607        } else {
608            sb.append(" ??");
609        }
610        stringName = sb.toString();
611        return toString();
612    }
613}
614