Improve test flakiness around FGS notifications

Make sure that even transient package-associated state is cleared when
the package is uninstalled, and provide a testing hook for instrumenting
global rate limits appropriately to high-rate test sequences.

Bug: 190019349
Test: atest CtsAppTestCases:android.app.cts.ServiceTest
Test: atest CtsAppTestCases:android.app.cts.NotificationManagerTest
Change-Id: Ic38157c6f21bcf09bcf74de591e41dc64f7bf0f7
This commit is contained in:
Christopher Tate
2021-06-03 09:30:03 -07:00
parent cec02064d4
commit 9396c029ac
4 changed files with 88 additions and 2 deletions

View File

@@ -685,6 +685,14 @@ interface IActivityManager {
*/
boolean enableAppFreezer(in boolean enable);
/**
* Suppress or reenable the rate limit on foreground service notification deferral.
* This is for use within CTS and is protected by android.permission.WRITE_DEVICE_CONFIG.
*
* @param enable false to suppress rate-limit policy; true to reenable it.
*/
boolean enableFgsNotificationRateLimit(in boolean enable);
/**
* Holds the AM lock for the specified amount of milliseconds.
* This is intended for use by the tests that need to imitate lock contention.

View File

@@ -249,6 +249,14 @@ public final class ActiveServices {
*/
final ArrayList<ServiceRecord> mPendingFgsNotifications = new ArrayList<>();
/**
* Whether there is a rate limit that suppresses immediate re-deferral of new FGS
* notifications from each app. On by default, disabled only by shell command for
* test-suite purposes. To disable the behavior more generally, use the usual
* DeviceConfig mechanism to set the rate limit interval to zero.
*/
private boolean mFgsDeferralRateLimited = true;
/**
* Uptime at which a given uid becomes eliglible again for FGS notification deferral
*/
@@ -2142,8 +2150,10 @@ public final class ActiveServices {
}
}
final long nextEligible = when + mAm.mConstants.mFgsNotificationDeferralExclusionTime;
mFgsDeferralEligible.put(uid, nextEligible);
if (mFgsDeferralRateLimited) {
final long nextEligible = when + mAm.mConstants.mFgsNotificationDeferralExclusionTime;
mFgsDeferralEligible.put(uid, nextEligible);
}
r.fgDisplayTime = when;
r.mFgsNotificationDeferred = true;
r.mFgsNotificationShown = false;
@@ -2204,6 +2214,38 @@ public final class ActiveServices {
}
};
/**
* Suppress or reenable the rate limit on foreground service notification deferral.
* Invoked from the activity manager shell command.
*
* @param enable false to suppress rate-limit policy; true to reenable it.
*/
boolean enableFgsNotificationRateLimitLocked(final boolean enable) {
if (enable != mFgsDeferralRateLimited) {
mFgsDeferralRateLimited = enable;
if (!enable) {
// make sure to reset any active rate limiting
mFgsDeferralEligible.clear();
}
}
return enable;
}
private void removeServiceNotificationDeferralsLocked(String packageName,
final @UserIdInt int userId) {
for (int i = mPendingFgsNotifications.size() - 1; i >= 0; i--) {
final ServiceRecord r = mPendingFgsNotifications.get(i);
if (userId == r.userId
&& r.appInfo.packageName.equals(packageName)) {
mPendingFgsNotifications.remove(i);
if (DEBUG_FOREGROUND_SERVICE) {
Slog.d(TAG_SERVICE, "Removing notification deferral for "
+ r);
}
}
}
}
private void maybeLogFGSStateEnteredLocked(ServiceRecord r) {
if (r.mLogEntering) {
logFGSStateChangeLocked(r,
@@ -4690,6 +4732,7 @@ public final class ActiveServices {
}
}
removeServiceRestartBackoffEnabledLocked(packageName);
removeServiceNotificationDeferralsLocked(packageName, userId);
}
void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {

View File

@@ -17065,6 +17065,19 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
/**
* Suppress or reenable the rate limit on foreground service notification deferral.
* @param enable false to suppress rate-limit policy; true to reenable it.
*/
@Override
public boolean enableFgsNotificationRateLimit(boolean enable) {
enforceCallingPermission(permission.WRITE_DEVICE_CONFIG,
"enableFgsNotificationRateLimit");
synchronized (this) {
return mServices.enableFgsNotificationRateLimitLocked(enable);
}
}
/**
* Holds the AM lock for the specified amount of milliseconds.
* Intended for use by the tests that need to imitate lock contention.

View File

@@ -230,6 +230,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runBugReport(pw);
case "force-stop":
return runForceStop(pw);
case "fgs-notification-rate-limit":
return runFgsNotificationRateLimit(pw);
case "crash":
return runCrash(pw);
case "kill":
@@ -1103,6 +1105,24 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
int runFgsNotificationRateLimit(PrintWriter pw) throws RemoteException {
final String toggleValue = getNextArgRequired();
final boolean enable;
switch (toggleValue) {
case "enable":
enable = true;
break;
case "disable":
enable = false;
break;
default:
throw new IllegalArgumentException(
"Argument must be either 'enable' or 'disable'");
}
mInterface.enableFgsNotificationRateLimit(enable);
return 0;
}
int runCrash(PrintWriter pw) throws RemoteException {
int userId = UserHandle.USER_ALL;
@@ -3303,6 +3323,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" when done to select where it should be delivered. Options are:");
pw.println(" --progress: will launch a notification right away to show its progress.");
pw.println(" --telephony: will dump only telephony sections.");
pw.println(" fgs-notification-rate-limit {enable | disable}");
pw.println(" Enable/disable rate limit on FGS notification deferral policy.");
pw.println(" force-stop [--user <USER_ID> | all | current] <PACKAGE>");
pw.println(" Completely stop the given application package.");
pw.println(" crash [--user <USER_ID>] <PACKAGE|PID>");