Add APIs to remove tasks.

You can remove sub-tasks inside of a task, or an entire task.

When removing an entire task, you can have its process killed
as well.

When the process is killed, any running services will get an
onTaskRemoved() callback for them to do cleanup before their
process is killed (and the service possibly restarted).

Or they can set a new android:stopWithTask attribute to just
have the service automatically (cleanly) stopped at this point.

Change-Id: I1891bc2da006fa53b99c52f9040f1145650e6808
diff --git a/api/current.txt b/api/current.txt
index e946c06..4d3f8c6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -87,6 +87,7 @@
     field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
     field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
     field public static final java.lang.String RECORD_AUDIO = "android.permission.RECORD_AUDIO";
+    field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
@@ -855,6 +856,7 @@
     field public static final int state_window_focused = 16842909; // 0x101009d
     field public static final int staticWallpaperPreview = 16843569; // 0x1010331
     field public static final int stepSize = 16843078; // 0x1010146
+    field public static final int stopWithTask = 16843623; // 0x1010367
     field public static final int streamType = 16843273; // 0x1010209
     field public static final int stretchColumns = 16843081; // 0x1010149
     field public static final int stretchMode = 16843030; // 0x1010116
@@ -929,9 +931,9 @@
     field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314
     field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f
     field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e
-    field public static final int textEditSuggestionItemLayout = 16843626; // 0x101036a
-    field public static final int textEditSuggestionsBottomWindowLayout = 16843624; // 0x1010368
-    field public static final int textEditSuggestionsTopWindowLayout = 16843625; // 0x1010369
+    field public static final int textEditSuggestionItemLayout = 16843627; // 0x101036b
+    field public static final int textEditSuggestionsBottomWindowLayout = 16843625; // 0x1010369
+    field public static final int textEditSuggestionsTopWindowLayout = 16843626; // 0x101036a
     field public static final int textFilterEnabled = 16843007; // 0x10100ff
     field public static final int textIsSelectable = 16843542; // 0x1010316
     field public static final int textOff = 16843045; // 0x1010125
@@ -943,7 +945,7 @@
     field public static final int textSelectHandleWindowStyle = 16843464; // 0x10102c8
     field public static final int textSize = 16842901; // 0x1010095
     field public static final int textStyle = 16842903; // 0x1010097
-    field public static final int textSuggestionsWindowStyle = 16843623; // 0x1010367
+    field public static final int textSuggestionsWindowStyle = 16843624; // 0x1010368
     field public static final int textViewStyle = 16842884; // 0x1010084
     field public static final int theme = 16842752; // 0x1010000
     field public static final int thickness = 16843360; // 0x1010260
@@ -3384,6 +3386,7 @@
     method public void onRebind(android.content.Intent);
     method public deprecated void onStart(android.content.Intent, int);
     method public int onStartCommand(android.content.Intent, int, int);
+    method public void onTaskRemoved(android.content.Intent);
     method public boolean onUnbind(android.content.Intent);
     method public final void startForeground(int, android.app.Notification);
     method public final void stopForeground(boolean);
@@ -5840,6 +5843,8 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
+    field public int flags;
     field public java.lang.String permission;
   }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ebe403b..fca6868 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -205,13 +205,6 @@
     public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002;
 
     /**
-     * Flag for use with {@link #getRecentTasks}: also return the thumbnail
-     * bitmap (if available) for each recent task.
-     * @hide
-     */
-    public static final int TASKS_GET_THUMBNAILS = 0x0001000;
-    
-    /**
      * Return a list of the tasks that the user has recently launched, with
      * the most recent being first and older ones after in order.
      * 
@@ -241,7 +234,7 @@
     /**
      * Information you can retrieve about a particular task that is currently
      * "running" in the system.  Note that a running task does not mean the
-     * given task actual has a process it is actively running in; it simply
+     * given task actually has a process it is actively running in; it simply
      * means that the user has gone to it and never closed it, but currently
      * the system may have killed its process and is only holding on to its
      * last state in order to restart it when the user returns.
@@ -396,6 +389,55 @@
         return getRunningTasks(maxNum, 0, null);
     }
 
+    /**
+     * Remove some end of a task's activity stack that is not part of
+     * the main application.  The selected activities will be finished, so
+     * they are no longer part of the main task.
+     *
+     * @param taskId The identifier of the task.
+     * @param subTaskIndex The number of the sub-task; this corresponds
+     * to the index of the thumbnail returned by {@link #getTaskThumbnails(int)}.
+     * @return Returns true if the sub-task was found and was removed.
+     *
+     * @hide
+     */
+    public boolean removeSubTask(int taskId, int subTaskIndex)
+            throws SecurityException {
+        try {
+            return ActivityManagerNative.getDefault().removeSubTask(taskId, subTaskIndex);
+        } catch (RemoteException e) {
+            // System dead, we will be dead too soon!
+            return false;
+        }
+    }
+
+    /**
+     * If set, the process of the root activity of the task will be killed
+     * as part of removing the task.
+     * @hide
+     */
+    public static final int REMOVE_TASK_KILL_PROCESS = 0x0001;
+
+    /**
+     * Completely remove the given task.
+     *
+     * @param taskId Identifier of the task to be removed.
+     * @param flags Additional operational flags.  May be 0 or
+     * {@link #REMOVE_TASK_KILL_PROCESS}.
+     * @return Returns true if the given task was found and removed.
+     *
+     * @hide
+     */
+    public boolean removeTask(int taskId, int flags)
+            throws SecurityException {
+        try {
+            return ActivityManagerNative.getDefault().removeTask(taskId, flags);
+        } catch (RemoteException e) {
+            // System dead, we will be dead too soon!
+            return false;
+        }
+    }
+
     /** @hide */
     public static class TaskThumbnails implements Parcelable {
         public Bitmap mainThumbnail;
@@ -405,9 +447,6 @@
         /** @hide */
         public IThumbnailRetriever retriever;
 
-        /** @hide Magic for ActivityManagerService.  Not marshalled */
-        public ArrayList<Bitmap> otherThumbnails;
-
         public TaskThumbnails() {
         }
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d41c2d0..4b09b34c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1405,6 +1405,28 @@
             reply.writeInt(result ? 1 : 0);
             return true;
         }
+        
+        case REMOVE_SUB_TASK_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            int subTaskIndex = data.readInt();
+            boolean result = removeSubTask(taskId, subTaskIndex);
+            reply.writeNoException();
+            reply.writeInt(result ? 1 : 0);
+            return true;
+        }
+
+        case REMOVE_TASK_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            int fl = data.readInt();
+            boolean result = removeTask(taskId, fl);
+            reply.writeNoException();
+            reply.writeInt(result ? 1 : 0);
+            return true;
+        }
 
         }
 
@@ -3162,6 +3184,34 @@
         data.recycle();
         return result;
     }
+    
+    public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        data.writeInt(subTaskIndex);
+        mRemote.transact(REMOVE_SUB_TASK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean result = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
+
+    public boolean removeTask(int taskId, int flags) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        data.writeInt(flags);
+        mRemote.transact(REMOVE_TASK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean result = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
 
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 57a79a9..4dfba91 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -334,6 +334,7 @@
 
     private static final class ServiceArgsData {
         IBinder token;
+        boolean taskRemoved;
         int startId;
         int flags;
         Intent args;
@@ -534,10 +535,11 @@
             queueOrSendMessage(H.UNBIND_SERVICE, s);
         }
 
-        public final void scheduleServiceArgs(IBinder token, int startId,
+        public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
             int flags ,Intent args) {
             ServiceArgsData s = new ServiceArgsData();
             s.token = token;
+            s.taskRemoved = taskRemoved;
             s.startId = startId;
             s.flags = flags;
             s.args = args;
@@ -2129,7 +2131,13 @@
                 if (data.args != null) {
                     data.args.setExtrasClassLoader(s.getClassLoader());
                 }
-                int res = s.onStartCommand(data.args, data.flags, data.startId);
+                int res;
+                if (!data.taskRemoved) {
+                    res = s.onStartCommand(data.args, data.flags, data.startId);
+                } else {
+                    s.onTaskRemoved(data.args);
+                    res = Service.START_TASK_REMOVED_COMPLETE;
+                }
 
                 QueuedWork.waitToFinish();
 
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index a82234e..0e511f2 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -219,6 +219,7 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             IBinder token = data.readStrongBinder();
+            boolean taskRemoved = data.readInt() != 0;
             int startId = data.readInt();
             int fl = data.readInt();
             Intent args;
@@ -227,7 +228,7 @@
             } else {
                 args = null;
             }
-            scheduleServiceArgs(token, startId, fl, args);
+            scheduleServiceArgs(token, taskRemoved, startId, fl, args);
             return true;
         }
 
@@ -688,11 +689,12 @@
         data.recycle();
     }
 
-    public final void scheduleServiceArgs(IBinder token, int startId,
+    public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
 	    int flags, Intent args) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
+        data.writeInt(taskRemoved ? 1 : 0);
         data.writeInt(startId);
         data.writeInt(flags);
         if (args != null) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5a15b08..bec697a 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -344,6 +344,10 @@
 
     // Multi-user APIs
     public boolean switchUser(int userid) throws RemoteException;
+    
+    public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
+
+    public boolean removeTask(int taskId, int flags) throws RemoteException;
 
     /*
      * Private non-Binder interfaces
@@ -561,4 +565,6 @@
     int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
     int ACTIVITY_SLEPT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+122;
     int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+123;
+    int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+124;
+    int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+125;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 55177a9..b29b088 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -73,8 +73,8 @@
             Intent intent, boolean rebind) throws RemoteException;
     void scheduleUnbindService(IBinder token,
             Intent intent) throws RemoteException;
-    void scheduleServiceArgs(IBinder token, int startId, int flags, Intent args)
-            throws RemoteException;
+    void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
+            int flags, Intent args) throws RemoteException;
     void scheduleStopService(IBinder token) throws RemoteException;
     static final int DEBUG_OFF = 0;
     static final int DEBUG_ON = 1;
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 05b9781..c179b35 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -371,6 +371,13 @@
     public static final int START_REDELIVER_INTENT = 3;
     
     /**
+     * Special constant for reporting that we are done processing
+     * {@link #onTaskRemoved(Intent)}.
+     * @hide
+     */
+    public static final int START_TASK_REMOVED_COMPLETE = 1000;
+
+    /**
      * This flag is set in {@link #onStartCommand} if the Intent is a
      * re-delivery of a previously delivered intent, because the service
      * had previously returned {@link #START_REDELIVER_INTENT} but had been
@@ -500,6 +507,19 @@
     }
     
     /**
+     * This is called if the service is currently running and the user has
+     * removed a task that comes from the service's application.  If you have
+     * set {@link android.content.pm.ServiceInfo#FLAG_STOP_WITH_TASK ServiceInfo.FLAG_STOP_WITH_TASK}
+     * then you will not receive this callback; instead, the service will simply
+     * be stopped.
+     *
+     * @param rootIntent The original root Intent that was used to launch
+     * the task that is being removed.
+     */
+    public void onTaskRemoved(Intent rootIntent) {
+    }
+
+    /**
      * Stop the service, if it was previously started.  This is the same as
      * calling {@link android.content.Context#stopService} for this particular service.
      *  
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 67cd4a2..54a8842 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2473,6 +2473,13 @@
             s.info.permission = str.length() > 0 ? str.toString().intern() : null;
         }
 
+        s.info.flags = 0;
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_stopWithTask,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
+        }
+
         sa.recycle();
 
         if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 087a4fe..612e345 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -33,17 +33,35 @@
      */
     public String permission;
 
+    /**
+     * Bit in {@link #flags}: If set, the service will automatically be
+     * stopped by the system if the user removes a task that is rooted
+     * in one of the application's activities.  Set from the
+     * {@link android.R.attr#stopWithTask} attribute.
+     */
+    public static final int FLAG_STOP_WITH_TASK = 0x0001;
+
+    /**
+     * Options that have been set in the service declaration in the
+     * manifest.
+     * These include:
+     * {@link #FLAG_STOP_WITH_TASK}
+     */
+    public int flags;
+
     public ServiceInfo() {
     }
 
     public ServiceInfo(ServiceInfo orig) {
         super(orig);
         permission = orig.permission;
+        flags = orig.flags;
     }
 
     public void dump(Printer pw, String prefix) {
         super.dumpFront(pw, prefix);
         pw.println(prefix + "permission=" + permission);
+        pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
     }
     
     public String toString() {
@@ -59,6 +77,7 @@
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         super.writeToParcel(dest, parcelableFlags);
         dest.writeString(permission);
+        dest.writeInt(flags);
     }
 
     public static final Creator<ServiceInfo> CREATOR =
@@ -74,5 +93,6 @@
     private ServiceInfo(Parcel source) {
         super(source);
         permission = source.readString();
+        flags = source.readInt();
     }
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2a4d1b2..7f18121 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -607,6 +607,13 @@
         android:label="@string/permlab_reorderTasks"
         android:description="@string/permdesc_reorderTasks" />
 
+    <!-- Allows an application to change to remove/kill tasks -->
+    <permission android:name="android.permission.REMOVE_TASKS"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_removeTasks"
+        android:description="@string/permdesc_removeTasks" />
+
     <!-- Allows an application to modify the current configuration, such
          as locale. -->
     <permission android:name="android.permission.CHANGE_CONFIGURATION"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 80beaa5..9b04f78 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1169,6 +1169,10 @@
              component specific values). -->
         <attr name="enabled" />
         <attr name="exported" />
+        <!-- If set to true, this service with be automatically stopped
+             when the user remove a task rooted in an activity owned by
+             the application.  The default is false. -->
+        <attr name="stopWithTask" format="boolean" />
     </declare-styleable>
     
     <!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d5c374d..778d934 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1656,6 +1656,7 @@
   <public type="attr" name="state_hovered" />
   <public type="attr" name="state_drag_can_accept" />
   <public type="attr" name="state_drag_hovered" />
+  <public type="attr" name="stopWithTask" />
 
   <public type="style" name="Theme.Holo.Light.NoActionBar" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8ef9a3b..bc419ec 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -506,6 +506,13 @@
         tasks to the foreground and background. Malicious applications can force
         themselves to the front without your control.</string>
 
+    <!-- Title of an application permission, allowing an application to remove/kill tasks -->
+    <string name="permlab_removeTasks">stop running applications</string>
+    <!-- Description of an application permission, allowing an application to remove/kill tasks -->
+    <string name="permdesc_removeTasks">Allows an application to remove
+        tasks and kill their applications. Malicious applications can disrupt
+        the behavior of other applications.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setDebugApp">enable application debugging</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index 8c6eefb..7d4629e 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -410,7 +410,7 @@
 
     void updateRunningTasks() {
         mRunningTaskList = mActivityManager.getRunningTasks(MAX_TASKS,
-                ActivityManager.TASKS_GET_THUMBNAILS, mThumbnailReceiver);
+                0, mThumbnailReceiver);
         if (DBG) Log.v(TAG, "Portrait: " + mPortraitMode);
         for (RunningTaskInfo r : mRunningTaskList) {
             if (r.thumbnail != null) {
@@ -441,8 +441,7 @@
         final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
 
         final List<ActivityManager.RecentTaskInfo> recentTasks =
-                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE
-                        | ActivityManager.TASKS_GET_THUMBNAILS);
+                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
 
         ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
                     .resolveActivityInfo(pm, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index a39bef8..cfb4975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -429,8 +429,7 @@
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
         final List<ActivityManager.RecentTaskInfo> recentTasks =
-                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE
-                        | ActivityManager.TASKS_GET_THUMBNAILS);
+                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
 
         ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
                     .resolveActivityInfo(pm, 0);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 9990b7b..768d990 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -441,8 +441,7 @@
     /**
      * List of intents that were used to start the most recent tasks.
      */
-    final ArrayList<TaskRecord> mRecentTasks
-            = new ArrayList<TaskRecord>();
+    final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
 
     /**
      * All of the applications we currently have running organized by name.
@@ -450,8 +449,7 @@
      * returned by the package manager), and the keys are ApplicationRecord
      * objects.
      */
-    final ProcessMap<ProcessRecord> mProcessNames
-            = new ProcessMap<ProcessRecord>();
+    final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
 
     /**
      * The currently running heavy-weight process, if any.
@@ -480,8 +478,7 @@
      * <p>NOTE: This object is protected by its own lock, NOT the global
      * activity manager lock!
      */
-    final SparseArray<ProcessRecord> mPidsSelfLocked
-            = new SparseArray<ProcessRecord>();
+    final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
 
     /**
      * All of the processes that have been forced to be foreground.  The key
@@ -2981,7 +2978,10 @@
         
         synchronized (this) {
             if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
-                Process.killProcess(app.pid);
+                Slog.w(TAG, "Killing " + app + ": background ANR");
+                EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                        app.processName, app.setAdj, "background ANR");
+                Process.killProcessQuiet(app.pid);
                 return;
             }
     
@@ -3446,7 +3446,9 @@
                     bringDownServiceLocked(sr, true);
                 }
             }
-            Process.killProcess(pid);
+            EventLog.writeEvent(EventLogTags.AM_KILL, pid,
+                    app.processName, app.setAdj, "start timeout");
+            Process.killProcessQuiet(pid);
             if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
                 Slog.w(TAG, "Unattached app died before backup, skipping");
                 try {
@@ -3492,7 +3494,7 @@
                     + " (IApplicationThread " + thread + "); dropping process");
             EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
             if (pid > 0 && pid != MY_PID) {
-                Process.killProcess(pid);
+                Process.killProcessQuiet(pid);
             } else {
                 try {
                     thread.scheduleExit();
@@ -4832,11 +4834,6 @@
                 throw new SecurityException(msg);
             }
 
-            final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0
-                    && checkCallingPermission(
-                            android.Manifest.permission.READ_FRAME_BUFFER)
-                            == PackageManager.PERMISSION_GRANTED;
-
             int pos = mMainStack.mHistory.size()-1;
             ActivityRecord next =
                 pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
@@ -4876,13 +4873,6 @@
                     ci.id = curTask.taskId;
                     ci.baseActivity = r.intent.getComponent();
                     ci.topActivity = top.intent.getComponent();
-                    if (canReadFb) {
-                        if (top.state == ActivityState.RESUMED) {
-                            ci.thumbnail = top.stack.screenshotActivities(top);
-                        } else if (top.thumbHolder != null) {
-                            ci.thumbnail = top.thumbHolder.lastThumbnail;
-                        }
-                    }
                     if (top.thumbHolder != null) {
                         ci.description = top.thumbHolder.lastDescription;
                     }
@@ -4955,8 +4945,6 @@
 
             IPackageManager pm = AppGlobals.getPackageManager();
             
-            ActivityRecord resumed = mMainStack.mResumedActivity;
-            
             final int N = mRecentTasks.size();
             ArrayList<ActivityManager.RecentTaskInfo> res
                     = new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -5002,75 +4990,122 @@
         }
     }
 
-    public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
-        synchronized (this) {
-            enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
-                    "getTaskThumbnail()");
-            ActivityRecord resumed = mMainStack.mResumedActivity;
-            final int N = mRecentTasks.size();
-            for (int i=0; i<N; i++) {
-                TaskRecord tr = mRecentTasks.get(i);
-                if (tr.taskId == id) {
-                    final ActivityManager.TaskThumbnails thumbs
-                            = new ActivityManager.TaskThumbnails();
-                    if (resumed != null && resumed.thumbHolder == tr) {
-                        thumbs.mainThumbnail = resumed.stack.screenshotActivities(resumed);
-                    } else {
-                        thumbs.mainThumbnail = tr.lastThumbnail;
-                    }
-                    // How many different sub-thumbnails?
-                    final int NA = mMainStack.mHistory.size();
-                    int j = 0;
-                    ThumbnailHolder holder = null;
-                    while (j < NA) {
-                        ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j);
-                        j++;
-                        if (ar.task == tr) {
-                            holder = ar.thumbHolder;
-                            break;
-                        }
-                    }
-                    ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
-                    thumbs.otherThumbnails = bitmaps;
-                    ActivityRecord lastActivity = null;
-                    while (j < NA) {
-                        ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j);
-                        j++;
-                        if (ar.task != tr) {
-                            break;
-                        }
-                        lastActivity = ar;
-                        if (ar.thumbHolder != holder && holder != null) {
-                            thumbs.numSubThumbbails++;
-                            holder = ar.thumbHolder;
-                            bitmaps.add(holder.lastThumbnail);
-                        }
-                    }
-                    if (lastActivity != null && bitmaps.size() > 0) {
-                        if (resumed == lastActivity) {
-                            Bitmap bm = lastActivity.stack.screenshotActivities(lastActivity);
-                            if (bm != null) {
-                                bitmaps.remove(bitmaps.size()-1);
-                                bitmaps.add(bm);
-                            }
-                        }
-                    }
-                    if (thumbs.numSubThumbbails > 0) {
-                        thumbs.retriever = new IThumbnailRetriever.Stub() {
-                            public Bitmap getThumbnail(int index) {
-                                if (index < 0 || index >= thumbs.otherThumbnails.size()) {
-                                    return null;
-                                }
-                                return thumbs.otherThumbnails.get(index);
-                            }
-                        };
-                    }
-                    return thumbs;
-                }
+    private TaskRecord taskForIdLocked(int id) {
+        final int N = mRecentTasks.size();
+        for (int i=0; i<N; i++) {
+            TaskRecord tr = mRecentTasks.get(i);
+            if (tr.taskId == id) {
+                return tr;
             }
         }
         return null;
     }
+
+    public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
+        synchronized (this) {
+            enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
+                    "getTaskThumbnails()");
+            TaskRecord tr = taskForIdLocked(id);
+            if (tr != null) {
+                return mMainStack.getTaskThumbnailsLocked(tr);
+            }
+        }
+        return null;
+    }
+
+    public boolean removeSubTask(int taskId, int subTaskIndex) {
+        synchronized (this) {
+            enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+                    "removeSubTask()");
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return mMainStack.removeTaskActivitiesLocked(taskId, subTaskIndex) != null;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    private void removeTaskProcessesLocked(ActivityRecord root) {
+        TaskRecord tr = root.task;
+        Intent baseIntent = new Intent(
+                tr.intent != null ? tr.intent : tr.affinityIntent);
+        ComponentName component = baseIntent.getComponent();
+        if (component == null) {
+            Slog.w(TAG, "Now component for base intent of task: " + tr);
+            return;
+        }
+
+        // Find any running services associated with this app.
+        ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+        for (ServiceRecord sr : mServices.values()) {
+            if (sr.packageName.equals(component.getPackageName())) {
+                services.add(sr);
+            }
+        }
+
+        // Take care of any running services associated with the app.
+        for (int i=0; i<services.size(); i++) {
+            ServiceRecord sr = services.get(i);
+            if (sr.startRequested) {
+                if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
+                    stopServiceLocked(sr);
+                } else {
+                    sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
+                            sr.makeNextStartId(), baseIntent, -1));
+                    if (sr.app != null && sr.app.thread != null) {
+                        sendServiceArgsLocked(sr, false);
+                    }
+                }
+            }
+        }
+
+        // Find any running processes associated with this app.
+        ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
+        SparseArray<ProcessRecord> appProcs
+                = mProcessNames.getMap().get(component.getPackageName());
+        if (appProcs != null) {
+            for (int i=0; i<appProcs.size(); i++) {
+                procs.add(appProcs.valueAt(i));
+            }
+        }
+
+        // Kill the running processes.
+        for (int i=0; i<procs.size(); i++) {
+            ProcessRecord pr = procs.get(i);
+            if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+                Slog.i(TAG, "Killing " + pr + ": remove task");
+                EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid,
+                        pr.processName, pr.setAdj, "remove task");
+                Process.killProcessQuiet(pr.pid);
+            } else {
+                pr.waitingToKill = "remove task";
+            }
+        }
+    }
+
+    public boolean removeTask(int taskId, int flags) {
+        synchronized (this) {
+            enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+                    "removeTask()");
+            long ident = Binder.clearCallingIdentity();
+            try {
+                ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1);
+                if (r != null) {
+                    mRecentTasks.remove(r.task);
+
+                    if ((flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0) {
+                        removeTaskProcessesLocked(r);
+                    }
+
+                    return true;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+        return false;
+    }
     
     private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
         int j;
@@ -5123,21 +5158,18 @@
             }
             final long origId = Binder.clearCallingIdentity();
             try {
-                int N = mRecentTasks.size();
-                for (int i=0; i<N; i++) {
-                    TaskRecord tr = mRecentTasks.get(i);
-                    if (tr.taskId == task) {
-                        if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
-                            mMainStack.mUserLeaving = true;
-                        }
-                        if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
-                            // Caller wants the home activity moved with it.  To accomplish this,
-                            // we'll just move the home task to the top first.
-                            mMainStack.moveHomeToFrontLocked();
-                        }
-                        mMainStack.moveTaskToFrontLocked(tr, null);
-                        return;
+                TaskRecord tr = taskForIdLocked(task);
+                if (tr != null) {
+                    if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+                        mMainStack.mUserLeaving = true;
                     }
+                    if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+                        // Caller wants the home activity moved with it.  To accomplish this,
+                        // we'll just move the home task to the top first.
+                        mMainStack.moveHomeToFrontLocked();
+                    }
+                    mMainStack.moveTaskToFrontLocked(tr, null);
+                    return;
                 }
                 for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
                     ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
@@ -6661,11 +6693,10 @@
             }
             if (app.pid > 0 && app.pid != MY_PID) {
                 handleAppCrashLocked(app);
-                Slog.i(ActivityManagerService.TAG, "Killing "
-                        + app.processName + " (pid=" + app.pid + "): user's request");
+                Slog.i(ActivityManagerService.TAG, "Killing " + app + ": user's request");
                 EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
                         app.processName, app.setAdj, "user's request after error");
-                Process.killProcess(app.pid);
+                Process.killProcessQuiet(app.pid);
             }
         }
     }
@@ -8946,7 +8977,7 @@
                         + " in dying process " + proc.processName);
                 EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
                         capp.processName, capp.setAdj, "dying provider " + proc.processName);
-                Process.killProcess(capp.pid);
+                Process.killProcessQuiet(capp.pid);
             }
         }
         
@@ -9453,7 +9484,7 @@
                 if (si.doneExecutingCount > 0) {
                     flags |= Service.START_FLAG_REDELIVERY;
                 }
-                r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
+                r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
             } catch (RemoteException e) {
                 // Remote process gone...  we'll let the normal cleanup take
                 // care of this.
@@ -9539,11 +9570,8 @@
         // pending arguments, then fake up one so its onStartCommand() will
         // be called.
         if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
-            r.lastStartId++;
-            if (r.lastStartId < 1) {
-                r.lastStartId = 1;
-            }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
+                    null, -1));
         }
         
         sendServiceArgsLocked(r, true);
@@ -9897,11 +9925,7 @@
             }
             r.startRequested = true;
             r.callStart = false;
-            r.lastStartId++;
-            if (r.lastStartId < 1) {
-                r.lastStartId = 1;
-            }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                     service, targetPermissionUid));
             r.lastActivity = SystemClock.uptimeMillis();
             synchronized (r.stats.getBatteryStats()) {
@@ -9943,6 +9967,15 @@
         }
     }
 
+    private void stopServiceLocked(ServiceRecord service) {
+        synchronized (service.stats.getBatteryStats()) {
+            service.stats.stopRunningLocked();
+        }
+        service.startRequested = false;
+        service.callStart = false;
+        bringDownServiceLocked(service, false);
+    }
+
     public int stopService(IApplicationThread caller, Intent service,
             String resolvedType) {
         // Refuse possible leaked file descriptors
@@ -9966,14 +9999,12 @@
             ServiceLookupResult r = findServiceLocked(service, resolvedType);
             if (r != null) {
                 if (r.record != null) {
-                    synchronized (r.record.stats.getBatteryStats()) {
-                        r.record.stats.stopRunningLocked();
-                    }
-                    r.record.startRequested = false;
-                    r.record.callStart = false;
                     final long origId = Binder.clearCallingIdentity();
-                    bringDownServiceLocked(r.record, false);
-                    Binder.restoreCallingIdentity(origId);
+                    try {
+                        stopServiceLocked(r.record);
+                    } finally {
+                        Binder.restoreCallingIdentity(origId);
+                    }
                     return 1;
                 }
                 return -1;
@@ -10035,7 +10066,7 @@
                         }
                     }
                     
-                    if (r.lastStartId != startId) {
+                    if (r.getLastStartId() != startId) {
                         return false;
                     }
                     
@@ -10476,7 +10507,7 @@
                         case Service.START_NOT_STICKY: {
                             // We are done with the associated start arguments.
                             r.findDeliveredStart(startId, true);
-                            if (r.lastStartId == startId) {
+                            if (r.getLastStartId() == startId) {
                                 // There is no more work, and this service
                                 // doesn't want to hang around if killed.
                                 r.stopIfKilled = true;
@@ -10496,6 +10527,12 @@
                             }
                             break;
                         }
+                        case Service.START_TASK_REMOVED_COMPLETE: {
+                            // Special processing for onTaskRemoved().  Don't
+                            // impact normal onStartCommand() processing.
+                            r.findDeliveredStart(startId, true);
+                            break;
+                        }
                         default:
                             throw new IllegalArgumentException(
                                     "Unknown service start result: " + res);
@@ -12885,23 +12922,31 @@
                 if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
                         "Setting process group of " + app.processName
                         + " to " + app.curSchedGroup);
-                if (true) {
-                    long oldId = Binder.clearCallingIdentity();
-                    try {
-                        Process.setProcessGroup(app.pid, app.curSchedGroup);
-                    } catch (Exception e) {
-                        Slog.w(TAG, "Failed setting process group of " + app.pid
-                                + " to " + app.curSchedGroup);
-                        e.printStackTrace();
-                    } finally {
-                        Binder.restoreCallingIdentity(oldId);
-                    }
-                }
-                if (false) {
-                    if (app.thread != null) {
+                if (app.waitingToKill != null &&
+                        app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+                    Slog.i(TAG, "Killing " + app + ": " + app.waitingToKill);
+                    EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                            app.processName, app.setAdj, app.waitingToKill);
+                    Process.killProcessQuiet(app.pid);
+                } else {
+                    if (true) {
+                        long oldId = Binder.clearCallingIdentity();
                         try {
-                            app.thread.setSchedulingGroup(app.curSchedGroup);
-                        } catch (RemoteException e) {
+                            Process.setProcessGroup(app.pid, app.curSchedGroup);
+                        } catch (Exception e) {
+                            Slog.w(TAG, "Failed setting process group of " + app.pid
+                                    + " to " + app.curSchedGroup);
+                            e.printStackTrace();
+                        } finally {
+                            Binder.restoreCallingIdentity(oldId);
+                        }
+                    }
+                    if (false) {
+                        if (app.thread != null) {
+                            try {
+                                app.thread.setSchedulingGroup(app.curSchedGroup);
+                            } catch (RemoteException e) {
+                            }
                         }
                     }
                 }
@@ -13024,7 +13069,9 @@
                         + (app.thread != null ? app.thread.asBinder() : null)
                         + ")\n");
                     if (app.pid > 0 && app.pid != MY_PID) {
-                        Process.killProcess(app.pid);
+                        EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                                app.processName, app.setAdj, "empty");
+                        Process.killProcessQuiet(app.pid);
                     } else {
                         try {
                             app.thread.scheduleExit();
@@ -13090,7 +13137,9 @@
                             + (app.thread != null ? app.thread.asBinder() : null)
                             + ")\n");
                         if (app.pid > 0 && app.pid != MY_PID) {
-                            Process.killProcess(app.pid);
+                            EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                                    app.processName, app.setAdj, "empty");
+                            Process.killProcessQuiet(app.pid);
                         } else {
                             try {
                                 app.thread.scheduleExit();
@@ -13147,7 +13196,9 @@
                               + (app.thread != null ? app.thread.asBinder() : null)
                               + ")\n");
                         if (app.pid > 0 && app.pid != MY_PID) {
-                            Process.killProcess(app.pid);
+                            EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                                    app.processName, app.setAdj, "old background");
+                            Process.killProcessQuiet(app.pid);
                         } else {
                             try {
                                 app.thread.scheduleExit();
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index e1d380b..9558895 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -21,8 +21,10 @@
 import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
+import android.app.IThumbnailRetriever;
 import static android.app.IActivityManager.START_CLASS_NOT_FOUND;
 import static android.app.IActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
@@ -69,7 +71,7 @@
 /**
  * State and management of a single stack of activities.
  */
-final public class ActivityStack {
+final class ActivityStack {
     static final String TAG = ActivityManagerService.TAG;
     static final boolean localLOGV = ActivityManagerService.localLOGV;
     static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
@@ -135,14 +137,14 @@
      * The back history of all previous (and possibly still
      * running) activities.  It contains HistoryRecord objects.
      */
-    final ArrayList mHistory = new ArrayList();
+    final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();
     
     /**
      * List of running activities, sorted by recent usage.
      * The first entry in the list is the least recently used.
      * It contains HistoryRecord objects.
      */
-    final ArrayList mLRUActivities = new ArrayList();
+    final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<ActivityRecord>();
 
     /**
      * List of activities that are waiting for a new activity
@@ -346,7 +348,7 @@
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
         int i = mHistory.size()-1;
         while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (!r.finishing && r != notTop) {
                 return r;
             }
@@ -358,7 +360,7 @@
     final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
         int i = mHistory.size()-1;
         while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (!r.finishing && !r.delayedResume && r != notTop) {
                 return r;
             }
@@ -379,7 +381,7 @@
     final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
         int i = mHistory.size()-1;
         while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             // Note: the taskId check depends on real taskId fields being non-zero
             if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
                 return r;
@@ -425,7 +427,7 @@
 
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (!r.finishing && r.task != cp
                     && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                 cp = r.task;
@@ -469,7 +471,7 @@
 
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (!r.finishing) {
                 if (r.intent.getComponent().equals(cls)) {
                     //Slog.i(TAG, "Found matching class!");
@@ -504,6 +506,7 @@
         }
 
         r.app = app;
+        app.waitingToKill = null;
 
         if (localLOGV) Slog.v(TAG, "Launching: " + r);
 
@@ -678,7 +681,7 @@
         }
         // Ensure activities are no longer sleeping.
         for (int i=mHistory.size()-1; i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             r.setSleeping(false);
         }
         mGoingToSleepActivities.clear();
@@ -724,7 +727,7 @@
             // Make sure any stopped but visible activities are now sleeping.
             // This ensures that the activity's onStop() is called.
             for (int i=mHistory.size()-1; i>=0; i--) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(i);
+                ActivityRecord r = mHistory.get(i);
                 if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
                     r.setSleeping(true);
                 }
@@ -862,7 +865,7 @@
         synchronized (mService) {
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
-                r = (ActivityRecord)mHistory.get(index);
+                r = mHistory.get(index);
                 mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
                 if (mPausingActivity == r) {
                     r.state = ActivityState.PAUSED;
@@ -1024,7 +1027,7 @@
         ActivityRecord r;
         boolean behindFullscreen = false;
         for (; i>=0; i--) {
-            r = (ActivityRecord)mHistory.get(i);
+            r = mHistory.get(i);
             if (DEBUG_VISBILITY) Slog.v(
                     TAG, "Make visible? " + r + " finishing=" + r.finishing
                     + " state=" + r.state);
@@ -1108,7 +1111,7 @@
         // Now for any activities that aren't visible to the user, make
         // sure they no longer are keeping the screen frozen.
         while (i >= 0) {
-            r = (ActivityRecord)mHistory.get(i);
+            r = mHistory.get(i);
             if (DEBUG_VISBILITY) Slog.v(
                     TAG, "Make invisible? " + r + " finishing=" + r.finishing
                     + " state=" + r.state
@@ -1499,7 +1502,7 @@
             // If starting in an existing task, find where that is...
             boolean startIt = true;
             for (int i = NH-1; i >= 0; i--) {
-                ActivityRecord p = (ActivityRecord)mHistory.get(i);
+                ActivityRecord p = mHistory.get(i);
                 if (p.finishing) {
                     continue;
                 }
@@ -1646,7 +1649,7 @@
         int replyChainEnd = -1;
         int lastReparentPos = -1;
         for (int i=mHistory.size()-1; i>=-1; i--) {
-            ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
+            ActivityRecord below = i >= 0 ? mHistory.get(i) : null;
             
             if (below != null && below.finishing) {
                 continue;
@@ -1700,7 +1703,7 @@
                         // bottom of the activity stack.  This also keeps it
                         // correctly ordered with any activities we previously
                         // moved.
-                        ActivityRecord p = (ActivityRecord)mHistory.get(0);
+                        ActivityRecord p = mHistory.get(0);
                         if (target.taskAffinity != null
                                 && target.taskAffinity.equals(p.task.affinity)) {
                             // If the activity currently at the bottom has the
@@ -1727,7 +1730,7 @@
                         int dstPos = 0;
                         ThumbnailHolder curThumbHolder = target.thumbHolder;
                         for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                            p = (ActivityRecord)mHistory.get(srcPos);
+                            p = mHistory.get(srcPos);
                             if (p.finishing) {
                                 continue;
                             }
@@ -1764,7 +1767,7 @@
                             // like these are all in the reply chain.
                             replyChainEnd = targetI+1;
                             while (replyChainEnd < mHistory.size() &&
-                                    ((ActivityRecord)mHistory.get(
+                                    (mHistory.get(
                                                 replyChainEnd)).task == task) {
                                 replyChainEnd++;
                             }
@@ -1774,7 +1777,7 @@
                         }
                         ActivityRecord p = null;
                         for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                            p = (ActivityRecord)mHistory.get(srcPos);
+                            p = mHistory.get(srcPos);
                             if (p.finishing) {
                                 continue;
                             }
@@ -1834,7 +1837,7 @@
                     }
                     ActivityRecord p = null;
                     for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                        p = (ActivityRecord)mHistory.get(srcPos);
+                        p = mHistory.get(srcPos);
                         if (p.finishing) {
                             continue;
                         }
@@ -1852,7 +1855,7 @@
                         replyChainEnd = targetI;
                     }
                     for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
-                        ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
+                        ActivityRecord p = mHistory.get(srcPos);
                         if (p.finishing) {
                             continue;
                         }
@@ -1881,7 +1884,7 @@
                     // below so it remains singleTop.
                     if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
                         for (int j=lastReparentPos-1; j>=0; j--) {
-                            ActivityRecord p = (ActivityRecord)mHistory.get(j);
+                            ActivityRecord p = mHistory.get(j);
                             if (p.finishing) {
                                 continue;
                             }
@@ -1922,7 +1925,7 @@
         // First find the requested task.
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (r.task.taskId == taskId) {
                 i++;
                 break;
@@ -1932,7 +1935,7 @@
         // Now clear it.
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (r.finishing) {
                 continue;
             }
@@ -1944,7 +1947,7 @@
                 ActivityRecord ret = r;
                 while (i < (mHistory.size()-1)) {
                     i++;
-                    r = (ActivityRecord)mHistory.get(i);
+                    r = mHistory.get(i);
                     if (r.task.taskId != taskId) {
                         break;
                     }
@@ -1980,6 +1983,28 @@
     }
 
     /**
+     * Completely remove all activities associated with an existing
+     * task starting at a specified index.
+     */
+    private final void performClearTaskAtIndexLocked(int taskId, int i) {
+        while (i < (mHistory.size()-1)) {
+            ActivityRecord r = mHistory.get(i);
+            if (r.task.taskId != taskId) {
+                // Whoops hit the end.
+                return;
+            }
+            if (r.finishing) {
+                i++;
+                continue;
+            }
+            if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+                    null, "clear")) {
+                i++;
+            }
+        }
+    }
+
+    /**
      * Completely remove all activities associated with an existing task.
      */
     private final void performClearTaskLocked(int taskId) {
@@ -1988,37 +2013,23 @@
         // First find the requested task.
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (r.task.taskId == taskId) {
                 i++;
                 break;
             }
         }
 
-        // Now clear it.
+        // Now find the start and clear it.
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (r.finishing) {
                 continue;
             }
             if (r.task.taskId != taskId) {
                 // We hit the bottom.  Now finish it all...
-                while (i < (mHistory.size()-1)) {
-                    i++;
-                    r = (ActivityRecord)mHistory.get(i);
-                    if (r.task.taskId != taskId) {
-                        // Whoops hit the end.
-                        return;
-                    }
-                    if (r.finishing) {
-                        continue;
-                    }
-                    if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
-                            null, "clear")) {
-                        i--;
-                    }
-                }
+                performClearTaskAtIndexLocked(taskId, i+1);
                 return;
             }
         }
@@ -2032,7 +2043,7 @@
         int i = mHistory.size();
         while (i > 0) {
             i--;
-            ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
+            ActivityRecord candidate = mHistory.get(i);
             if (candidate.task.taskId != task) {
                 break;
             }
@@ -2049,9 +2060,9 @@
      * brought to the front.
      */
     private final ActivityRecord moveActivityToFrontLocked(int where) {
-        ActivityRecord newTop = (ActivityRecord)mHistory.remove(where);
+        ActivityRecord newTop = mHistory.remove(where);
         int top = mHistory.size();
-        ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
+        ActivityRecord oldTop = mHistory.get(top-1);
         mHistory.add(top, newTop);
         oldTop.frontOfTask = false;
         newTop.frontOfTask = true;
@@ -2094,7 +2105,7 @@
             if (DEBUG_RESULTS) Slog.v(
                 TAG, "Sending result to " + resultTo + " (index " + index + ")");
             if (index >= 0) {
-                sourceRecord = (ActivityRecord)mHistory.get(index);
+                sourceRecord = mHistory.get(index);
                 if (requestCode >= 0 && !sourceRecord.finishing) {
                     resultRecord = sourceRecord;
                 }
@@ -2576,7 +2587,7 @@
             // this case should never happen.
             final int N = mHistory.size();
             ActivityRecord prev =
-                N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
+                N > 0 ? mHistory.get(N-1) : null;
             r.setTask(prev != null
                     ? prev.task
                     : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
@@ -3021,7 +3032,7 @@
             // Get the activity record.
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(index);
+                ActivityRecord r = mHistory.get(index);
 
                 if (fromTimeout) {
                     reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
@@ -3153,12 +3164,12 @@
         if (index < 0) {
             return false;
         }
-        ActivityRecord r = (ActivityRecord)mHistory.get(index);
+        ActivityRecord r = mHistory.get(index);
 
         // Is this the last activity left?
         boolean lastActivity = true;
         for (int i=mHistory.size()-1; i>=0; i--) {
-            ActivityRecord p = (ActivityRecord)mHistory.get(i);
+            ActivityRecord p = mHistory.get(i);
             if (!p.finishing && p != r) {
                 lastActivity = false;
                 break;
@@ -3193,7 +3204,7 @@
                 System.identityHashCode(r),
                 r.task.taskId, r.shortComponentName, reason);
         if (index < (mHistory.size()-1)) {
-            ActivityRecord next = (ActivityRecord)mHistory.get(index+1);
+            ActivityRecord next = mHistory.get(index+1);
             if (next.task == r.task) {
                 if (r.frontOfTask) {
                     // The next activity is now the front of the task.
@@ -3249,7 +3260,7 @@
 
         if (mResumedActivity == r) {
             boolean endTask = index <= 0
-                    || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
+                    || (mHistory.get(index-1)).task != r.task;
             if (DEBUG_TRANSITION) Slog.v(TAG,
                     "Prepare close transition: finishing " + r);
             mService.mWindowManager.prepareAppTransition(endTask
@@ -3391,6 +3402,7 @@
         // Get rid of any pending idle timeouts.
         mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
         mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
     }
 
     private final void removeActivityFromHistoryLocked(ActivityRecord r) {
@@ -3516,7 +3528,7 @@
             
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(index);
+                ActivityRecord r = mHistory.get(index);
                 if (r.state == ActivityState.DESTROYING) {
                     final long origId = Binder.clearCallingIdentity();
                     removeActivityFromHistoryLocked(r);
@@ -3558,7 +3570,7 @@
     final void moveHomeToFrontLocked() {
         TaskRecord homeTask = null;
         for (int i=mHistory.size()-1; i>=0; i--) {
-            ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+            ActivityRecord hr = mHistory.get(i);
             if (hr.isHomeActivity) {
                 homeTask = hr.task;
                 break;
@@ -3576,7 +3588,7 @@
         final int task = tr.taskId;
         int top = mHistory.size()-1;
 
-        if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
+        if (top < 0 || (mHistory.get(top)).task.taskId == task) {
             // nothing to do!
             return;
         }
@@ -3591,7 +3603,7 @@
         // Shift all activities with this task up to the top
         // of the stack, keeping them in the same internal order.
         while (pos >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+            ActivityRecord r = mHistory.get(pos);
             if (localLOGV) Slog.v(
                 TAG, "At " + pos + " ckp " + r.task + ": " + r);
             if (r.task.taskId == task) {
@@ -3680,7 +3692,7 @@
         // Shift all activities with this task down to the bottom
         // of the stack, keeping them in the same internal order.
         while (pos < N) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+            ActivityRecord r = mHistory.get(pos);
             if (localLOGV) Slog.v(
                 TAG, "At " + pos + " ckp " + r.task + ": " + r);
             if (r.task.taskId == task) {
@@ -3714,6 +3726,106 @@
         return true;
     }
     
+    public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) {
+        TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true);
+        ActivityRecord resumed = mResumedActivity;
+        if (resumed != null && resumed.thumbHolder == tr) {
+            info.mainThumbnail = resumed.stack.screenshotActivities(resumed);
+        } else {
+            info.mainThumbnail = tr.lastThumbnail;
+        }
+        return info;
+    }
+
+    public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex) {
+        TaskAccessInfo info = getTaskAccessInfoLocked(taskId, false);
+        if (info.root == null) {
+            Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
+            return null;
+        }
+
+        if (subTaskIndex < 0) {
+            // Just remove the entire task.
+            performClearTaskAtIndexLocked(taskId, info.rootIndex);
+            return info.root;
+        }
+
+        if (subTaskIndex >= info.subtasks.size()) {
+            Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex);
+            return null;
+        }
+
+        // Remove all of this task's activies starting at the sub task.
+        TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex);
+        performClearTaskAtIndexLocked(taskId, subtask.index);
+        return subtask.activity;
+    }
+
+    public TaskAccessInfo getTaskAccessInfoLocked(int taskId, boolean inclThumbs) {
+        ActivityRecord resumed = mResumedActivity;
+        final TaskAccessInfo thumbs = new TaskAccessInfo();
+        // How many different sub-thumbnails?
+        final int NA = mHistory.size();
+        int j = 0;
+        ThumbnailHolder holder = null;
+        while (j < NA) {
+            ActivityRecord ar = mHistory.get(j);
+            if (!ar.finishing && ar.task.taskId == taskId) {
+                holder = ar.thumbHolder;
+                break;
+            }
+            j++;
+        }
+
+        if (j >= NA) {
+            return thumbs;
+        }
+
+        thumbs.root = mHistory.get(j);
+        thumbs.rootIndex = j;
+
+        ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>();
+        thumbs.subtasks = subtasks;
+        ActivityRecord lastActivity = null;
+        while (j < NA) {
+            ActivityRecord ar = mHistory.get(j);
+            j++;
+            if (ar.finishing) {
+                continue;
+            }
+            if (ar.task.taskId != taskId) {
+                break;
+            }
+            lastActivity = ar;
+            if (ar.thumbHolder != holder && holder != null) {
+                thumbs.numSubThumbbails++;
+                holder = ar.thumbHolder;
+                TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask();
+                sub.thumbnail = holder.lastThumbnail;
+                sub.activity = ar;
+                sub.index = j-1;
+                subtasks.add(sub);
+            }
+        }
+        if (lastActivity != null && subtasks.size() > 0) {
+            if (resumed == lastActivity) {
+                TaskAccessInfo.SubTask sub = subtasks.get(subtasks.size()-1);
+                sub.thumbnail = lastActivity.stack.screenshotActivities(lastActivity);
+            }
+        }
+        if (thumbs.numSubThumbbails > 0) {
+            thumbs.retriever = new IThumbnailRetriever.Stub() {
+                public Bitmap getThumbnail(int index) {
+                    if (index < 0 || index >= thumbs.subtasks.size()) {
+                        return null;
+                    }
+                    return thumbs.subtasks.get(index).thumbnail;
+                }
+            };
+        }
+        return thumbs;
+    }
+
     private final void logStartActivity(int tag, ActivityRecord r,
             TaskRecord task) {
         EventLog.writeEvent(tag,
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 353ff6d..e39c239 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -65,6 +65,7 @@
     boolean foregroundServices; // Running any services that are foreground?
     boolean bad;                // True if disabled in the bad process list
     boolean killedBackground;   // True when proc has been killed due to too many bg
+    String waitingToKill;       // Process is waiting to be killed when in the bg; reason
     IBinder forcingToForeground;// Token that is forcing this process to be foreground
     int adjSeq;                 // Sequence id for identifying oom_adj assignment cycles
     int lruSeq;                 // Sequence id for identifying LRU update cycles
@@ -202,8 +203,9 @@
                 pw.print(" lastLowMemory=");
                 TimeUtils.formatDuration(lastLowMemory, now, pw);
                 pw.print(" reportLowMemory="); pw.println(reportLowMemory);
-        if (killedBackground) {
-            pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
+        if (killedBackground || waitingToKill != null) {
+            pw.print(prefix); pw.print("killedBackground="); pw.print(killedBackground);
+                    pw.print(" waitingToKill="); pw.println(waitingToKill);
         }
         if (debugging || crashing || crashDialog != null || notResponding
                 || anrDialog != null || bad) {
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 1a617dd..da00c0c 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -84,7 +84,6 @@
     boolean startRequested; // someone explicitly called start?
     boolean stopIfKilled;   // last onStart() said to stop if service killed?
     boolean callStart;      // last onStart() has asked to alway be called on restart.
-    int lastStartId;        // identifier of most recent start request.
     int executeNesting;     // number of outstanding operations keeping foreground.
     long executingStart;    // start time of last execute request.
     int crashCount;         // number of times proc has crashed with service running
@@ -96,8 +95,11 @@
 
     String stringName;      // caching of toString
     
+    private int lastStartId;    // identifier of most recent start request.
+
     static class StartItem {
         final ServiceRecord sr;
+        final boolean taskRemoved;
         final int id;
         final Intent intent;
         final int targetPermissionUid;
@@ -108,8 +110,10 @@
 
         String stringName;      // caching of toString
 
-        StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+        StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
+                int _targetPermissionUid) {
             sr = _sr;
+            taskRemoved = _taskRemoved;
             id = _id;
             intent = _intent;
             targetPermissionUid = _targetPermissionUid;
@@ -321,6 +325,18 @@
         return null;
     }
     
+    public int getLastStartId() {
+        return lastStartId;
+    }
+
+    public int makeNextStartId() {
+        lastStartId++;
+        if (lastStartId < 1) {
+            lastStartId = 1;
+        }
+        return lastStartId;
+    }
+
     public void postNotification() {
         final int appUid = appInfo.uid;
         final int appPid = app.pid;
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index 6fa27ed..de3b6d1 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.google.android.test.activity">
     <uses-permission android:name="android.permission.GET_TASKS" />
+    <uses-permission android:name="android.permission.REORDER_TASKS" />
+    <uses-permission android:name="android.permission.REMOVE_TASKS" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <application android:label="ActivityTest">
         <activity android:name="ActivityTestMain">
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 8c5c35a..583c13c 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -23,6 +23,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.Application;
+import android.content.ActivityNotFoundException;
 import android.os.Bundle;
 import android.graphics.BitmapFactory;
 import android.graphics.Bitmap;
@@ -43,7 +44,11 @@
 import android.util.Log;
 
 public class ActivityTestMain extends Activity {
-    private void addThumbnail(LinearLayout container, Bitmap bm) {
+    ActivityManager mAm;
+
+    private void addThumbnail(LinearLayout container, Bitmap bm,
+            final ActivityManager.RecentTaskInfo task,
+            final ActivityManager.TaskThumbnails thumbs, final int subIndex) {
         ImageView iv = new ImageView(this);
         if (bm != null) {
             iv.setImageBitmap(bm);
@@ -52,24 +57,72 @@
         int w = getResources().getDimensionPixelSize(android.R.dimen.thumbnail_width);
         int h = getResources().getDimensionPixelSize(android.R.dimen.thumbnail_height);
         container.addView(iv, new LinearLayout.LayoutParams(w, h));
+
+        iv.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (task.id >= 0 && thumbs != null) {
+                    if (subIndex < (thumbs.numSubThumbbails-1)) {
+                        mAm.removeSubTask(task.id, subIndex+1);
+                    }
+                    mAm.moveTaskToFront(task.id, ActivityManager.MOVE_TASK_WITH_HOME);
+                } else {
+                    try {
+                        startActivity(task.baseIntent);
+                    } catch (ActivityNotFoundException e) {
+                        Log.w("foo", "Unable to start task: " + e);
+                    }
+                }
+                buildUi();
+            }
+        });
+        iv.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                if (task.id >= 0 && thumbs != null) {
+                    if (subIndex < 0) {
+                        mAm.removeTask(task.id, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+                    } else {
+                        mAm.removeSubTask(task.id, subIndex);
+                    }
+                    buildUi();
+                    return true;
+                }
+                return false;
+            }
+        });
     }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        ActivityManager am = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
+        mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
+    }
 
+    @Override
+    protected void onStart() {
+        super.onStart();
+        buildUi();
+    }
+
+    private View scrollWrap(View view) {
+        ScrollView scroller = new ScrollView(this);
+        scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,
+                ScrollView.LayoutParams.MATCH_PARENT));
+        return scroller;
+    }
+
+    private void buildUi() {
         LinearLayout top = new LinearLayout(this);
         top.setOrientation(LinearLayout.VERTICAL);
 
-        List<ActivityManager.RecentTaskInfo> recents = am.getRecentTasks(10,
+        List<ActivityManager.RecentTaskInfo> recents = mAm.getRecentTasks(10,
                 ActivityManager.RECENT_WITH_EXCLUDED);
         if (recents != null) {
             for (int i=0; i<recents.size(); i++) {
                 ActivityManager.RecentTaskInfo r = recents.get(i);
-                ActivityManager.TaskThumbnails tt = r != null
-                        ? am.getTaskThumbnails(r.persistentId) : null;
+                ActivityManager.TaskThumbnails tt = mAm.getTaskThumbnails(r.persistentId);
                 TextView tv = new TextView(this);
                 tv.setText(r.baseIntent.getComponent().flattenToShortString());
                 top.addView(tv, new LinearLayout.LayoutParams(
@@ -77,9 +130,9 @@
                         LinearLayout.LayoutParams.WRAP_CONTENT));
                 LinearLayout item = new LinearLayout(this);
                 item.setOrientation(LinearLayout.HORIZONTAL);
-                addThumbnail(item, tt != null ? tt.mainThumbnail : null);
+                addThumbnail(item, tt != null ? tt.mainThumbnail : null, r, tt, -1);
                 for (int j=0; j<tt.numSubThumbbails; j++) {
-                    addThumbnail(item, tt.getSubThumbnail(j));
+                    addThumbnail(item, tt.getSubThumbnail(j), r, tt, j);
                 }
                 top.addView(item, new LinearLayout.LayoutParams(
                         LinearLayout.LayoutParams.WRAP_CONTENT,
@@ -89,11 +142,4 @@
 
         setContentView(scrollWrap(top));
     }
-
-    private View scrollWrap(View view) {
-        ScrollView scroller = new ScrollView(this);
-        scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,
-                ScrollView.LayoutParams.MATCH_PARENT));
-        return scroller;
-    }
 }