summaryrefslogtreecommitdiffstats
path: root/services/core
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2014-08-24 16:45:38 -0700
committerDianne Hackborn <hackbod@google.com>2014-08-26 11:16:59 -0700
commit89ad456ea49cb62615ebdcac83a2515743bbe5fa (patch)
tree4fdd6b948f74930ad10beca0a042e40f36efc50c /services/core
parent1ce1ba68acbfcbd4100d8c4be7d17a1f0623fd62 (diff)
downloadframeworks_base-89ad456ea49cb62615ebdcac83a2515743bbe5fa.zip
frameworks_base-89ad456ea49cb62615ebdcac83a2515743bbe5fa.tar.gz
frameworks_base-89ad456ea49cb62615ebdcac83a2515743bbe5fa.tar.bz2
Fix issue #16311398: Limit number of documents a process can open
In application processes, monitor for when we start getting close to the Dalvik heap limit, and ask the activity manager to try to prune old activity instances in that case. Add an explicit API for apps to ask that they have their own activity instances cleaned up, if they want. Fix some bugs in launching activities that were not correctly applying the "multi task" behavior in the appropriate situations of document-centric recents. Clean up the activity manager's process removal code to all share a common path. Add a new "Spam" option to ActivityTests, which continually creates new tasks, checking that the activity manager will now prune old tasks rather than letting the app run out of RAM. And while I was was doing this, I found problems with the path for bringing an empty task to the foreground -- it could make a new task instead of re-starting the root activity in the existing task. This is fixed, and some code in the recents UI for working around the bug is removed. And as long as I am doing that, we now have nice hooks in to the activity manager for AppTask to give some APIs for better managing the task, so add those along with more tests for these APIs in ActivityTests. We should look at also having the activity manager try to prune old tasks when it sees app processes being killed, to better balance memory use across multiple processes when some processes may host many documents. That however is for another CL... Change-Id: I2bb81c3f92819350c868c7a7470b35817eb9bea9
Diffstat (limited to 'services/core')
-rw-r--r--services/core/java/com/android/server/Watchdog.java16
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityManagerService.java275
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java18
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java67
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java168
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java17
7 files changed, 432 insertions, 131 deletions
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 1ce073a..89e3f49 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -332,6 +332,7 @@ public class Watchdog extends Thread {
final ArrayList<HandlerChecker> blockedCheckers;
final String subject;
final boolean allowRestart;
+ int debuggerWasConnected = 0;
synchronized (this) {
long timeout = CHECK_INTERVAL;
// Make sure we (re)spin the checkers that have become idle within
@@ -341,17 +342,27 @@ public class Watchdog extends Thread {
hc.scheduleCheckLocked();
}
+ if (debuggerWasConnected > 0) {
+ debuggerWasConnected--;
+ }
+
// NOTE: We use uptimeMillis() here because we do not want to increment the time we
// wait while asleep. If the device is asleep then the thing that we are waiting
// to timeout on is asleep as well and won't have a chance to run, causing a false
// positive on when to kill things.
long start = SystemClock.uptimeMillis();
while (timeout > 0) {
+ if (Debug.isDebuggerConnected()) {
+ debuggerWasConnected = 2;
+ }
try {
wait(timeout);
} catch (InterruptedException e) {
Log.wtf(TAG, e);
}
+ if (Debug.isDebuggerConnected()) {
+ debuggerWasConnected = 2;
+ }
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
@@ -450,7 +461,12 @@ public class Watchdog extends Thread {
// Only kill the process if the debugger is not attached.
if (Debug.isDebuggerConnected()) {
+ debuggerWasConnected = 2;
+ }
+ if (debuggerWasConnected >= 2) {
Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
+ } else if (debuggerWasConnected > 0) {
+ Slog.w(TAG, "Debugger was connected: Watchdog is *not* killing the system process");
} else if (!allowRestart) {
Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
} else {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e794b83..296b0e2 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -33,6 +33,7 @@ import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import android.Manifest;
import android.app.AppOpsManager;
+import android.app.ApplicationThreadNative;
import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
import android.app.IAppTask;
@@ -2831,8 +2832,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
}
- killUnneededProcessLocked(proc, Long.toString(proc.lastCachedPss)
- + "k from cached");
+ proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
} else if (proc != null && !keepIfLarge
&& mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
&& proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
@@ -2841,8 +2841,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
}
- killUnneededProcessLocked(proc, Long.toString(proc.lastCachedPss)
- + "k from cached");
+ proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
}
}
return proc;
@@ -3318,7 +3317,8 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
- null, null, null, null, 0, 0, 0, null, 0, null, false, null, null);
+ null, null, null, null, 0, 0, 0, null, 0, null, false, null, null,
+ null);
}
}
}
@@ -3462,7 +3462,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// TODO: Switch to user app stacks here.
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
null, null, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
- null, null, options, userId, null);
+ null, null, options, userId, null, null);
}
@Override
@@ -3512,7 +3512,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int ret = mStackSupervisor.startActivityMayWait(null, targetUid, targetPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
null, null, null, null, options, UserHandle.getUserId(sourceRecord.app.uid),
- null);
+ null, null);
return ret;
} catch (SecurityException e) {
// XXX need to figure out how to propagate to original app.
@@ -3542,7 +3542,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// TODO: Switch to user app stacks here.
mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
null, null, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
- res, null, options, userId, null);
+ res, null, options, userId, null, null);
return res;
}
@@ -3557,7 +3557,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// TODO: Switch to user app stacks here.
int ret = mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- null, null, null, config, options, userId, null);
+ null, null, null, config, options, userId, null, null);
return ret;
}
@@ -3615,7 +3615,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// TODO: Switch to user app stacks here.
return mStackSupervisor.startActivityMayWait(null, callingUid, callingPackage, intent,
resolvedType, session, interactor, null, null, 0, startFlags,
- profileFile, profileFd, null, null, options, userId, null);
+ profileFile, profileFd, null, null, options, userId, null, null);
}
@Override
@@ -3713,7 +3713,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
r.resolvedType, aInfo, null, null, resultTo != null ? resultTo.appToken : null,
resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0,
- options, false, null, null);
+ options, false, null, null, null);
Binder.restoreCallingIdentity(origId);
r.finishing = wasFinishing;
@@ -3732,36 +3732,42 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+ return startActivityFromRecentsInner(taskId, options);
+ }
+
+ final int startActivityFromRecentsInner(int taskId, Bundle options) {
+ final TaskRecord task;
final int callingUid;
final String callingPackage;
final Intent intent;
final int userId;
synchronized (this) {
- final TaskRecord task = recentTaskForIdLocked(taskId);
+ task = recentTaskForIdLocked(taskId);
if (task == null) {
- throw new ActivityNotFoundException("Task " + taskId + " not found.");
+ throw new IllegalArgumentException("Task " + taskId + " not found.");
}
callingUid = task.mCallingUid;
callingPackage = task.mCallingPackage;
intent = task.intent;
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
userId = task.userId;
}
return startActivityInPackage(callingUid, callingPackage, intent, null, null, null, 0, 0,
- options, userId, null);
+ options, userId, null, task);
}
final int startActivityInPackage(int uid, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Bundle options, int userId,
- IActivityContainer container) {
+ IActivityContainer container, TaskRecord inTask) {
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
// TODO: Switch to user app stacks here.
- int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent, resolvedType,
- null, null, resultTo, resultWho, requestCode, startFlags,
- null, null, null, null, options, userId, container);
+ int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent,
+ resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
+ null, null, null, null, options, userId, container, inTask);
return ret;
}
@@ -4155,6 +4161,35 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public boolean releaseActivityInstance(IBinder token) {
+ synchronized(this) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r.task == null || r.task.stack == null) {
+ return false;
+ }
+ return r.task.stack.safelyDestroyActivityLocked(r, "app-req");
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ @Override
+ public void releaseSomeActivities(IApplicationThread appInt) {
+ synchronized(this) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ ProcessRecord app = getRecordForAppLocked(appInt);
+ mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ @Override
public boolean willActivityBeVisible(IBinder token) {
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
@@ -4568,8 +4603,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// 0 == continue, -1 = kill process immediately
int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);
if (res < 0 && app.pid != MY_PID) {
- Process.killProcess(app.pid);
- Process.killProcessGroup(app.info.uid, app.pid);
+ app.kill("anr", true);
}
} catch (RemoteException e) {
mController = null;
@@ -4675,8 +4709,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int res = mController.appNotResponding(app.processName, app.pid, info.toString());
if (res != 0) {
if (res < 0 && app.pid != MY_PID) {
- Process.killProcess(app.pid);
- Process.killProcessGroup(app.info.uid, app.pid);
+ app.kill("anr", true);
} else {
synchronized (this) {
mServices.scheduleServiceTimeoutLocked(app);
@@ -4696,7 +4729,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
- killUnneededProcessLocked(app, "background ANR");
+ app.kill("bg anr", true);
return;
}
@@ -5420,8 +5453,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
- killUnneededProcessLocked(app, reason);
- Process.killProcessGroup(app.info.uid, app.pid);
+ app.kill(reason, true);
handleAppDiedLocked(app, true, allowRestart);
removeLruProcessLocked(app);
@@ -5469,7 +5501,7 @@ public final class ActivityManagerService extends ActivityManagerNative
checkAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
mServices.processStartTimedOutLocked(app);
- killUnneededProcessLocked(app, "start timeout");
+ app.kill("start timeout", true);
if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
Slog.w(TAG, "Unattached app died before backup, skipping");
try {
@@ -7915,17 +7947,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- private void killUnneededProcessLocked(ProcessRecord pr, String reason) {
- if (!pr.killedByAm) {
- Slog.i(TAG, "Killing " + pr.toShortString() + " (adj " + pr.setAdj + "): " + reason);
- EventLog.writeEvent(EventLogTags.AM_KILL, pr.userId, pr.pid,
- pr.processName, pr.setAdj, reason);
- pr.killedByAm = true;
- Process.killProcessQuiet(pr.pid);
- Process.killProcessGroup(pr.info.uid, pr.pid);
- }
- }
-
private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) {
tr.disposeThumbnail();
mRecentTasks.remove(tr);
@@ -7969,7 +7990,7 @@ public final class ActivityManagerService extends ActivityManagerNative
continue;
}
if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
- killUnneededProcessLocked(pr, "remove task");
+ pr.kill("remove task", true);
} else {
pr.waitingToKill = "remove task";
}
@@ -8022,32 +8043,36 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_STACK) Slog.d(TAG, "moveTaskToFront: moving taskId=" + taskId);
synchronized(this) {
- if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
- Binder.getCallingUid(), "Task to front")) {
- ActivityOptions.abort(options);
+ moveTaskToFrontLocked(taskId, flags, options);
+ }
+ }
+
+ void moveTaskToFrontLocked(int taskId, int flags, Bundle options) {
+ if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+ Binder.getCallingUid(), "Task to front")) {
+ ActivityOptions.abort(options);
+ return;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
return;
}
- final long origId = Binder.clearCallingIdentity();
- try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
- if (task == null) {
- return;
- }
- if (mStackSupervisor.isLockTaskModeViolation(task)) {
- mStackSupervisor.showLockTaskToast();
- Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
- return;
- }
- final ActivityRecord prev = mStackSupervisor.topRunningActivityLocked();
- if (prev != null && prev.isRecentsActivity()) {
- task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE);
- }
- mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options);
- } finally {
- Binder.restoreCallingIdentity(origId);
+ if (mStackSupervisor.isLockTaskModeViolation(task)) {
+ mStackSupervisor.showLockTaskToast();
+ Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
+ return;
}
- ActivityOptions.abort(options);
+ final ActivityRecord prev = mStackSupervisor.topRunningActivityLocked();
+ if (prev != null && prev.isRecentsActivity()) {
+ task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE);
+ }
+ mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
+ ActivityOptions.abort(options);
}
@Override
@@ -10145,7 +10170,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
int adj = proc.setAdj;
if (adj >= worstType && !proc.killedByAm) {
- killUnneededProcessLocked(proc, reason);
+ proc.kill(reason, true);
killed = true;
}
}
@@ -10189,7 +10214,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final int adj = proc.setAdj;
if (adj > belowAdj && !proc.killedByAm) {
- killUnneededProcessLocked(proc, reason);
+ proc.kill(reason, true);
killed = true;
}
}
@@ -10308,8 +10333,8 @@ public final class ActivityManagerService extends ActivityManagerNative
&& proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
if (doKilling && proc.initialIdlePss != 0
&& proc.lastPss > ((proc.initialIdlePss*3)/2)) {
- killUnneededProcessLocked(proc, "idle maint (pss " + proc.lastPss
- + " from " + proc.initialIdlePss + ")");
+ proc.kill("idle maint (pss " + proc.lastPss
+ + " from " + proc.initialIdlePss + ")", true);
}
}
} else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME) {
@@ -10778,7 +10803,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (app.pid > 0 && app.pid != MY_PID) {
handleAppCrashLocked(app, null, null, null);
- killUnneededProcessLocked(app, "user request after error");
+ app.kill("user request after error", true);
}
}
}
@@ -11345,8 +11370,11 @@ public final class ActivityManagerService extends ActivityManagerNative
} else {
Slog.w(TAG, "Force-killing crashed app " + name
+ " at watcher's request");
- Process.killProcess(pid);
if (r != null) {
+ r.kill("crash", true);
+ } else {
+ // Huh.
+ Process.killProcess(pid);
Process.killProcessGroup(uid, pid);
}
}
@@ -13695,9 +13723,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!capp.persistent && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
- killUnneededProcessLocked(capp, "depends on provider "
+ capp.kill("depends on provider "
+ cpr.name.flattenToShortString()
- + " in dying proc " + (proc != null ? proc.processName : "??"));
+ + " in dying proc " + (proc != null ? proc.processName : "??"), true);
}
} else if (capp.thread != null && conn.provider.provider != null) {
try {
@@ -16553,8 +16581,7 @@ public final class ActivityManagerService extends ActivityManagerNative
stats.reportExcessiveWakeLocked(app.info.uid, app.processName,
realtimeSince, wtimeUsed);
}
- killUnneededProcessLocked(app, "excessive wake held " + wtimeUsed
- + " during " + realtimeSince);
+ app.kill("excessive wake held " + wtimeUsed + " during " + realtimeSince, true);
app.baseProcessTracker.reportExcessiveWake(app.pkgList);
} else if (doCpuKills && uptimeSince > 0
&& ((cputimeUsed*100)/uptimeSince) >= 25) {
@@ -16562,8 +16589,7 @@ public final class ActivityManagerService extends ActivityManagerNative
stats.reportExcessiveCpuLocked(app.info.uid, app.processName,
uptimeSince, cputimeUsed);
}
- killUnneededProcessLocked(app, "excessive cpu " + cputimeUsed
- + " during " + uptimeSince);
+ app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince, true);
app.baseProcessTracker.reportExcessiveCpu(app.pkgList);
} else {
app.lastWakeTime = wtime;
@@ -16598,7 +16624,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " to " + app.curSchedGroup);
if (app.waitingToKill != null &&
app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
- killUnneededProcessLocked(app, app.waitingToKill);
+ app.kill(app.waitingToKill, true);
success = false;
} else {
if (true) {
@@ -16978,19 +17004,19 @@ public final class ActivityManagerService extends ActivityManagerNative
mNumCachedHiddenProcs++;
numCached++;
if (numCached > cachedProcessLimit) {
- killUnneededProcessLocked(app, "cached #" + numCached);
+ app.kill("cached #" + numCached, true);
}
break;
case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
if (numEmpty > ProcessList.TRIM_EMPTY_APPS
&& app.lastActivityTime < oldTime) {
- killUnneededProcessLocked(app, "empty for "
+ app.kill("empty for "
+ ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
- / 1000) + "s");
+ / 1000) + "s", true);
} else {
numEmpty++;
if (numEmpty > emptyProcessLimit) {
- killUnneededProcessLocked(app, "empty #" + numEmpty);
+ app.kill("empty #" + numEmpty, true);
}
}
break;
@@ -17006,7 +17032,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// definition not re-use the same process again, and it is
// good to avoid having whatever code was running in them
// left sitting around after no longer needed.
- killUnneededProcessLocked(app, "isolated not needed");
+ app.kill("isolated not needed", true);
}
if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
@@ -17234,11 +17260,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (app.thread != null ? app.thread.asBinder() : null)
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
- EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
- app.processName, app.setAdj, "empty");
- app.killedByAm = true;
- Process.killProcessQuiet(app.pid);
- Process.killProcessGroup(app.info.uid, app.pid);
+ app.kill("empty", false);
} else {
try {
app.thread.scheduleExit();
@@ -18290,14 +18312,15 @@ public final class ActivityManagerService extends ActivityManagerNative
long origId = Binder.clearCallingIdentity();
try {
TaskRecord tr = recentTaskForIdLocked(mTaskId);
- if (tr != null) {
- // Only kill the process if we are not a new document
- int flags = tr.getBaseIntent().getFlags();
- boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
- Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
- removeTaskByIdLocked(mTaskId,
- !isDocument ? ActivityManager.REMOVE_TASK_KILL_PROCESS : 0);
- }
+ if (tr == null) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+ }
+ // Only kill the process if we are not a new document
+ int flags = tr.getBaseIntent().getFlags();
+ boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
+ Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+ removeTaskByIdLocked(mTaskId,
+ !isDocument ? ActivityManager.REMOVE_TASK_KILL_PROCESS : 0);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -18312,17 +18335,64 @@ public final class ActivityManagerService extends ActivityManagerNative
long origId = Binder.clearCallingIdentity();
try {
TaskRecord tr = recentTaskForIdLocked(mTaskId);
- if (tr != null) {
- return createRecentTaskInfoFromTaskRecord(tr);
+ if (tr == null) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
+ return createRecentTaskInfoFromTaskRecord(tr);
} finally {
Binder.restoreCallingIdentity(origId);
}
- return null;
}
}
@Override
+ public void moveToFront() {
+ checkCaller();
+
+ final TaskRecord tr;
+ synchronized (ActivityManagerService.this) {
+ tr = recentTaskForIdLocked(mTaskId);
+ if (tr == null) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+ }
+ if (tr.getRootActivity() != null) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ moveTaskToFrontLocked(tr.taskId, 0, null);
+ return;
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ startActivityFromRecentsInner(tr.taskId, null);
+ }
+
+ @Override
+ public int startActivity(IBinder whoThread, String callingPackage,
+ Intent intent, String resolvedType, Bundle options) {
+ checkCaller();
+
+ int callingUser = UserHandle.getCallingUserId();
+ TaskRecord tr;
+ IApplicationThread appThread;
+ synchronized (ActivityManagerService.this) {
+ tr = recentTaskForIdLocked(mTaskId);
+ if (tr == null) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+ }
+ appThread = ApplicationThreadNative.asInterface(whoThread);
+ if (appThread == null) {
+ throw new IllegalArgumentException("Bad app thread " + appThread);
+ }
+ }
+ return mStackSupervisor.startActivityMayWait(appThread, -1, callingPackage, intent,
+ resolvedType, null, null, null, null, 0, 0, null, null,
+ null, null, options, callingUser, null, tr);
+ }
+
+ @Override
public void setExcludeFromRecents(boolean exclude) {
checkCaller();
@@ -18330,14 +18400,15 @@ public final class ActivityManagerService extends ActivityManagerNative
long origId = Binder.clearCallingIdentity();
try {
TaskRecord tr = recentTaskForIdLocked(mTaskId);
- if (tr != null) {
- Intent intent = tr.getBaseIntent();
- if (exclude) {
- intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- } else {
- intent.setFlags(intent.getFlags()
- & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- }
+ if (tr == null) {
+ throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+ }
+ Intent intent = tr.getBaseIntent();
+ if (exclude) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ } else {
+ intent.setFlags(intent.getFlags()
+ & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index fcbe71e..694142a 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1054,6 +1054,24 @@ final class ActivityRecord {
return null;
}
+ final boolean isDestroyable() {
+ if (finishing || app == null || state == ActivityState.DESTROYING
+ || state == ActivityState.DESTROYED) {
+ // This would be redundant.
+ return false;
+ }
+ if (task == null || task.stack == null || this == task.stack.mResumedActivity
+ || this == task.stack.mPausingActivity || !haveState || !stopped) {
+ // We're not ready for this kind of thing.
+ return false;
+ }
+ if (visible) {
+ // The user would notice this!
+ return false;
+ }
+ return true;
+ }
+
private static String createImageFilename(long createTime, int taskId) {
return String.valueOf(taskId) + ACTIVITY_ICON_SUFFIX + createTime +
TaskPersister.IMAGE_EXTENSION;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 41ed4ce..fd1474f 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -36,11 +36,13 @@ import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_CONTAINERS;
+import static com.android.server.am.ActivityStackSupervisor.DEBUG_RELEASE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import android.util.ArraySet;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.Watchdog;
@@ -2910,7 +2912,7 @@ final class ActivityStack {
int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
null, aInfo, null, null, parent.appToken, null,
0, -1, parent.launchedFromUid, parent.launchedFromPackage,
- 0, null, true, null, null);
+ 0, null, true, null, null, null);
foundParentInTask = res == ActivityManager.START_SUCCESS;
} catch (RemoteException e) {
foundParentInTask = false;
@@ -3058,15 +3060,10 @@ final class ActivityStack {
if (!lastIsOpaque) {
continue;
}
- // We can destroy this one if we have its icicle saved and
- // it is not in the process of pausing/stopping/finishing.
- if (r.app != null && r != mResumedActivity && r != mPausingActivity
- && r.haveState && !r.visible && r.stopped
- && r.state != ActivityState.DESTROYING
- && r.state != ActivityState.DESTROYED) {
+ if (r.isDestroyable()) {
if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
+ " resumed=" + mResumedActivity
- + " pausing=" + mPausingActivity);
+ + " pausing=" + mPausingActivity + " for reason " + reason);
if (destroyActivityLocked(r, true, reason)) {
activityRemoved = true;
}
@@ -3078,6 +3075,60 @@ final class ActivityStack {
}
}
+ final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) {
+ if (r.isDestroyable()) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
+ + " resumed=" + mResumedActivity
+ + " pausing=" + mPausingActivity + " for reason " + reason);
+ return destroyActivityLocked(r, true, reason);
+ }
+ return false;
+ }
+
+ final int releaseSomeActivitiesLocked(ProcessRecord app, ArraySet<TaskRecord> tasks,
+ String reason) {
+ // Iterate over tasks starting at the back (oldest) first.
+ if (DEBUG_RELEASE) Slog.d(TAG, "Trying to release some activities in " + app);
+ int maxTasks = tasks.size() / 4;
+ if (maxTasks < 1) {
+ maxTasks = 1;
+ }
+ int numReleased = 0;
+ for (int taskNdx = 0; taskNdx < mTaskHistory.size() && maxTasks > 0; taskNdx++) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ if (!tasks.contains(task)) {
+ continue;
+ }
+ if (DEBUG_RELEASE) Slog.d(TAG, "Looking for activities to release in " + task);
+ int curNum = 0;
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ for (int actNdx = 0; actNdx < activities.size(); actNdx++) {
+ final ActivityRecord activity = activities.get(actNdx);
+ if (activity.app == app && activity.isDestroyable()) {
+ if (DEBUG_RELEASE) Slog.v(TAG, "Destroying " + activity
+ + " in state " + activity.state + " resumed=" + mResumedActivity
+ + " pausing=" + mPausingActivity + " for reason " + reason);
+ destroyActivityLocked(activity, true, reason);
+ if (activities.get(actNdx) != activity) {
+ // Was removed from list, back up so we don't miss the next one.
+ actNdx--;
+ }
+ curNum++;
+ }
+ }
+ if (curNum > 0) {
+ numReleased += curNum;
+ maxTasks--;
+ if (mTaskHistory.get(taskNdx) != task) {
+ // The entire task got removed, back up so we don't miss the next one.
+ taskNdx--;
+ }
+ }
+ }
+ if (DEBUG_RELEASE) Slog.d(TAG, "Done releasing: did " + numReleased + " activities");
+ return numReleased;
+ }
+
/**
* Destroy the current CLIENT SIDE instance of an activity. This may be
* called both when actually finishing an activity, or when performing
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8aec392..8058c05 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -84,6 +84,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -115,10 +116,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
static final boolean DEBUG_APP = DEBUG || false;
static final boolean DEBUG_CONTAINERS = DEBUG || false;
static final boolean DEBUG_IDLE = DEBUG || false;
- static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false;
+ static final boolean DEBUG_RELEASE = DEBUG || false;
static final boolean DEBUG_SAVED_STATE = DEBUG || false;
static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
static final boolean DEBUG_STATES = DEBUG || false;
+ static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false;
public static final int HOME_STACK_ID = 0;
@@ -781,7 +783,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
void startHomeActivity(Intent intent, ActivityInfo aInfo) {
moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
- null, false, null, null);
+ null, false, null, null, null);
}
final int startActivityMayWait(IApplicationThread caller, int callingUid,
@@ -789,7 +791,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile,
ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
- Bundle options, int userId, IActivityContainer iContainer) {
+ Bundle options, int userId, IActivityContainer iContainer, TaskRecord inTask) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -899,7 +901,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
int res = startActivityLocked(caller, intent, resolvedType, aInfo,
voiceSession, voiceInteractor, resultTo, resultWho,
requestCode, callingPid, callingUid, callingPackage, startFlags, options,
- componentSpecified, null, container);
+ componentSpecified, null, container, inTask);
Binder.restoreCallingIdentity(origId);
@@ -1014,7 +1016,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
int res = startActivityLocked(caller, intent, resolvedTypes[i],
aInfo, null, null, resultTo, null, -1, callingPid, callingUid, callingPackage,
- 0, theseOptions, componentSpecified, outActivity, null);
+ 0, theseOptions, componentSpecified, outActivity, null, null);
if (res < 0) {
return res;
}
@@ -1241,7 +1243,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode,
int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
- boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container) {
+ boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container,
+ TaskRecord inTask) {
int err = ActivityManager.START_SUCCESS;
ProcessRecord callerApp = null;
@@ -1453,7 +1456,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
doPendingActivityLaunchesLocked(false);
err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, true, options);
+ startFlags, true, options, inTask);
if (err < 0) {
// If someone asked to have the keyguard dismissed on the next
@@ -1536,10 +1539,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
- final int startActivityUncheckedLocked(ActivityRecord r,
- ActivityRecord sourceRecord,
+ final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
- boolean doResume, Bundle options) {
+ boolean doResume, Bundle options, TaskRecord inTask) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
@@ -1571,8 +1573,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
- final boolean launchTaskBehind = r.mLaunchTaskBehind &&
- (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
+ final boolean launchTaskBehind = r.mLaunchTaskBehind
+ && !launchSingleTask && !launchSingleInstance
+ && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new
@@ -1591,6 +1594,15 @@ public final class ActivityStackSupervisor implements DisplayListener {
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
+ // If we are actually going to launch in to a new task, there are some cases where
+ // we further want to do multiple task.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ if (launchTaskBehind
+ || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) {
+ launchFlags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+ }
+ }
+
// We'll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
@@ -1624,7 +1636,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (sourceRecord == null) {
// This activity is not being started from another... in this
// case we -always- start a new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -1642,7 +1654,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
ActivityInfo newTaskInfo = null;
Intent newTaskIntent = null;
- final ActivityStack sourceStack;
+ ActivityStack sourceStack;
if (sourceRecord != null) {
if (sourceRecord.finishing) {
// If the source is finishing, we can't further count it as our source. This
@@ -1666,12 +1678,51 @@ public final class ActivityStackSupervisor implements DisplayListener {
sourceStack = null;
}
- intent.setFlags(launchFlags);
-
boolean addingToTask = false;
boolean movedHome = false;
TaskRecord reuseTask = null;
ActivityStack targetStack;
+
+ intent.setFlags(launchFlags);
+
+ // If the caller is not coming from another activity, but has given us an
+ // explicit task into which they would like us to launch the new activity,
+ // then let's see about doing that.
+ if (sourceRecord == null && inTask != null && inTask.stack != null) {
+ // If this task is empty, then we are adding the first activity -- it
+ // determines the root, and must be launching as a NEW_TASK.
+ if (inTask.getRootActivity() == null) {
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
+ && !launchSingleInstance && !launchSingleTask) {
+ throw new IllegalStateException("Caller has inTask " + inTask
+ + " but target is not a new task");
+ } else if (inTask.getBaseIntent() == null || !intent.getComponent().equals(
+ inTask.getBaseIntent().getComponent())) {
+ throw new IllegalStateException("Caller requested " + inTask + " is component "
+ + inTask.getBaseIntent() + " but starting " + intent);
+ }
+ inTask.setIntent(r);
+
+ // If the task is not empty, then we are going to add the new activity on top
+ // of the task, so it can not be launching as a new task.
+ } else {
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
+ || launchSingleInstance || launchSingleTask) {
+ throw new IllegalStateException("Caller has inTask " + inTask
+ + " but target is a new task");
+ }
+ }
+ sourceStack = inTask.stack;
+ reuseTask = inTask;
+ } else {
+ inTask = null;
+ }
+
+ // We may want to try to place the new activity in to an existing task. We always
+ // do this if the target activity is singleTask or singleInstance; we will also do
+ // this if NEW_TASK has been requested, and there is not an additional qualifier telling
+ // us to still place it in a new task: multi task, always doc mode, or being asked to
+ // launch this as a new task behind the current one.
if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| launchSingleInstance || launchSingleTask) {
@@ -1908,8 +1959,13 @@ public final class ActivityStackSupervisor implements DisplayListener {
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
- newTask = true;
- targetStack = adjustStackFocus(r, newTask);
+ if (inTask == null) {
+ // If we have an incoming task, we are just going to use that.
+ newTask = true;
+ targetStack = adjustStackFocus(r, newTask);
+ } else {
+ targetStack = inTask.stack;
+ }
if (!launchTaskBehind) {
targetStack.moveToFront();
}
@@ -1986,6 +2042,20 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in existing task " + r.task + " from source " + sourceRecord);
+ } else if (inTask != null) {
+ // The calling is asking that the new activity be started in an explicit
+ // task it has provided to us.
+ if (isLockTaskModeViolation(inTask)) {
+ Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+ return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ }
+ targetStack = inTask.stack;
+ targetStack.moveToFront();
+ mWindowManager.moveTaskToTop(targetStack.topTask().taskId);
+ r.setTask(inTask, null);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in explicit task " + r.task);
+
} else {
// This not being started from an existing activity, and not part
// of a new task... just put it in the top task, though these days
@@ -2023,7 +2093,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
while (!mPendingActivityLaunches.isEmpty()) {
PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
- doResume && mPendingActivityLaunches.isEmpty(), null);
+ doResume && mPendingActivityLaunches.isEmpty(), null, null);
}
}
@@ -2726,6 +2796,64 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
+ void releaseSomeActivitiesLocked(ProcessRecord app, String reason) {
+ // Examine all activities currently running in the process.
+ TaskRecord firstTask = null;
+ // Tasks is non-null only if two or more tasks are found.
+ ArraySet<TaskRecord> tasks = null;
+ if (DEBUG_RELEASE) Slog.d(TAG, "Trying to release some activities in " + app);
+ for (int i=0; i<app.activities.size(); i++) {
+ ActivityRecord r = app.activities.get(i);
+ // First, if we find an activity that is in the process of being destroyed,
+ // then we just aren't going to do anything for now; we want things to settle
+ // down before we try to prune more activities.
+ if (r.finishing || r.state == ActivityState.DESTROYING
+ || r.state == ActivityState.DESTROYED) {
+ if (DEBUG_RELEASE) Slog.d(TAG, "Abort release; already destroying: " + r);
+ return;
+ }
+ // Don't consider any activies that are currently not in a state where they
+ // can be destroyed.
+ if (r.visible || !r.stopped || !r.haveState
+ || r.state == ActivityState.RESUMED || r.state == ActivityState.PAUSING
+ || r.state == ActivityState.PAUSED || r.state == ActivityState.STOPPING) {
+ if (DEBUG_RELEASE) Slog.d(TAG, "Not releasing in-use activity: " + r);
+ continue;
+ }
+ if (r.task != null) {
+ if (DEBUG_RELEASE) Slog.d(TAG, "Collecting release task " + r.task
+ + " from " + r);
+ if (firstTask == null) {
+ firstTask = r.task;
+ } else if (firstTask != r.task) {
+ if (tasks == null) {
+ tasks = new ArraySet<>();
+ tasks.add(firstTask);
+ }
+ tasks.add(r.task);
+ }
+ }
+ }
+ if (tasks == null) {
+ if (DEBUG_RELEASE) Slog.d(TAG, "Didn't find two or more tasks to release");
+ return;
+ }
+ // If we have activities in multiple tasks that are in a position to be destroyed,
+ // let's iterate through the tasks and release the oldest one.
+ final int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ // Step through all stacks starting from behind, to hit the oldest things first.
+ for (int stackNdx = 0; stackNdx < stacks.size(); stackNdx++) {
+ final ActivityStack stack = stacks.get(stackNdx);
+ // Try to release activities in this stack; if we manage to, we are done.
+ if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
+ return;
+ }
+ }
+ }
+ }
+
boolean switchUserLocked(int userId, UserStartedState uss) {
mUserStackInFront.put(mCurrentUser, getFocusedStack().getStackId());
final int restoreStackId = mUserStackInFront.get(userId, HOME_STACK_ID);
@@ -3499,7 +3627,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
mimeType = mService.getProviderMimeType(intent.getData(), userId);
}
return startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null, 0, 0, null,
- null, null, null, null, userId, this);
+ null, null, null, null, userId, this, null);
}
@Override
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 98999e9..433ab60 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -254,7 +254,7 @@ final class PendingIntentRecord extends IIntentSender.Stub {
} else {
owner.startActivityInPackage(uid, key.packageName, finalIntent,
resolvedType, resultTo, resultWho, requestCode, 0,
- options, userId, container);
+ options, userId, container, null);
}
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index f1bcb60..0817dd8 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -17,6 +17,8 @@
package com.android.server.am;
import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.Slog;
import com.android.internal.app.ProcessStats;
import com.android.internal.os.BatteryStatsImpl;
@@ -502,6 +504,21 @@ final class ProcessRecord {
return adj;
}
+ void kill(String reason, boolean noisy) {
+ if (!killedByAm) {
+ if (noisy) {
+ Slog.i(ActivityManagerService.TAG, "Killing " + toShortString() + " (adj " + setAdj
+ + "): " + reason);
+ }
+ EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
+ Process.killProcessQuiet(pid);
+ Process.killProcessGroup(info.uid, pid);
+ if (!persistent) {
+ killedByAm = true;
+ }
+ }
+ }
+
public String toShortString() {
if (shortStringName != null) {
return shortStringName;