• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.am;
18 
19 import static com.android.server.am.ActivityManagerService.TAG;
20 import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
21 
22 import android.app.Activity;
23 import android.app.ActivityManager;
24 import android.app.ActivityOptions;
25 import android.app.IThumbnailRetriever;
26 import android.content.ComponentName;
27 import android.content.Intent;
28 import android.content.pm.ActivityInfo;
29 import android.graphics.Bitmap;
30 import android.os.UserHandle;
31 import android.util.Slog;
32 
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 
36 final 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 
TaskRecord(int _taskId, ActivityInfo info, Intent _intent)66     TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
67         taskId = _taskId;
68         affinity = info.taskAffinity;
69         setIntent(_intent, info);
70     }
71 
touchActiveTime()72     void touchActiveTime() {
73         lastActiveTime = android.os.SystemClock.elapsedRealtime();
74     }
75 
getInactiveDuration()76     long getInactiveDuration() {
77         return android.os.SystemClock.elapsedRealtime() - lastActiveTime;
78     }
79 
setIntent(Intent _intent, ActivityInfo info)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 
disposeThumbnail()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 
getTopActivity()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 
topRunningActivityLocked(ActivityRecord notTop)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 */
setFrontOfTask()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      */
moveActivityToFrontLocked(ActivityRecord newTop)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 
addActivityAtBottom(ActivityRecord r)191     void addActivityAtBottom(ActivityRecord r) {
192         addActivityAtIndex(0, r);
193     }
194 
addActivityToTop(ActivityRecord r)195     void addActivityToTop(ActivityRecord r) {
196         addActivityAtIndex(mActivities.size(), r);
197     }
198 
addActivityAtIndex(int index, ActivityRecord r)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 */
removeActivity(ActivityRecord r)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      */
performClearTaskAtIndexLocked(int activityNdx)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      */
performClearTaskLocked()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      */
performClearTaskLocked(ActivityRecord newR, int launchFlags)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 
getTaskThumbnailsLocked()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 
getTaskTopThumbnailLocked()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 
removeTaskActivitiesLocked(int subTaskIndex, boolean taskRequired)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 
isHomeTask()363     boolean isHomeTask() {
364         return mTaskType == ActivityRecord.HOME_ACTIVITY_TYPE;
365     }
366 
isApplicationTask()367     boolean isApplicationTask() {
368         return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
369     }
370 
getTaskAccessInfoLocked(boolean inclThumbs)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      */
findActivityInHistoryLocked(ActivityRecord r)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 
dump(PrintWriter pw, String prefix)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
toString()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