summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt13
-rw-r--r--core/java/android/app/ActivityManager.java61
-rw-r--r--core/java/android/app/ActivityManagerNative.java50
-rw-r--r--core/java/android/app/ActivityThread.java12
-rw-r--r--core/java/android/app/ApplicationThreadNative.java6
-rw-r--r--core/java/android/app/IActivityManager.java6
-rw-r--r--core/java/android/app/IApplicationThread.java4
-rw-r--r--core/java/android/app/Service.java20
-rw-r--r--core/java/android/content/pm/PackageParser.java7
-rw-r--r--core/java/android/content/pm/ServiceInfo.java20
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--core/res/res/values/public.xml1
-rwxr-xr-xcore/res/res/values/strings.xml7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java3
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java331
-rw-r--r--services/java/com/android/server/am/ActivityStack.java228
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java6
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java20
-rw-r--r--tests/ActivityTests/AndroidManifest.xml2
-rw-r--r--tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java74
22 files changed, 645 insertions, 242 deletions
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 @@ package android {
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 @@ package android {
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 @@ package android {
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 @@ package android {
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 @@ package android.app {
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 @@ package android.content.pm {
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 class ActivityManager {
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 @@ public class ActivityManager {
/**
* 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 @@ public class ActivityManager {
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 @@ public class ActivityManager {
/** @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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ public final class ActivityThread {
private static final class ServiceArgsData {
IBinder token;
+ boolean taskRemoved;
int startId;
int flags;
Intent args;
@@ -534,10 +535,11 @@ public final class ActivityThread {
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 @@ public final class ActivityThread {
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 @@ public abstract class ApplicationThreadNative extends Binder
{
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 @@ public abstract class ApplicationThreadNative extends Binder
} else {
args = null;
}
- scheduleServiceArgs(token, startId, fl, args);
+ scheduleServiceArgs(token, taskRemoved, startId, fl, args);
return true;
}
@@ -688,11 +689,12 @@ class ApplicationThreadProxy implements IApplicationThread {
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 @@ public interface IActivityManager extends IInterface {
// 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 @@ public interface IActivityManager extends IInterface {
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 @@ public interface IApplicationThread extends IInterface {
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 abstract class Service extends ContextWrapper implements ComponentCallbac
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 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
+ * 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 @@ public class PackageParser {
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 class ServiceInfo extends ComponentInfo
*/
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 class ServiceInfo extends ComponentInfo
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 @@ public class ServiceInfo extends ComponentInfo
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 @@ public class RecentApplicationsActivity extends Activity {
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 @@ public class RecentApplicationsActivity extends Activity {
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 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
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 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* 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 @@ public final class ActivityManagerService extends ActivityManagerNative
* 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 @@ public final class ActivityManagerService extends ActivityManagerNative
* <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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " (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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
IPackageManager pm = AppGlobals.getPackageManager();
- ActivityRecord resumed = mMainStack.mResumedActivity;
-
final int N = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res
= new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -5002,74 +4990,121 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ 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,
- "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);
- }
- }
+ "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);
}
- 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);
- }
- };
+ }
+ }
+ }
+
+ // 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 thumbs;
+
+ return true;
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
- return null;
+ return false;
}
private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
@@ -5123,21 +5158,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
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 @@ public final class ActivityManagerService extends ActivityManagerNative
}
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 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " 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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
// 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 @@ public final class ActivityManagerService extends ActivityManagerNative
}
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 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ 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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- if (r.lastStartId != startId) {
+ if (r.getLastStartId() != startId) {
return false;
}
@@ -10476,7 +10507,7 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
}
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 @@ public final class ActivityManagerService extends ActivityManagerNative
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 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (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 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (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 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (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.internal.os.BatteryStatsImpl;
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 @@ import java.util.List;
/**
* 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 @@ final public class ActivityStack {
* 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 public class ActivityStack {
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 public class ActivityStack {
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 public class ActivityStack {
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 public class ActivityStack {
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 public class ActivityStack {
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 @@ final public class ActivityStack {
}
r.app = app;
+ app.waitingToKill = null;
if (localLOGV) Slog.v(TAG, "Launching: " + r);
@@ -678,7 +681,7 @@ final public class ActivityStack {
}
// 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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
}
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 @@ final public class ActivityStack {
}
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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
}
/**
+ * 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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
* 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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
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 public class ActivityStack {
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 public class ActivityStack {
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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
// 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 @@ final public class ActivityStack {
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 @@ class ProcessRecord {
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 @@ class ProcessRecord {
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 @@ class ServiceRecord extends Binder {
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 @@ class ServiceRecord extends Binder {
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 @@ class ServiceRecord extends Binder {
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 @@ class ServiceRecord extends Binder {
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.Activity;
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.DisplayMetrics;
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 @@ public class ActivityTestMain extends Activity {
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 @@ public class ActivityTestMain extends Activity {
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 @@ public class ActivityTestMain extends Activity {
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;
- }
}