diff options
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; - } } |