Merge "Track per-process freezable state in BroadcastQueue." into udc-dev

This commit is contained in:
Sudheer Shanka
2023-05-17 22:33:40 +00:00
committed by Android (Google) Code Review
9 changed files with 167 additions and 68 deletions

View File

@@ -17236,6 +17236,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
void onProcessFreezableChangedLocked(ProcessRecord app) {
if (mEnableModernQueue) {
mBroadcastQueues[0].onProcessFreezableChangedLocked(app);
}
}
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal
implements ActivityManagerLocal {

View File

@@ -201,7 +201,7 @@ class BroadcastProcessQueue {
private boolean mLastDeferredStates;
private boolean mUidForeground;
private boolean mUidCached;
private boolean mProcessFreezable;
private boolean mProcessInstrumented;
private boolean mProcessPersistent;
@@ -411,7 +411,7 @@ class BroadcastProcessQueue {
*/
@CheckResult
public boolean setProcessAndUidState(@Nullable ProcessRecord app, boolean uidForeground,
boolean uidCached) {
boolean processFreezable) {
this.app = app;
// Since we may have just changed our PID, invalidate cached strings
@@ -420,13 +420,13 @@ class BroadcastProcessQueue {
boolean didSomething = false;
if (app != null) {
didSomething |= setUidCached(uidCached);
didSomething |= setUidForeground(uidForeground);
didSomething |= setProcessFreezable(processFreezable);
didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null);
didSomething |= setProcessPersistent(app.isPersistent());
} else {
didSomething |= setUidCached(uidCached);
didSomething |= setUidForeground(false);
didSomething |= setProcessFreezable(false);
didSomething |= setProcessInstrumented(false);
didSomething |= setProcessPersistent(false);
}
@@ -450,13 +450,13 @@ class BroadcastProcessQueue {
}
/**
* Update if this process is in the "cached" state, typically signaling that
* Update if this process is in the "freezable" state, typically signaling that
* broadcast dispatch should be paused or delayed.
*/
@CheckResult
private boolean setUidCached(boolean uidCached) {
if (mUidCached != uidCached) {
mUidCached = uidCached;
private boolean setProcessFreezable(boolean freezable) {
if (mProcessFreezable != freezable) {
mProcessFreezable = freezable;
invalidateRunnableAt();
return true;
} else {
@@ -1115,7 +1115,7 @@ class BroadcastProcessQueue {
} else if (mCountManifest > 0) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_MANIFEST;
} else if (mUidCached) {
} else if (mProcessFreezable) {
if (r.deferUntilActive) {
// All enqueued broadcasts are deferrable, defer
if (mCountDeferred == mCountEnqueued) {
@@ -1185,7 +1185,7 @@ class BroadcastProcessQueue {
// When all we have pending is deferred broadcasts, and we're cached,
// then we want everything to be marked deferred
final boolean wantDeferredStates = (mCountDeferred > 0)
&& (mCountDeferred == mCountEnqueued) && mUidCached;
&& (mCountDeferred == mCountEnqueued) && mProcessFreezable;
if (mLastDeferredStates != wantDeferredStates) {
mLastDeferredStates = wantDeferredStates;
@@ -1372,9 +1372,9 @@ class BroadcastProcessQueue {
if (mUidForeground) {
sb.append("FG");
}
if (mUidCached) {
if (mProcessFreezable) {
if (sb.length() > 0) sb.append("|");
sb.append("CACHED");
sb.append("FRZN");
}
if (mProcessInstrumented) {
if (sb.length() > 0) sb.append("|");

View File

@@ -172,6 +172,13 @@ public abstract class BroadcastQueue {
@GuardedBy("mService")
public abstract void onApplicationCleanupLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process is in a freezable state and will be
* freezed soon after.
*/
@GuardedBy("mService")
public abstract void onProcessFreezableChangedLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given package (or some subset of that
* package) has been disabled or uninstalled, and that any pending

View File

@@ -449,6 +449,10 @@ public class BroadcastQueueImpl extends BroadcastQueue {
skipCurrentOrPendingReceiverLocked(app);
}
public void onProcessFreezableChangedLocked(ProcessRecord app) {
// Not supported; ignore
}
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast;

View File

@@ -223,16 +223,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
@GuardedBy("mService")
private final SparseBooleanArray mUidForeground = new SparseBooleanArray();
/**
* Map from UID to its last known "cached" state.
* <p>
* We manually maintain this data structure since the lifecycle of
* {@link ProcessRecord} and {@link BroadcastProcessQueue} can be
* mismatched.
*/
@GuardedBy("mService")
private final SparseBooleanArray mUidCached = new SparseBooleanArray();
private final BroadcastConstants mConstants;
private final BroadcastConstants mFgConstants;
private final BroadcastConstants mBgConstants;
@@ -574,6 +564,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
}
@Override
public void onProcessFreezableChangedLocked(@NonNull ProcessRecord app) {
synchronized (mService) {
refreshProcessQueueLocked(app);
}
}
@Override
public int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app) {
final BroadcastProcessQueue queue = getProcessQueue(app);
@@ -1305,7 +1302,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
};
broadcastPredicate = BROADCAST_PREDICATE_ANY;
cleanupUserStateLocked(mUidCached, userId);
cleanupUserStateLocked(mUidForeground, userId);
}
return forEachMatchingBroadcast(queuePredicate, broadcastPredicate,
@@ -1448,20 +1444,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
refreshProcessQueuesLocked(uid);
}
}
@Override
public void onUidCachedChanged(int uid, boolean cached) {
synchronized (mService) {
if (cached) {
mUidCached.put(uid, true);
} else {
mUidCached.delete(uid);
}
refreshProcessQueuesLocked(uid);
}
}
}, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CACHED,
ActivityManager.PROCESS_STATE_TOP, "android");
}, ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_TOP, "android");
// Kick off periodic health checks
mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH);
@@ -1650,10 +1633,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// warm via this operation, we're going to immediately promote it to
// be running, and any side effect of this operation will then apply
// after it's finished and is returned to the runnable list.
queue.setProcessAndUidState(
mService.getProcessRecordLocked(queue.processName, queue.uid),
mUidForeground.get(queue.uid, false),
mUidCached.get(queue.uid, false));
final ProcessRecord app = mService.getProcessRecordLocked(queue.processName, queue.uid);
queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false),
isProcessFreezable(app));
}
}
@@ -1665,11 +1647,21 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
private void setQueueProcess(@NonNull BroadcastProcessQueue queue,
@Nullable ProcessRecord app) {
if (queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false),
mUidCached.get(queue.uid, false))) {
isProcessFreezable(app))) {
updateRunnableList(queue);
}
}
@GuardedBy("mService")
private boolean isProcessFreezable(@Nullable ProcessRecord app) {
if (app == null) {
return false;
}
synchronized (mService.mProcLock) {
return app.mOptRecord.isPendingFreeze() || app.mOptRecord.isFrozen();
}
}
/**
* Refresh the process queues with the latest process state so that runnableAt
* can be updated.
@@ -1686,6 +1678,20 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
enqueueUpdateRunningList();
}
/**
* Refresh the process queue corresponding to {@code app} with the latest process state
* so that runnableAt can be updated.
*/
@GuardedBy("mService")
private void refreshProcessQueueLocked(@NonNull ProcessRecord app) {
final BroadcastProcessQueue queue = getProcessQueue(app.processName, app.uid);
if (queue == null || queue.app == null || queue.app.getPid() != app.getPid()) {
return;
}
setQueueProcess(queue, queue.app);
enqueueUpdateRunningList();
}
/**
* Inform other parts of OS that the given broadcast queue has started
* running, typically for internal bookkeeping.
@@ -2005,12 +2011,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
ipw.decreaseIndent();
ipw.println();
ipw.println("Cached UIDs:");
ipw.increaseIndent();
ipw.println(mUidCached);
ipw.decreaseIndent();
ipw.println();
ipw.println("Foreground UIDs:");
ipw.increaseIndent();
ipw.println(mUidForeground);

View File

@@ -300,6 +300,7 @@ public final class CachedAppOptimizer {
static final int COMPACT_NATIVE_MSG = 5;
static final int UID_FROZEN_STATE_CHANGED_MSG = 6;
static final int DEADLOCK_WATCHDOG_MSG = 7;
static final int REPORT_PROCESS_FREEZABLE_CHANGED = 8;
// When free swap falls below this percentage threshold any full (file + anon)
// compactions will be downgraded to file only compactions to reduce pressure
@@ -1352,6 +1353,7 @@ public final class CachedAppOptimizer {
}
}
}
postProcessFreezableChangedMessage(app);
mFreezeHandler.sendMessageDelayed(
mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
delayMillis);
@@ -1389,6 +1391,7 @@ public final class CachedAppOptimizer {
uidRec.setFrozen(false);
postUidFrozenMessage(uidRec.getUid(), false);
}
postProcessFreezableChangedMessage(app);
opt.setFreezerOverride(false);
if (pid == 0 || !opt.isFrozen()) {
@@ -2066,6 +2069,21 @@ public final class CachedAppOptimizer {
0, uidObj));
}
private void reportProcessFreezableChanged(ProcessRecord app) {
synchronized (mAm) {
mAm.onProcessFreezableChangedLocked(app);
}
}
@GuardedBy("mAm")
private void postProcessFreezableChangedMessage(ProcessRecord app) {
if (app.getPid() == 0) {
return;
}
mFreezeHandler.obtainMessage(REPORT_PROCESS_FREEZABLE_CHANGED, 0, 0, app)
.sendToTarget();
}
private final class FreezeHandler extends Handler implements
ProcLocksReader.ProcLocksReaderCallback {
private FreezeHandler() {
@@ -2075,7 +2093,7 @@ public final class CachedAppOptimizer {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SET_FROZEN_PROCESS_MSG:
case SET_FROZEN_PROCESS_MSG: {
ProcessRecord proc = (ProcessRecord) msg.obj;
synchronized (mAm) {
freezeProcess(proc);
@@ -2085,8 +2103,8 @@ public final class CachedAppOptimizer {
removeMessages(DEADLOCK_WATCHDOG_MSG);
sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS);
}
break;
case REPORT_UNFREEZE_MSG:
} break;
case REPORT_UNFREEZE_MSG: {
int pid = msg.arg1;
int frozenDuration = msg.arg2;
Pair<String, Integer> obj = (Pair<String, Integer>) msg.obj;
@@ -2094,13 +2112,13 @@ public final class CachedAppOptimizer {
int reason = obj.second;
reportUnfreeze(pid, frozenDuration, processName, reason);
break;
case UID_FROZEN_STATE_CHANGED_MSG:
} break;
case UID_FROZEN_STATE_CHANGED_MSG: {
final boolean frozen = (msg.arg1 == 1);
final int uid = (int) msg.obj;
reportOneUidFrozenStateChanged(uid, frozen);
break;
case DEADLOCK_WATCHDOG_MSG:
} break;
case DEADLOCK_WATCHDOG_MSG: {
try {
// post-check to prevent deadlock
if (DEBUG_FREEZER) {
@@ -2110,7 +2128,11 @@ public final class CachedAppOptimizer {
} catch (IOException e) {
Slog.w(TAG_AM, "Unable to check file locks");
}
break;
} break;
case REPORT_PROCESS_FREEZABLE_CHANGED: {
final ProcessRecord app = (ProcessRecord) msg.obj;
reportProcessFreezableChanged(app);
} break;
default:
return;
}

View File

@@ -1192,6 +1192,9 @@ class ProcessRecord implements WindowProcessListener {
"Killing " + toShortString() + " (adj " + mState.getSetAdj()
+ "): " + reason, info.uid);
}
// Since the process is getting killed, reset the freezable related state.
mOptRecord.setPendingFreeze(false);
mOptRecord.setFrozen(false);
if (mPid > 0) {
mService.mProcessList.noteAppKill(this, reasonCode, subReason, description);
EventLog.writeEvent(EventLogTags.AM_KILL,

View File

@@ -393,9 +393,9 @@ public final class BroadcastQueueModernImplTest {
List.of(makeMockRegisteredReceiver()), false);
enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
queue.setProcessAndUidState(null, false, false);
queue.setProcessAndUidState(mProcess, false, false);
final long notCachedRunnableAt = queue.getRunnableAt();
queue.setProcessAndUidState(null, false, true);
queue.setProcessAndUidState(mProcess, false, true);
final long cachedRunnableAt = queue.getRunnableAt();
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertFalse(queue.isRunnable());
@@ -420,9 +420,9 @@ public final class BroadcastQueueModernImplTest {
List.of(makeMockRegisteredReceiver()), false);
enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
queue.setProcessAndUidState(null, false, false);
queue.setProcessAndUidState(mProcess, false, false);
final long notCachedRunnableAt = queue.getRunnableAt();
queue.setProcessAndUidState(null, false, true);
queue.setProcessAndUidState(mProcess, false, true);
final long cachedRunnableAt = queue.getRunnableAt();
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertTrue(queue.isRunnable());

View File

@@ -399,6 +399,12 @@ public class BroadcastQueueTest {
UserHandle.USER_SYSTEM);
}
private ProcessRecord makeActiveProcessRecord(String packageName, String processName)
throws Exception {
return makeActiveProcessRecord(packageName, processName, ProcessBehavior.NORMAL,
UserHandle.USER_SYSTEM);
}
private ProcessRecord makeActiveProcessRecord(String packageName,
ProcessBehavior behavior) throws Exception {
return makeActiveProcessRecord(packageName, packageName, behavior, UserHandle.USER_SYSTEM);
@@ -596,6 +602,11 @@ public class BroadcastQueueTest {
BackgroundStartPrivileges.NONE, false, null);
}
private void setProcessFreezable(ProcessRecord app, boolean pendingFreeze, boolean frozen) {
app.mOptRecord.setPendingFreeze(pendingFreeze);
app.mOptRecord.setFrozen(frozen);
}
private void assertHealth() {
if (mImpl == Impl.MODERN) {
// If this fails, it'll throw a clear reason message
@@ -1659,8 +1670,10 @@ public class BroadcastQueueTest {
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE);
mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true);
mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true);
setProcessFreezable(receiverGreenApp, true, false);
mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
setProcessFreezable(receiverBlueApp, false, true);
mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -1704,12 +1717,14 @@ public class BroadcastQueueTest {
eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
// Shift blue to be active and confirm that deferred broadcast is delivered
mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false);
setProcessFreezable(receiverBlueApp, false, false);
mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
// Shift green to be active and confirm that deferred broadcast is delivered
mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
setProcessFreezable(receiverGreenApp, false, false);
mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
}
@@ -2106,9 +2121,12 @@ public class BroadcastQueueTest {
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true);
mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true);
mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_YELLOW), false);
setProcessFreezable(receiverGreenApp, true, true);
mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
setProcessFreezable(receiverBlueApp, true, false);
mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
setProcessFreezable(receiverYellowApp, false, false);
mQueue.onProcessFreezableChangedLocked(receiverYellowApp);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -2131,11 +2149,50 @@ public class BroadcastQueueTest {
verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
// Shift green to be active and confirm that deferred broadcast is delivered
mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
setProcessFreezable(receiverGreenApp, false, false);
mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
}
/**
* Verify broadcasts to a runtime receiver in cached process is deferred even when a different
* process in the same package is not cached.
*/
@Test
public void testDeferralPolicy_UntilActive_WithMultiProcessUid() throws Exception {
// Legacy stack doesn't support deferral
Assume.assumeTrue(mImpl == Impl.MODERN);
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final ProcessRecord receiverGreenApp1 = makeActiveProcessRecord(PACKAGE_GREEN);
final ProcessRecord receiverGreenApp2 = makeActiveProcessRecord(PACKAGE_GREEN,
PACKAGE_GREEN + "_proc2");
setProcessFreezable(receiverGreenApp1, true, true);
mQueue.onProcessFreezableChangedLocked(receiverGreenApp1);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final BroadcastOptions opts = BroadcastOptions.makeBasic()
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, opts,
List.of(makeRegisteredReceiver(receiverGreenApp1),
makeRegisteredReceiver(receiverGreenApp2))));
waitForIdle();
// 1st process in Green package is ignored since it is in a cached state
// but the 2nd process should still receive the broadcast.
verifyScheduleRegisteredReceiver(never(), receiverGreenApp1, airplane);
verifyScheduleRegisteredReceiver(times(1), receiverGreenApp2, airplane);
// Shift the 1st process in Green package to be active and confirm that deferred broadcast
// is delivered
setProcessFreezable(receiverGreenApp1, false, false);
mQueue.onProcessFreezableChangedLocked(receiverGreenApp1);
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverGreenApp1, airplane);
}
@Test
public void testBroadcastDelivery_uidForeground() throws Exception {
// Legacy stack doesn't support prioritization to foreground app.