summaryrefslogtreecommitdiffstats
path: root/services/core
diff options
context:
space:
mode:
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;