Merge "Freeze package cgroup before killing"
This commit is contained in:
@@ -1481,6 +1481,18 @@ public class Process {
|
||||
*/
|
||||
public static final native int killProcessGroup(int uid, int pid);
|
||||
|
||||
/**
|
||||
* Freeze the cgroup for the given UID.
|
||||
* This cgroup may contain child cgroups which will also be frozen. If this cgroup or its
|
||||
* children contain processes with Binder interfaces, those interfaces should be frozen before
|
||||
* the cgroup to avoid blocking synchronous callers indefinitely.
|
||||
*
|
||||
* @param uid The UID to be frozen
|
||||
* @param freeze true = freeze; false = unfreeze
|
||||
* @hide
|
||||
*/
|
||||
public static final native void freezeCgroupUid(int uid, boolean freeze);
|
||||
|
||||
/**
|
||||
* Remove all process groups. Expected to be called when ActivityManager
|
||||
* is restarted.
|
||||
|
||||
@@ -1252,6 +1252,20 @@ static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, j
|
||||
return fd;
|
||||
}
|
||||
|
||||
void android_os_Process_freezeCgroupUID(JNIEnv* env, jobject clazz, jint uid, jboolean freeze) {
|
||||
bool success = true;
|
||||
|
||||
if (freeze) {
|
||||
success = SetUserProfiles(uid, {"Frozen"});
|
||||
} else {
|
||||
success = SetUserProfiles(uid, {"Unfrozen"});
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
jniThrowRuntimeException(env, "Could not apply user profile");
|
||||
}
|
||||
}
|
||||
|
||||
static const JNINativeMethod methods[] = {
|
||||
{"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
|
||||
{"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
|
||||
@@ -1293,6 +1307,7 @@ static const JNINativeMethod methods[] = {
|
||||
{"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
|
||||
{"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups},
|
||||
{"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen},
|
||||
{"freezeCgroupUid", "(IZ)V", (void*)android_os_Process_freezeCgroupUID},
|
||||
};
|
||||
|
||||
int register_android_os_Process(JNIEnv* env)
|
||||
|
||||
@@ -112,6 +112,8 @@ public final class CachedAppOptimizer {
|
||||
private static final String ATRACE_COMPACTION_TRACK = "Compaction";
|
||||
private static final String ATRACE_FREEZER_TRACK = "Freezer";
|
||||
|
||||
private static final int FREEZE_BINDER_TIMEOUT_MS = 100;
|
||||
|
||||
// Defaults for phenotype flags.
|
||||
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
|
||||
@VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
|
||||
@@ -929,11 +931,13 @@ public final class CachedAppOptimizer {
|
||||
* @param pid the target pid for which binder transactions are to be frozen
|
||||
* @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze
|
||||
* binder for the specificed pid.
|
||||
* @param timeoutMs the timeout in milliseconds to wait for the binder interface to freeze
|
||||
* before giving up.
|
||||
*
|
||||
* @throws RuntimeException in case a flush/freeze operation could not complete successfully.
|
||||
* @return 0 if success, or -EAGAIN indicating there's pending transaction.
|
||||
*/
|
||||
private static native int freezeBinder(int pid, boolean freeze);
|
||||
public static native int freezeBinder(int pid, boolean freeze, int timeoutMs);
|
||||
|
||||
/**
|
||||
* Retrieves binder freeze info about a process.
|
||||
@@ -1300,7 +1304,7 @@ public final class CachedAppOptimizer {
|
||||
long freezeTime = opt.getFreezeUnfreezeTime();
|
||||
|
||||
try {
|
||||
freezeBinder(pid, false);
|
||||
freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
|
||||
} catch (RuntimeException e) {
|
||||
Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
|
||||
+ ". Killing it");
|
||||
@@ -1355,7 +1359,7 @@ public final class CachedAppOptimizer {
|
||||
}
|
||||
Slog.d(TAG_AM, "quick sync unfreeze " + pid);
|
||||
try {
|
||||
freezeBinder(pid, false);
|
||||
freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
|
||||
} catch (RuntimeException e) {
|
||||
Slog.e(TAG_AM, "Unable to quick unfreeze binder for " + pid);
|
||||
return;
|
||||
@@ -1950,7 +1954,7 @@ public final class CachedAppOptimizer {
|
||||
// Freeze binder interface before the process, to flush any
|
||||
// transactions that might be pending.
|
||||
try {
|
||||
if (freezeBinder(pid, true) != 0) {
|
||||
if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {
|
||||
rescheduleFreeze(proc, "outstanding txns");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import static android.os.Process.getFreeMemory;
|
||||
import static android.os.Process.getTotalMemory;
|
||||
import static android.os.Process.killProcessQuiet;
|
||||
import static android.os.Process.startWebView;
|
||||
import static android.system.OsConstants.*;
|
||||
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
|
||||
@@ -2711,6 +2712,50 @@ public final class ProcessList {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean freezePackageCgroup(int packageUID, boolean freeze) {
|
||||
try {
|
||||
Process.freezeCgroupUid(packageUID, freeze);
|
||||
} catch (RuntimeException e) {
|
||||
final String logtxt = freeze ? "freeze" : "unfreeze";
|
||||
Slog.e(TAG, "Unable to " + logtxt + " cgroup uid: " + packageUID + ": " + e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void freezeBinderAndPackageCgroup(ArrayList<Pair<ProcessRecord, Boolean>> procs,
|
||||
int packageUID) {
|
||||
// Freeze all binder processes under the target UID (whose cgroup is about to be frozen).
|
||||
// Since we're going to kill these, we don't need to unfreze them later.
|
||||
// The procs list may not include all processes under the UID cgroup, but unincluded
|
||||
// processes (forks) should not be Binder users.
|
||||
int N = procs.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final int uid = procs.get(i).first.uid;
|
||||
final int pid = procs.get(i).first.getPid();
|
||||
int nRetries = 0;
|
||||
// We only freeze the cgroup of the target package, so we do not need to freeze the
|
||||
// Binder interfaces of dependant processes in other UIDs.
|
||||
if (pid > 0 && uid == packageUID) {
|
||||
try {
|
||||
int rc;
|
||||
do {
|
||||
rc = CachedAppOptimizer.freezeBinder(pid, true, 10 /* timeout_ms */);
|
||||
} while (rc == -EAGAIN && nRetries++ < 1);
|
||||
if (rc != 0) Slog.e(TAG, "Unable to freeze binder for " + pid + ": " + rc);
|
||||
} catch (RuntimeException e) {
|
||||
Slog.e(TAG, "Unable to freeze binder for " + pid + ": " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We freeze the entire UID (parent) cgroup so that newly-specialized processes also freeze
|
||||
// despite being added to a new child cgroup. The cgroups of package dependant processes are
|
||||
// not frozen, since it's possible this would freeze processes with no dependency on the
|
||||
// package being killed here.
|
||||
freezePackageCgroup(packageUID, true);
|
||||
}
|
||||
|
||||
@GuardedBy({"mService", "mProcLock"})
|
||||
boolean killPackageProcessesLSP(String packageName, int appId,
|
||||
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
|
||||
@@ -2763,7 +2808,7 @@ public final class ProcessList {
|
||||
boolean shouldAllowRestart = false;
|
||||
|
||||
// If no package is specified, we call all processes under the
|
||||
// give user id.
|
||||
// given user id.
|
||||
if (packageName == null) {
|
||||
if (userId != UserHandle.USER_ALL && app.userId != userId) {
|
||||
continue;
|
||||
@@ -2806,14 +2851,24 @@ public final class ProcessList {
|
||||
}
|
||||
}
|
||||
|
||||
final int packageUID = UserHandle.getUid(userId, appId);
|
||||
final boolean doFreeze = appId >= Process.FIRST_APPLICATION_UID
|
||||
&& appId <= Process.LAST_APPLICATION_UID;
|
||||
if (doFreeze) {
|
||||
freezeBinderAndPackageCgroup(procs, packageUID);
|
||||
}
|
||||
|
||||
int N = procs.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
final Pair<ProcessRecord, Boolean> proc = procs.get(i);
|
||||
removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
|
||||
reasonCode, subReason, reason);
|
||||
reasonCode, subReason, reason, !doFreeze /* async */);
|
||||
}
|
||||
killAppZygotesLocked(packageName, appId, userId, false /* force */);
|
||||
mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
|
||||
if (doFreeze) {
|
||||
freezePackageCgroup(packageUID, false);
|
||||
}
|
||||
return N > 0;
|
||||
}
|
||||
|
||||
@@ -2821,12 +2876,19 @@ public final class ProcessList {
|
||||
boolean removeProcessLocked(ProcessRecord app,
|
||||
boolean callerWillRestart, boolean allowRestart, int reasonCode, String reason) {
|
||||
return removeProcessLocked(app, callerWillRestart, allowRestart, reasonCode,
|
||||
ApplicationExitInfo.SUBREASON_UNKNOWN, reason);
|
||||
ApplicationExitInfo.SUBREASON_UNKNOWN, reason, true);
|
||||
}
|
||||
|
||||
@GuardedBy("mService")
|
||||
boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart,
|
||||
boolean allowRestart, int reasonCode, int subReason, String reason) {
|
||||
return removeProcessLocked(app, callerWillRestart, allowRestart, reasonCode, subReason,
|
||||
reason, true);
|
||||
}
|
||||
|
||||
@GuardedBy("mService")
|
||||
boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart,
|
||||
boolean allowRestart, int reasonCode, int subReason, String reason, boolean async) {
|
||||
final String name = app.processName;
|
||||
final int uid = app.uid;
|
||||
if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
|
||||
@@ -2863,7 +2925,7 @@ public final class ProcessList {
|
||||
needRestart = true;
|
||||
}
|
||||
}
|
||||
app.killLocked(reason, reasonCode, subReason, true);
|
||||
app.killLocked(reason, reasonCode, subReason, true, async);
|
||||
mService.handleAppDiedLocked(app, pid, willRestart, allowRestart,
|
||||
false /* fromBinderDied */);
|
||||
if (willRestart) {
|
||||
|
||||
@@ -1056,18 +1056,30 @@ class ProcessRecord implements WindowProcessListener {
|
||||
|
||||
@GuardedBy("mService")
|
||||
void killLocked(String reason, @Reason int reasonCode, boolean noisy) {
|
||||
killLocked(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy);
|
||||
killLocked(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy, true);
|
||||
}
|
||||
|
||||
@GuardedBy("mService")
|
||||
void killLocked(String reason, @Reason int reasonCode, @SubReason int subReason,
|
||||
boolean noisy) {
|
||||
killLocked(reason, reason, reasonCode, subReason, noisy);
|
||||
killLocked(reason, reason, reasonCode, subReason, noisy, true);
|
||||
}
|
||||
|
||||
@GuardedBy("mService")
|
||||
void killLocked(String reason, String description, @Reason int reasonCode,
|
||||
@SubReason int subReason, boolean noisy) {
|
||||
killLocked(reason, description, reasonCode, subReason, noisy, true);
|
||||
}
|
||||
|
||||
@GuardedBy("mService")
|
||||
void killLocked(String reason, @Reason int reasonCode, @SubReason int subReason,
|
||||
boolean noisy, boolean asyncKPG) {
|
||||
killLocked(reason, reason, reasonCode, subReason, noisy, asyncKPG);
|
||||
}
|
||||
|
||||
@GuardedBy("mService")
|
||||
void killLocked(String reason, String description, @Reason int reasonCode,
|
||||
@SubReason int subReason, boolean noisy, boolean asyncKPG) {
|
||||
if (!mKilledByAm) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
|
||||
if (reasonCode == ApplicationExitInfo.REASON_ANR
|
||||
@@ -1084,7 +1096,8 @@ class ProcessRecord implements WindowProcessListener {
|
||||
EventLog.writeEvent(EventLogTags.AM_KILL,
|
||||
userId, mPid, processName, mState.getSetAdj(), reason);
|
||||
Process.killProcessQuiet(mPid);
|
||||
ProcessList.killProcessGroup(uid, mPid);
|
||||
if (asyncKPG) ProcessList.killProcessGroup(uid, mPid);
|
||||
else Process.killProcessGroup(uid, mPid);
|
||||
} else {
|
||||
mPendingStart = false;
|
||||
}
|
||||
|
||||
@@ -491,10 +491,10 @@ static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, job
|
||||
compactProcessOrFallback(pid, compactionFlags);
|
||||
}
|
||||
|
||||
static jint com_android_server_am_CachedAppOptimizer_freezeBinder(
|
||||
JNIEnv *env, jobject clazz, jint pid, jboolean freeze) {
|
||||
|
||||
jint retVal = IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */);
|
||||
static jint com_android_server_am_CachedAppOptimizer_freezeBinder(JNIEnv* env, jobject clazz,
|
||||
jint pid, jboolean freeze,
|
||||
jint timeout_ms) {
|
||||
jint retVal = IPCThreadState::freeze(pid, freeze, timeout_ms);
|
||||
if (retVal != 0 && retVal != -EAGAIN) {
|
||||
jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder");
|
||||
}
|
||||
@@ -548,7 +548,7 @@ static const JNINativeMethod sMethods[] = {
|
||||
(void*)com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction},
|
||||
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
|
||||
{"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
|
||||
{"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
|
||||
{"freezeBinder", "(IZI)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
|
||||
{"getBinderFreezeInfo", "(I)I",
|
||||
(void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo},
|
||||
{"getFreezerCheckPath", "()Ljava/lang/String;",
|
||||
|
||||
@@ -364,7 +364,7 @@ public class BroadcastQueueTest {
|
||||
mActiveProcesses.remove(r);
|
||||
mRegisteredReceivers.remove(r.getPid());
|
||||
return invocation.callRealMethod();
|
||||
}).when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean());
|
||||
}).when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean());
|
||||
|
||||
// If we're entirely dead, rely on default behaviors above
|
||||
if (dead) return r;
|
||||
|
||||
Reference in New Issue
Block a user