diff options
5 files changed, 211 insertions, 9 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 9bb16ae7fa02..a6c1a57a8131 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -270,4 +270,6 @@ interface INotificationManager int[] getAllowedAdjustmentKeyTypes(); void setAssistantAdjustmentKeyTypeState(int type, boolean enabled); + String[] getTypeAdjustmentDeniedPackages(); + void setTypeAdjustmentForPackageState(String pkg, boolean enabled); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index c310f95814dc..87c861912036 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1868,6 +1868,19 @@ public class NotificationManager { /** * @hide */ + @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public void setTypeAdjustmentForPackageState(@NonNull String pkg, boolean enabled) { + INotificationManager service = getService(); + try { + service.setTypeAdjustmentForPackageState(pkg, enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ public List<String> getEnabledNotificationListenerPackages() { INotificationManager service = getService(); try { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 5182dfe60fcc..d2b2fad8aa35 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -26,6 +26,7 @@ import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.OP_RECEIVE_SENSITIVE_NOTIFICATIONS; import static android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR; import static android.app.Flags.lifetimeExtensionRefactor; +import static android.app.Flags.notificationClassificationUi; import static android.app.Flags.sortSectionByTime; import static android.app.Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; import static android.app.Notification.EXTRA_BUILDER_APPLICATION_INFO; @@ -4225,6 +4226,22 @@ public class NotificationManagerService extends SystemService { } @Override + @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public @NonNull String[] getTypeAdjustmentDeniedPackages() { + checkCallerIsSystemOrSystemUiOrShell(); + return mAssistants.getTypeAdjustmentDeniedPackages(); + } + + @Override + @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) { + checkCallerIsSystemOrSystemUiOrShell(); + mAssistants.setTypeAdjustmentForPackageState(pkg, enabled); + + handleSavePolicyFile(); + } + + @Override @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING) public boolean appCanBePromoted(String pkg, int uid) { checkCallerIsSystemOrSystemUiOrShell(); @@ -7006,6 +7023,10 @@ public class NotificationManagerService extends SystemService { if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) { if (!mAssistants.isAdjustmentKeyTypeAllowed(adjustments.getInt(KEY_TYPE))) { toRemove.add(potentialKey); + } else if (notificationClassificationUi() + && !mAssistants.isTypeAdjustmentAllowedForPackage( + r.getSbn().getPackageName())) { + toRemove.add(potentialKey); } } } @@ -11643,6 +11664,7 @@ public class NotificationManagerService extends SystemService { private static final String ATT_DENIED = "denied_adjustments"; private static final String ATT_ENABLED_TYPES = "enabled_key_types"; private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments"; + private static final String ATT_TYPES_DENIED_APPS = "types_denied_apps"; private final Object mLock = new Object(); @@ -11658,6 +11680,9 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mLock") private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>(); + @GuardedBy("mLock") + private Set<String> mClassificationTypeDeniedPackages = new ArraySet<>(); + protected ComponentName mDefaultFromConfig = null; @Override @@ -11857,6 +11882,44 @@ public class NotificationManagerService extends SystemService { } } + @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + protected @NonNull boolean isTypeAdjustmentAllowedForPackage(String pkg) { + synchronized (mLock) { + if (notificationClassificationUi()) { + return !mClassificationTypeDeniedPackages.contains(pkg); + } + } + return true; + } + + @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + protected @NonNull String[] getTypeAdjustmentDeniedPackages() { + synchronized (mLock) { + if (notificationClassificationUi()) { + return mClassificationTypeDeniedPackages.toArray(new String[0]); + } + } + return new String[]{}; + } + + /** + * Set whether a particular package can have its notification channels adjusted to have a + * different type by NotificationAssistants. + */ + @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) { + if (!notificationClassificationUi()) { + return; + } + synchronized (mLock) { + if (enabled) { + mClassificationTypeDeniedPackages.remove(pkg); + } else { + mClassificationTypeDeniedPackages.add(pkg); + } + } + } + protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) { for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { ArrayList<String> keys = new ArrayList<>(records.size()); @@ -12315,6 +12378,12 @@ public class NotificationManagerService extends SystemService { out.attribute(null, ATT_TYPES, TextUtils.join(",", mAllowedAdjustmentKeyTypes)); out.endTag(null, ATT_ENABLED_TYPES); + if (notificationClassificationUi()) { + out.startTag(null, ATT_TYPES_DENIED_APPS); + out.attribute(null, ATT_TYPES, + TextUtils.join(",", mClassificationTypeDeniedPackages)); + out.endTag(null, ATT_TYPES_DENIED_APPS); + } } } @@ -12346,6 +12415,14 @@ public class NotificationManagerService extends SystemService { } } } + } else if (notificationClassificationUi() && ATT_TYPES_DENIED_APPS.equals(tag)) { + final String apps = XmlUtils.readStringAttribute(parser, ATT_TYPES); + synchronized (mLock) { + mClassificationTypeDeniedPackages.clear(); + if (!TextUtils.isEmpty(apps)) { + mClassificationTypeDeniedPackages.addAll(Arrays.asList(apps.split(","))); + } + } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index 9eddcc94e650..decbaacdcef9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -60,8 +60,6 @@ import android.testing.TestableContext; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; -import android.util.Log; -import android.util.Slog; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -729,4 +727,79 @@ public class NotificationAssistantsTest extends UiServiceTestCase { assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactly(TYPE_PROMOTION); } + + @Test + @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsAndDenies() { + // Given that a package is allowed to have its type adjusted, + String allowedPackage = "allowed.package"; + assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty(); + mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true); + + assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty(); + assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage)); + + // Set type adjustment disallowed for this package + mAssistants.setTypeAdjustmentForPackageState(allowedPackage, false); + + // Then the package is marked as denied + assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList() + .containsExactly(allowedPackage); + assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage)); + + // Set type adjustment allowed again + mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true); + + // Then the package is marked as allowed again + assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty(); + assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage)); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public void testSetAssistantAdjustmentKeyTypeStateForPackage_deniesMultiple() { + // Given packages not allowed to have their type adjusted, + String deniedPkg1 = "denied.Pkg1"; + String deniedPkg2 = "denied.Pkg2"; + String deniedPkg3 = "denied.Pkg3"; + // Set type adjustment disallowed for these packages + mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false); + mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, false); + mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false); + + // Then the packages are marked as denied + assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList() + .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg2, deniedPkg3)); + assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1)); + assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2)); + assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3)); + + // And when we re-allow one of them, + mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, true); + + // Then the rest of the original packages are still marked as denied. + assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList() + .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3)); + assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1)); + assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2)); + assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3)); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public void testSetAssistantAdjustmentKeyTypeStateForPackage_readWriteXml() throws Exception { + mAssistants.loadDefaultsFromConfig(true); + String deniedPkg1 = "denied.Pkg1"; + String allowedPkg2 = "allowed.Pkg2"; + String deniedPkg3 = "denied.Pkg3"; + // Set type adjustment disallowed or allowed for these packages + mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false); + mAssistants.setTypeAdjustmentForPackageState(allowedPkg2, true); + mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false); + + writeXmlAndReload(USER_ALL); + + assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList() + .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3)); + } }
\ No newline at end of file diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index e6b4bc98ea4c..9b37347e1161 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -232,7 +232,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.ModuleInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; @@ -337,12 +336,12 @@ import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; -import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; -import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; - import com.google.android.collect.Lists; import com.google.common.collect.ImmutableList; +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -361,9 +360,6 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import platform.test.runner.parameterized.ParameterizedAndroidJunit4; -import platform.test.runner.parameterized.Parameters; - import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; @@ -378,6 +374,9 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + @SmallTest @RunWith(ParameterizedAndroidJunit4.class) @RunWithLooper @@ -7534,6 +7533,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true); when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); // Set up notifications that will be adjusted final NotificationRecord r1 = spy(generateNotificationRecord( @@ -17142,6 +17142,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.WorkerHandler.class); mService.setHandler(handler); when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); Bundle signals = new Bundle(); signals.putInt(KEY_TYPE, TYPE_NEWS); @@ -17176,6 +17177,42 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION, + android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI}) + public void testApplyAdjustment_keyTypeForDisallowedPackage_DoesNotApply() throws Exception { + final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + mService.addNotification(r); + NotificationManagerService.WorkerHandler handler = mock( + NotificationManagerService.WorkerHandler.class); + mService.setHandler(handler); + when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); + + Bundle signals = new Bundle(); + signals.putInt(KEY_TYPE, TYPE_NEWS); + Adjustment adjustment = new Adjustment( + r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier()); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); + mBinderService.applyAdjustmentFromAssistant(null, adjustment); + + waitForIdle(); + + r.applyAdjustments(); + + assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID); + + // When we block adjustments for this package + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(false); + + signals.putInt(KEY_TYPE, TYPE_PROMOTION); + mBinderService.applyAdjustmentFromAssistant(null, adjustment); + waitForIdle(); + r.applyAdjustments(); + // Then the adjustment is not applied. + assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID); + } + + @Test @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING) public void testSetCanBePromoted_granted() throws Exception { // qualifying posted notification |