Loading core/java/android/os/Process.java +12 −0 Original line number Diff line number Diff line Loading @@ -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. Loading core/jni/android_util_Process.cpp +15 −0 Original line number Diff line number Diff line Loading @@ -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}, Loading Loading @@ -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) Loading services/core/java/com/android/server/am/CachedAppOptimizer.java +8 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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"); Loading Loading @@ -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; Loading Loading @@ -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; } Loading services/core/java/com/android/server/am/ProcessList.java +66 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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, Loading Loading @@ -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) { Loading services/core/java/com/android/server/am/ProcessRecord.java +16 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; } Loading Loading
core/java/android/os/Process.java +12 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
core/jni/android_util_Process.cpp +15 −0 Original line number Diff line number Diff line Loading @@ -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}, Loading Loading @@ -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) Loading
services/core/java/com/android/server/am/CachedAppOptimizer.java +8 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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"); Loading Loading @@ -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; Loading Loading @@ -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; } Loading
services/core/java/com/android/server/am/ProcessList.java +66 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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, Loading Loading @@ -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) { Loading
services/core/java/com/android/server/am/ProcessRecord.java +16 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; } Loading