Loading core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -402,6 +402,7 @@ package android.app { method @FlaggedApi("android.service.notification.notification_classification") @NonNull public java.util.Set<java.lang.String> getUnsupportedAdjustmentTypes(); method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String); method @FlaggedApi("android.app.modes_api") public boolean removeAutomaticZenRule(@NonNull String, boolean); method @FlaggedApi("android.service.notification.notification_classification") public void setAssistantAdjustmentKeyTypeState(int, boolean); method @FlaggedApi("android.app.api_rich_ongoing") public void setCanPostPromotedNotifications(@NonNull String, int, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean); Loading core/java/android/app/INotificationManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -267,4 +267,7 @@ interface INotificationManager void setAdjustmentTypeSupportedState(in INotificationListener token, String key, boolean supported); List<String> getUnsupportedAdjustmentTypes(); int[] getAllowedAdjustmentKeyTypes(); void setAssistantAdjustmentKeyTypeState(int type, boolean enabled); } core/java/android/app/NotificationManager.java +14 −0 Original line number Diff line number Diff line Loading @@ -1845,6 +1845,20 @@ public class NotificationManager { } } /** * @hide */ @TestApi @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type, boolean enabled) { INotificationManager service = getService(); try { service.setAssistantAdjustmentKeyTypeState(type, enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * @hide */ Loading services/core/java/com/android/server/notification/NotificationManagerService.java +91 −6 Original line number Diff line number Diff line Loading @@ -474,6 +474,10 @@ public class NotificationManagerService extends SystemService { Adjustment.KEY_TYPE }; static final Integer[] DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES = new Integer[] { TYPE_PROMOTION }; static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] { RoleManager.ROLE_DIALER, RoleManager.ROLE_EMERGENCY Loading Loading @@ -4194,6 +4198,22 @@ public class NotificationManagerService extends SystemService { } } @Override @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public @NonNull int[] getAllowedAdjustmentKeyTypes() { checkCallerIsSystemOrSystemUiOrShell(); return mAssistants.getAllowedAdjustmentKeyTypes(); } @Override @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void setAssistantAdjustmentKeyTypeState(int type, boolean enabled) { checkCallerIsSystemOrSystemUiOrShell(); mAssistants.setAssistantAdjustmentKeyTypeState(type, enabled); handleSavePolicyFile(); } @Override @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING) public boolean appCanBePromoted(String pkg, int uid) { Loading Loading @@ -6983,12 +7003,16 @@ public class NotificationManagerService extends SystemService { if (!mAssistants.isAdjustmentAllowed(potentialKey)) { toRemove.add(potentialKey); } if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) { if (!mAssistants.isAdjustmentKeyTypeAllowed(adjustments.getInt(KEY_TYPE))) { toRemove.add(potentialKey); } } } for (String removeKey : toRemove) { adjustments.remove(removeKey); } if (android.service.notification.Flags.notificationClassification() && adjustments.containsKey(KEY_TYPE)) { if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) { final NotificationChannel newChannel = getClassificationChannelLocked(r, adjustments); if (newChannel == null || newChannel.getId().equals(r.getChannel().getId())) { Loading Loading @@ -11565,10 +11589,14 @@ public class NotificationManagerService extends SystemService { private static final String ATT_TYPES = "types"; 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 final Object mLock = new Object(); @GuardedBy("mLock") private Set<Integer> mAllowedAdjustmentKeyTypes = new ArraySet<>(); @GuardedBy("mLock") private Set<String> mAllowedAdjustments = new ArraySet<>(); Loading Loading @@ -11652,6 +11680,8 @@ public class NotificationManagerService extends SystemService { for (int i = 0; i < DEFAULT_ALLOWED_ADJUSTMENTS.length; i++) { mAllowedAdjustments.add(DEFAULT_ALLOWED_ADJUSTMENTS[i]); } } else { mAllowedAdjustmentKeyTypes.addAll(List.of(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES)); } } Loading Loading @@ -11739,6 +11769,42 @@ public class NotificationManagerService extends SystemService { } } @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) protected @NonNull boolean isAdjustmentKeyTypeAllowed(@Adjustment.Types int type) { synchronized (mLock) { if (notificationClassification()) { return mAllowedAdjustmentKeyTypes.contains(type); } } return false; } @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) protected @NonNull int[] getAllowedAdjustmentKeyTypes() { synchronized (mLock) { if (notificationClassification()) { return mAllowedAdjustmentKeyTypes.stream() .mapToInt(Integer::intValue).toArray(); } } return new int[]{}; } @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type, boolean enabled) { if (!android.service.notification.Flags.notificationClassification()) { return; } synchronized (mLock) { if (enabled) { mAllowedAdjustmentKeyTypes.add(type); } else { mAllowedAdjustmentKeyTypes.remove(type); } } } protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) { for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { ArrayList<String> keys = new ArrayList<>(records.size()); Loading Loading @@ -12178,27 +12244,46 @@ public class NotificationManagerService extends SystemService { @Override protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException { if (!android.service.notification.Flags.notificationClassification()) { if (!notificationClassification()) { return; } synchronized (mLock) { out.startTag(null, ATT_DENIED); out.attribute(null, ATT_TYPES, TextUtils.join(",", mDeniedAdjustments)); out.endTag(null, ATT_DENIED); out.startTag(null, ATT_ENABLED_TYPES); out.attribute(null, ATT_TYPES, TextUtils.join(",", mAllowedAdjustmentKeyTypes)); out.endTag(null, ATT_ENABLED_TYPES); } } @Override protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException { if (!android.service.notification.Flags.notificationClassification()) { if (!notificationClassification()) { return; } if (ATT_DENIED.equals(tag)) { final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES); final String keys = XmlUtils.readStringAttribute(parser, ATT_TYPES); synchronized (mLock) { mDeniedAdjustments.clear(); if (!TextUtils.isEmpty(keys)) { mDeniedAdjustments.addAll(Arrays.asList(keys.split(","))); } } } else if (ATT_ENABLED_TYPES.equals(tag)) { final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES); synchronized (mLock) { mAllowedAdjustmentKeyTypes.clear(); if (!TextUtils.isEmpty(types)) { mDeniedAdjustments.addAll(Arrays.asList(types.split(","))); List<String> typeList = Arrays.asList(types.split(",")); for (String type : typeList) { try { mAllowedAdjustmentKeyTypes.add(Integer.parseInt(type)); } catch (NumberFormatException e) { Slog.wtf(TAG, "Bad type specified", e); } } } } } services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +48 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ package com.android.server.notification; import static android.os.UserHandle.USER_ALL; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION; import static android.service.notification.Adjustment.TYPE_NEWS; import static android.service.notification.Adjustment.TYPE_PROMOTION; import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS; Loading Loading @@ -57,6 +60,8 @@ 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; Loading Loading @@ -680,4 +685,47 @@ public class NotificationAssistantsTest extends UiServiceTestCase { assertThat(mAssistants.getAllowedAssistantAdjustments()) .containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENTS); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testSetAssistantAdjustmentKeyTypeState_allow() { assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactly(TYPE_PROMOTION); mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactlyElementsIn(List.of(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION)); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testSetAssistantAdjustmentKeyTypeState_disallow() { mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).isEmpty(); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testDisallowAdjustmentKeyType_readWriteXml() throws Exception { mAssistants.loadDefaultsFromConfig(true); mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false); mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true); mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true); writeXmlAndReload(USER_ALL); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactlyElementsIn(List.of(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION)); } @Test public void testDefaultAllowedKeyAdjustments_readWriteXml() throws Exception { mAssistants.loadDefaultsFromConfig(true); writeXmlAndReload(USER_ALL); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactly(TYPE_PROMOTION); } } No newline at end of file Loading
core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -402,6 +402,7 @@ package android.app { method @FlaggedApi("android.service.notification.notification_classification") @NonNull public java.util.Set<java.lang.String> getUnsupportedAdjustmentTypes(); method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String); method @FlaggedApi("android.app.modes_api") public boolean removeAutomaticZenRule(@NonNull String, boolean); method @FlaggedApi("android.service.notification.notification_classification") public void setAssistantAdjustmentKeyTypeState(int, boolean); method @FlaggedApi("android.app.api_rich_ongoing") public void setCanPostPromotedNotifications(@NonNull String, int, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean); Loading
core/java/android/app/INotificationManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -267,4 +267,7 @@ interface INotificationManager void setAdjustmentTypeSupportedState(in INotificationListener token, String key, boolean supported); List<String> getUnsupportedAdjustmentTypes(); int[] getAllowedAdjustmentKeyTypes(); void setAssistantAdjustmentKeyTypeState(int type, boolean enabled); }
core/java/android/app/NotificationManager.java +14 −0 Original line number Diff line number Diff line Loading @@ -1845,6 +1845,20 @@ public class NotificationManager { } } /** * @hide */ @TestApi @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type, boolean enabled) { INotificationManager service = getService(); try { service.setAssistantAdjustmentKeyTypeState(type, enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * @hide */ Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +91 −6 Original line number Diff line number Diff line Loading @@ -474,6 +474,10 @@ public class NotificationManagerService extends SystemService { Adjustment.KEY_TYPE }; static final Integer[] DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES = new Integer[] { TYPE_PROMOTION }; static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] { RoleManager.ROLE_DIALER, RoleManager.ROLE_EMERGENCY Loading Loading @@ -4194,6 +4198,22 @@ public class NotificationManagerService extends SystemService { } } @Override @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public @NonNull int[] getAllowedAdjustmentKeyTypes() { checkCallerIsSystemOrSystemUiOrShell(); return mAssistants.getAllowedAdjustmentKeyTypes(); } @Override @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void setAssistantAdjustmentKeyTypeState(int type, boolean enabled) { checkCallerIsSystemOrSystemUiOrShell(); mAssistants.setAssistantAdjustmentKeyTypeState(type, enabled); handleSavePolicyFile(); } @Override @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING) public boolean appCanBePromoted(String pkg, int uid) { Loading Loading @@ -6983,12 +7003,16 @@ public class NotificationManagerService extends SystemService { if (!mAssistants.isAdjustmentAllowed(potentialKey)) { toRemove.add(potentialKey); } if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) { if (!mAssistants.isAdjustmentKeyTypeAllowed(adjustments.getInt(KEY_TYPE))) { toRemove.add(potentialKey); } } } for (String removeKey : toRemove) { adjustments.remove(removeKey); } if (android.service.notification.Flags.notificationClassification() && adjustments.containsKey(KEY_TYPE)) { if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) { final NotificationChannel newChannel = getClassificationChannelLocked(r, adjustments); if (newChannel == null || newChannel.getId().equals(r.getChannel().getId())) { Loading Loading @@ -11565,10 +11589,14 @@ public class NotificationManagerService extends SystemService { private static final String ATT_TYPES = "types"; 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 final Object mLock = new Object(); @GuardedBy("mLock") private Set<Integer> mAllowedAdjustmentKeyTypes = new ArraySet<>(); @GuardedBy("mLock") private Set<String> mAllowedAdjustments = new ArraySet<>(); Loading Loading @@ -11652,6 +11680,8 @@ public class NotificationManagerService extends SystemService { for (int i = 0; i < DEFAULT_ALLOWED_ADJUSTMENTS.length; i++) { mAllowedAdjustments.add(DEFAULT_ALLOWED_ADJUSTMENTS[i]); } } else { mAllowedAdjustmentKeyTypes.addAll(List.of(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES)); } } Loading Loading @@ -11739,6 +11769,42 @@ public class NotificationManagerService extends SystemService { } } @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) protected @NonNull boolean isAdjustmentKeyTypeAllowed(@Adjustment.Types int type) { synchronized (mLock) { if (notificationClassification()) { return mAllowedAdjustmentKeyTypes.contains(type); } } return false; } @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) protected @NonNull int[] getAllowedAdjustmentKeyTypes() { synchronized (mLock) { if (notificationClassification()) { return mAllowedAdjustmentKeyTypes.stream() .mapToInt(Integer::intValue).toArray(); } } return new int[]{}; } @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type, boolean enabled) { if (!android.service.notification.Flags.notificationClassification()) { return; } synchronized (mLock) { if (enabled) { mAllowedAdjustmentKeyTypes.add(type); } else { mAllowedAdjustmentKeyTypes.remove(type); } } } protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) { for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { ArrayList<String> keys = new ArrayList<>(records.size()); Loading Loading @@ -12178,27 +12244,46 @@ public class NotificationManagerService extends SystemService { @Override protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException { if (!android.service.notification.Flags.notificationClassification()) { if (!notificationClassification()) { return; } synchronized (mLock) { out.startTag(null, ATT_DENIED); out.attribute(null, ATT_TYPES, TextUtils.join(",", mDeniedAdjustments)); out.endTag(null, ATT_DENIED); out.startTag(null, ATT_ENABLED_TYPES); out.attribute(null, ATT_TYPES, TextUtils.join(",", mAllowedAdjustmentKeyTypes)); out.endTag(null, ATT_ENABLED_TYPES); } } @Override protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException { if (!android.service.notification.Flags.notificationClassification()) { if (!notificationClassification()) { return; } if (ATT_DENIED.equals(tag)) { final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES); final String keys = XmlUtils.readStringAttribute(parser, ATT_TYPES); synchronized (mLock) { mDeniedAdjustments.clear(); if (!TextUtils.isEmpty(keys)) { mDeniedAdjustments.addAll(Arrays.asList(keys.split(","))); } } } else if (ATT_ENABLED_TYPES.equals(tag)) { final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES); synchronized (mLock) { mAllowedAdjustmentKeyTypes.clear(); if (!TextUtils.isEmpty(types)) { mDeniedAdjustments.addAll(Arrays.asList(types.split(","))); List<String> typeList = Arrays.asList(types.split(",")); for (String type : typeList) { try { mAllowedAdjustmentKeyTypes.add(Integer.parseInt(type)); } catch (NumberFormatException e) { Slog.wtf(TAG, "Bad type specified", e); } } } } }
services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +48 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ package com.android.server.notification; import static android.os.UserHandle.USER_ALL; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION; import static android.service.notification.Adjustment.TYPE_NEWS; import static android.service.notification.Adjustment.TYPE_PROMOTION; import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS; Loading Loading @@ -57,6 +60,8 @@ 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; Loading Loading @@ -680,4 +685,47 @@ public class NotificationAssistantsTest extends UiServiceTestCase { assertThat(mAssistants.getAllowedAssistantAdjustments()) .containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENTS); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testSetAssistantAdjustmentKeyTypeState_allow() { assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactly(TYPE_PROMOTION); mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactlyElementsIn(List.of(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION)); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testSetAssistantAdjustmentKeyTypeState_disallow() { mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).isEmpty(); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testDisallowAdjustmentKeyType_readWriteXml() throws Exception { mAssistants.loadDefaultsFromConfig(true); mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false); mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true); mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true); writeXmlAndReload(USER_ALL); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactlyElementsIn(List.of(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION)); } @Test public void testDefaultAllowedKeyAdjustments_readWriteXml() throws Exception { mAssistants.loadDefaultsFromConfig(true); writeXmlAndReload(USER_ALL); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() .containsExactly(TYPE_PROMOTION); } } No newline at end of file