freezer: reentrant enable/disable method

the app freezer state can be toggled in multiple situations when a
debugging tool like heapdump is invoked, or when system_server dumps its
binder connections. All these uses are potentially concurrent and a
robust method to handle reentrancy is necessary to avoid leaving the
freezer in a state incompatible with a specific operation.

This patch moves freezer enable and disable operations from Process to
CachedAppOptimizer (ActivityManager), introduces a new ActivityManager API
to centralize all freezer state changes to ActivityManager and modifies
client code accordingly.

Bug: 151225245
Test: manually verified that no regression are introduced on freezer
behavior, verified that concurrent behavior is handled properly

Change-Id: I7d588cc6e0499012dce64ed4e42ff2adb336062d
This commit is contained in:
Marco Ballesio
2020-06-03 08:56:46 -07:00
parent 37ae22f321
commit db203fcabf
7 changed files with 124 additions and 47 deletions

View File

@@ -695,4 +695,14 @@ interface IActivityManager {
* android.permission.RESET_APP_ERRORS. * android.permission.RESET_APP_ERRORS.
*/ */
void resetAppErrors(); void resetAppErrors();
/**
* Control the app freezer state. Returns true in case of success, false if the operation
* didn't succeed (for example, when the app freezer isn't supported).
* Handling the freezer state via this method is reentrant, that is it can be
* disabled and re-enabled multiple times in parallel. As long as there's a 1:1 disable to
* enable match, the freezer is re-enabled at last enable only.
* @param enable set it to true to enable the app freezer, false to disable it.
*/
boolean enableAppFreezer(in boolean enable);
} }

View File

@@ -18,6 +18,7 @@ package android.os;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.util.Log; import android.util.Log;
import android.util.SparseIntArray; import android.util.SparseIntArray;
@@ -255,7 +256,12 @@ public final class BinderProxy implements IBinder {
// out of system_server to all processes hosting binder objects it holds a reference to; // out of system_server to all processes hosting binder objects it holds a reference to;
// since some of those processes might be frozen, we don't want to block here // since some of those processes might be frozen, we don't want to block here
// forever. Disable the freezer. // forever. Disable the freezer.
Process.enableFreezer(false); try {
ActivityManager.getService().enableAppFreezer(false);
} catch (RemoteException e) {
Log.e(Binder.TAG, "RemoteException while disabling app freezer");
}
for (WeakReference<BinderProxy> weakRef : proxiesToQuery) { for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
BinderProxy bp = weakRef.get(); BinderProxy bp = weakRef.get();
String key; String key;
@@ -278,7 +284,11 @@ public final class BinderProxy implements IBinder {
counts.put(key, i + 1); counts.put(key, i + 1);
} }
} }
Process.enableFreezer(true); try {
ActivityManager.getService().enableAppFreezer(true);
} catch (RemoteException e) {
Log.e(Binder.TAG, "RemoteException while re-enabling app freezer");
}
Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray( Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
new Map.Entry[counts.size()]); new Map.Entry[counts.size()]);

View File

@@ -372,22 +372,6 @@ void android_os_Process_setProcessFrozen(
} }
} }
void android_os_Process_enableFreezer(
JNIEnv *env, jobject clazz, jboolean enable)
{
bool success = true;
if (enable) {
success = SetTaskProfiles(0, {"FreezerEnabled"}, true);
} else {
success = SetTaskProfiles(0, {"FreezerDisabled"}, true);
}
if (!success) {
jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
}
}
jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid) jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid)
{ {
SchedPolicy sp; SchedPolicy sp;
@@ -1424,7 +1408,6 @@ static const JNINativeMethod methods[] = {
{"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
{"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet},
{"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen}, {"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen},
{"enableFreezer", "(Z)V", (void*)android_os_Process_enableFreezer},
{"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
{"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory}, {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory},
{"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V",

View File

@@ -2103,17 +2103,13 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
try { try {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
Process.enableFreezer(false);
}
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return; "meminfo", pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args); PriorityDump.dump(mPriorityDumper, fd, pw, args);
} finally { } finally {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
Process.enableFreezer(true);
}
} }
} }
} }
@@ -2127,17 +2123,13 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
try { try {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
Process.enableFreezer(false);
}
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"gfxinfo", pw)) return; "gfxinfo", pw)) return;
mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args); mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
} finally { } finally {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
Process.enableFreezer(true);
}
} }
} }
} }
@@ -2151,17 +2143,13 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
try { try {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
Process.enableFreezer(false);
}
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"dbinfo", pw)) return; "dbinfo", pw)) return;
mActivityManagerService.dumpDbInfo(fd, pw, args); mActivityManagerService.dumpDbInfo(fd, pw, args);
} finally { } finally {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
Process.enableFreezer(true);
}
} }
} }
} }
@@ -2207,9 +2195,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
try { try {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
Process.enableFreezer(false);
}
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"cacheinfo", pw)) { "cacheinfo", pw)) {
@@ -2218,9 +2204,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mActivityManagerService.dumpBinderCacheContents(fd, pw, args); mActivityManagerService.dumpBinderCacheContents(fd, pw, args);
} finally { } finally {
if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
Process.enableFreezer(true);
}
} }
} }
} }
@@ -16800,14 +16784,14 @@ public class ActivityManagerService extends IActivityManager.Stub
} }
} }
Process.enableFreezer(false); mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
final RemoteCallback intermediateCallback = new RemoteCallback( final RemoteCallback intermediateCallback = new RemoteCallback(
new RemoteCallback.OnResultListener() { new RemoteCallback.OnResultListener() {
@Override @Override
public void onResult(Bundle result) { public void onResult(Bundle result) {
finishCallback.sendResult(result); finishCallback.sendResult(result);
Process.enableFreezer(true); mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
} }
}, null); }, null);
@@ -18737,4 +18721,16 @@ public class ActivityManagerService extends IActivityManager.Stub
mAppErrors.resetStateLocked(); mAppErrors.resetStateLocked();
} }
} }
@Override
public boolean enableAppFreezer(boolean enable) {
int callerUid = Binder.getCallingUid();
// Only system can toggle the freezer state
if (callerUid == SYSTEM_UID) {
return mOomAdjuster.mCachedAppOptimizer.enableFreezer(enable);
} else {
throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state ");
}
}
} }

View File

@@ -208,6 +208,8 @@ public final class CachedAppOptimizer {
@GuardedBy("mPhenotypeFlagLock") @GuardedBy("mPhenotypeFlagLock")
private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION; private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER; private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER;
@GuardedBy("this")
private int mFreezerDisableCount = 1; // Freezer is initially disabled, until enabled
private final Random mRandom = new Random(); private final Random mRandom = new Random();
@GuardedBy("mPhenotypeFlagLock") @GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE; @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
@@ -419,6 +421,63 @@ public final class CachedAppOptimizer {
} }
} }
/**
* Enables or disabled the app freezer.
* @param enable Enables the freezer if true, disables it if false.
* @return true if the operation completed successfully, false otherwise.
*/
public synchronized boolean enableFreezer(boolean enable) {
if (!mUseFreezer) {
return false;
}
if (enable) {
mFreezerDisableCount--;
if (mFreezerDisableCount > 0) {
return true;
} else if (mFreezerDisableCount < 0) {
Slog.e(TAG_AM, "unbalanced call to enableFreezer, ignoring");
mFreezerDisableCount = 0;
return false;
}
} else {
mFreezerDisableCount++;
if (mFreezerDisableCount > 1) {
return true;
}
}
try {
enableFreezerInternal(enable);
return true;
} catch (java.lang.RuntimeException e) {
if (enable) {
mFreezerDisableCount = 0;
} else {
mFreezerDisableCount = 1;
}
Slog.e(TAG_AM, "Exception handling freezer state (enable: " + enable + "): "
+ e.toString());
}
return false;
}
/**
* Enable or disable the freezer. When enable == false all frozen processes are unfrozen,
* but aren't removed from the freezer. While in this state, processes can be added or removed
* by using Process.setProcessFrozen(), but they wouldn't be actually frozen until the freezer
* is enabled. If enable == true all processes in the freezer are frozen.
*
* @param enable Specify whether to enable (true) or disable (false) the freezer.
*
* @hide
*/
private static native void enableFreezerInternal(boolean enable);
/** /**
* Determines whether the freezer is supported by this system * Determines whether the freezer is supported by this system
*/ */
@@ -471,7 +530,7 @@ public final class CachedAppOptimizer {
if (mUseFreezer && mFreezeHandler == null) { if (mUseFreezer && mFreezeHandler == null) {
Slog.d(TAG_AM, "Freezer enabled"); Slog.d(TAG_AM, "Freezer enabled");
Process.enableFreezer(true); enableFreezer(true);
if (!mCachedAppOptimizerThread.isAlive()) { if (!mCachedAppOptimizerThread.isAlive()) {
mCachedAppOptimizerThread.start(); mCachedAppOptimizerThread.start();
@@ -482,7 +541,7 @@ public final class CachedAppOptimizer {
Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
Process.THREAD_GROUP_SYSTEM); Process.THREAD_GROUP_SYSTEM);
} else { } else {
Process.enableFreezer(false); enableFreezer(false);
} }
} }

View File

@@ -97,6 +97,7 @@ cc_defaults {
"libnativehelper", "libnativehelper",
"libnativewindow", "libnativewindow",
"libpowermanager", "libpowermanager",
"libprocessgroup",
"libutils", "libutils",
"libui", "libui",
"libvibratorservice", "libvibratorservice",

View File

@@ -30,6 +30,7 @@
#include <nativehelper/JNIHelp.h> #include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h> #include <android_runtime/AndroidRuntime.h>
#include <jni.h> #include <jni.h>
#include <processgroup/processgroup.h>
using android::base::StringPrintf; using android::base::StringPrintf;
using android::base::WriteStringToFile; using android::base::WriteStringToFile;
@@ -74,9 +75,26 @@ static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, job
} }
} }
static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal(
JNIEnv *env, jobject clazz, jboolean enable) {
bool success = true;
if (enable) {
success = SetTaskProfiles(0, {"FreezerEnabled"}, true);
} else {
success = SetTaskProfiles(0, {"FreezerDisabled"}, true);
}
if (!success) {
jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
}
}
static const JNINativeMethod sMethods[] = { static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */ /* name, signature, funcPtr */
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"enableFreezerInternal", "(Z)V",
(void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
}; };
int register_android_server_am_CachedAppOptimizer(JNIEnv* env) int register_android_server_am_CachedAppOptimizer(JNIEnv* env)