diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index b8fb9e85ac08b..37bf665b2a96a 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -185,6 +185,12 @@ public final class AppRestrictionController { */ private static final boolean ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER = true; + /** + * Whether or not to show the action to the foreground service manager when + * posting the notification for background restriction. + */ + private static final boolean ENABLE_SHOW_FGS_MANAGER_ACTION_ON_BG_RESTRICTION = false; + private final Context mContext; private final HandlerThread mBgHandlerThread; private final BgHandler mBgHandler; @@ -664,6 +670,14 @@ public final class AppRestrictionController { static final String KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED = DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_with_noti_to_bg_restricted"; + /** + * The behavior for an app with a FGS and its notification is still showing, when the system + * detects it's running for a very long time, should we prompt the user. + * {@code true} - we'll show the prompt to user, {@code false} - we'll not show it. + */ + static final String KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING = + DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_with_noti_on_long_running"; + /** * The list of packages to be exempted from all these background restrictions. */ @@ -685,6 +699,11 @@ public final class AppRestrictionController { */ static final long DEFAULT_BG_LONG_FGS_NOTIFICATION_MINIMAL_INTERVAL_MS = 30 * ONE_DAY; + /** + * Default value to {@link #mBgPromptFgsWithNotiOnLongRunning}. + */ + static final boolean DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING = false; + /** * Default value to {@link #mBgPromptFgsWithNotiToBgRestricted}. */ @@ -710,6 +729,11 @@ public final class AppRestrictionController { */ volatile boolean mBgPromptFgsWithNotiToBgRestricted; + /** + * @see #KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING. + */ + volatile boolean mBgPromptFgsWithNotiOnLongRunning; + ConstantsObserver(Handler handler, Context context) { super(handler); mDefaultBgPromptFgsWithNotiToBgRestricted = context.getResources().getBoolean( @@ -735,6 +759,9 @@ public final class AppRestrictionController { case KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED: updateBgPromptFgsWithNotiToBgRestricted(); break; + case KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING: + updateBgPromptFgsWithNotiOnLongRunning(); + break; case KEY_BG_RESTRICTION_EXEMPTED_PACKAGES: updateBgRestrictionExemptedPackages(); break; @@ -771,6 +798,7 @@ public final class AppRestrictionController { updateBgAbusiveNotificationMinimalInterval(); updateBgLongFgsNotificationMinimalInterval(); updateBgPromptFgsWithNotiToBgRestricted(); + updateBgPromptFgsWithNotiOnLongRunning(); updateBgRestrictionExemptedPackages(); } @@ -806,6 +834,13 @@ public final class AppRestrictionController { mDefaultBgPromptFgsWithNotiToBgRestricted); } + private void updateBgPromptFgsWithNotiOnLongRunning() { + mBgPromptFgsWithNotiOnLongRunning = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING, + DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING); + } + private void updateBgRestrictionExemptedPackages() { final String settings = DeviceConfig.getString( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -1685,7 +1720,9 @@ public final class AppRestrictionController { return; } } - if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER && hasForegroundServices) { + if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER + && ENABLE_SHOW_FGS_MANAGER_ACTION_ON_BG_RESTRICTION + && hasForegroundServices) { final Intent trampoline = new Intent(ACTION_FGS_MANAGER_TRAMPOLINE); trampoline.setPackage("android"); trampoline.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); @@ -1710,6 +1747,13 @@ public final class AppRestrictionController { void postLongRunningFgsIfNecessary(String packageName, int uid) { PendingIntent pendingIntent; + if (!mBgController.mConstantsObserver.mBgPromptFgsWithNotiOnLongRunning + && mBgController.hasForegroundServiceNotifications(packageName, uid)) { + if (DEBUG_BG_RESTRICTION_CONTROLLER) { + Slog.i(TAG, "Not prompt long-running due to FGS with notification"); + } + return; + } if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER) { final Intent intent = new Intent(ACTION_SHOW_FOREGROUND_SERVICE_MANAGER); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index 47d4c8d9af466..800558b28cee7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -955,6 +955,8 @@ public final class BackgroundRestrictionTest { final int testUid2 = UserHandle.getUid(testUser2, TEST_PACKAGE_APPID_BASE + testPkgIndex2); final int testPid2 = 1235; + final int fgsNotificationId = 1000; + final long windowMs = 2_000; final long thresholdMs = 1_000; final long shortMs = 100; @@ -962,6 +964,7 @@ public final class BackgroundRestrictionTest { DeviceConfigSession longRunningFGSMonitor = null; DeviceConfigSession longRunningFGSWindow = null; DeviceConfigSession longRunningFGSThreshold = null; + DeviceConfigSession longRunningFGSWithNotification = null; try { longRunningFGSMonitor = new DeviceConfigSession<>( @@ -985,6 +988,13 @@ public final class BackgroundRestrictionTest { AppFGSPolicy.DEFAULT_BG_FGS_LONG_RUNNING_THRESHOLD); longRunningFGSThreshold.set(thresholdMs); + longRunningFGSWithNotification = new DeviceConfigSession<>( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + ConstantsObserver.KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING, + DeviceConfig::getBoolean, + ConstantsObserver.DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING); + longRunningFGSWithNotification.set(true); + // Basic case mAppFGSTracker.onForegroundServiceStateChanged(testPkgName1, testUid1, testPid1, true); @@ -1084,10 +1094,42 @@ public final class BackgroundRestrictionTest { mAppFGSTracker.onForegroundServiceStateChanged(testPkgName1, testUid1, testPid1, false); checkNotificationGone(testPkgName1, timeout(windowMs), notificationId); + + // Start over with the flag to not show prompt when it has an active notification. + clearInvocations(mInjector.getNotificationManager()); + mBgRestrictionController.resetRestrictionSettings(); + longRunningFGSWithNotification.set(false); + + // Start an FGS with notification. + mAppFGSTracker.onForegroundServiceStateChanged(testPkgName1, testUid1, + testPid1, true); + mAppFGSTracker.onForegroundServiceNotificationUpdated( + testPkgName1, testUid1, fgsNotificationId); + mAppFGSTracker.mNotificationListener.onNotificationPosted(new StatusBarNotification( + testPkgName1, null, fgsNotificationId, null, testUid1, testPid1, + new Notification(), UserHandle.of(testUser1), null, mCurrentTimeMillis), null); + + // Verify we won't prompt the user because it has a visible FGS notification. + checkNotificationShown( + new String[] {testPkgName1}, timeout(windowMs * 2).times(0), false); + + // Pretend we have the notification dismissed. + mAppFGSTracker.onForegroundServiceNotificationUpdated( + testPkgName1, testUid1, -fgsNotificationId); + + // Verify we have the notification. + notificationId = checkNotificationShown( + new String[] {testPkgName1}, timeout(windowMs * 2).times(2), true)[0]; + + // Stop the FGS. + mAppFGSTracker.onForegroundServiceStateChanged(testPkgName1, testUid1, + testPid1, false); + checkNotificationGone(testPkgName1, timeout(windowMs), notificationId); } finally { closeIfNotNull(longRunningFGSMonitor); closeIfNotNull(longRunningFGSWindow); closeIfNotNull(longRunningFGSThreshold); + closeIfNotNull(longRunningFGSWithNotification); } } @@ -1113,6 +1155,7 @@ public final class BackgroundRestrictionTest { DeviceConfigSession longRunningFGSThreshold = null; DeviceConfigSession mediaPlaybackFGSThreshold = null; DeviceConfigSession locationFGSThreshold = null; + DeviceConfigSession longRunningFGSWithNotification = null; doReturn(testPkgName1).when(mInjector).getPackageName(testPid1); doReturn(testPkgName2).when(mInjector).getPackageName(testPid2); @@ -1153,6 +1196,13 @@ public final class BackgroundRestrictionTest { AppFGSPolicy.DEFAULT_BG_FGS_LOCATION_THRESHOLD); locationFGSThreshold.set(thresholdMs); + longRunningFGSWithNotification = new DeviceConfigSession<>( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + ConstantsObserver.KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING, + DeviceConfig::getBoolean, + ConstantsObserver.DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING); + longRunningFGSWithNotification.set(true); + // Long-running FGS with type "location", but ran for a very short time. runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1, FOREGROUND_SERVICE_TYPE_LOCATION, 0, null, OP_NONE, null, null, @@ -1260,6 +1310,7 @@ public final class BackgroundRestrictionTest { closeIfNotNull(longRunningFGSThreshold); closeIfNotNull(mediaPlaybackFGSThreshold); closeIfNotNull(locationFGSThreshold); + closeIfNotNull(longRunningFGSWithNotification); } }