Home | History | Annotate | Download | only in am
      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 
     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