ActivityManager: kill frozen processes receiving sync transactions

Synchronous transactions to frozen processes are rejected by binder,
which can potentially leave these processes in an unclean state when
unfrozen. Kill such processes insted of unfreezing then when they're
removed from the cache. This patch also increases the timeout before
freezing an app, to allow pending transactions to be processes for
a longer window after caching and so to reduce the amount of background
kills.

Bug: 143717177
Test: cached/frozen a few apps, sent sync and async transactions to them
and verified that killing happened when expected.

Change-Id: Ieca9d49e1befd1358862aa7afeeec15a408d7ef3
This commit is contained in:
Marco Ballesio
2020-09-10 13:36:14 -07:00
parent a2da145ff6
commit 5b5fdb80ec
2 changed files with 81 additions and 4 deletions

View File

@@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -132,11 +133,15 @@ public final class CachedAppOptimizer {
static final int REPORT_UNFREEZE_MSG = 4;
//TODO:change this static definition into a configurable flag.
static final int FREEZE_TIMEOUT_MS = 500;
static final int FREEZE_TIMEOUT_MS = 10000;
static final int DO_FREEZE = 1;
static final int REPORT_UNFREEZE = 2;
// Bitfield values for sync/async transactions reveived by frozen processes
static final int SYNC_RECEIVED_WHILE_FROZEN = 1;
static final int ASYNC_RECEIVED_WHILE_FROZEN = 2;
/**
* This thread must be moved to the system background cpuset.
* If that doesn't happen, it's probably going to draw a lot of power.
@@ -493,6 +498,15 @@ public final class CachedAppOptimizer {
*/
private static native void freezeBinder(int pid, boolean freeze);
/**
* Retrieves binder freeze info about a process.
* @param pid the pid for which binder freeze info is to be retrieved.
*
* @throws RuntimeException if the operation could not complete successfully.
* @return a bit field reporting the binder freeze info for the process.
*/
private static native int getBinderFreezeInfo(int pid);
/**
* Determines whether the freezer is supported by this system
*/
@@ -729,6 +743,37 @@ public final class CachedAppOptimizer {
return;
}
boolean processKilled = false;
try {
int freezeInfo = getBinderFreezeInfo(app.pid);
if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+ " received sync transactions while frozen, killing");
app.kill("Sync transaction while in frozen state",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
processKilled = true;
}
if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {
Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+ " received async transactions while frozen");
}
} catch (Exception e) {
Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + app.pid + " "
+ app.processName + ". Killing it. Exception: " + e);
app.kill("Unable to query binder frozen stats",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
processKilled = true;
}
if (processKilled) {
return;
}
long freezeTime = app.freezeUnfreezeTime;
try {
@@ -745,8 +790,12 @@ public final class CachedAppOptimizer {
try {
freezeBinder(app.pid, false);
} catch (RuntimeException e) {
// TODO: it might be preferable to kill the target pid in this case
Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName);
Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
+ ". Killing it");
app.kill("Unable to unfreeze",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
return;
}
if (DEBUG_FREEZER) {

View File

@@ -36,6 +36,9 @@
using android::base::StringPrintf;
using android::base::WriteStringToFile;
#define SYNC_RECEIVED_WHILE_FROZEN (1)
#define ASYNC_RECEIVED_WHILE_FROZEN (2)
namespace android {
// This performs per-process reclaim on all processes belonging to non-app UIDs.
@@ -99,12 +102,37 @@ static void com_android_server_am_CachedAppOptimizer_freezeBinder(
}
}
static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env,
jobject clazz, jint pid) {
bool syncReceived = false, asyncReceived = false;
int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
if (error < 0) {
jniThrowException(env, "java/lang/RuntimeException", strerror(error));
}
jint retVal = 0;
if(syncReceived) {
retVal |= SYNC_RECEIVED_WHILE_FROZEN;;
}
if(asyncReceived) {
retVal |= ASYNC_RECEIVED_WHILE_FROZEN;
}
return retVal;
}
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"enableFreezerInternal", "(Z)V",
(void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
{"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}
{"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
{"getBinderFreezeInfo", "(I)I",
(void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}
};
int register_android_server_am_CachedAppOptimizer(JNIEnv* env)